Solidity

如何將結構作為委託呼叫中的參數傳遞給代理庫?

  • May 30, 2022

我正在嘗試使用 delegatecall 呼叫庫buyAssetsForEth中的函式,OpenSeaMarketV2如下所示:

pragma solidity 0.8.4;

contract Owned {
   address public owner;

   constructor(address _owner) public {
       owner = _owner;
   }

   modifier onlyOwner {
       require(msg.sender == owner);
       _;
   }
}

contract Router is Owned {
   struct OpenSeaBuy {
       address a;
   }

   address public openSeaMarketV2 = 0xd9145CCE52D386f254917e481eB44e9943F39138;

   constructor() Owned(msg.sender) public {
   }

   function buyAsset(OpenSeaBuy memory openSeaBuys, bool revertIfTrxFails) public payable {

       (bool success, bytes memory result)=openSeaMarketV2.delegatecall(abi.encodeWithSignature("buyAssetsForEth((address),bool)",openSeaBuys,revertIfTrxFails));
       require(success, 'Delegate Call failed');

   }

   receive() payable external {}

   function contractAddress()
   external view
   returns (address)
   {
       return address(this);
   }

}

OpenSeaMarketV2庫單獨部署到0xd9145CCE52D386f254917e481eB44e9943F39138

library OpenSeaMarketV2 {

   struct OpenSeaBuy {
       address a;
   }

   function buyAssetsForEth(OpenSeaBuy memory openSeaBuys, bool revertIfTrxFails) public {
       uint256 x = 3;
   }
}

但執行buyAsset失敗並顯示消息:“委託呼叫失敗”

關於它為什麼失敗的任何想法?

您剛剛遇到了庫 ABI 編碼的注意事項之一。你可以在這裡閱讀。本質上,與庫函式選擇器相關的這一行是解釋:

非儲存結構由它們的完全限定名稱引用,即契約 C { struct S { … } } 的 CS。

這意味著 OpenSeaMarketV2.buyAssetsForEth 的函式選擇器不是從以下計算得出的:

  • keccak256(buyAssetsForEth((address),bool)): 0x443e309e

而是來自:

  • keccak256(buyAssetsForEth(OpenSeaMarketV2.OpenSeaBuy,bool)): 0x2baccc78

這是一個庫特定的編碼方案。要解決您的問題,主要有兩種方法。更改您的簽名字元串以顯式使用編譯庫時使用的簽名字元串:

function buyAsset(OpenSeaBuy memory openSeaBuys, bool revertIfTrxFails) public payable {

   (bool success, bytes memory result)=openSeaMarketV2.delegatecall(abi.encodeWithSignature("buyAssetsForEth(OpenSeaMarketV2.OpenSeaBuy,bool)",openSeaBuys,revertIfTrxFails));
   require(success, 'Delegate Call failed');
}

或者直接參考庫函式選擇器以獲得更簡潔的語法,但絕對不容易出錯:

function buyAsset(OpenSeaBuy memory openSeaBuys, bool revertIfTrxFails) public payable {

   (bool success, bytes memory result)= openSeaMarketV2.delegatecall(abi.encodeWithSelector(OpenSeaMarketV2.buyAssetsForEth.selector,openSeaBuys,revertIfTrxFails));
   require(success, 'Delegate Call failed');
}

我希望這能回答你的問題。

引用自:https://ethereum.stackexchange.com/questions/129201