将 delegatecall 结构作为参数

Struct on delegatecall as an argument

是否可以将结构作为参数传递给 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, '');
    }

...但它不起作用并且每次都 returns 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.encodeWithSignaturehttps://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)