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 ae45988
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 80 deletions.
142 changes: 71 additions & 71 deletions contracts/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,75 +1,75 @@
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)
ExaAccountFactoryTest:testFuzz_createAccount_EOAOwners(uint256,address[63]) (runs: 256, μ: 3558282, ~: 3424192)
ExaAccountFactoryTest:test_deploy_deploysToSameAddress() (gas: 26255734)
ExaPluginTest:testFork_crossRepay_repays() (gas: 15238277)
ExaPluginTest:testFork_debitCollateral_collects() (gas: 15327356)
ExaPluginTest:testFork_swap_swaps() (gas: 12508201)
ExaPluginTest:test_borrowAtMaturity_reverts_withUnauthorized_whenReceiverNotCollector() (gas: 409049)
ExaPluginTest:test_borrow_reverts_withUnauthorized_whenReceiverNotCollector() (gas: 408605)
ExaPluginTest:test_collectCredit_collects() (gas: 918972)
ExaPluginTest:test_collectCredit_collects_whenHealthFactorHigherThanOne() (gas: 799574)
ExaPluginTest:test_collectCredit_collects_withEnoughSlippage() (gas: 798243)
ExaPluginTest:test_collectCredit_collects_withPrevIssuerSignature() (gas: 994810)
ExaPluginTest:test_collectCredit_passes_whenProposalLeavesEnoughLiquidity() (gas: 1010699)
ExaPluginTest:test_collectCredit_reverts_asNotKeeper() (gas: 362825)
ExaPluginTest:test_collectCredit_reverts_whenDisagreement() (gas: 556237)
ExaPluginTest:test_collectCredit_reverts_whenExpired() (gas: 358906)
ExaPluginTest:test_collectCredit_reverts_whenHealthFactorLowerThanOne() (gas: 1000947)
ExaPluginTest:test_collectCredit_reverts_whenPrevSignatureNotValidAnymore() (gas: 461648)
ExaPluginTest:test_collectCredit_reverts_whenProposalCausesInsufficientLiquidity() (gas: 1010853)
ExaPluginTest:test_collectCredit_reverts_whenReplay() (gas: 841164)
ExaPluginTest:test_collectCredit_reverts_whenTimelocked() (gas: 355051)
ExaPluginTest:test_collectCredit_toleratesTimeDrift() (gas: 801588)
ExaPluginTest:test_collectDebit_collects() (gas: 651810)
ExaPluginTest:test_collectDebit_collects_whenProposalLeavesEnoughLiquidity() (gas: 857927)
ExaPluginTest:test_collectDebit_reverts_asNotKeeper() (gas: 362636)
ExaPluginTest:test_collectDebit_reverts_whenExpired() (gas: 358623)
ExaPluginTest:test_collectDebit_reverts_whenProposalCausesInsufficientLiquidity() (gas: 854815)
ExaPluginTest:test_collectDebit_reverts_whenReplay() (gas: 691206)
ExaPluginTest:test_collectDebit_reverts_whenTimelocked() (gas: 354881)
ExaPluginTest:test_collectDebit_toleratesTimeDrift() (gas: 651906)
ExaPluginTest:test_collectInstallments_collects() (gas: 1336785)
ExaPluginTest:test_collectInstallments_revertsWhenNoSlippage() (gas: 1203106)
ExaPluginTest:test_collectInstallments_reverts_asNotKeeper() (gas: 363677)
ExaPluginTest:test_collectInstallments_reverts_whenExpired() (gas: 360956)
ExaPluginTest:test_collectInstallments_reverts_whenReplay() (gas: 1068675)
ExaPluginTest:test_collectInstallments_reverts_whenTimelocked() (gas: 357222)
ExaPluginTest:test_collectInstallments_toleratesTimeDrift() (gas: 1193376)
ExaPluginTest:test_crossRepay_repays() (gas: 1462145)
ExaPluginTest:test_crossRepay_repays_whenKeeper() (gas: 1458924)
ExaPluginTest:test_crossRepay_reverts_whenDisagreement() (gas: 1572378)
ExaPluginTest:test_crossRepay_reverts_whenNotKeeper() (gas: 958869)
ExaPluginTest:test_debitCollateral_collects() (gas: 774590)
ExaPluginTest:test_exitMarket_reverts() (gas: 389196)
ExaPluginTest:test_marketWithdraw_transfersAsset_asOwner() (gas: 824331)
ExaPluginTest:test_onUninstall_uninstalls() (gas: 284175)
ExaPluginTest:test_poke() (gas: 316041)
ExaPluginTest:test_pokeETH_deposits() (gas: 378077)
ExaPluginTest:test_propose_emitsProposed() (gas: 221380)
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)
ExaPluginTest:test_repay_partiallyRepays() (gas: 1244517)
ExaPluginTest:test_repay_partiallyRepays_whenKeeper() (gas: 1186677)
ExaPluginTest:test_repay_repays() (gas: 1162865)
ExaPluginTest:test_repay_repays_whenKeeper() (gas: 1105519)
ExaPluginTest:test_rollDebt_rolls() (gas: 1231945)
ExaPluginTest:test_rollDebt_rolls_asKeeper() (gas: 1231637)
ExaPluginTest:test_setCollector_emitsCollectorSet() (gas: 40472)
ExaPluginTest:test_setCollector_reverts_whenAddressZero() (gas: 32267)
ExaPluginTest:test_setCollector_reverts_whenNotAdmin() (gas: 33751)
ExaPluginTest:test_setCollector_sets_whenAdmin() (gas: 39862)
ExaPluginTest:test_swap_reverts_withDisagreement() (gas: 288926)
ExaPluginTest:test_swap_swaps() (gas: 268670)
ExaPluginTest:test_withdrawWETH_transfersETH() (gas: 854611)
ExaPluginTest:test_withdraw_reverts_whenNoProposal() (gas: 417738)
ExaPluginTest:test_withdraw_reverts_whenNoProposalKeeper() (gas: 358083)
ExaPluginTest:test_withdraw_reverts_whenNotKeeper() (gas: 355165)
ExaPluginTest:test_withdraw_reverts_whenReceiverIsContractAndMarketNotWETH() (gas: 626754)
ExaPluginTest:test_withdraw_reverts_whenTimelocked() (gas: 312707)
ExaPluginTest:test_withdraw_reverts_whenTimelockedKeeper() (gas: 314550)
ExaPluginTest:test_withdraw_reverts_whenWrongAmount() (gas: 314127)
ExaPluginTest:test_withdraw_reverts_whenWrongMarket() (gas: 314297)
ExaPluginTest:test_withdraw_reverts_whenWrongReceiver() (gas: 313731)
ExaPluginTest:test_withdraw_transfersAsset_asKeeper() (gas: 826029)
ExaPluginTest:test_withdraw_withdrawsProposed() (gas: 816775)
InstallmentsPreviewerTest:test_preview_returns() (gas: 135598)
IssuerCheckerTest:test_setIssuer_emits_IssuerSet() (gas: 70861)
IssuerCheckerTest:test_setIssuer_reverts_whenNotAdmin() (gas: 37272)
Expand Down
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 = 649
deny_warnings = true
verbosity = 3
isolate = true
Expand Down
46 changes: 38 additions & 8 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,30 @@ 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)
// TODO add to manifest, only self. or merge with propose
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, msg.sender, amount, block.timestamp + PROPOSAL_DELAY, assetOut, minAmountOut, route
);
}

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 +221,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 @@ -750,3 +774,9 @@ struct RepayCallbackData {
uint256 positionAssets;
uint256 maxRepay;
}

