Add managed quote-push treasury workflows

This commit is contained in:
defiQUG
2026-04-13 21:37:33 -07:00
parent 2b52cc6e32
commit 7517869ea6
9 changed files with 429 additions and 39 deletions

View File

@@ -8,7 +8,7 @@ import {QuotePushTreasuryManager} from "../../contracts/flash/QuotePushTreasuryM
/**
* @title DeployQuotePushTreasuryManager
* @notice Deploy a treasury manager for quote-push retained surplus and optionally
* hand receiver ownership to it in the same broadcast.
* hand receiver ownership to it in the same broadcast sequence.
*
* Env:
* PRIVATE_KEY required
@@ -63,6 +63,7 @@ contract DeployQuotePushTreasuryManager is Script {
if (takeReceiverOwnership) {
AaveQuotePushFlashReceiver(receiver).transferOwnership(address(manager));
require(manager.isReceiverOwnedByManager(), "receiver ownership not finalized");
console.log("Receiver ownership transferred to manager");
}
vm.stopBroadcast();

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script, console} from "forge-std/Script.sol";
import {TwoHopDodoToUniswapV3MultiHopExternalUnwinder} from "../../contracts/flash/TwoHopDodoToUniswapV3MultiHopExternalUnwinder.sol";
/**
* @title DeployTwoHopDodoToUniswapV3MultiHopExternalUnwinder
* @notice Deploy three-hop DODO -> 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...`
*/
contract DeployTwoHopDodoToUniswapV3MultiHopExternalUnwinder 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);
TwoHopDodoToUniswapV3MultiHopExternalUnwinder unwinder =
new TwoHopDodoToUniswapV3MultiHopExternalUnwinder(integration, router);
vm.stopBroadcast();
console.log("TwoHopDodoToUniswapV3MultiHopExternalUnwinder:", address(unwinder));
}
}

View File

