# 《Solidity 教程》特殊變數和函數

By [Samumu.eth](https://paragraph.com/@samumu) · 2022-03-01

---

在全域命名空間中已經存在了（預設了）一些特殊的變數和函數，他們主要用來提供關於區塊鏈的資訊或一些通用的工具函數。

### **區塊和交易屬性**

*   `block.chainid` (`uint`）： 當前鏈 id
    
*   `block.coinbase` ( `address`）： 挖出當前區塊的礦工位址
    
*   `block.difficulty` ( `uint`）： 當前區塊難度
    
*   `block.gaslimit` ( `uint`）： 當前區塊 gas 限額
    
*   `block.number` ( `uint`）： 當前區塊號
    
*   `block.timestamp` ( `uint`）： 自 unix epoch 起始當前區塊以秒計的時間戳
    
*   `msg.data` ( `bytes`）： 完整的 calldata
    
*   `msg.sender` ( `address`）： 訊息發送者（當前呼叫）
    
*   `msg.sig` ( `bytes4`）： calldata 的前 4 位元組（也就是函數識別子）
    
*   `msg.value` ( `uint`）： 隨消息發送的 wei 的數量
    
*   `tx.gasprice` (`uint`）： 交易的 gas 價格
    
*   `tx.origin` (`address payable`）： 交易發起者（完全的調用鏈）
    

以上這些都經常會使用到，請牢記這些每個的含義，以 `Azuki.sol` 為例

*   `block.timestamp` 會用來判斷目前的區塊時間使否已經某個特定時間，例如當區塊時間超過拍賣開啟時間後，拍賣的 mint 函數才能成功被使用者呼叫
    
*   `msg.value` 會用來判斷使用者給的 eth 數量是否足夠，例如公售的鑄造最低價格為0.1 eth，假設鑄造者只給了 0.05 eth，他理應不能鑄造
    
*   `msg.sender` 則代表該呼叫者，大多需要與鏈上交互的函數都會需要使用到這個值
    

💡 對於每一個\*\*外部函數\*\*調用，包括 \`msg.sender\`和 \`msg.value\`在內所有 \`msg\`成員的值都會變化，包括對庫函數的調用

### **ABI 編碼及解碼函數**

*   `abi.decode(bytes memory encodedData, (...)) returns (...)`： 對給定的數據進行ABI解碼，而數據的類型在括弧中第二個參數給出 。 例如：
    
        (uint a, uint[2] memory b, bytes memory c) = 
            abi.decode(data, (uint, uint[2], bytes))
        
    
*   `abi.encode(...) returns (bytes)`： [ABI](https://learnblockchain.cn/docs/solidity/abi-spec.html#abi) - 對給定參數進行編碼
    
*   `abi.encodePacked(...) returns (bytes)`：對給定參數執行 [緊打包編碼](https://learnblockchain.cn/docs/solidity/abi-spec.html#abi-packed-mode) ，注意，可以不明確打包編碼。
    
*   `abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)`： [ABI](https://learnblockchain.cn/docs/solidity/abi-spec.html#abi) - 對給定第二個開始的參數進行編碼，並以給定的函數選擇器作為起始的 4 位元組數據一起返回
    
*   `abi.encodeWithSignature(string signature, ...) returns (bytes)`：等價於 `abi.encodeWithSelector(bytes4(keccak256(signature), ...)`
    

更多詳情請參考 [ABI](https://learnblockchain.cn/docs/solidity/abi-spec.html#abi) 和 [緊打包編碼](https://learnblockchain.cn/docs/solidity/abi-spec.html#abi-packed-mode)

💡 \`keccak256\` 是常用的計算 kecccak256 hash 的方式，是目前最常用的加密方式

### **錯誤處理**

*   \*\*`assert(bool condition)`\*\*如果不滿足條件，則會導致Panic 錯誤，則撤銷狀態更改 - 用於檢查內部錯誤
    
*   \*\*`require(bool condition)`\*\*如果條件不滿足則撤銷狀態更改 - 用於檢查由輸入或者外部元件引起的錯誤
    
*   \*\*`require(bool condition, string memory message)`\*\*如果條件不滿足則撤銷狀態更改 - 用於檢查由輸入或者外部元件引起的錯誤，可以同時提供一個錯誤消息
    
*   \*\*`revert()`\*\*終止運行並撤銷狀態更改
    
*   \*\*`revert(string memory reason)`\*\*終止運行並撤銷狀態更改，可以同時提供一個解釋性的字串
    

💡 請不要忽略如何寫錯誤處理，這是增加合約安全性最基本也是必備的方式

### **數學和密碼學函數**

*   `addmod(uint x, uint y, uint k) returns (uint)` 計算`(x + y) % k` ，加法會在任意精度下執行，並且加法的結果即使超過 `2**256`也不會被截取。 從 0.5.0 版本的編譯器開始會加入對`k != 0` 的校驗（assert）
    
*   `mulmod(uint x, uint y, uint k) returns (uint)` 計算 `(x * y) % k`，乘法會在任意精度下執行，並且乘法的結果即使超過 `2**256`也不會被截取。 從 0.5.0 版本的編譯器開始會加入對`k != 0` 的校驗（assert）
    
*   \*\*`keccak256((bytes memory) returns (bytes32)`\*\*計算 Keccak256 哈希，Keccak256 在 solidity 的[使用範例](https://solidity-by-example.org/hashing/)
    
*   \*\*`sha256(bytes memory) returns (bytes32)`\*\*計算參數的SHA-256哈希
    
*   \*\*`ripemd160(bytes memory) returns (bytes20)`\*\*計算參數的 RIPEMD-160 哈希
    
*   \*\*`ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)`\*\*利用橢圓曲線簽名恢復與公鑰相關的地址，錯誤返回零值
    

💡 在一個私鏈上，你很有可能碰到由於 \`sha256\`、\`ripemd160\` 或者 \`ecrecover\`引起的 Out-of-Gas。 這個原因就是他們被當做所謂的預編譯合約而執行，並且在第一次收到消息后這些合約才真正存在（儘管合約代碼是硬代碼）。 發送到不存在的合約的消息非常昂貴，所以實際的執行會導致 Out-of-Gas 錯誤。 在你的合約中實際使用它們之前，給每個合約發送一點乙太幣，比如 1 Wei。 這在官方網路或測試網路上不是問題。

### **地址成員**

*   \*\*`<address>.balance` (`uint256`)\*\*以 Wei 為單位的 [位址類型 Address](https://learnblockchain.cn/docs/solidity/types.html#address) 的餘額。
    
*   \*\*`<address>.code` (`bytes memory`)\*\*在 [位址類型 Address](https://learnblockchain.cn/docs/solidity/types.html#address) 上的代碼（可以為空）
    
*   `<address>.codehash` (`bytes32`)：Address 的 codehash
    
*   \*\*`<address payable>.transfer(uint256 amount)`\*\*向 [位址類型 Address](https://learnblockchain.cn/docs/solidity/types.html#address) 發送數量為 amount 的 Wei，失敗時拋出異常，使用固定（不可調節）的 2300 gas 的礦工費
    
*   \*\*`<address payable>.send(uint256 amount) returns (bool)`\*\*向 [位址類型 Address](https://learnblockchain.cn/docs/solidity/types.html#address) 發送數量為amount的 Wei，失敗時傳回 `false`，發送 2300 gas 的礦工費用，不可調節
    
*   \*\*`<address>.call(bytes memory) returns (bool, bytes memory)`\*\*用给定的有效payload發出低級`CALL` 調用，返回成功狀態及數據，發送所有可用 gas，也可以調節 gas。
    
*   \*\*`<address>.delegatecall(bytes memory) returns (bool, bytes memory)`\*\*用给定的有效payload發出低级 `DELEGATECALL`调用 ，返回成功狀態并返回數據，發送所有可用 gas，也可以調節 gas。 發出低级函數`DELEGATECALL` ，失敗时返回 `false`，發送所有可用 gas，可調節
    
*   \*\*`<address>.staticcall(bytes memory) returns (bool, bytes memory)`\*\*用给定的有效payload發出低級`STATICCALL` 調用，返回成功狀態及數據，發送所有可用 gas，也可以調節 gas
    

### **合約相關**

*   \*\*`this` （ 目前的合約型態 ）\*\*當前合約，可以顯示轉換為 [位址類型 Address](https://learnblockchain.cn/docs/solidity/types.html#address)
    
*   \*\*`selfdestruct(address payable recipient)`\*\*銷毀合約，並把餘額發送到指定 [位址類型 Address](https://learnblockchain.cn/docs/solidity/types.html#address) ，請注意， `selfdestruct`具有從EVM繼承的一些特性：
    
    *   接收合約的 receive 函數 不會執行
        
    *   合約僅在交易結束時才真正被銷毀，並且 `revert`可能會「撤銷」銷毀
        

此外，當前合約內的所有函數都可以被直接調用，包括當前函數。

### **類型資訊**

表達式`type(X)` 可用於檢索參數`X`的類型資訊。 目前，此功能還比較有限（ `X`僅能是合約和整型），但是未來應該會擴展

用於合約類型 `C`支援以下屬性：

*   `type(C).name` : 獲得合約名
    
*   `type(C).creationCode` : 獲得包含創建合同位元組碼的記憶體位元組陣組。 它可以在內聯彙編中構建自定義創建例程，尤其是使用 `create2`操作碼。 不能在合同本身或派生的合同訪問此屬性。 因為會引起迴圈引用。
    
*   `type(C).runtimeCode` : 獲得合同的運行時位元組碼的記憶體位元組陣組。 這是通常由`C`的構造函數部署的代碼。 如果 `C`有一個使用內聯彙編的構造函數，那麼可能與實際部署的位元元組碼不同。 還要注意庫在部署時修改其運行時位元組碼以防範定期調用（guard against regular calls）。 與`.creationCode` 有相同的限制，不能在合同本身或派生的合同訪問此屬性。 因為會引起迴圈引用。
    

除上面的屬性， 下面的屬性在介面類型 `I` 下可使用：

*   \*\*`type(I).interfaceId`:\*\*返回介面`I` 的 `bytes4`類型的介面 ID，介面 ID 參考： [EIP-165](https://learnblockchain.cn/docs/eips/eip-165.html) 定義
    

對於整型 `T`有下面的屬性可訪問：

*   `type(T).min` : `T` 的最小值
    
*   `type(T).max` : `T` 的最大值

---

*Originally published on [Samumu.eth](https://paragraph.com/@samumu/solidity-10)*
