Flash unwinder contracts and scripts, relay lane tuning, trustless bridge and token-aggregation updates.

Made-with: Cursor
This commit is contained in:
defiQUG
2026-04-12 06:33:54 -07:00
parent 662b35ad69
commit 6817f53591
40 changed files with 682 additions and 88 deletions

View File

@@ -13,7 +13,8 @@ contract DeployEnhancedSwapRouter is Script {
address constant PLACEHOLDER = 0x000000000000000000000000000000000000dEaD;
// Ethereum Mainnet addresses
address constant UNISWAP_V3_ROUTER = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;
// Legacy SwapRouter exposes the exactInputSingle ABI used by EnhancedSwapRouter.
address constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
address constant CURVE_3POOL = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
address constant DODOEX_ROUTER = 0xa356867fDCEa8e71AEaF87805808803806231FdC;
address constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;

View File

@@ -24,7 +24,8 @@ contract DeployTrustlessBridge is Script {
uint256 constant DEFAULT_MIN_LIQUIDITY_RATIO_BPS = 11000; // 110%
// Ethereum Mainnet addresses
address constant UNISWAP_V3_ROUTER = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;
// Legacy SwapRouter exposes the exactInputSingle ABI used by SwapRouter.
address constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
address constant CURVE_3POOL = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
address constant ONEINCH_ROUTER = 0x1111111254EEB25477B68fb85Ed929f73A960582;
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

View File

@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script, console} from "forge-std/Script.sol";
import {DODOIntegrationExternalUnwinder} from "../../contracts/flash/DODOIntegrationExternalUnwinder.sol";
/**
* @title DeployDODOIntegrationExternalUnwinder
* @notice Deploy DODOIntegrationExternalUnwinder (unwind via another registered PMM pool).
*
* Env:
* PRIVATE_KEY required
* DODO_PMM_INTEGRATION_MAINNET required (same integration as quote-push source pool)
*
* Usage:
* forge script script/deploy/DeployDODOIntegrationExternalUnwinder.s.sol:DeployDODOIntegrationExternalUnwinder \
* --rpc-url $ETHEREUM_MAINNET_RPC --broadcast -vvvv
*/
contract DeployDODOIntegrationExternalUnwinder is Script {
function run() external {
uint256 pk = vm.envUint("PRIVATE_KEY");
address integration = vm.envAddress("DODO_PMM_INTEGRATION_MAINNET");
address deployer = vm.addr(pk);
console.log("Deployer:", deployer);
console.log("DODO PMM integration:", integration);
vm.startBroadcast(pk);
DODOIntegrationExternalUnwinder unwinder = new DODOIntegrationExternalUnwinder(integration);
vm.stopBroadcast();
console.log("DODOIntegrationExternalUnwinder:", address(unwinder));
}
}

View File

@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script, console} from "forge-std/Script.sol";
import {DODOToUniswapV3MultiHopExternalUnwinder} from "../../contracts/flash/DODOToUniswapV3MultiHopExternalUnwinder.sol";
/**
* @title DeployDODOToUniswapV3MultiHopExternalUnwinder
* @notice Deploy mixed DODO->Uniswap V3 unwinder for quote-push workflows.
*
* Env:
* PRIVATE_KEY required
* DODO_PMM_INTEGRATION_MAINNET required
* UNISWAP_V3_SWAP_ROUTER_MAINNET optional; defaults to legacy SwapRouter `0xE592...`
*
* Usage:
* forge script script/deploy/DeployDODOToUniswapV3MultiHopExternalUnwinder.s.sol:DeployDODOToUniswapV3MultiHopExternalUnwinder \
* --rpc-url $ETHEREUM_MAINNET_RPC --broadcast -vvvv
*/
contract DeployDODOToUniswapV3MultiHopExternalUnwinder is Script {
address internal constant DEFAULT_UNISWAP_V3_ROUTER_MAINNET = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
function run() external {
uint256 pk = vm.envUint("PRIVATE_KEY");
address integration = vm.envAddress("DODO_PMM_INTEGRATION_MAINNET");
address router = vm.envOr("UNISWAP_V3_SWAP_ROUTER_MAINNET", DEFAULT_UNISWAP_V3_ROUTER_MAINNET);
address deployer = vm.addr(pk);
console.log("Deployer:", deployer);
console.log("DODO PMM integration:", integration);
console.log("Uniswap V3 router:", router);
vm.startBroadcast(pk);
DODOToUniswapV3MultiHopExternalUnwinder unwinder =
new DODOToUniswapV3MultiHopExternalUnwinder(integration, router);
vm.stopBroadcast();
console.log("DODOToUniswapV3MultiHopExternalUnwinder:", address(unwinder));
}
}

View File

@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script, console} from "forge-std/Script.sol";
import {TwoHopDodoIntegrationUnwinder} from "../../contracts/flash/TwoHopDodoIntegrationUnwinder.sol";
/**
* @title DeployTwoHopDodoIntegrationUnwinder
* @notice Deploy TwoHopDodoIntegrationUnwinder for Mainnet cWUSDC unwind via cWUSDT.
*
* Env:
* PRIVATE_KEY required
* DODO_PMM_INTEGRATION_MAINNET required
*
* Usage:
* forge script script/deploy/DeployTwoHopDodoIntegrationUnwinder.s.sol:DeployTwoHopDodoIntegrationUnwinder \
* --rpc-url $ETHEREUM_MAINNET_RPC --broadcast -vvvv
*/
contract DeployTwoHopDodoIntegrationUnwinder is Script {
function run() external {
uint256 pk = vm.envUint("PRIVATE_KEY");
address integration = vm.envAddress("DODO_PMM_INTEGRATION_MAINNET");
address deployer = vm.addr(pk);
console.log("Deployer:", deployer);
console.log("DODO PMM integration:", integration);
vm.startBroadcast(pk);
TwoHopDodoIntegrationUnwinder unwinder = new TwoHopDodoIntegrationUnwinder(integration);
vm.stopBroadcast();
console.log("TwoHopDodoIntegrationUnwinder:", address(unwinder));
}
}

View File

@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script, console} from "forge-std/Script.sol";
import {UniswapV3ExternalUnwinder} from "../../contracts/flash/UniswapV3ExternalUnwinder.sol";
/**
* @title DeployUniswapV3ExternalUnwinder
* @notice Deploy UniswapV3ExternalUnwinder for quote-push unwind legs (cW* -> USDC on V3).
*
* Env:
* PRIVATE_KEY required
* UNISWAP_V3_SWAP_ROUTER_MAINNET optional; defaults to legacy SwapRouter `0xE592...` on Ethereum mainnet
*
* Usage:
* forge script script/deploy/DeployUniswapV3ExternalUnwinder.s.sol:DeployUniswapV3ExternalUnwinder \
* --rpc-url $ETHEREUM_MAINNET_RPC --broadcast -vvvv
*/
contract DeployUniswapV3ExternalUnwinder is Script {
/// @dev SwapRouter02 (`0x68b3…`) is multicall-based and does not expose the legacy `exactInputSingle` ABI used by `UniswapV3ExternalUnwinder`.
address internal constant DEFAULT_UNISWAP_V3_ROUTER_MAINNET = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
function run() external {
uint256 pk = vm.envUint("PRIVATE_KEY");
address router = vm.envOr("UNISWAP_V3_SWAP_ROUTER_MAINNET", DEFAULT_UNISWAP_V3_ROUTER_MAINNET);
address deployer = vm.addr(pk);
console.log("Deployer:", deployer);
console.log("Uniswap V3 router:", router);
vm.startBroadcast(pk);
UniswapV3ExternalUnwinder unwinder = new UniswapV3ExternalUnwinder(router);
vm.stopBroadcast();
console.log("UniswapV3ExternalUnwinder:", address(unwinder));
}
}

View File

@@ -0,0 +1,139 @@
// 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();
}
}