@@ -15,6 +15,7 @@ import {QuotePushTreasuryManager} from "../../contracts/flash/QuotePushTreasuryM
* QUOTE_PUSH_TREASURY_HARVEST optional; default 1
* QUOTE_PUSH_TREASURY_GAS_DISTRIBUTION_RAW optional; default 0
* QUOTE_PUSH_TREASURY_RECYCLE_DISTRIBUTION_RAW optional; default 0
* QUOTE_PUSH_TREASURY_TRANSFER_RECEIVER_OWNER_TO optional; when set, transfer receiver ownership
*/
contract ManageQuotePushTreasuryManager is Script {
function run() external {
@@ -23,6 +24,7 @@ contract ManageQuotePushTreasuryManager is Script {
bool harvest = vm.envOr("QUOTE_PUSH_TREASURY_HARVEST", uint256(1)) == 1;
uint256 gasAmount = vm.envOr("QUOTE_PUSH_TREASURY_GAS_DISTRIBUTION_RAW", uint256(0));
uint256 recycleAmount = vm.envOr("QUOTE_PUSH_TREASURY_RECYCLE_DISTRIBUTION_RAW", uint256(0));
address transferReceiverOwnerTo = vm.envOr("QUOTE_PUSH_TREASURY_TRANSFER_RECEIVER_OWNER_TO", address(0));
QuotePushTreasuryManager manager = QuotePushTreasuryManager(managerAddr);
@@ -30,6 +32,7 @@ contract ManageQuotePushTreasuryManager is Script {
console.log("harvest", harvest);
console.log("gasAmount", gasAmount);
console.log("recycleAmount", recycleAmount);
console.log("transferReceiverOwnerTo", transferReceiverOwnerTo);
console.log("quoteBalanceBefore", manager.quoteBalance());
console.log("availableBefore", manager.availableQuote());
console.log("receiverSweepableBefore", manager.receiverSweepableQuote());
@@ -42,6 +45,9 @@ contract ManageQuotePushTreasuryManager is Script {
if (gasAmount > 0 || recycleAmount > 0) {
manager.distributeToConfiguredRecipients(gasAmount, recycleAmount);
}
if (transferReceiverOwnerTo != address(0)) {
manager.transferManagedReceiverOwnership(transferReceiverOwnerTo);
}
vm.stopBroadcast();
console.log("quoteBalanceAfter", manager.quoteBalance());

View File

@@ -41,6 +41,10 @@ interface IDODOPMMPoolQuote {
* 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)
* UNWIND_MODE=6 TwoHopDodoToUniswapV3MultiHopExternalUnwinder:
* set UNWIND_TWO_HOP_POOL_A, UNWIND_TWO_HOP_POOL_B, UNWIND_TWO_HOP_MID_TOKEN,
* optional UNWIND_MIN_MID_OUT_RAW, then UNWIND_INTERMEDIATE_TOKEN,
* UNWIND_MIN_INTERMEDIATE_OUT_RAW, UNWIND_V3_PATH_HEX
*/
contract RunMainnetAaveCwusdcUsdcQuotePushOnce is Script {
address internal constant DEFAULT_POOL = 0x69776fc607e9edA8042e320e7e43f54d06c68f0E;
@@ -99,8 +103,18 @@ contract RunMainnetAaveCwusdcUsdcQuotePushOnce is Script {
string memory pathHex = vm.envString("UNWIND_V3_PATH_HEX");
bytes memory path = vm.parseBytes(pathHex);
unwindData = abi.encode(dodoPool, intermediateToken, minIntermediateOut, path);
} else if (unwindMode == 6) {
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));
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(poolA, poolB, midToken, minMidOut, intermediateToken, minIntermediateOut, path);
} else {
revert("UNWIND_MODE must be 0, 1, 2, 4, or 5");
revert("UNWIND_MODE must be 0, 1, 2, 4, 5, or 6");
}
AaveQuotePushFlashReceiver.QuotePushParams memory p = AaveQuotePushFlashReceiver.QuotePushParams({

View File

@@ -58,29 +58,13 @@ contract RunManagedMainnetAaveCwusdcUsdcQuotePushCycle is Script {
console.log("gasHoldbackTargetRaw", gasHoldbackTargetRaw);
vm.startBroadcast(pk);
AaveQuotePushFlashReceiver(receiver).flashQuotePush(usdc, amount, p);
uint256 harvested = 0;
if (harvest) {
uint256 receiverSweepableAfterFlash = manager.receiverSweepableQuote();
if (receiverSweepableAfterFlash > 0) {
harvested = manager.harvestReceiverSurplus();
}
console.log("receiverSweepableAfterFlash", receiverSweepableAfterFlash);
}
uint256 available = manager.availableQuote();
uint256 gasAmount = _min(available, gasHoldbackTargetRaw);
uint256 recycleAmount = available - gasAmount;
if (gasAmount > 0 || recycleAmount > 0) {
manager.distributeToConfiguredRecipients(gasAmount, recycleAmount);
}
(uint256 harvested, uint256 gasAmount, uint256 recycleAmount) =
manager.runManagedCycle(usdc, amount, p, harvest, gasHoldbackTargetRaw);
vm.stopBroadcast();
console.log("harvested", harvested);
console.log("gasDistributionRaw", gasAmount);
console.log("recycleDistributionRaw", recycleAmount);
console.log("managedCycleHarvestedRaw", harvested);
console.log("managedCycleGasDistributionRaw", gasAmount);
console.log("managedCycleRecycleDistributionRaw", recycleAmount);
console.log("managerQuoteAfter", manager.quoteBalance());
console.log("managerAvailableAfter", manager.availableQuote());
console.log("receiverSweepableAfter", manager.receiverSweepableQuote());
@@ -136,8 +120,18 @@ contract RunManagedMainnetAaveCwusdcUsdcQuotePushCycle is Script {
string memory pathHex = vm.envString("UNWIND_V3_PATH_HEX");
bytes memory path = vm.parseBytes(pathHex);
unwindData = abi.encode(dodoPool, intermediateToken, minIntermediateOut, path);
} else if (unwindMode == 6) {
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));
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(poolA, poolB, midToken, minMidOut, intermediateToken, minIntermediateOut, path);
} else {
revert("UNWIND_MODE must be 0, 1, 2, 4, or 5");
revert("UNWIND_MODE must be 0, 1, 2, 4, 5, or 6");
}
p = AaveQuotePushFlashReceiver.QuotePushParams({
@@ -166,8 +160,4 @@ contract RunManagedMainnetAaveCwusdcUsdcQuotePushCycle is Script {
console.log("minOutPmm", minOutPmm);
console.log("minOutUnwind", minOutUnwind);
}
function _min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}