// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Script, console} from "forge-std/Script.sol"; import {AaveQuotePushFlashReceiver} from "../../contracts/flash/AaveQuotePushFlashReceiver.sol"; interface IDODOPMMPoolQuote { function querySellQuote(address trader, uint256 payQuoteAmount) external view returns (uint256 receiveBaseAmount, uint256 mtFee); } /** * @title RunMainnetAaveCwusdcUsdcQuotePushOnce * @notice Single Aave flash quote-push round: borrow USDC -> PMM quote-in -> external unwind -> repay. * * Prereqs: deploy AaveQuotePushFlashReceiver + an IAaveExternalUnwinder (e.g. UniswapV3ExternalUnwinder). * * Env (required unless noted): * PRIVATE_KEY * AAVE_QUOTE_PUSH_RECEIVER_MAINNET * DODO_PMM_INTEGRATION_MAINNET * QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET * FLASH_QUOTE_AMOUNT_RAW gross USDC borrowed / pushed (6 decimals raw) * * Optional: * POOL_CWUSDC_USDC_MAINNET default 0x69776fc607e9edA8042e320e7e43f54d06c68f0E * CWUSDC_MAINNET default canonical cWUSDC * USDC_MAINNET default official USDC * MIN_OUT_PMM if unset, derived from querySellQuote(receiver, amount) * MIN_OUT_PMM_NUM / MIN_OUT_PMM_DEN (defaults 985/1000) * MIN_OUT_PMM_NUM / MIN_OUT_PMM_DEN * MIN_OUT_UNWIND if unset, amount + ceil(amount * AAVE_FLASH_PREMIUM_BPS / 10000) + MIN_OUT_UNWIND_BUFFER_RAW * AAVE_FLASH_PREMIUM_BPS default 5 (Aave V3 simple flash typical) * MIN_OUT_UNWIND_BUFFER_RAW default 5000 raw (~0.005 USDC) headroom * UNWIND_MODE 0 = Uniswap V3 exactInputSingle (abi.encode uint24 fee); 1 = DODO pool (abi.encode address pool); * 2 = Uniswap V3 exactInput multi-hop: unwindData = abi.encode(path), path from UNWIND_V3_PATH_HEX * UNWIND_V3_FEE_U24 required when UNWIND_MODE=0 (e.g. 500, 3000, 10000) * UNWIND_DODO_POOL required when UNWIND_MODE=1 * UNWIND_V3_PATH_HEX required when UNWIND_MODE=2 — packed exactInput path (see UniswapV3ExternalUnwinder); build via: * bash scripts/verify/build-uniswap-v3-exact-input-path-hex.sh ADDR0 FEE0 ... ADDRN * UNWIND_MODE=4 TwoHopDodoIntegrationUnwinder: set UNWIND_TWO_HOP_POOL_A, UNWIND_TWO_HOP_POOL_B, * UNWIND_TWO_HOP_MID_TOKEN, optional UNWIND_MIN_MID_OUT_RAW (default 1 wei) * UNWIND_MODE=5 DODOToUniswapV3MultiHopExternalUnwinder: * set UNWIND_DODO_POOL, UNWIND_INTERMEDIATE_TOKEN, UNWIND_MIN_INTERMEDIATE_OUT_RAW, * UNWIND_V3_PATH_HEX (path starts at intermediate token and ends at tokenOut) */ contract RunMainnetAaveCwusdcUsdcQuotePushOnce is Script { address internal constant DEFAULT_POOL = 0x69776fc607e9edA8042e320e7e43f54d06c68f0E; address internal constant DEFAULT_CWUSDC = 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a; address internal constant DEFAULT_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; function run() external { uint256 pk = vm.envUint("PRIVATE_KEY"); address receiver = vm.envAddress("AAVE_QUOTE_PUSH_RECEIVER_MAINNET"); address pool = vm.envOr("POOL_CWUSDC_USDC_MAINNET", DEFAULT_POOL); address integration = vm.envAddress("DODO_PMM_INTEGRATION_MAINNET"); address baseToken = vm.envOr("CWUSDC_MAINNET", DEFAULT_CWUSDC); address usdc = vm.envOr("USDC_MAINNET", DEFAULT_USDC); address unwinder = vm.envAddress("QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET"); uint256 amount = vm.envUint("FLASH_QUOTE_AMOUNT_RAW"); uint256 minPmmNum = vm.envOr("MIN_OUT_PMM_NUM", uint256(985)); uint256 minPmmDen = vm.envOr("MIN_OUT_PMM_DEN", uint256(1000)); uint256 minOutPmm = vm.envOr("MIN_OUT_PMM", uint256(0)); if (minOutPmm == 0) { (uint256 baseOut,) = IDODOPMMPoolQuote(pool).querySellQuote(receiver, amount); minOutPmm = (baseOut * minPmmNum) / minPmmDen; } uint256 premiumBps = vm.envOr("AAVE_FLASH_PREMIUM_BPS", uint256(5)); uint256 buf = vm.envOr("MIN_OUT_UNWIND_BUFFER_RAW", uint256(5000)); uint256 minOutUnwind = vm.envOr("MIN_OUT_UNWIND", uint256(0)); if (minOutUnwind == 0) { uint256 premium = (amount * premiumBps) / 10000; minOutUnwind = amount + premium + buf; } uint256 unwindMode = vm.envOr("UNWIND_MODE", uint256(0)); bytes memory unwindData; if (unwindMode == 0) { uint24 fee = uint24(vm.envUint("UNWIND_V3_FEE_U24")); unwindData = abi.encode(fee); } else if (unwindMode == 1) { address dodoPool = vm.envAddress("UNWIND_DODO_POOL"); unwindData = abi.encode(dodoPool); } else if (unwindMode == 2) { string memory pathHex = vm.envString("UNWIND_V3_PATH_HEX"); bytes memory path = vm.parseBytes(pathHex); unwindData = abi.encode(path); } else if (unwindMode == 4) { address poolA = vm.envAddress("UNWIND_TWO_HOP_POOL_A"); address poolB = vm.envAddress("UNWIND_TWO_HOP_POOL_B"); address midToken = vm.envAddress("UNWIND_TWO_HOP_MID_TOKEN"); uint256 minMidOut = vm.envOr("UNWIND_MIN_MID_OUT_RAW", uint256(1)); unwindData = abi.encode(poolA, poolB, midToken, minMidOut); } else if (unwindMode == 5) { address dodoPool = vm.envAddress("UNWIND_DODO_POOL"); address intermediateToken = vm.envAddress("UNWIND_INTERMEDIATE_TOKEN"); uint256 minIntermediateOut = vm.envOr("UNWIND_MIN_INTERMEDIATE_OUT_RAW", uint256(1)); string memory pathHex = vm.envString("UNWIND_V3_PATH_HEX"); bytes memory path = vm.parseBytes(pathHex); unwindData = abi.encode(dodoPool, intermediateToken, minIntermediateOut, path); } else { revert("UNWIND_MODE must be 0, 1, 2, 4, or 5"); } AaveQuotePushFlashReceiver.QuotePushParams memory p = AaveQuotePushFlashReceiver.QuotePushParams({ integration: integration, pmmPool: pool, baseToken: baseToken, externalUnwinder: unwinder, minOutPmm: minOutPmm, minOutUnwind: minOutUnwind, unwindData: unwindData, atomicBridge: AaveQuotePushFlashReceiver.AtomicBridgeParams({ coordinator: address(0), sourceChain: 0, destinationChain: 0, destinationAsset: address(0), bridgeAmount: 0, minDestinationAmount: 0, destinationRecipient: address(0), destinationDeadline: 0, routeId: bytes32(0), settlementMode: bytes32(0), submitCommitment: false }) }); console.log("receiver", receiver); console.log("pool", pool); console.log("amount", amount); console.log("minOutPmm", minOutPmm); console.log("minOutUnwind", minOutUnwind); vm.startBroadcast(pk); AaveQuotePushFlashReceiver(receiver).flashQuotePush(usdc, amount, p); vm.stopBroadcast(); } }