9. King

本關是一個模擬的遊戲,要求玩家把遊戲中的國王寶座強佔,強制令任何人都不能搶回國王寶座來中止遊戲。

觀看合約代碼,問題在於fallback函數裏

king.transfer(msg.value);

這裏同樣是犯了常見的錯誤︰未有考慮呼叫者為另一智能合約的情況

基本上只要king的地址為一智能合約,而該智能合約又並未定義fallback或是receive,transfer就會失敗。參考官方文檔

The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception.

未定義fallback或是receive的智能合約收到transfer傳送的以太幣會以exception處理。

因此只要一個未定義fallback或是receive的智能合約佔用king,合約就會在transfer時出現錯誤,令king的地址永遠屬於該智能合約。

本關另一難點在於關卡合約把邏輯定義在fallback裏面,因為transfer和send都只限使用2300 gas,因此只能用call的方法,可以參考這裏的官方文檔

首先打開Console (F12),輸入

> instance

下方會回傳當前關卡合約地址。

然後打開Remix IDE,新建檔案King.sol貼上:

pragma solidity ^0.8.0;

contract King {
    address levelInstance;

    constructor(address _levelInstance) {
        levelInstance = _levelInstance;
    }

    function give() public payable {
        levelInstance.call{value: msg.value}("");
    }
}

在constructor填入關卡合約地址後發佈。需注意兩點的是︰

  • Value 需大於或等於1 ether (關卡合約要求)
  • 需要手動調高Gas limit,一次調到100000就可以了(不然呼叫關卡合約的fallback時會因Out of gas 失敗)

需注意使用call來傳送以太幣在編寫正常合約時是一種極差的寫法,因為很可能會引致下一關介紹的Re-entrancy attack。有關傳送以太幣的三種方法及使用call可能導致的Re-entrancy attack可以參考這篇文章

最後回關卡頁面按提交,本關完成。

發表留言

%d 位部落客按了讚: