From 2d94fe4c1cccf15f7555e4f16ad78ed9d40f5d53 Mon Sep 17 00:00:00 2001 From: Igor Yalovoy Date: Thu, 14 Nov 2024 10:51:34 -0600 Subject: [PATCH] Add Bridger integration to AaveWithdrawWorkflow with new withdrawAndBridge function and corresponding tests. --- .../145-upgrade_access_protocol.s.sol | 3 +- src/access/workflows/AaveWithdrawWorkflow.sol | 24 +++++++- .../fork/workflows/AaveWithdrawWorkflow.t.sol | 57 ++++++++++++++++++- test/helpers/BridgeDataHelper.sol | 9 +++ 4 files changed, 88 insertions(+), 5 deletions(-) diff --git a/script/migrations/145-upgrade_access_protocol.s.sol b/script/migrations/145-upgrade_access_protocol.s.sol index 19624095..0f43eb62 100644 --- a/script/migrations/145-upgrade_access_protocol.s.sol +++ b/script/migrations/145-upgrade_access_protocol.s.sol @@ -50,7 +50,8 @@ contract DeployScript is Script, MigrationHelper { registry.allowWorkflow(address(aaveRepayWorkflow)); vm.broadcast(deployerPrivateKey); - AaveWithdrawWorkflow aaveWithdrawWorkflow = new AaveWithdrawWorkflow(getAavePoolProvider()); + AaveWithdrawWorkflow aaveWithdrawWorkflow = + new AaveWithdrawWorkflow(getAavePoolProvider(), _getChainDeployment("AccessRegistry")); saveContractAddress("AaveWithdrawWorkflow", address(aaveWithdrawWorkflow)); vm.broadcast(deployerPrivateKey); diff --git a/src/access/workflows/AaveWithdrawWorkflow.sol b/src/access/workflows/AaveWithdrawWorkflow.sol index 7495621e..6e425c2e 100644 --- a/src/access/workflows/AaveWithdrawWorkflow.sol +++ b/src/access/workflows/AaveWithdrawWorkflow.sol @@ -5,6 +5,7 @@ import {IERC20} from "@openzeppelin-5.0.1/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin-5.0.1/contracts/token/ERC20/utils/SafeERC20.sol"; import {Address} from "@openzeppelin-5.0.1/contracts/utils/Address.sol"; import {IWETH} from "@kinto-core/interfaces/IWETH.sol"; +import {IBridger} from "@kinto-core/interfaces/bridger/IBridger.sol"; import {IAavePool, IPoolAddressesProvider} from "@kinto-core/interfaces/external/IAavePool.sol"; @@ -20,6 +21,8 @@ contract AaveWithdrawWorkflow { /// @notice Address of the PoolAddressesProvider contract IPoolAddressesProvider public immutable poolAddressProvider; + /// @notice Address of the Bridger contract + IBridger public immutable bridger; /* ============ Constructor ============ */ @@ -27,8 +30,9 @@ contract AaveWithdrawWorkflow { * @notice Initializes the contract with Aave's pool address provider * @param poolAddressProvider_ The address of Aave's pool address provider */ - constructor(address poolAddressProvider_) { + constructor(address poolAddressProvider_, address bridger_) { poolAddressProvider = IPoolAddressesProvider(poolAddressProvider_); + bridger = IBridger(bridger_); } /* ============ External Functions ============ */ @@ -38,7 +42,7 @@ contract AaveWithdrawWorkflow { * @param asset The address of the asset to withdraw * @param amount The amount to withdraw (use type(uint256).max for max available) */ - function withdraw(address asset, uint256 amount) external { + function withdraw(address asset, uint256 amount) public { address pool = poolAddressProvider.getPool(); // If amount is max uint256, withdraw all available @@ -49,4 +53,20 @@ contract AaveWithdrawWorkflow { // Withdraw from Aave IAavePool(pool).withdraw(asset, amount, address(this)); } + + function withdrawAndBridge( + address asset, + uint256 amount, + address kintoWallet, + IBridger.BridgeData calldata bridgeData + ) external payable returns (uint256 amountOut) { + withdraw(asset, amount); + + // Approve max allowance to save on gas for future transfers + if (IERC20(asset).allowance(address(this), address(bridger)) < amount) { + IERC20(asset).forceApprove(address(bridger), type(uint256).max); + } + + return bridger.depositERC20(asset, amount, kintoWallet, asset, amount, bytes(""), bridgeData); + } } diff --git a/test/fork/workflows/AaveWithdrawWorkflow.t.sol b/test/fork/workflows/AaveWithdrawWorkflow.t.sol index 7e008ee9..994ba1bc 100644 --- a/test/fork/workflows/AaveWithdrawWorkflow.t.sol +++ b/test/fork/workflows/AaveWithdrawWorkflow.t.sol @@ -9,6 +9,9 @@ import {IAccessPoint} from "@kinto-core/interfaces/IAccessPoint.sol"; import {AccessRegistry} from "@kinto-core/access/AccessRegistry.sol"; import {AaveWithdrawWorkflow} from "@kinto-core/access/workflows/AaveWithdrawWorkflow.sol"; +import {IBridger} from "@kinto-core/interfaces/bridger/IBridger.sol"; +import {Bridger} from "@kinto-core/bridger/Bridger.sol"; +import {BridgeDataHelper} from "@kinto-core-test/helpers/BridgeDataHelper.sol"; import "@kinto-core-test/fork/const.sol"; import "@kinto-core-test/helpers/UUPSProxy.sol"; @@ -23,9 +26,10 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "forge-std/console2.sol"; -contract AaveWithdrawWorkflowTest is SignatureHelper, ForkTest, ArtifactsReader, Constants { +contract AaveWithdrawWorkflowTest is SignatureHelper, ForkTest, ArtifactsReader, Constants, BridgeDataHelper { using stdJson for string; + Bridger internal bridger; AccessRegistry internal accessRegistry; IAccessPoint internal accessPoint; AaveWithdrawWorkflow internal aaveWithdrawWorkflow; @@ -34,6 +38,7 @@ contract AaveWithdrawWorkflowTest is SignatureHelper, ForkTest, ArtifactsReader, function setUp() public override { super.setUp(); + bridger = Bridger(payable(_getChainDeployment("Bridger"))); accessRegistry = AccessRegistry(_getChainDeployment("AccessRegistry")); aavePool = IAavePool(IPoolAddressesProvider(ARB_AAVE_POOL_PROVIDER).getPool()); @@ -45,7 +50,7 @@ contract AaveWithdrawWorkflowTest is SignatureHelper, ForkTest, ArtifactsReader, accessPoint = accessRegistry.deployFor(address(alice0)); vm.label(address(accessPoint), "accessPoint"); - aaveWithdrawWorkflow = new AaveWithdrawWorkflow(ARB_AAVE_POOL_PROVIDER); + aaveWithdrawWorkflow = new AaveWithdrawWorkflow(ARB_AAVE_POOL_PROVIDER, address(bridger)); vm.label(address(aaveWithdrawWorkflow), "aaveWithdrawWorkflow"); vm.prank(accessRegistry.owner()); @@ -126,4 +131,52 @@ contract AaveWithdrawWorkflowTest is SignatureHelper, ForkTest, ArtifactsReader, ); assertEq(IERC20(aToken).balanceOf(address(accessPoint)), 0, "Invalid aToken balance"); } + + function testWithdrawAndBridge() public { + address assetToWithdraw = USDC_ARBITRUM; + uint256 amountToWithdraw = 1e6; + address aToken = aavePool.getReserveData(assetToWithdraw).aTokenAddress; + + // Supply first to have something to withdraw + deal(assetToWithdraw, address(accessPoint), amountToWithdraw); + vm.startPrank(address(accessPoint)); + IERC20(assetToWithdraw).approve(address(aavePool), amountToWithdraw); + aavePool.supply(assetToWithdraw, amountToWithdraw, address(accessPoint), 0); + vm.stopPrank(); + + IBridger.BridgeData memory bridgeData = bridgeData[block.chainid][USDC_ARBITRUM]; + + // Get initial balances + uint256 initialAccessPointBalance = IERC20(assetToWithdraw).balanceOf(address(accessPoint)); + uint256 initialATokenBalance = IERC20(aToken).balanceOf(address(accessPoint)); + uint256 initialBridgerBalance = IERC20(assetToWithdraw).balanceOf(address(bridger)); + uint256 initialVaultBalance = IERC20(assetToWithdraw).balanceOf(address(bridgeData.vault)); + + // Prepare workflow data + bytes memory workflowData = abi.encodeWithSelector( + AaveWithdrawWorkflow.withdrawAndBridge.selector, assetToWithdraw, amountToWithdraw, alice0, bridgeData + ); + + // Execute the withdrawAndBridge workflow + vm.prank(alice0); + accessPoint.execute(address(aaveWithdrawWorkflow), workflowData); + + // Assert balances changed correctly + assertEq( + IERC20(assetToWithdraw).balanceOf(address(accessPoint)), + initialAccessPointBalance, + "Invalid access point balance" + ); + assertEq( + IERC20(aToken).balanceOf(address(accessPoint)), + initialATokenBalance - amountToWithdraw, + "Invalid aToken balance" + ); + assertEq(IERC20(assetToWithdraw).balanceOf(address(bridger)), initialBridgerBalance, "Invalid bridger balance"); + assertEq( + IERC20(assetToWithdraw).balanceOf(address(bridgeData.vault)), + initialVaultBalance + amountToWithdraw, + "Invalid vault balance" + ); + } } diff --git a/test/helpers/BridgeDataHelper.sol b/test/helpers/BridgeDataHelper.sol index 10d862f7..5aa725e8 100644 --- a/test/helpers/BridgeDataHelper.sol +++ b/test/helpers/BridgeDataHelper.sol @@ -120,6 +120,15 @@ abstract contract BridgeDataHelper is Constants { options: bytes("") }); + bridgeData[ARBITRUM_CHAINID][USDC_ARBITRUM] = IBridger.BridgeData({ + vault: 0xC88A469B96A62d4DA14Dc5e23BDBC495D2b15C6B, + gasFee: 1e16, + msgGasLimit: 500_000, + connector: 0xD97E3cD27fb8af306b2CD42A61B7cbaAF044D08D, + execPayload: bytes(""), + options: bytes("") + }); + bridgeData[ARBITRUM_CHAINID][DAI_ARBITRUM] = IBridger.BridgeData({ vault: 0x36E2DBe085eE4d028fD60f70670f662365d0E978, gasFee: 1e16,