Skip to content

Commit

Permalink
🚧 add collect collateral
Browse files Browse the repository at this point in the history
  • Loading branch information
itofarina committed Nov 28, 2024
1 parent d056282 commit fee37b6
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 17 deletions.
85 changes: 68 additions & 17 deletions contracts/src/ExaPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,34 @@ contract ExaPlugin is AccessControl, BasePlugin, IExaAccount {
_checkLiquidity(msg.sender);
}

function collectCollateral(
uint256 amount,
IMarket collateral,
uint256 amountIn,
uint256 timestamp,
bytes memory route,
bytes calldata signature
) external onlyIssuer(amount, timestamp, signature) {
bytes memory data = _hash(
abi.encodePacked(
bytes1(0x03),
abi.encode(
CollectCollateralCallbackData({
debit: amount,
owner: msg.sender,
collateral: collateral,
amountIn: amountIn,
route: route
})
)
)
);
IPluginExecutor(msg.sender).executeFromPluginExternal(
address(collateral), 0, abi.encodeCall(IERC20.approve, (address(this), amountIn))
);
_flashLoan(amount, data);
}

function collectInstallments(
uint256 firstMaturity,
uint256[] calldata amounts,
Expand Down Expand Up @@ -399,20 +427,21 @@ contract ExaPlugin is AccessControl, BasePlugin, IExaAccount {

/// @inheritdoc BasePlugin
function pluginManifest() external pure override returns (PluginManifest memory manifest) {
manifest.executionFunctions = new bytes4[](13);
manifest.executionFunctions = new bytes4[](14);
manifest.executionFunctions[0] = this.propose.selector;
manifest.executionFunctions[1] = this.repay.selector;
manifest.executionFunctions[2] = this.crossRepay.selector;
manifest.executionFunctions[3] = this.rollDebt.selector;
manifest.executionFunctions[4] = bytes4(keccak256("collectCredit(uint256,uint256,uint256,bytes)"));
manifest.executionFunctions[5] = bytes4(keccak256("collectCredit(uint256,uint256,uint256,uint256,bytes)"));
manifest.executionFunctions[6] = this.collectDebit.selector;
manifest.executionFunctions[7] = this.collectInstallments.selector;
manifest.executionFunctions[8] = this.lifiSwap.selector;
manifest.executionFunctions[9] = this.poke.selector;
manifest.executionFunctions[10] = this.pokeETH.selector;
manifest.executionFunctions[11] = this.withdraw.selector;
manifest.executionFunctions[12] = this.receiveFlashLoan.selector;
manifest.executionFunctions[7] = this.collectCollateral.selector;
manifest.executionFunctions[8] = this.collectInstallments.selector;
manifest.executionFunctions[9] = this.lifiSwap.selector;
manifest.executionFunctions[10] = this.poke.selector;
manifest.executionFunctions[11] = this.pokeETH.selector;
manifest.executionFunctions[12] = this.withdraw.selector;
manifest.executionFunctions[13] = this.receiveFlashLoan.selector;

ManifestFunction memory selfRuntimeValidationFunction = ManifestFunction({
functionType: ManifestAssociatedFunctionType.SELF,
Expand All @@ -429,7 +458,7 @@ contract ExaPlugin is AccessControl, BasePlugin, IExaAccount {
functionId: uint8(FunctionId.RUNTIME_VALIDATION_BALANCER),
dependencyIndex: 0
});
manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](13);
manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](14);
manifest.runtimeValidationFunctions[0] = ManifestAssociatedFunction({
executionSelector: IExaAccount.propose.selector,
associatedFunction: selfRuntimeValidationFunction
Expand Down Expand Up @@ -463,22 +492,26 @@ contract ExaPlugin is AccessControl, BasePlugin, IExaAccount {
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[8] = ManifestAssociatedFunction({
executionSelector: IExaAccount.collectInstallments.selector,
executionSelector: IExaAccount.collectCollateral.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[9] = ManifestAssociatedFunction({
executionSelector: IExaAccount.poke.selector,
executionSelector: IExaAccount.collectInstallments.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[10] = ManifestAssociatedFunction({
executionSelector: IExaAccount.pokeETH.selector,
executionSelector: IExaAccount.poke.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[11] = ManifestAssociatedFunction({
executionSelector: IExaAccount.withdraw.selector,
executionSelector: IExaAccount.pokeETH.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[12] = ManifestAssociatedFunction({
executionSelector: IExaAccount.withdraw.selector,
associatedFunction: keeperRuntimeValidationFunction
});
manifest.runtimeValidationFunctions[13] = ManifestAssociatedFunction({
executionSelector: this.receiveFlashLoan.selector,
associatedFunction: balancerRuntimeValidationFunction
});
Expand Down Expand Up @@ -523,28 +556,38 @@ contract ExaPlugin is AccessControl, BasePlugin, IExaAccount {
assert(msg.sender == address(BALANCER_VAULT) && callHash == keccak256(data));
delete callHash;

uint256 actualRepay;
if (data[0] == 0x01) {
RepayCallbackData memory r = abi.decode(data[1:], (RepayCallbackData));
actualRepay = EXA_USDC.repayAtMaturity(r.maturity, r.positionAssets, r.maxRepay, r.borrower).min(
uint256 actualRepay = EXA_USDC.repayAtMaturity(r.maturity, r.positionAssets, r.maxRepay, r.borrower).min(
EXA_USDC.maxWithdraw(r.borrower)
);

if (actualRepay < r.maxRepay) EXA_USDC.deposit(r.maxRepay - actualRepay, r.borrower);

EXA_USDC.withdraw(r.maxRepay, address(BALANCER_VAULT), r.borrower);

_checkLiquidity(r.borrower);
} else if (data[0] == 0x02) {
CrossRepayCallbackData memory c = abi.decode(data[1:], (CrossRepayCallbackData));
actualRepay = EXA_USDC.repayAtMaturity(c.maturity, c.positionAssets, c.maxRepay, c.borrower);
uint256 actualRepay = EXA_USDC.repayAtMaturity(c.maturity, c.positionAssets, c.maxRepay, c.borrower);

c.marketIn.withdraw(c.amountIn, address(this), c.borrower);
uint256 out = _lifiSwap(IERC20(c.marketIn.asset()), IERC20(EXA_USDC.asset()), c.amountIn, actualRepay, c.route);
IERC20(EXA_USDC.asset()).safeTransfer(address(BALANCER_VAULT), c.maxRepay);
EXA_USDC.deposit(out - actualRepay, c.borrower);

_checkLiquidity(c.borrower);
} else {
CollectCollateralCallbackData memory c = abi.decode(data[1:], (CollectCollateralCallbackData));

c.collateral.withdraw(c.amountIn, address(this), c.owner);
uint256 out = _lifiSwap(IERC20(c.collateral.asset()), IERC20(EXA_USDC.asset()), c.amountIn, c.debit, c.route);

IERC20(EXA_USDC.asset()).safeTransfer(address(BALANCER_VAULT), c.debit);
IERC20(EXA_USDC.asset()).safeTransfer(collector, c.debit);
if (out > c.debit) EXA_USDC.deposit(out - c.debit, c.owner);

_checkLiquidity(c.owner);
}
}

Expand Down Expand Up @@ -697,3 +740,11 @@ struct CrossRepayCallbackData {
uint256 amountIn;
bytes route;
}

struct CollectCollateralCallbackData {
uint256 debit;
address owner;
IMarket collateral;
uint256 amountIn;
bytes route;
}
8 changes: 8 additions & 0 deletions contracts/src/IExaAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ interface IExaAccount {
bytes calldata signature
) external;
function collectDebit(uint256 amount, uint256 timestamp, bytes calldata signature) external;
function collectCollateral(
uint256 amount,
IMarket collateral,
uint256 amountIn,
uint256 timestamp,
bytes memory route,
bytes calldata signature
) external;
function collectInstallments(
uint256 firstMaturity,
uint256[] calldata amounts,
Expand Down
27 changes: 27 additions & 0 deletions contracts/test/ExaPlugin.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,27 @@ contract ExaPluginTest is ForkTest {
assertEq(usdc.balanceOf(collector), 0);
}

// TODO
function test_debitCollateral_collects() external {
setUpLifiFork();
uint256 amount = 0.0004e8;
IMarket exaWBTC = IMarket(protocol("MarketWBTC"));
deal(exaWBTC.asset(), address(account), amount);

vm.startPrank(keeper);
account.poke(exaWBTC);

bytes memory route = bytes.concat(
hex"4666fc80b7c64668375a12ff485d0a88dee3ac5d82d77587a6be542b9233c5eb13830c4c00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000",
abi.encodePacked(address(exaPlugin)),
hex"00000000000000000000000000000000000000000000000000000000018f7705000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000034578610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a30783030303030303030303030303030303030303030303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000000000111111125421ca6dc452d289314280a0f8842a65000000000000000000000000111111125421ca6dc452d289314280a0f8842a6500000000000000000000000068f180fcce6836688e9084f035309e29bf0a20950000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000000000000009c4000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000004e807ed2379000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba00000000000000000000000068f180fcce6836688e9084f035309e29bf0a20950000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000000000000000009c4000000000000000000000000000000000000000000000000000000000018f770400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000039600000000000000000000000000000000000000000000000000000000037800a007e5c0d20000000000000000000000000000000000000000000000000003540000f051204c4af8dbc524681930a27b2f1af5bcc8062e6fb768f180fcce6836688e9084f035309e29bf0a209500447dc2038200000000000000000000000068f180fcce6836688e9084f035309e29bf0a209500000000000000000000000042000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000002495763e3d93b6000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba00000000000000000000000042f527f50f16a103b6ccab48bccca214500c102100a0c9e75c480000000000000013130c0000000000000000000000000000000000000000000002360001d300006302a000000000000000000000000000000000000000000000000000000000005f6305ee63c1e580c1738d90e2e26c35784a0d3e3d8a9f795074bca44200000000000000000000000000000000000006111111125421ca6dc452d289314280a0f8842a655106a062ae8a9c5e11aaa026fc2670b0d65ccc8b285842000000000000000000000000000000000000060004cac88ea9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009708bd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000111111125421ca6dc452d289314280a0f8842a6500000000000000000000000000000000000000000000000000000000671fb839000000000000000000000000000000000000000000000000000000000000000100000000000000000000000042000000000000000000000000000000000000060000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a02a0000000000000000000000000000000000000000000000000000000000097095eee63c1e580d4cb5566b5c16ef2f4a08b1438052013171212a24200000000000000000000000000000000000006111111125421ca6dc452d289314280a0f8842a65000000000000000000002a94d114000000000000000000000000000000000000000000000000"
);

uint256 balance = usdc.balanceOf(exaPlugin.collector());
account.collectCollateral(21e6, exaWBTC, amount, block.timestamp, route, _issuerOp(21e6, block.timestamp));
assertEq(usdc.balanceOf(exaPlugin.collector()) - balance, 21e6);
}

function test_collectInstallments_collects() external {
vm.startPrank(keeper);
account.poke(exaEXA);
Expand Down Expand Up @@ -783,6 +804,11 @@ contract ExaPluginTest is ForkTest {
vm.setEnv("BROADCAST_ISSUERCHECKER_ADDRESS", "");
vm.setEnv("BROADCAST_REFUNDER_ADDRESS", "");

issuerChecker = IssuerChecker(broadcast("IssuerChecker"));
vm.prank(acct("deployer"));
issuerChecker.setIssuer(issuer);
domainSeparator = issuerChecker.DOMAIN_SEPARATOR();

exaPlugin = new ExaPlugin(
IAuditor(protocol("Auditor")),
IMarket(protocol("MarketUSDC")),
Expand All @@ -793,6 +819,7 @@ contract ExaPluginTest is ForkTest {
issuerChecker,
acct("collector")
);

exaPlugin.grantRole(exaPlugin.KEEPER_ROLE(), keeper);
usdc = MockERC20(protocol("USDC"));

Expand Down

0 comments on commit fee37b6

Please sign in to comment.