--- title: 'Solidity WTF 102 22 ~ 23 單元' lang: zh-tw --- Solidity WTF 102 22 ~ 23 單元 === :::info :date: 2024/10/04 ::: [TOC] # 課程學習 ## Call ### call 簡介 `call` 是 `address` 類型的低級成員函數,返回值為`(bool, bytes memory)` - `call`是`發送ETH`的方法,他在發送後會觸發接收方合約的`receive`或是`fallback` - 不推薦用`call`調用其他合約,以免調用到不安全的合約 - 不確定合約函數或是`ABI`時,還是能透過`call`調用 :::info :bulb: `call` 是 `address` 類型的低級成員函數,這可能意味著 `call` 函數是直接處理「地址」相關的操作,而「地址」通常是在底層的內存或硬體層級上運作的。這就是為什麼它被稱為低級。 ::: ### call 使用規則 ```javascript= 目標合约地址.call(字節碼); ``` 字節碼是由結構化編碼函數abi.encodeWithSignature獲得 ```javascript= // call 也可以指定ETH數量和gas 目标合约地址.call{value:发送数额, gas:gas数额}(字节码); abi.encodeWithSignature("函数签名", 逗号分隔的具体参数) // abi.encodeWithSignature("f(uint256,address)", _x, _addr) ``` ### 目標合約 假設今天合約內容如下: ```javascript= contract OtherContract { uint256 private _x = 0; // 状态变量x // 收到eth的事件,记录amount和gas event Log(uint amount, uint gas); fallback() external payable{} // 返回合约ETH余额 function getBalance() view public returns(uint) { return address(this).balance; } // 可以调整状态变量_x的函数,并且可以往合约转ETH (payable) function setX(uint256 x) external payable{ _x = x; // 如果转入ETH,则释放Log事件 if(msg.value > 0){ emit Log(msg.value, gasleft()); } } // 读取x function getX() external view returns(uint x){ x = _x; } } ``` 那我要如何使用`call`調用其中的函數? 使用方法如下: ```javascript= // 定义Response事件,输出call返回的结果success和data event Response(bool success, bytes data); function callSetX(address payable _addr, uint256 x) public payable { // call setX(),同时可以发送ETH // 這邊使用與call使用方法相同的abi方式,去調用其他合約內的funciton (bool success, bytes memory data) = _addr.call{value: msg.value}( abi.encodeWithSignature("setX(uint256)", x) ); emit Response(success, data); //释放事件 } ``` ### call問題 > 假設`call`一個不存在的函數名稱,會有什麼結果? :::success 會調用`fallback`函數,若`fallback`函數沒有實現,則會報錯。 ::: > call推薦用來做什麼? :::success 用來`發送ETH`,因為`transfe`r與`send`都有`gas limit`,若超過會報`out of gas` ::: ## Delegatecall ### delegatecall 簡介 與`call`類似,都是`address`類型的低級函數,有委託/代表的意思。 課程中的圖片很清楚,直接引用,並且在付上較簡單的解釋。 ![image](https://hackmd.io/_uploads/SJ8l3LaCC.png) ![image](https://hackmd.io/_uploads/rJTe2I6AR.png) `call`會直接調用`C合約`,結果會直接儲存在`C合約`中。 `delegatecall`委託調用,如果`delegatecall` `C合約`,那我只會借用他`function logic`,值會儲存在`B合約`而不是`C合約`。 ### 用法 與`call`大致相同,不同的地方是`delegatecall`在调用合约时可以指定交易发送的`gas`,但不能指定发送的`ETH数额`。 ```javascript= // delegatecall 也可以指定gas 但不能指令eth數量 目标合约地址.delegatecall{value:发送数额, gas:gas数额}(字节码); abi.encodeWithSignature("函数签名", 逗号分隔的具体参数) // abi.encodeWithSignature("f(uint256,address)", _x, _addr) ``` #### 何時會用到 - 代理合約 - EIP-2535 Diamonds(鑽石合約) ##### 代理合約 `代理合約`只儲存相關的變數,並保存邏輯合約地址; 函數都會在邏輯合約實作。 如果今天要升級,只需要將`代理合約指向新的邏輯合約`即可。 - B 就是代理合約,C則是邏輯合約。 ![image](https://hackmd.io/_uploads/B1hdNDTCC.png) :::info :memo: 鑽石合約將獨立出來講解 ::: ## 本章重點 應著重在使用方式,以及`delegatecall`觀念。 :::success :bulb: 兩者差別只能不能指定`eth數量`與儲存參數的位置。 :::