Solidity 进阶编程 | 注意一下合约中的细节

Posted 小生凡一

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Solidity 进阶编程 | 注意一下合约中的细节相关的知识,希望对你有一定的参考价值。

补充知识:

  1. 转账的话是与部署者无关,与交易者有关!
  2. gas是以太坊网络中的一个计量单位,是为了对算力消耗进行量化而设计的指标,也就是说有了gas这个计算单位,我们可以方便的计算出用户完成一笔交易需要支出多少gas费用,矿工完成一个区块的打包确认能收到多少gas报酬。

1. 内置的全局变量

  • msg.sender:

获取调用者地址,下面的例子是部署者调用了,所以是部署者的地址,
比如a账户部署,但是是使用b账户调用下getOwner的话,那么这个时候的owner就是b账户的地址了。

pragma solidity 0.4.22;
contract Owner {
    address public owner;
    function getOwner() public {
        owner = msg.sender;   // 获取调用者的地址
    }
}
  • msg.value:

可以接收payable修饰的转账金额,单位:wei,必须要有payable

可以用接收的value进行判断限制,比如转账金额必须在某个范围,balance做不到

pragma solidity 0.4.22;
contract Value {
    uint256 public money;
    function getValue() public payable {
        money = msg.value;
    }
}

如果加if判断:

pragma solidity 0.4.22;

contract Value {
    uint256 public money;
    function getValue() public payable {
        if( msg.value == 666) {  // 转钱金额为666wei才赋值 
            money = msg.value;
        }
    }
}

注意:需要转钱,注意单位默认是wei

内置的全局变量简称类型
blockhash哈希值byte32
block.coinbase当前块矿工的地址address
block.difficulty当前块的难度uint
block.gaslimi当前块的gaslimituint
block.number当前块的块号uint
block.timestamp当前块的时间戳uint
gasleft()剩余的gasuint
msg.sender调用者的地址address
msg.value转账的金额(单位:wei)uint
msg.data完整的调用数据calldata bytes
now当前块的时间戳uint(和block.timestamp相同)
tx.gasprice交易的gas价格uint
tx.origin交易的发送者address

2. 错误处理

  • require:
    require 式的异常不会消耗任何 gas,官方推荐使用require,相当于if {throw},没有else的
    最终底层触发的也是assert式的错误
require(msg.value == 666);    // 如果不满足则报错,否则继续往下执行

类似于下面的:注意条件正好是相反的

if (msg.value <= 6 * 10 ** 18)  {
    // todo 涉及到错误处理
    throw;
}
  • assert:
    即使有错误,也会执行并扣除gas
assert(msg.value == 666 );    // 如果不满足则报错,否则继续往下执行
  • revert():处理更复杂逻辑的场景,比如if/else
if (msg.value <= 6 * 10 ** 18){
    revert();
} else {
    b = 666;
}
  • require(条件); 如果满足条件则继续往下执行,如果不满足则报错,不扣除gas,推荐使用
  • assert(条件); assert扣除gas,不推荐使用
  • revert(): 负责逻辑中使用,if(条件){revert();}else{…}

3. 访问函数

原来要返回一个变量,得写个函数返回

pragma solidity 0.4.22;
contract Hello {
    string name = "hallen";
    function get_name() public view returns(string){
        return name;
    }
}

如果用public修饰了变量,会生成一个同名的访问函数,可以直接访问

pragma solidity 0.4.22;
contract Fan {
    string public name = "fanone";
    function getName() public view returns(string){
        return name;
        // return this.data(); 合约内部使用this也是要调用访问函数的
    }
}
contract One {
    function getHelloName() public view returns (string){
        Fan h = new Fan();   // 地址直接强转合约类型
        return h.name();    // 这里必须加括号,是个访问函数
    }
}

4. 创建合约

  • new : 返回的是地址,需要转合约类型
    初始化合约的时候用到这种方式
Fan h = new Fan();  // 把地址直接强转为合约类型

和java有点像
合约变量(Fan public h),此时是空的,需要赋值地址才能使用,否则报错
合约类型作为参数的场景用到这种方式

Fan public h;
// 函数中赋值
address addr = new Fan();
h = Fan(addr); // 赋值地址

转账语法:
h.get_money.value(20).gas(800)();

h必须是赋值地址后的合约对象

pragma solidity 0.4.24;
contract Test1{
    // 获取转账钱
    function contractGetMoney() public payable{
    }
    // 查看余额
    function getBalance() public view returns(uint256){
        return address(this).balance;
    }
}
contract Test2 {
    // 查看余额
    function getBalance() public view returns(uint256){
        return address(this).balance;
    }
    // 获取转账钱
    function contractGetMoney()public payable{
    }
    // transfer:谁调用就给谁转钱
    Test1 public t1;
    function getAddr(address addr) public{
        t1 = Test1(addr);
    }
    // 转钱给Test1合约
    function payToT1() public{
        t1.contract_get_money.value(5 * 10 **18).gas(200)();
    }
    // 付钱得用payable修饰,使用匿名函数
    function () public payable {}
}

5. 合约继承

  • 使用is关键字,多个父合约用逗号隔开
    contract 合约名 is 父合约1,父合约2,… {}
    constract Cat is Animail,Lactation{} // Lactation:哺乳动物
    如果两个父合约中有相同的函数,则遵循最远继承原则(继承顺序,Animail最近,Lactation最远,所以是Lactation中的)

6. 修饰器modifier

在函数执行前检查是否满足前置条件,满足条件才执行函数

pragma solidity 0.4.22;
contract Value {
    uint256 public money;
    modifier check_money(){
        require(msg.value == 10);
        _;  // 修饰的代码,指进入函数后的所有代码
    }
    function get_value() public payable check_money{  // check_money就是前面定义的修饰器
        money = msg.value;
    }
}

可以结合构造函数,判断是不是管理员(部署者),只有是管理员才可以访问的函数可以加修饰器

pragma solidity 0.4.22;
contract Value {
    address public owner;
    uint256 public money;
    constructor() public{
        owner = msg.sender;
    }
    modifier check_owner(){
        require(msg.sender == owner);
        _;   // 修饰的代码,指进入函数后的所有代码
    }
    function get_value() public payable check_money{
        money = msg.value;
    }
}

最后

小生凡一,期待你的关注。

以上是关于Solidity 进阶编程 | 注意一下合约中的细节的主要内容,如果未能解决你的问题,请参考以下文章

区块链-智能合约工程师第三篇:Solidity进阶

区块链-智能合约工程师第三篇:Solidity进阶

0基础学习solidity开发智能合约-初识solidity

Solidity编程 四 之 智能合约的结构

区块链开发之Solidity编程基础合约语句及函数修饰符

二solidity 基础进阶(2.1)—— library 库合约《实战NFT web3 solidity(新版本0.8.+)》