diff --git a/contracts/src/ExaPlugin.sol b/contracts/src/ExaPlugin.sol index 257d6f59..5ba5e5fa 100644 --- a/contracts/src/ExaPlugin.sol +++ b/contracts/src/ExaPlugin.sol @@ -112,12 +112,28 @@ 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 })) + }); + // TODO emit + } + + 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); @@ -202,14 +218,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( @@ -750,3 +771,9 @@ struct RepayCallbackData { uint256 positionAssets; uint256 maxRepay; } + +struct SwapData { + IERC20 assetOut; + uint256 minAmountOut; + bytes route; +} diff --git a/contracts/src/IExaAccount.sol b/contracts/src/IExaAccount.sol index 62e315e4..28b7bfd4 100644 --- a/contracts/src/IExaAccount.sol +++ b/contracts/src/IExaAccount.sol @@ -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); @@ -87,6 +89,7 @@ struct Proposal { IMarket market; address receiver; uint256 timestamp; + bytes swapData; } error BorrowLimitExceeded();