智能合約開發

事件與日誌

事件是合約向外部世界宣告「這件事發生了」的方式。發出一個事件會寫下一筆日誌項目,記錄在交易的收據與區塊裡——就像鏈永久保存的一張列印收據。關鍵的陷阱是:合約自己永遠無法讀回自己的日誌;它們純粹是給鏈下的消費者使用,例如錢包介面、索引器與分析工具。

機制上,emit 會編譯成 LOG0 到 LOG4 操作碼。對一個正常(非匿名)事件,第一個 topic(topic0)是事件簽章的 keccak-256 雜湊,例如 keccak256("Transfer(address,address,uint256)")。最多三個參數可標記為 indexed;每個都成為一個額外的 topic,這正是日誌能透過區塊的布隆過濾器被高效篩選的原因。其餘未索引的參數則被 ABI 編碼進日誌的 data 欄位。日誌存在收據樹而非狀態樹,這就是它們遠比儲存便宜的原因——大約每筆 LOG 375 gas、每個 topic 375 gas,加上每位元組資料 8 gas。

由於便宜又可搜尋,事件是鏈下系統的標準管道:前端透過 eth_getLogs 或 WebSocket 訂閱,而像 The Graph 這類索引器則把它們轉成可查詢的資料庫。要牢記的紀律是:從鏈的角度看日誌是唯寫的——它們非常適合記錄歷史,但你絕不能讓鏈上邏輯依賴某個事件,因為沒有任何合約能讀取它。

event Transfer(address indexed from, address indexed to, uint256 value);
// topic0 = keccak256("Transfer(address,address,uint256)")
emit Transfer(msg.sender, to, amount);

便宜、可搜尋,而且對鏈上程式碼隱形。

indexed 參數可被搜尋,但只有在能塞進 32 位元組時才能完整還原;indexed 的 string 或 bytes,topic 裡存的只是資料的 keccak-256 雜湊,而非資料本身。