特殊函數
receive() and fallback()
合約裡的特殊函數,用來應對一些特殊情況,宣告的時候不用加 function。
receive()
專門用來接收 ETH ,只處理有值沒有 data 的情況,宣告不用加 function 但ㄧ定要有 external 跟 payable,不能有引數,不能有回傳值。
1 | receive() external payable{} |
fallback()
處理當data function selector 對不上 contract 內 function 的情況,一定要加 external ,要收款要記得加 payable , solidity > 0.8.1 可以有引數跟回傳值。
1 | fallback() external {} |
fallback其中一個用途是代理合約 proxy contract。
代理合約
代理合約是固定一個地址不動,不過所有的呼叫會進到 fallback, fallback 會指向另一個合約再去用其他 contract 的邏輯,不過最終的 data 是存在 proxy contract,這樣可以做到主要一個地址不換,如果要 upgrade contract 只要重新指向一個新 address 即可。
觸發特殊函數的邏輯
有 value → 看有沒有data
有 data → fallback()
沒有 data → 看有沒有 receive
有 receive → receive()
沒 receive→ fallback()
如果 input data & value 都有,合約也 fallback & receive 都有,最終是會跑哪一個?
有 data, function selector 對不上直接進 fallback()。
transfer and send
transfer() 和 send() 是 Solidity 中用來發送 ETH 的內建方法
會傳送一筆只有 value 但沒有 data 的 Tx,邏輯上會優先進到 receive(),但如果沒有 receive(),會到 payable fallback()。
1 | Address.transfer(amount); |
兩個的差別是 transfer 會直接 revert , send 只會回傳 bool。如果轉帳 function 裡有其他邏輯 bool 會需要額外處理,避免自行設計的紀錄已經減少但轉帳失敗。
transfer and send 的問題
transfer and send 只能提供給 2300 gas 給接收方合約使用,所以如果 receive 或 fallback 裡的邏輯過於複雜,操作過多消耗太多 gas 就會失敗,交易失敗就會被revert。
call
1 | Address.call{value: ,gas: }("data"); |
因應transfer and send 只能提供 2300gas 的限制,要使用沒有限制的 call。
回傳值是bool(代表成功失敗)跟 bytes(函數的回傳值)
發送 ETH 的三種方式安全性對比: transfer, send, call
| method | gas limit | tx fail |
|---|---|---|
| transfer | 2300 | revert |
| send | 2300 | return false |
| call | non | return false |
transfer 會直接 revert 不用作後續處理,send 只會回傳 bool 但要做另外處理,可是他們的 gas 只有 2300 沒辦法執行複雜合約,這兩個 function 的安全性相比應該是 transfer >send。因爲 transfer 會直接 revert。
call,return bool,沒有 gas 限制,但視合約本身安全性可能會面對重入攻擊( B 合約先向 A 合約提一次錢收到之後在 receive() 又提一次,如果 A 合約是先轉帳後改狀態就會被重複扣款),transfer 與 send 有 gas 限制就沒辦法在合約裡做那麽多複雜的操作。因此 call 的安全性 < transfer 跟 send,不過優勢在於call 有 gas 的靈活度。
總體安全性 transfer > send > call,但考慮複雜度 transfer 跟 send 很可能就用不了。
沒有 receive() 是否就無法接收 ether 了,如果不是,可以透過哪些方法
- fallback() 加 payable
- 自定義其他 function 寫 payable
- selfdestruct(addr) 強制發送ETH,不論有沒有 receive fallback payable