🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 地址(Address) 地址: 以太坊地址的长度,大小20个字节,160位,所以可以用一个`uint160`编码。地址是所有合约的基础,所有的合约都会继承地址对象,也可以随时将一个地址串,得到对应的代码进行调用。当然地址代表一个普通帐户时,就没有这么多丰富的功能啦。 ## 支持的运算符 - `<=`,`<`,`==`,`!=`,`>=`和`>` ## 地址类型的成员 属性:`balance`<br/> 函数:`send()`,`call()`,`delegatecall()`,`callcode()`。 ## 地址字面量 十六进制的字符串,凡是能通过地址合法性检查(address checksum test),就会被认为是地址,如`0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`。需要注意的是39到41位长的没有通过地址合法性检查的,会提示一个警告,但会被视为普通的有理数字面量。 ## `balance` 通过它能得到一个地址的余额。 ``` pragma solidity ^0.4.0; contract addressTest{ function getBalance(address addr) returns (uint){ return addr.balance; } } ``` 我们可以把上述代码放入[remix](https://ethereum.github.io/browser-solidity)中,看看效果,参见下面的操作演示: ![address_demo](https://box.kancloud.cn/f7e0c054857ca3c86d2378a6f8eaaa66_1277x681.gif) 演示中的一个核心要点是,编译后,我们能得到当前合约的地址,并将该地址复制到输入框中,记得录入地址项时要加英文的双引号,否则会报`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](https://box.kancloud.cn/a39dd6005bebacf3c3bfa5b35df06e7a_1277x681.gif) 关于发送者的帐号,发送的以太币数量设置,需切换到Remix的小飞机图标的配置页。要在调用`deposit`前在`Value`输入项填入要发的以太币数量。在`getBalance`时要记得将`value`项内填的值去掉,因为`getBalance`方法,并不是`payable`的,不支持货币。 `send()`方法执行时有一些风险 1. 调用递归深度不能超1024。 1. 如果`gas`不够,执行会失败。 1. 所以使用这个方法要检查成功与否。或为保险起见,货币操作时要使用一些最佳实践。 如果执行失败,将会回撤所有交易,所以务必留意返回结果。 ## `call()`,`callcode()`和`delegatecall()` 为了同一些不支持ABI协议的进行直接交互(一般的`web3.js`,`soldity`都是支持的)。可以使用`call()`函数,用来向另一个合约发送原始数据。参数支持任何类型任意数量。每个参数会按规则(规则是按ABI)打包成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()`函数究竟发的什么消息体,函数选择器究竟怎么用,参见[这个文章](http://me.tryblockchain.org/Solidity-call-callcode-delegatecall.html)的挖掘。 > 上述的函数都是底层的函数,使用时要异常小心。当调用一个未知的,可能是恶意的合约时,当你把控制权交给它,它可能回调回你的合约,所以要准备好在调用返回时,应对你的状态变量可能被恶意篡改的情况。