智能合約開發

fallback 與 receive 函數

fallback 與 receive 是合約的兩個全包入口。receive 處理最單純的情況:有人不附帶任何資料、單純地把以太幣送進合約。fallback 處理不符合任何具名函數的呼叫——而它正是每個代理合約背後的引擎,因為「對一個我沒有的函數的呼叫」正是代理想要轉發的東西。

分派規則很精確。若一個呼叫帶著空的 calldata 到來,當 receive() 有定義時 EVM 執行它,否則退回 fallback()。若一個呼叫帶著非空的 calldata 到來、而其開頭的 4 位元組選擇器不符合任何函數,則執行 fallback()。receive() 必須宣告為 external payable;fallback() 是 external,可選擇性地加上 payable。一個合約若要能接受純粹的 ETH 轉帳,這兩者之一必須存在且為 payable——否則轉帳會回滾。

兩個實務重點。代理把它的 delegatecall 轉發邏輯放在 fallback() 裡,於是任何未知的選擇器都被路由到實作合約。並要當心 2,300 gas 的津貼:舊式的 .transfer() 與 .send() 方法只轉發 2,300 gas 給收款方的 receive/fallback——足以發出一個事件,卻不足以執行一次儲存寫入——所以一個在 receive/fallback 裡做實事的收款方在它們之下會回滾。這種脆弱性正是生態系改用 call{value: amount}("") 搭配重入防護來發送以太幣的主要原因。

receive() external payable {}      // plain ETH, empty calldata
fallback() external payable {       // unknown selector
  // a proxy delegatecalls to its implementation here
}
// .transfer()/.send() forward only 2300 gas to these

兩個全包者:純粹的以太幣,與未匹配的呼叫。

別倚賴 .transfer()/.send() 付款給合約:它們 2,300 gas 的津貼會卡死任何做超過瑣事的收款方。優先使用 call{value:...}("") 搭配重入防護與檢查回傳值。