Solidity 之地址(address)
概览
- 什么是 ETH 地址?
- 可支付地址
- 地址对应的方法(solidity)
- 检查地址是否存在
- 零地址
- 地址相关的 web3.js
介绍
ETH 中的地址都是唯一的,因为他们都是来自一个公钥或者合约。
在 ETH 交易中的支付环节,预期的收款人都是一个地址。类似于银行中的转账账号。
ETH 中的地址(address)分为两种:
- Externally Owned Accounts (EOA)(外部拥有账户):这些账户通过私钥来控制。通过私钥可以控制账户中的 TOKEN,在和智能合约进行交互的时候,私钥还可以用来提供身份认证。
- Contracts Accounts (Smart Contracts)(合约地址):这些账号是通过代码(solidity)控制的。和 EOAs 账号不同的是,智能合约账号是没有关联一个秘钥的。因为智能合约不受私钥控制,所以我们可以说智能合约是独立的。每个合约的地址都来自于合约创建的交易。ETH 的合约地址可以作为交易中的接收者,发送 token 到合约或者调用别的合约中的方法。
什么是 ETH 地址(技术上)?
hash 函数是把 ETH 公钥(或者合约)转化为地址的关键。ETH 使用 hash 函数(keccak-256)来生成地址。在 ETH 或者 solidity 中,一个地址由 20byte 长度的值组成,这个地址对应于公钥的 hash 函数(keccak-256)的最后 20bytes 的值。一个地址总固定是以 ox 开头,因为它是以 16 进制的格式表示的。
下面就来详细的介绍下如何创建地址:
首先我们拿到一个公钥
pubKey = 6e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683b......
对这个公钥进行 hash(keccak-256)运算,最后输出一个 64 个字符/32bytes 长度的字符串。
Keccak256(pubKey) = 2a5bc342ed616b5ba5732269001d3f1ef827552ae1114027bd3ecf1f086ba0f9
截取 hash 结果字符串末尾的 40 个字符/20bytes.这个 40 个字符/20bytes 就是我们的地址
Address = 0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9
当我们加上 0x 的固定开头之后,这个地址就变成 42 个字符。ETH 的地址以原始的 16 进制表示,而且需要重点注意的是它不区分大小写。所有的钱包都支持接收大写或者小写的地址,解析起来没有任何的区别。
0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9 or 0X001D3F1EF827552AE1114027BD3ECF1F086BA0F9
如何来定义一个地址变量?
在 ETH 中只需要简单的在变量的前面加上 address 关键词就可以定义一个变量为一个地址。
下面的例子展示了 solidity 中我们获取当前与合约进行交互的地址。
address user = msg.sender
地址字面量(address literal)
地址 VS 可支付地址
地址(address)和可支付地址(payable address)的区别在 solidity0.5 版本中有过介绍。可支付地址可以接受 ETH TOKEN,而普通的地址则不行。但给地址分配了一个 payable 关键字之后,这个地址可以接受和发送 ETH TOKEN。结果就是别的一些方法(transfer, send, call, delegatecall and staticcall),对这个地址也是可行的。
地址和可支付地址之间的类型转换
- 从可支付地址到地址的隐式转换(Implicit conversions)是被允许的
- 从地址到可支付地址的隐式转换(Implicit conversions)是不被允许的
- 从地址或者到地址的显式转换只允许:integers、integers literal、bytes20、contract type
- 显式转换方式 payable(x),x 可以是 integers, integer literal, bytes 或者 contract type
合约地址
在 0.5.0 之前的版本中,合约直接来源于地址类型(因为地址和可支付地址之间没有任何区别)。
从 0.5.0 版本开始,合约不再来源于地址类型。然而如果合约有一个 payable fallback 方法,它们仍然可以转化为地址和可支付地址。
contract NotPayable { // No payable function here}contract Payable { // Payable function function() payable { // do something with payable function }}contract HelloWorld { address x = address(NotPayable); address y = address(Payable); function hello_world() public pure returns (string memory) { return "hello world"; }}
让我们看下上面这个 HelloWorld 合约:
- NotPayable 是一个合约不包含 payable fallback 方法。变量 X 分配了 address(NotPayable)将会是一个地址类型。
- Payable 是一个合约包含了 payable fallback 方法。变量 Y 分配了一个 address(Payable)将会是一个可支付地址。
- 外部函数签名地址用于地址类型和可支付地址类型。
地址类型比较
- 小于等于
<=
- 小于
<
- 等于
==
- 不等于
≠
- 大于等于
≥
- 大于
>
检查一个地址是否存在?
modifier addressExist(address _address) { require(_address != address(0)); _;}
可以用于地址的交易方法
你可以想象到,地址可以像别的地址发送资金(ethers)。这块内容主要介绍不同的可用的方法。
1 Ether = 1¹⁸ Wei = 1,000,000,000,000,000,000 wei
1.address.transfer() (not recommend)
- 转账一定量的 ether(以 wei 为单位)到指定的账户.
- 失败后还原并且抛出一个错误异常.
- 花费 2300gas,并且不可调节
2.address.send() (not recommend)
- 和 address.transfer()类似
- 失败之后返回 false
- 花费 2300gas,并且不可调节
3.address.balance
- 返回账号对应的金额以 Wei 为单位
有两种方法可以查看 eth 地址/合约地址的余额
address public _account = msg.sender;// look-up balance from sender (Method 1)uint public sender_balance = _account.balance;// look-up balance from _account (Method 2)uint public sender_balance = address(_account).balance;// look-up balance from the current contract (Method 2)uint public contract_balance = address(this).balance;
4.address.call(bytes memory) returns (bool, bytes memory)
创建一个任意的消息调用,通过传递一个 memory 类型的参数
返回一个 tuple 类型包括:
- 一个调用结果的 bool 值(成功的时候是 true,失败的时候是 false)
- data 以 bytes 格式
转发所有可用 gas,可调节
5.address.delegatecall(bytes memory) returns (bool, bytes memory)
底层的 DELEGATECALL,通过传递一个 memory 类型的参数
返回一个 tuple 类型包括:
- 一个调用结果的 bool 值(成功的时候是 true,失败的时候是 false)
- data 以 bytes 格式
转发所有可用 gas,可调节
6.address.staticcall(bytes memory) returns (bool, bytes memory)
7.address.callcode(payload) (deprecated)
相关的返回一个地址的方法
- msg.sender()
- tx.origin
- block.coinbase()
- block.coinbase(_address payable)
- ecrecover()
零地址
一旦一个只能合约被编译之后,它就会使用一个特殊的合约创建交易来部署到 eth 平台上。然后,一笔交易可以会得到一个具体的地址。所以创建合约的时候应该拿到哪个具体的地址呢?
当在接受者的字段上填上了一个特殊的地址的时候,EVM 就会明白这笔交易打算创建一个新的合约。这个特殊的地址就被称作为零地址(zero-address)。
一个零地址在 ETH 中也是 20bytes 的长度,但是只包含了空的 bytes。这就解释了为什么叫做零地址,因为它只包含 0x0。
参考
<address>.balance(uint256)
<address payable>.transfer(uint256 _amount)
<address payable>.send(uint256 _amount) returns (bool)
<address payable>.call(bytes memory) returns (bool, bytes memory)
<address payable>.delegatecall(bytes memory) returns (bool, bytes memory)
<address payable>.staticcall(bytes memory) returns (bool, bytes memory)