地址类型
address:地址类型存储一个 20 字节的值(以太坊地址的大小)。 地址类型也有成员变量,并作为所有合约的基础。
运算符:
<=, <, ==, !=, >= 和 >
从 0.5.0 版本开始,合约不会从地址类型派生,但仍然可以显式地转换成地址类型。
地址类型成员变量
balance 和 transfer
快速参考,请见 地址相关。
可以使用 balance 属性来查询一个地址的余额, 也可以使用 transfer 函数向一个地址发送 以太币Ether (以 wei 为单位):
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
如果 x 是一个合约地址,它的代码(更具体来说是它的 fallback 函数,如果有的话)会跟 transfer 函数调用一起执行(这是 EVM 的一个特性,无法阻止)。 如果在执行过程中用光了 gas 或者因为任何原因执行失败,以太币Ether 交易会被打回,当前的合约也会在终止的同时抛出异常。
send
send 是 transfer 的低级版本。如果执行失败,当前的合约不会因为异常而终止,但 send 会返回 false。
警告:在使用 send 的时候会有些风险:如果调用栈深度是 1024 会导致发送失败(这总是可以被调用者强制),如果接收者用光了 gas 也会导致发送失败。 所以为了保证 以太币Ether 发送的安全,一定要检查 send 的返回值,使用 transfer 或者更好的办法: 使用一种接收者可以取回资金的模式。
call, callcode 和 delegatecall
此外,为了与不符合 应用二进制接口Application Binary Interface(ABI) 的合约交互,于是就有了可以接受任意类型任意数量参数的 call 函数。 这些参数会被打包到以 32 字节为单位的连续区域中存放。 其中一个例外是当第一个参数被编码成正好 4 个字节的情况。 在这种情况下,这个参数后边不会填充后续参数编码,以允许使用函数签名。
address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
nameReg.call("register", "MyName");
nameReg.call(bytes4(keccak256("fun(uint256)")), a);
call 返回的布尔值表明了被调用的函数已经执行完毕(true)或者引发了一个 EVM 异常(false)。 无法访问返回的真实数据(为此我们需要事先知道编码和大小)。
可以使用 .gas() 修饰器modifier 调整提供的 gas 数量
namReg.call.gas(1000000)("register", "MyName");
类似地,也能控制提供的 以太币Ether 的值
nameReg.call.value(1 ether)("register", "MyName");
最后一点,这些 修饰器modifier 可以联合使用。每个修改器出现的顺序不重要
nameReg.call.gas(1000000).value(1 ether)("register", "MyName");
目前还不能在重载函数中使用 gas 或者 value 修饰器modifier 。
一种解决方案是给 gas 和值引入一个特例,并重新检查它们是否在重载的地方出现。
类似地,也可以使用 delegatecall: 区别在于只使用给定地址的代码,其它属性(存储,余额,……)都取自当前合约。 delegatecall 的目的是使用存储在另外一个合约中的库代码。 用户必须确保两个合约中的存储结构都适用于 delegatecall。 在 homestead 版本之前,只有一个功能类似但作用有限的 callcode 的函数可用,但它不能获取委托方的 msg.sender 和 msg.value。
这三个函数 call, delegatecall 和 callcode 都是非常低级的函数,应该只把它们当作 最后一招 来使用,因为它们破坏了 Solidity 的类型安全性。
所有合约都继承了地址(address)的成员变量,因此可以使用 this.balance 查询当前合约的余额。
不鼓励使用 callcode,在未来也会将其移除。
警告:这三个函数都属于低级函数,需要谨慎使用。 具体来说,任何未知的合约都可能是恶意的。 你在调用一个合约的同时就将控制权交给了它,它可以反过来调用你的合约, 因此,当调用返回时要为你的状态变量的改变做好准备。