地址(Address)

地址1

地址: 以太坊地址的长度,大小20个字节,160位,所以可以用一个uint160编码。地址是所有合约的基础,所有的合约都会继承地址对象,也可以随时将一个地址串,得到对应的代码进行调用。当然地址代表一个普通帐户时,就没有这么多丰富的功能啦。

支持的运算符

  • <=<==!=>=>

地址类型的成员

属性:balance
函数:send()call()delegatecall()callcode()

地址字面量

十六进制的字符串,凡是能通过地址合法性检查(address checksum test)2,就会被认为是地址,如0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF。需要注意的是39到41位长的没有通过地址合法性检查的,会提示一个警告,但会被视为普通的有理数字面量。

balance

通过它能得到一个地址的余额。

pragma solidity ^0.4.0;

contract addressTest{
    
    function getBalance(address addr) returns (uint){
        return addr.balance;
    }

}

我们可以把上述代码放入remix中,看看效果,参见下面的操作演示:

address_demo

演示中的一个核心要点是,编译后,我们能得到当前合约的地址,并将该地址复制到输入框中,记得录入地址项时要加英文的双引号,否则会报Error encoding arguments: SyntaxError: JSON Parse error: Expected ']'

this

如果只是想得到当前合约的余额,其实可以这样写:


pragma solidity ^0.4.0;

contract addressTest{
    
    function getBalance() returns (uint){
        return this.balance;
    }

}


原因是对于合约来说,地址代表的就是合约本身,合约对象默认继承自地址对象,所以内部有地址的属性。

地址的方法send()

用来向某个地址发送货币(货币单位是wei)。

pragma solidity ^0.4.0;

//请注意这个仅是Demo,请不要用到正式环境
contract PayTest {
    //得到当前合约的余额
    function getBalance() returns (uint) {
        return this.balance;//0
    }  
    
    //向当前合约存款
    function deposit() payable returns(address addr, uint amount, bool success){
        //msg.sender 全局变量,调用合约的发起方
        //msg.value 全局变量,调用合约的发起方转发的货币量,以wei为单位。
        //send() 执行的结果
        return (msg.sender, msg.value, this.send(msg.value));
    }
}
 

这个合约实现的是充值。this.send(msg.value)意指向合约自身发送msg.value量的以太币。msg.value是合约调用方附带的以太币。

下面是操作演示:

address_demo_2

关于发送者的帐号,发送的以太币数量设置,需切换到Remix的小飞机图标的配置页。要在调用deposit前在Value输入项填入要发的以太币数量。在getBalance时要记得将value项内填的值去掉,因为getBalance方法,并不是payable的,不支持货币3

send()方法执行时有一些风险

  1. 调用递归深度不能超1024。
  2. 如果gas不够,执行会失败。
  3. 所以使用这个方法要检查成功与否。或为保险起见,货币操作时要使用一些最佳实践。

如果执行失败,将会回撤所有交易,所以务必留意返回结果。

call()callcode()delegatecall()

为了同一些不支持ABI协议的进行直接交互(一般的web3.jssoldity都是支持的)。可以使用call()函数,用来向另一个合约发送原始数据。参数支持任何类型任意数量。每个参数会按规则(规则是按ABI4)打包成32字节并一一拼接到一起。

call()方法支持ABI协议[ABI]定义的函数选择器。如果第一个参数恰好4个字节,在这种情况下,会被认为根据ABI协议定义的函数器指定的函数签名[ABI]。所以如果你只是想发送消息体,需要避免第一个参数是4个字节。

call方法返回一个bool值,以表明执行成功还是失败。正常结束返回true,异常终止返回false。我们无法解析返回结果,因为这样我们得事前知道返回的数据的编码和数据大小(这里的潜在假设是不知道对方使用的协议格式,所以也不会知道返回的结果如何解析,有点祼协议测试的感觉)。

同样我们也可以使用delegatecall(),它与call方法的区别在于,仅仅是代码会执行,而其它方面,如(存储,余额等)都是用的当前的合约的数据。delegatecall()方法的目的是用来执行另一个合约中的工具库。所以开发者需要保证两个合约中的存储变量能兼容,来保证delegatecall()能顺利执行。

在homestead阶段之前,仅有一个受限的多样的callcode()方法可用,但并未提供对msg.sendermsg.value的访问权限。

上面的这三个方法call()delegatecall()callcode()都是底层的消息传递调用,最好仅在万不得已才进行使用,因为他们破坏了Solidity的类型安全。

关于call()函数究竟发的什么消息体,函数选择器究竟怎么用,参见这个文章的挖掘。

上述的函数都是底层的函数,使用时要异常小心。当调用一个未知的,可能是恶意的合约时,当你把控制权交给它,它可能回调回你的合约,所以要准备好在调用返回时,应对你的状态变量可能被恶意篡改的情况。


  1. 如果你想了解更多关于地址的由来,UTXO等,可以参考: http://me.tryblockchain.org/Solidity%E7%9A%84%E5%9C%B0%E5%9D%80%E7%B1%BB%E5%9E%8B.html 

  2. 为防止录入地址有误,一种格式化地址后来确认地址有效性的方案,https://github.com/ethereum/EIPs/issues/55 

  3. 原因详见实现以太币支付的文章,http://me.tryblockchain.org/%E6%94%AF%E4%BB%98%E7%9B%B8%E5%85%B3.html 

  4. 关于ABI协议的详细说明:http://me.tryblockchain.org/Solidity-abi-abstraction.html 

处于某些特定的环境下,可以看到评论框,欢迎留言交流^_^。