“地址”和“應付地址”有什麼區別?
我看到了一個 Solidity 智能合約,其中一些變數用 type 聲明,一些變數用 type
address
聲明address payable
。兩者有什麼區別?
- 他們儲存相同的數據嗎?
- 如何將值從一種地址類型轉換為另一種類型?
EVM 中的數據儲存和表示
和類型
address
都address payable
儲存一個 160 位的乙太坊地址。支付和非支付地址的概念只存在於編譯時的 Solidity 類型系統中。應付地址和非應付地址之間的差異在編譯的合約程式碼中消失了。內置方法
您可以使用
.transfer(..)
and.send(..)
onaddress payable
,但不能使用 onaddress
。即使您附加值,您也可以
.call(..)
在address
和上使用低級別。address payable
鑄造從
address payable
到address
address payable
可以隱式或顯式轉換為address
:address payable addr1 = msg.sender; address addr2 = addr1; // This is correct address addr3 = address(addr1); // This is correct
鑄造從
address
到address payable
address
只能顯式轉換為address payable
:address addr1 = msg.sender; address payable addr2 = addr1; // Incorrect address payable addr3 = address(uint160(addr1)); // Correct since Solidity >= 0.5.0 address payable addr4 = payable(addr1); // Correct since Solidity >= 0.6.0
鑄造
address[]
或address payable[]
儘管
address payable
可以將 single 轉換為address
,但不能將一種地址類型的數組轉換為另一種地址類型的數組:function testCast(address payable[] memory _addresses) returns (address[] memory) { return _addresses; // Type error! }
基本原理
程式語言中存在類型有兩個原因:
- 提供特定類型的特殊語義(例如
+
,在數字上是加法,但+
在字元串上可能是連接)- 限制編譯器接受的錯誤程序的數量
將地址拆分為
address
後address payable
一個目的。它迫使智能合約程序員考慮一個地址是否應該從智能合約中接收乙太幣。如果它永遠不會收到 Ether,則address
可以使用該類型。如果程序員出錯並試圖將 Ether 傳輸到該地址,編譯將失敗並出現類型錯誤。內置地址常量
msg.sender
是一個address payable
tx.origin
是一個address payable
block.coinbase
是一個address payable
是的,它們儲存相同的數據,即有效的乙太坊地址。
不同之處在於,當遇到“應付地址”時,編譯器(在編譯時)準備好允許(如果在下面的程式碼中需要)該地址訪問對管理乙太有用的原語(即呼叫、傳輸、發送)。
從這個意義上說,在聲明如下:
address payable spender;
編譯器應接受任何
spender.send(); spender.transfer(); (1) spender.call();
並生成實現它們所需的字節碼。
另一方面,如果聲明是:
address spender;
如果遇到 (1) 中的任何一個,編譯器將生成錯誤。
在編譯時間之後,payable 或 not address 沒有區別。特別是它們在記憶體中具有相同的大小並且無法區分。
簡而言之,它是一個一致性標籤,僅此而已。
了解這一點,使用 cast 將任何應付地址轉換為address和反之亦然是非常簡單的:
address payable spender = msg.sender; address owner; address payable newspender; owner = address(spender); // this is no more payable newspender = address( uint160(owner) ); // this is again payable
正如您所看到的相反(不應付應付),需要明確使用 uint160 作為中間轉換
在 0.6.0 solidity 更新後添加:
現在您可以使用“payable”關鍵字直接將任何地址轉換為應付地址:
address payable spender = msg.sender; address owner; address payable newspender; owner = address(spender); // this is no more payable newspender = payable(owner); // this is again payable