Solidity编程语言(13)--地址(Address)

这里要说的地址并不是通常的编程语言中的内存地址,而是交易地址。我们知道区块链进行价值传递的时候必须是通过一个地址传递到另一个地址,无论交易或者合约都离不开地址。因此把地址作Address为Solidity语言的内嵌数据类型会十分方便开发的,更重要的一点就是在Solidity中,所有合约都继承地址Address类型。这不仅仅是单纯的为了在语法上的实现,而在实际情况是合约本身就离不开地址,包括合约的部署和执行都需要地址的参与。
地址是两种类型,一种是address,一种是address payable。其中address payable相比address类型多了两个成员方法send和transfer,表示可以向address payable类型的变量进行转帐操作。

###地址类型格式要求
以太坊中的地址是20个字节,比如0x52908400098527886E0F7030069857D2E4169EE7,由于一个字节等于8位,所以地址也可以使用uint160来声明。地址通常可以进行比较运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pragma solidity >=0.4.0 <0.6.0;

contract EgAddress{

function testType() public {
address _addr = 0x52908400098527886E0F7030069857D2E4169EE7;
uint160 _addi = 471360049350540642640628028220064440840608208820;
string memory h = hex"52908400098527886E0F7030069857D2E4169EE7";
address addr = address(_addi);
//addr = address(h);
uint160 addi = uint160(_addr);

}

}
  1. 注意的是hex类型是不能直接和address进行转换的

  2. address虽然是十六进制串,address是有格式要求的,检验方法是可参考EIP55
    ###合约地址
    我们知道每个合约都是径过一个帐户部署到区块连上的,同时这个合约也会生成一个地址,并可以向这个合约地址进行转帐操作。那么这个部署的帐户地址和合约地址是怎么得到呢。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    pragma solidity >=0.4.0 <0.6.0;

    contract EgAddress{
    address public owner;

    constructor() public {
    owner = msg.sender;
    }

    function getOwner() public returns(address){
    return owner;
    }

    function getContractAddr() public view returns(address){
    return address(this);
    }

    function getSenderAddr() public returns (address){
    return msg.sender;
    }
    }
  3. msg.sender是内置方法,他返回当前方法的调用者地址,也就是说我通过A帐户调用合约方法getSenderAddr,则返回的是A的地址,通过B帐户调用合约方法getSenderAddr,则返回的是B的地址。(另外还有msg.value和msg.data都通过调用者传进来的数据)

  4. 构造函数constructor是在合约进行部署时执行,且只执行一次。

  5. 在获取合约的创建者则是通过“曲线”的方式获取,首先声明状态变量owner,接着在构造函数中将方法的调用者的地址赋值给owner,因为构造函数只有在合约创建时才会被调用,所以owner保存的就是合约创建者的地址,最后通过公有方法getOwner将状态变量的值返回出去。

  6. 在获取合约本身的地址是通过this来获取,旧的版本可能this本身就是address类型的,可以直接返回this,但本示例中的版本需要将this是EgAddress类型的,需要做个类型转换才能返回address类型。
    ###成员变量balance
    每个地址都代表着一个价值的持有者,所以可以直接查看其余额,即通过balance属性
    ###成员方法
    除了成员变量balance外,还有以下几个成员方法

    成员方法
    <address>.balance (uint256)
    <address payable>.transfer(uint256 amount)
    <address payable>.send(uint256 amount) returns (bool)
    <address>.call(bytes memory) returns (bool, bytes memory)
    <address>.delegatecall(bytes memory) returns (bool, bytes memory)
    <address>.staticcall(bytes memory) returns (bool, bytes memory)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
pragma solidity >=0.4.0 <0.6.0;

contract EgAddress{

function testSend()payable public {
address payable to = 0x52908400098527886E0F7030069857D2E4169EE7;
to.send(msg.value);
}

function testTranser()payable public {
address payable to = 0x52908400098527886E0F7030069857D2E4169EE7;
to.transfer(msg.value);
}

function testStack1024(uint depth) payable public{
address payable to = 0x52908400098527886E0F7030069857D2E4169EE7;
bool r = to.send(1 wei);
if (depth >1 && r){
testStack1024(depth -1);
}
}

/*function testCSend() payable public {
address payable to = address(this);
this.send(msg.value);
address(this).send(msg.value);
}*/

/*function testCTranser()payable public {
// address payable to = 0x52908400098527886E0F7030069857D2E4169EE7;
address(this).transfer(msg.value);
}*/
}
  1. send
  • send方法相比较transfer方法来说更“底层”一些,如果send方法执行失败,并不会抛出异常,而是返回false。
  • send调用栈深度不能超过1024,否则会执行失败。这个问题在testStack1024方法中进行了验证,不过传入的数字太大的话,会执行失败,即使没到1024,原因还没找到。。。。。
  • send方法会返回bool类型的结果来表示执行结果
  • 如果gas不够会执行失败
  • 建议使用transfer方法,相对更安全些
  1. transfer
  • transfer和send使用方法上一样,也是用来进行转帐操作,如果当前帐户余额不足或者对方帐户拒绝转帐,则会执行失败。
  • 如果transfer的调用地址是一个合约地址,则合约的回调函数将被执行
  1. 关call及delegatecall、staticcall单独使用一篇幅来讲