Skip to content

Commit

Permalink
🚧 propose swap
Browse files Browse the repository at this point in the history
co-authored-by: danilo neves cruz <[email protected]>
  • Loading branch information
itofarina and cruzdanilo committed Jan 7, 2025
1 parent 8b358da commit d5e49ab
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 112 deletions.
180 changes: 90 additions & 90 deletions contracts/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,90 +1,90 @@
ExaAccountFactoryTest:testFuzz_createAccount_EOAOwners(uint256,address[63]) (runs: 256, μ: 3622088, ~: 3517377)
ExaAccountFactoryTest:test_deploy_deploysToSameAddress() (gas: 25497599)
ExaPluginTest:testFork_crossRepay_repays() (gas: 14850557)
ExaPluginTest:testFork_debitCollateral_collects() (gas: 14939416)
ExaPluginTest:testFork_swap_swaps() (gas: 12120371)
ExaPluginTest:test_borrowAtMaturity_reverts_withUnauthorized_whenReceiverNotCollector() (gas: 408851)
ExaPluginTest:test_borrow_reverts_withUnauthorized_whenReceiverNotCollector() (gas: 408407)
ExaPluginTest:test_collectCredit_collects() (gas: 918642)
ExaPluginTest:test_collectCredit_collects_whenHealthFactorHigherThanOne() (gas: 799244)
ExaPluginTest:test_collectCredit_collects_withEnoughSlippage() (gas: 797913)
ExaPluginTest:test_collectCredit_collects_withPrevIssuerSignature() (gas: 994480)
ExaPluginTest:test_collectCredit_passes_whenProposalLeavesEnoughLiquidity() (gas: 1007617)
ExaPluginTest:test_collectCredit_reverts_asNotKeeper() (gas: 362539)
ExaPluginTest:test_collectCredit_reverts_whenDisagreement() (gas: 555951)
ExaPluginTest:test_collectCredit_reverts_whenExpired() (gas: 358597)
ExaPluginTest:test_collectCredit_reverts_whenHealthFactorLowerThanOne() (gas: 997865)
ExaPluginTest:test_collectCredit_reverts_whenPrevSignatureNotValidAnymore() (gas: 461362)
ExaPluginTest:test_collectCredit_reverts_whenProposalCausesInsufficientLiquidity() (gas: 1007771)
ExaPluginTest:test_collectCredit_reverts_whenReplay() (gas: 840746)
ExaPluginTest:test_collectCredit_reverts_whenTimelocked() (gas: 354765)
ExaPluginTest:test_collectCredit_toleratesTimeDrift() (gas: 801258)
ExaPluginTest:test_collectDebit_collects() (gas: 651480)
ExaPluginTest:test_collectDebit_collects_whenProposalLeavesEnoughLiquidity() (gas: 854845)
ExaPluginTest:test_collectDebit_reverts_asNotKeeper() (gas: 362350)
ExaPluginTest:test_collectDebit_reverts_whenExpired() (gas: 358314)
ExaPluginTest:test_collectDebit_reverts_whenProposalCausesInsufficientLiquidity() (gas: 851733)
ExaPluginTest:test_collectDebit_reverts_whenReplay() (gas: 690788)
ExaPluginTest:test_collectDebit_reverts_whenTimelocked() (gas: 354595)
ExaPluginTest:test_collectDebit_toleratesTimeDrift() (gas: 651576)
ExaPluginTest:test_collectInstallments_collects() (gas: 1336411)
ExaPluginTest:test_collectInstallments_revertsWhenNoSlippage() (gas: 1202776)
ExaPluginTest:test_collectInstallments_reverts_asNotKeeper() (gas: 363391)
ExaPluginTest:test_collectInstallments_reverts_whenExpired() (gas: 360647)
ExaPluginTest:test_collectInstallments_reverts_whenReplay() (gas: 1068213)
ExaPluginTest:test_collectInstallments_reverts_whenTimelocked() (gas: 356936)
ExaPluginTest:test_collectInstallments_toleratesTimeDrift() (gas: 1193002)
ExaPluginTest:test_crossRepay_repays() (gas: 1461705)
ExaPluginTest:test_crossRepay_repays_whenKeeper() (gas: 1458484)
ExaPluginTest:test_crossRepay_reverts_whenDisagreement() (gas: 1571938)
ExaPluginTest:test_crossRepay_reverts_whenNotKeeper() (gas: 958451)
ExaPluginTest:test_debitCollateral_collects() (gas: 774260)
ExaPluginTest:test_exitMarket_reverts() (gas: 388998)
ExaPluginTest:test_marketWithdraw_transfersAsset_asOwner() (gas: 818760)
ExaPluginTest:test_onUninstall_uninstalls() (gas: 284188)
ExaPluginTest:test_poke() (gas: 315843)
ExaPluginTest:test_pokeETH_deposits() (gas: 377813)
ExaPluginTest:test_propose_emitsProposed() (gas: 218628)
ExaPluginTest:test_refund_refunds() (gas: 361573)
ExaPluginTest:test_repay_partiallyRepays() (gas: 1244077)
ExaPluginTest:test_repay_partiallyRepays_whenKeeper() (gas: 1186237)
ExaPluginTest:test_repay_repays() (gas: 1162425)
ExaPluginTest:test_repay_repays_whenKeeper() (gas: 1105079)
ExaPluginTest:test_rollDebt_rolls() (gas: 1231461)
ExaPluginTest:test_rollDebt_rolls_asKeeper() (gas: 1231153)
ExaPluginTest:test_setCollector_emitsCollectorSet() (gas: 40494)
ExaPluginTest:test_setCollector_reverts_whenAddressZero() (gas: 32289)
ExaPluginTest:test_setCollector_reverts_whenNotAdmin() (gas: 33750)
ExaPluginTest:test_setCollector_sets_whenAdmin() (gas: 39884)
ExaPluginTest:test_swap_reverts_withDisagreement() (gas: 288750)
ExaPluginTest:test_swap_swaps() (gas: 268450)
ExaPluginTest:test_withdrawWETH_transfersETH() (gas: 849226)
ExaPluginTest:test_withdraw_reverts_whenNoProposal() (gas: 414919)
ExaPluginTest:test_withdraw_reverts_whenNoProposalKeeper() (gas: 357797)
ExaPluginTest:test_withdraw_reverts_whenNotKeeper() (gas: 354879)
ExaPluginTest:test_withdraw_reverts_whenReceiverIsContractAndMarketNotWETH() (gas: 621183)
ExaPluginTest:test_withdraw_reverts_whenTimelocked() (gas: 307334)
ExaPluginTest:test_withdraw_reverts_whenTimelockedKeeper() (gas: 309092)
ExaPluginTest:test_withdraw_reverts_whenWrongAmount() (gas: 308754)
ExaPluginTest:test_withdraw_reverts_whenWrongMarket() (gas: 308924)
ExaPluginTest:test_withdraw_reverts_whenWrongReceiver() (gas: 308358)
ExaPluginTest:test_withdraw_transfersAsset_asKeeper() (gas: 820063)
ExaPluginTest:test_withdraw_withdrawsProposed() (gas: 810809)
InstallmentsPreviewerTest:test_preview_returns() (gas: 135598)
IssuerCheckerTest:test_setIssuer_emits_IssuerSet() (gas: 70861)
IssuerCheckerTest:test_setIssuer_reverts_whenNotAdmin() (gas: 37272)
IssuerCheckerTest:test_setIssuer_reverts_whenZeroAddress() (gas: 35607)
IssuerCheckerTest:test_setOperationExpiry_emits_OperationExpirySet() (gas: 46864)
IssuerCheckerTest:test_setOperationExpiry_reverts_whenNotAdmin() (gas: 38985)
IssuerCheckerTest:test_setOperationExpiry_reverts_whenZeroValue() (gas: 37451)
IssuerCheckerTest:test_setPrevIssuerWindow_emits_PrevIssuerWindowSet() (gas: 46944)
IssuerCheckerTest:test_setPrevIssuerWindow_reverts_whenNotAdmin() (gas: 39074)
MockSwapperTest:test_swapExactAmountIn_swaps() (gas: 220542)
MockSwapperTest:test_swapExactAmountOut_swaps() (gas: 220669)
RefunderTest:test_refund_refunds() (gas: 242406)
RefunderTest:test_refund_reverts_whenExpired() (gas: 69541)
RefunderTest:test_refund_reverts_whenNotKeeper() (gas: 56909)
RefunderTest:test_refund_reverts_whenReplay() (gas: 279286)
RefunderTest:test_refund_reverts_whenTimelocked() (gas: 63276)
RefunderTest:test_refund_reverts_whenWrongSignature() (gas: 71153)
RefunderTest:test_refund_toleratesTimeDrift() (gas: 242492)
ExaAccountFactoryTest:testFuzz_createAccount_EOAOwners(uint256,address[63]) (runs: 256, μ: 3560845, ~: 3438556)
ExaAccountFactoryTest:test_deploy_deploysToSameAddress() (gas: 26458645)
ExaPluginTest:testFork_crossRepay_repays() (gas: 15335730)
ExaPluginTest:testFork_debitCollateral_collects() (gas: 15424508)
ExaPluginTest:testFork_swap_swaps() (gas: 12602869)
ExaPluginTest:test_borrowAtMaturity_reverts_withUnauthorized_whenReceiverNotCollector() (gas: 406815)
ExaPluginTest:test_borrow_reverts_withUnauthorized_whenReceiverNotCollector() (gas: 406302)
ExaPluginTest:test_collectCredit_collects() (gas: 916192)
ExaPluginTest:test_collectCredit_collects_whenHealthFactorHigherThanOne() (gas: 796813)
ExaPluginTest:test_collectCredit_collects_withEnoughSlippage() (gas: 795427)
ExaPluginTest:test_collectCredit_collects_withPrevIssuerSignature() (gas: 991875)
ExaPluginTest:test_collectCredit_passes_whenProposalLeavesEnoughLiquidity() (gas: 1007011)
ExaPluginTest:test_collectCredit_reverts_asNotKeeper() (gas: 360704)
ExaPluginTest:test_collectCredit_reverts_whenDisagreement() (gas: 553562)
ExaPluginTest:test_collectCredit_reverts_whenExpired() (gas: 356755)
ExaPluginTest:test_collectCredit_reverts_whenHealthFactorLowerThanOne() (gas: 997226)
ExaPluginTest:test_collectCredit_reverts_whenPrevSignatureNotValidAnymore() (gas: 459338)
ExaPluginTest:test_collectCredit_reverts_whenProposalCausesInsufficientLiquidity() (gas: 1007161)
ExaPluginTest:test_collectCredit_reverts_whenReplay() (gas: 838057)
ExaPluginTest:test_collectCredit_reverts_whenTimelocked() (gas: 352911)
ExaPluginTest:test_collectCredit_toleratesTimeDrift() (gas: 798816)
ExaPluginTest:test_collectDebit_collects() (gas: 648964)
ExaPluginTest:test_collectDebit_collects_whenProposalLeavesEnoughLiquidity() (gas: 854201)
ExaPluginTest:test_collectDebit_reverts_asNotKeeper() (gas: 360559)
ExaPluginTest:test_collectDebit_reverts_whenExpired() (gas: 356506)
ExaPluginTest:test_collectDebit_reverts_whenProposalCausesInsufficientLiquidity() (gas: 851050)
ExaPluginTest:test_collectDebit_reverts_whenReplay() (gas: 688047)
ExaPluginTest:test_collectDebit_reverts_whenTimelocked() (gas: 352753)
ExaPluginTest:test_collectDebit_toleratesTimeDrift() (gas: 649016)
ExaPluginTest:test_collectInstallments_collects() (gas: 1333352)
ExaPluginTest:test_collectInstallments_revertsWhenNoSlippage() (gas: 1199723)
ExaPluginTest:test_collectInstallments_reverts_asNotKeeper() (gas: 361600)
ExaPluginTest:test_collectInstallments_reverts_whenExpired() (gas: 358805)
ExaPluginTest:test_collectInstallments_reverts_whenReplay() (gas: 1065074)
ExaPluginTest:test_collectInstallments_reverts_whenTimelocked() (gas: 355082)
ExaPluginTest:test_collectInstallments_toleratesTimeDrift() (gas: 1189988)
ExaPluginTest:test_crossRepay_repays() (gas: 1457085)
ExaPluginTest:test_crossRepay_repays_whenKeeper() (gas: 1453976)
ExaPluginTest:test_crossRepay_reverts_whenDisagreement() (gas: 1568103)
ExaPluginTest:test_crossRepay_reverts_whenNotKeeper() (gas: 955563)
ExaPluginTest:test_debitCollateral_collects() (gas: 771061)
ExaPluginTest:test_exitMarket_reverts() (gas: 387069)
ExaPluginTest:test_marketWithdraw_transfersAsset_asOwner() (gas: 820892)
ExaPluginTest:test_onUninstall_uninstalls() (gas: 286656)
ExaPluginTest:test_poke() (gas: 314199)
ExaPluginTest:test_pokeETH_deposits() (gas: 375863)
ExaPluginTest:test_propose_emitsProposed() (gas: 220492)
ExaPluginTest:test_refund_refunds() (gas: 360972)
ExaPluginTest:test_repay_partiallyRepays() (gas: 1239297)
ExaPluginTest:test_repay_partiallyRepays_whenKeeper() (gas: 1182073)
ExaPluginTest:test_repay_repays() (gas: 1157941)
ExaPluginTest:test_repay_repays_whenKeeper() (gas: 1101190)
ExaPluginTest:test_rollDebt_rolls() (gas: 1227087)
ExaPluginTest:test_rollDebt_rolls_asKeeper() (gas: 1226779)
ExaPluginTest:test_setCollector_emitsCollectorSet() (gas: 40433)
ExaPluginTest:test_setCollector_reverts_whenAddressZero() (gas: 32165)
ExaPluginTest:test_setCollector_reverts_whenNotAdmin() (gas: 33673)
ExaPluginTest:test_setCollector_sets_whenAdmin() (gas: 39779)
ExaPluginTest:test_swap_reverts_withDisagreement() (gas: 287747)
ExaPluginTest:test_swap_swaps() (gas: 267150)
ExaPluginTest:test_withdrawWETH_transfersETH() (gas: 850585)
ExaPluginTest:test_withdraw_reverts_whenNoProposal() (gas: 415417)
ExaPluginTest:test_withdraw_reverts_whenNoProposalKeeper() (gas: 355917)
ExaPluginTest:test_withdraw_reverts_whenNotKeeper() (gas: 353088)
ExaPluginTest:test_withdraw_reverts_whenReceiverIsContractAndMarketNotWETH() (gas: 623515)
ExaPluginTest:test_withdraw_reverts_whenTimelocked() (gas: 311256)
ExaPluginTest:test_withdraw_reverts_whenTimelockedKeeper() (gas: 312837)
ExaPluginTest:test_withdraw_reverts_whenWrongAmount() (gas: 312689)
ExaPluginTest:test_withdraw_reverts_whenWrongMarket() (gas: 312880)
ExaPluginTest:test_withdraw_reverts_whenWrongReceiver() (gas: 312279)
ExaPluginTest:test_withdraw_transfersAsset_asKeeper() (gas: 822371)
ExaPluginTest:test_withdraw_withdrawsProposed() (gas: 813140)
InstallmentsPreviewerTest:test_preview_returns() (gas: 134970)
IssuerCheckerTest:test_setIssuer_emits_IssuerSet() (gas: 70595)
IssuerCheckerTest:test_setIssuer_reverts_whenNotAdmin() (gas: 37087)
IssuerCheckerTest:test_setIssuer_reverts_whenZeroAddress() (gas: 35466)
IssuerCheckerTest:test_setOperationExpiry_emits_OperationExpirySet() (gas: 46653)
IssuerCheckerTest:test_setOperationExpiry_reverts_whenNotAdmin() (gas: 38890)
IssuerCheckerTest:test_setOperationExpiry_reverts_whenZeroValue() (gas: 37333)
IssuerCheckerTest:test_setPrevIssuerWindow_emits_PrevIssuerWindowSet() (gas: 46687)
IssuerCheckerTest:test_setPrevIssuerWindow_reverts_whenNotAdmin() (gas: 38889)
MockSwapperTest:test_swapExactAmountIn_swaps() (gas: 220282)
MockSwapperTest:test_swapExactAmountOut_swaps() (gas: 220424)
RefunderTest:test_refund_refunds() (gas: 242095)
RefunderTest:test_refund_reverts_whenExpired() (gas: 69299)
RefunderTest:test_refund_reverts_whenNotKeeper() (gas: 56701)
RefunderTest:test_refund_reverts_whenReplay() (gas: 278779)
RefunderTest:test_refund_reverts_whenTimelocked() (gas: 63103)
RefunderTest:test_refund_reverts_whenWrongSignature() (gas: 70935)
RefunderTest:test_refund_toleratesTimeDrift() (gas: 242181)
2 changes: 1 addition & 1 deletion contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
solc_version = "0.8.28"
evm_version = "cancun"
optimizer = true
optimizer_runs = 200
optimizer_runs = 449
deny_warnings = true
verbosity = 3
isolate = true
Expand Down
75 changes: 54 additions & 21 deletions contracts/src/ExaPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
NotMarket,
Proposal,
Proposed,
SwapProposed,
Timelocked,
Unauthorized
} from "./IExaAccount.sol";
Expand Down Expand Up @@ -112,12 +113,29 @@ contract ExaPlugin is AccessControl, BasePlugin, IExaAccount {

function propose(IMarket market, uint256 amount, address receiver) external {
_checkMarket(market);
proposals[msg.sender] = Proposal({ amount: amount, market: market, timestamp: block.timestamp, receiver: receiver });
proposals[msg.sender] =
Proposal({ amount: amount, market: market, timestamp: block.timestamp, receiver: receiver, swapData: "" });
emit Proposed(msg.sender, market, receiver, amount, block.timestamp + PROPOSAL_DELAY);
}

function swap(IERC20 assetIn, IERC20 assetOut, uint256 maxAmountIn, uint256 minAmountOut, bytes memory route)
function proposeSwap(IMarket market, uint256 amount, IERC20 assetOut, uint256 minAmountOut, bytes memory route)
external
{
_checkMarket(market);
proposals[msg.sender] = Proposal({
amount: amount,
market: market,
timestamp: block.timestamp,
receiver: msg.sender,
swapData: abi.encode(SwapData({ assetOut: assetOut, minAmountOut: minAmountOut, route: route }))
});
emit SwapProposed(
msg.sender, market, assetOut, amount, minAmountOut, route, block.timestamp + PROPOSAL_DELAY
);
}

function swap(IERC20 assetIn, IERC20 assetOut, uint256 maxAmountIn, uint256 minAmountOut, bytes memory route)
public
returns (uint256 amountIn, uint256 amountOut)
{
uint256 balanceIn = assetIn.balanceOf(msg.sender);
Expand Down Expand Up @@ -202,14 +220,19 @@ contract ExaPlugin is AccessControl, BasePlugin, IExaAccount {
address market = address(proposal.market);
if (market == address(0)) revert NoProposal();

uint256 amount = proposal.amount;
if (market != address(EXA_WETH)) {
_executeFromSender(market, 0, abi.encodeCall(IERC4626.withdraw, (proposal.amount, proposal.receiver, msg.sender)));
return;
_executeFromSender(market, 0, abi.encodeCall(IERC4626.withdraw, (amount, proposal.receiver, msg.sender)));
} else {
_executeFromSender(market, 0, abi.encodeCall(IERC4626.withdraw, (amount, address(this), msg.sender)));
WETH.withdraw(amount);
proposal.receiver.safeTransferETH(amount);
}

if (proposal.swapData.length != 0) {
SwapData memory data = abi.decode(proposal.swapData, (SwapData));
swap(IERC20(proposal.market.asset()), data.assetOut, amount, data.minAmountOut, data.route);
}
uint256 amount = proposal.amount;
_executeFromSender(market, 0, abi.encodeCall(IERC4626.withdraw, (amount, address(this), msg.sender)));
WETH.withdraw(amount);
proposal.receiver.safeTransferETH(amount);
}

function collectCollateral(
Expand Down Expand Up @@ -382,60 +405,64 @@ contract ExaPlugin is AccessControl, BasePlugin, IExaAccount {
functionId: uint8(FunctionId.RUNTIME_VALIDATION_BALANCER),
dependencyIndex: 0
});
manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](14);
manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](15);
manifest.runtimeValidationFunctions[0] = ManifestAssociatedFunction({
executionSelector: IExaAccount.propose.selector,
associatedFunction: selfRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[1] = ManifestAssociatedFunction({
executionSelector: IExaAccount.swap.selector,
executionSelector: IExaAccount.proposeSwap.selector,
associatedFunction: selfRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[2] = ManifestAssociatedFunction({
executionSelector: IExaAccount.swap.selector,
associatedFunction: selfRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[3] = ManifestAssociatedFunction({
executionSelector: IExaAccount.crossRepay.selector,
associatedFunction: keeperOrSelfRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[3] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[4] = ManifestAssociatedFunction({
executionSelector: IExaAccount.repay.selector,
associatedFunction: keeperOrSelfRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[4] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[5] = ManifestAssociatedFunction({
executionSelector: IExaAccount.rollDebt.selector,
associatedFunction: keeperOrSelfRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[5] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[6] = ManifestAssociatedFunction({
executionSelector: IExaAccount.withdraw.selector,
associatedFunction: keeperOrSelfRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[6] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[7] = ManifestAssociatedFunction({
executionSelector: IExaAccount.collectCollateral.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[7] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[8] = ManifestAssociatedFunction({
executionSelector: bytes4(keccak256("collectCredit(uint256,uint256,uint256,bytes)")),
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[8] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[9] = ManifestAssociatedFunction({
executionSelector: bytes4(keccak256("collectCredit(uint256,uint256,uint256,uint256,bytes)")),
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[9] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[10] = ManifestAssociatedFunction({
executionSelector: IExaAccount.collectDebit.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[10] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[11] = ManifestAssociatedFunction({
executionSelector: IExaAccount.collectInstallments.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[11] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[12] = ManifestAssociatedFunction({
executionSelector: IExaAccount.poke.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[12] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[13] = ManifestAssociatedFunction({
executionSelector: IExaAccount.pokeETH.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[13] = ManifestAssociatedFunction({
manifest.runtimeValidationFunctions[14] = ManifestAssociatedFunction({
executionSelector: this.receiveFlashLoan.selector,
associatedFunction: balancerRuntimeValidationFunction
});
Expand Down Expand Up @@ -750,3 +777,9 @@ struct RepayCallbackData {
uint256 positionAssets;
uint256 maxRepay;
}

struct SwapData {
IERC20 assetOut;
uint256 minAmountOut;
bytes route;
}
Loading

0 comments on commit d5e49ab

Please sign in to comment.