地址(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中,看看效果,参见下面的操作演示:
演示中的一个核心要点是,编译后,我们能得到当前合约的地址,并将该地址复制到输入框中,记得录入地址项时要加英文的双引号,否则会报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
是合约调用方附带的以太币。
下面是操作演示:
关于发送者的帐号,发送的以太币数量设置,需切换到Remix的小飞机图标的配置页。要在调用deposit
前在Value
输入项填入要发的以太币数量。在getBalance
时要记得将value
项内填的值去掉,因为getBalance
方法,并不是payable
的,不支持货币3。
send()
方法执行时有一些风险
- 调用递归深度不能超1024。
- 如果
gas
不够,执行会失败。 - 所以使用这个方法要检查成功与否。或为保险起见,货币操作时要使用一些最佳实践。
如果执行失败,将会回撤所有交易,所以务必留意返回结果。
call()
,callcode()
和delegatecall()
为了同一些不支持ABI协议的进行直接交互(一般的web3.js
,soldity
都是支持的)。可以使用call()
函数,用来向另一个合约发送原始数据。参数支持任何类型任意数量。每个参数会按规则(规则是按ABI4)打包成32字节并一一拼接到一起。
call()
方法支持ABI协议[ABI]定义的函数选择器。如果第一个参数恰好4个字节,在这种情况下,会被认为根据ABI协议定义的函数器指定的函数签名[ABI]。所以如果你只是想发送消息体,需要避免第一个参数是4个字节。
call
方法返回一个bool
值,以表明执行成功还是失败。正常结束返回true
,异常终止返回false
。我们无法解析返回结果,因为这样我们得事前知道返回的数据的编码和数据大小(这里的潜在假设是不知道对方使用的协议格式,所以也不会知道返回的结果如何解析,有点祼协议测试的感觉)。
同样我们也可以使用delegatecall()
,它与call
方法的区别在于,仅仅是代码会执行,而其它方面,如(存储,余额等)都是用的当前的合约的数据。delegatecall()
方法的目的是用来执行另一个合约中的工具库。所以开发者需要保证两个合约中的存储变量能兼容,来保证delegatecall()
能顺利执行。
在homestead阶段之前,仅有一个受限的多样的callcode()
方法可用,但并未提供对msg.sender
,msg.value
的访问权限。
上面的这三个方法call()
,delegatecall()
,callcode()
都是底层的消息传递调用,最好仅在万不得已才进行使用,因为他们破坏了Solidity的类型安全。
关于call()
函数究竟发的什么消息体,函数选择器究竟怎么用,参见这个文章的挖掘。
上述的函数都是底层的函数,使用时要异常小心。当调用一个未知的,可能是恶意的合约时,当你把控制权交给它,它可能回调回你的合约,所以要准备好在调用返回时,应对你的状态变量可能被恶意篡改的情况。
-
如果你想了解更多关于地址的由来,UTXO等,可以参考: http://me.tryblockchain.org/Solidity%E7%9A%84%E5%9C%B0%E5%9D%80%E7%B1%BB%E5%9E%8B.html ↩
-
为防止录入地址有误,一种格式化地址后来确认地址有效性的方案,https://github.com/ethereum/EIPs/issues/55 ↩
-
原因详见实现以太币支付的文章,http://me.tryblockchain.org/%E6%94%AF%E4%BB%98%E7%9B%B8%E5%85%B3.html ↩
-
关于ABI协议的详细说明:http://me.tryblockchain.org/Solidity-abi-abstraction.html ↩
处于某些特定的环境下,可以看到评论框,欢迎留言交流^_^。