Struct

構造委託呼叫

  • July 13, 2021

是否可以將結構作為參數傳遞給delegatecall?

我有這個函式,它呼叫delegatecall並接受一個結構(0x 引號)作為參數,稍後在函式簽名和正確呼叫中使用:

function executeDelegate(address _weth, address _contract, ZrxQuote memory _zrxQuote) private returns(uint, string memory) {
       console.log('spender address: ', _zrxQuote.spender); //----> testing
       (bool success, ) = logicContract.delegatecall(
               abi.encodeWithSignature('execute(address,address,uint256,ZrxQuote)', _weth, _contract, borrowed, _zrxQuote)
       );
       console.log(success);
       require(success, 'Delegate Call failed');
       return (0, '');
   }

…但它不起作用並false每次都返回錯誤Delegate Call failed

我有這個console.log('spender address: ', _zrxQuote.spender);來測試我的結構是否被成功讀取。

此外,如果我完全刪除方程的結構(從函式、從delegatecall、從呼叫、從邏輯合約),則delegatecall可以完美地工作,例如:

function executeDelegate(address _weth, address _contract) private returns(uint, string memory) {
       (bool success, ) = logicContract.delegatecall(
               abi.encodeWithSignature('execute(address,address,uint256)', _weth, _contract, borrowed)
       );
       require(success, 'Delegate Call failed');
       return (0, '');
   }

所以問題直接在於傳遞給的結構delegatecall,但我似乎無法在任何文件上找到問題所在(儲存變數是相同的)。

這些是契約:

Proxy:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;

import "@studydefi/money-legos/dydx/contracts/DydxFlashloanBase.sol";
import "@studydefi/money-legos/dydx/contracts/ICallee.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "hardhat/console.sol";


contract DydxFlashloaner is ICallee, DydxFlashloanBase {
   struct ZrxQuote {
       address sellTokenAddress;
       address buyTokenAddress;
       address spender;
       address swapTarget;
       bytes swapCallData;
   }

   struct MyCustomData {
       address token;
       uint256 repayAmount;
   } 

   address public logicContract;
   uint public borrowed;

   constructor(address _logicContract, uint _borrowed) public {
       logicContract = _logicContract;
       borrowed = _borrowed;
   }

/******* Part that matters ******/

   function callFunction(
       address sender,
       Account.Info memory account,
       bytes memory data
   ) public {
       (MyCustomData memory mcd, ZrxQuote memory zrx) = abi.decode(data, (MyCustomData, ZrxQuote));
       uint256 balOfLoanedToken = IERC20(mcd.token).balanceOf(address(this));

       require(
           balOfLoanedToken >= mcd.repayAmount,
           "Not enough funds to repay dydx loan!"
       );
       
       executeDelegate(mcd.token, address(this), zrx); //----> calls delegatecall
   }


   function executeDelegate(address _weth, address _contract, ZrxQuote memory _zrxQuote) private returns(uint, string memory) {
       console.log('this is: ', _zrxQuote.spender);
       (bool success, ) = logicContract.delegatecall(
               abi.encodeWithSignature('execute(address,address,uint256,ZrxQuote)', _weth, _contract, borrowed, _zrxQuote)
       );
       console.log(success);
       require(success, 'Delegate Call failed');
       return (0, '');
   }

/******* End ******/

   function initiateFlashLoan(
       address _solo, 
       address _token, 
       uint256 _amount, 
       address[] calldata _quoteAddr, 
       bytes calldata _quoteData
   ) external
   {
       ZrxQuote memory zrxQuote = ZrxQuote({
           sellTokenAddress: _quoteAddr[0],
           buyTokenAddress: _quoteAddr[1],
           spender: _quoteAddr[2],
           swapTarget: _quoteAddr[3],
           swapCallData: _quoteData
       });


       ISoloMargin solo = ISoloMargin(_solo);

       uint256 marketId = _getMarketIdFromTokenAddress(_solo, _token);

       uint256 repayAmount = _getRepaymentAmountInternal(_amount);
       IERC20(_token).approve(_solo, repayAmount);

       Actions.ActionArgs[] memory operations = new Actions.ActionArgs[](3);

       operations[0] = _getWithdrawAction(marketId, _amount);
       operations[1] = _getCallAction(
           abi.encode(MyCustomData({token: _token, repayAmount: repayAmount}), zrxQuote)
       );
       operations[2] = _getDepositAction(marketId, repayAmount);

       Account.Info[] memory accountInfos = new Account.Info[](1);
       accountInfos[0] = _getAccountInfo();

       solo.operate(accountInfos, operations);
   }
}

Logic:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
pragma abicoder v2; //tried with pragma experimental ABIEncoderV2 also

import '@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol';
import './interfaces/MyILendingPool.sol';
import './interfaces/MyIERC20.sol';
import "hardhat/console.sol";

contract FlashLoaner {
   struct ZrxQuote {
       address sellTokenAddress;
       address buyTokenAddress;
       address spender;
       address swapTarget;
       bytes swapCallData;
   }

   struct MyCustomData {
       address token;
       uint256 repayAmount;
   }

   address public logicContract;
   uint public borrowed;


   function execute(address _weth, address _contract, uint256 _borrowed, ZrxQuote memory _zrxQuote) public {
       console.log('hello');
    
  //I removed the code for simplicity, but it never executes, not even the 'hello'.
       
   }

謝謝您的幫助!

解決方案:

abi.encodeWithSignature根據文件,必須將元組傳遞給: https ://docs.soliditylang.org/en/v0.8.6/abi-spec.html#mapping-solidity-to-abi-types

所以它會是:

execute(address,address,uint256,(address, address, address, address, bytes))

…代替 :

execute(address,address,uint256,ZrxQuote)

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