Is it possible to pass a struct as an argument to delegatecall
?
I have this function that calls delegatecall
and takes a struct (a 0x quote) as an argument which is later used on the function signature and in the proper call:
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, '');
}
...but it doesn't work and returns false
every time and the error Delegate Call failed
.
I have this console.log('spender address: ', _zrxQuote.spender);
to test if my struct is being read successfully and it is.
Also, if I remove the struct entirely of the equation (from the function, from delegatecall
, from the call, from the logic contract), delegatecall
works perfectly, something like:
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, '');
}
So the problem is directly with the struct being passed to delegatecall
, but I can't seem to find on any docs what the issue is (storage variables are the same).
These are the contracts:
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'.
}
Thanks for the help!