struct SwapData {
IERC20 assetOut;
uint256 minAmountOut;
bytes route;
}
14 changes: 14 additions & 0 deletions contracts/src/IExaAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { IERC4626 } from "openzeppelin-contracts/contracts/interfaces/IERC4626.s

interface IExaAccount {
function propose(IMarket market, uint256 amount, address receiver) external;
function proposeSwap(IMarket market, uint256 amount, IERC20 assetOut, uint256 minAmountOut, bytes memory route)
external;
function swap(IERC20 assetIn, IERC20 assetOut, uint256 maxAmountIn, uint256 minAmountOut, bytes memory route)
external
returns (uint256 amountIn, uint256 amountOut);
Expand Down Expand Up @@ -62,6 +64,17 @@ event Proposed(
address indexed account, IMarket indexed market, address indexed receiver, uint256 amount, uint256 unlock
);

event SwapProposed(
address indexed account,
IMarket indexed market,
address indexed receiver,
uint256 amount,
uint256 unlock,
IERC20 assetOut,
uint256 minAmountOut,
bytes route
);

struct FixedPool {
uint256 borrowed;
uint256 supplied;
Expand All @@ -87,6 +100,7 @@ struct Proposal {
IMarket market;
address receiver;
uint256 timestamp;
bytes swapData;
}

error BorrowLimitExceeded();
Expand Down

0 comments on commit ae45988

Please sign in to comment.