feat: bridges, PMM, flash workflow, token-aggregation, and deployment docs
- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
This commit is contained in:
563
services/token-aggregation/src/config/provider-capabilities.ts
Normal file
563
services/token-aggregation/src/config/provider-capabilities.ts
Normal file
@@ -0,0 +1,563 @@
|
||||
import { AbiCoder } from 'ethers';
|
||||
import {
|
||||
PlannerProvider,
|
||||
ProviderCapabilityRecord,
|
||||
ProviderPairCapability,
|
||||
} from '../services/planner-v2-types';
|
||||
import { encodeChain138DodoV3ProviderData, isChain138DodoV3ExecutionLive } from '../services/dodo-v3-pilot';
|
||||
import { getChain138PilotVenueEdges } from '../services/chain138-pilot-venues';
|
||||
import { getChain138RoutingAssets } from './routing-assets';
|
||||
|
||||
const abiCoder = AbiCoder.defaultAbiCoder();
|
||||
const CHAIN_138 = 138;
|
||||
const CHAIN138_UNISWAP_V3_ROUTER = '0xde9cd8ee2811e6e64a41d5f68be315d33995975e';
|
||||
const CHAIN138_UNISWAP_V3_QUOTER = '0x6abbb1ceb2468e748a03a00cd6aa9bfe893afa1f';
|
||||
const CHAIN138_PILOT_BALANCER_VAULT = '0x96423d7c1727698d8a25ebfb88131e9422d1a3c3';
|
||||
const CHAIN138_PILOT_CURVE_3POOL = '0xe440ec15805be4c7babcd17a63b8c8a08a492e0f';
|
||||
const CHAIN138_PILOT_ONEINCH_ROUTER = '0x500b84b1bc6f59c1898a5fe538ea20a758757a4f';
|
||||
const CHAIN138_PILOT_BALANCER_WETH_USDT_POOL_ID = '0x877cd220759e8c94b82f55450c85d382ae06856c426b56d93092a420facbc324';
|
||||
const CHAIN138_PILOT_BALANCER_WETH_USDC_POOL_ID = '0xd8dfb18a6baf9b29d8c2dbd74639db87ac558af120df5261dab8e2a5de69013b';
|
||||
|
||||
function normalizeAddress(value?: string): string {
|
||||
return String(value || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function liveOrPlannedAddress(value?: string): 'live' | 'planned' {
|
||||
return normalizeAddress(value) ? 'live' : 'planned';
|
||||
}
|
||||
|
||||
function bidirectionalPair(args: {
|
||||
chainId: number;
|
||||
provider: PlannerProvider;
|
||||
tokenASymbol: string;
|
||||
tokenAAddress: string;
|
||||
tokenBSymbol: string;
|
||||
tokenBAddress: string;
|
||||
status: 'live' | 'planned' | 'blocked';
|
||||
target?: string;
|
||||
providerData?: Record<string, unknown>;
|
||||
providerDataHex?: string;
|
||||
notes?: string[];
|
||||
reason?: string;
|
||||
}): ProviderPairCapability[] {
|
||||
return [
|
||||
{
|
||||
chainId: args.chainId,
|
||||
provider: args.provider,
|
||||
legType: 'swap',
|
||||
status: args.status,
|
||||
tokenInSymbol: args.tokenASymbol,
|
||||
tokenInAddress: normalizeAddress(args.tokenAAddress),
|
||||
tokenOutSymbol: args.tokenBSymbol,
|
||||
tokenOutAddress: normalizeAddress(args.tokenBAddress),
|
||||
target: normalizeAddress(args.target),
|
||||
providerData: args.providerData,
|
||||
providerDataHex: args.providerDataHex,
|
||||
notes: args.notes,
|
||||
reason: args.reason,
|
||||
},
|
||||
{
|
||||
chainId: args.chainId,
|
||||
provider: args.provider,
|
||||
legType: 'swap',
|
||||
status: args.status,
|
||||
tokenInSymbol: args.tokenBSymbol,
|
||||
tokenInAddress: normalizeAddress(args.tokenBAddress),
|
||||
tokenOutSymbol: args.tokenASymbol,
|
||||
tokenOutAddress: normalizeAddress(args.tokenAAddress),
|
||||
target: normalizeAddress(args.target),
|
||||
providerData: args.providerData,
|
||||
providerDataHex: args.providerDataHex,
|
||||
notes: args.notes,
|
||||
reason: args.reason,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function encodeDodoPool(poolAddress: string): string {
|
||||
return abiCoder.encode(['address'], [poolAddress]);
|
||||
}
|
||||
|
||||
function encodeUniswapRoute(fee: number, quoter: string): string {
|
||||
return abiCoder.encode(['bytes', 'uint24', 'address', 'bool'], ['0x', fee, quoter, false]);
|
||||
}
|
||||
|
||||
function encodeBalancerRoute(poolId: string): string {
|
||||
return abiCoder.encode(['bytes32'], [poolId]);
|
||||
}
|
||||
|
||||
function encodeCurveRoute(i: number, j: number, useUnderlying: boolean): string {
|
||||
return abiCoder.encode(['int128', 'int128', 'bool'], [i, j, useUnderlying]);
|
||||
}
|
||||
|
||||
function encodeOneInchRoute(router: string): string {
|
||||
return abiCoder.encode(['address', 'address', 'bytes'], [router, router, '0x']);
|
||||
}
|
||||
|
||||
function chain138DodoCapabilities(): ProviderCapabilityRecord {
|
||||
const assets = getChain138RoutingAssets();
|
||||
const dodoProvider =
|
||||
normalizeAddress(process.env.DODO_PMM_PROVIDER_ADDRESS) ||
|
||||
normalizeAddress(process.env.DODO_PMM_PROVIDER) ||
|
||||
'0x3f729632e9553ebaccde2e9b4c8f2b285b014f2e';
|
||||
|
||||
const stablePool = '0x9e89bae009adf128782e19e8341996c596ac40dc';
|
||||
const cusdtUsdtPool = '0x866cb44b59303d8dc5f4f9e3e7a8e8b0bf238d66';
|
||||
const cusdcUsdcPool = '0xc39b7d0f40838cbfb54649d327f49a6dac964062';
|
||||
const cusdtXaucPool = '0x1aa55e2001e5651349aff5a63fd7a7ae44f0f1b0';
|
||||
const cusdcXaucPool = '0xea9ac6357cacb42a83b9082b870610363b177cba';
|
||||
const ceurtXaucPool = '0xba99bc1eaac164569d5aca96c806934ddaf970cf';
|
||||
const cbtcCusdtPool = normalizeAddress(process.env.CHAIN138_POOL_CBTC_CUSDT);
|
||||
const cbtcCusdcPool = normalizeAddress(process.env.CHAIN138_POOL_CBTC_CUSDC);
|
||||
const cbtcXaucPool = normalizeAddress(process.env.CHAIN138_POOL_CBTC_CXAUC);
|
||||
const wethUsdtPool = normalizeAddress(process.env.CHAIN138_POOL_WETH_USDT);
|
||||
const wethUsdcPool = normalizeAddress(process.env.CHAIN138_POOL_WETH_USDC);
|
||||
|
||||
const pairs: ProviderPairCapability[] = [
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cUSDT',
|
||||
tokenAAddress: assets.cUSDT.address,
|
||||
tokenBSymbol: 'cUSDC',
|
||||
tokenBAddress: assets.cUSDC.address,
|
||||
status: 'live',
|
||||
target: dodoProvider,
|
||||
providerData: { poolAddress: stablePool },
|
||||
providerDataHex: encodeDodoPool(stablePool),
|
||||
notes: ['Canonical stable pool on the official DODO V2 DVM-backed stack.'],
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cUSDT',
|
||||
tokenAAddress: assets.cUSDT.address,
|
||||
tokenBSymbol: 'USDT',
|
||||
tokenBAddress: assets.USDT.address,
|
||||
status: 'live',
|
||||
target: dodoProvider,
|
||||
providerData: { poolAddress: cusdtUsdtPool },
|
||||
providerDataHex: encodeDodoPool(cusdtUsdtPool),
|
||||
notes: ['Canonical stable pool on the official DODO V2 DVM-backed stack.'],
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cUSDC',
|
||||
tokenAAddress: assets.cUSDC.address,
|
||||
tokenBSymbol: 'USDC',
|
||||
tokenBAddress: assets.USDC.address,
|
||||
status: 'live',
|
||||
target: dodoProvider,
|
||||
providerData: { poolAddress: cusdcUsdcPool },
|
||||
providerDataHex: encodeDodoPool(cusdcUsdcPool),
|
||||
notes: ['Canonical stable pool on the official DODO V2 DVM-backed stack.'],
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cUSDT',
|
||||
tokenAAddress: assets.cUSDT.address,
|
||||
tokenBSymbol: 'cXAUC',
|
||||
tokenBAddress: assets.cXAUC.address,
|
||||
status: 'live',
|
||||
target: dodoProvider,
|
||||
providerData: { poolAddress: cusdtXaucPool },
|
||||
providerDataHex: encodeDodoPool(cusdtXaucPool),
|
||||
notes: ['Commodity route; excluded unless policy allows commodity intermediates.'],
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cUSDC',
|
||||
tokenAAddress: assets.cUSDC.address,
|
||||
tokenBSymbol: 'cXAUC',
|
||||
tokenBAddress: assets.cXAUC.address,
|
||||
status: 'live',
|
||||
target: dodoProvider,
|
||||
providerData: { poolAddress: cusdcXaucPool },
|
||||
providerDataHex: encodeDodoPool(cusdcXaucPool),
|
||||
notes: ['Commodity route; excluded unless policy allows commodity intermediates.'],
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cEURT',
|
||||
tokenAAddress: assets.cEURT.address,
|
||||
tokenBSymbol: 'cXAUC',
|
||||
tokenBAddress: assets.cXAUC.address,
|
||||
status: 'live',
|
||||
target: dodoProvider,
|
||||
providerData: { poolAddress: ceurtXaucPool },
|
||||
providerDataHex: encodeDodoPool(ceurtXaucPool),
|
||||
notes: ['Commodity route; excluded unless policy allows commodity intermediates.'],
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cBTC',
|
||||
tokenAAddress: assets.cBTC.address,
|
||||
tokenBSymbol: 'cUSDT',
|
||||
tokenBAddress: assets.cUSDT.address,
|
||||
status: liveOrPlannedAddress(cbtcCusdtPool),
|
||||
target: dodoProvider,
|
||||
providerData: cbtcCusdtPool ? { poolAddress: cbtcCusdtPool } : undefined,
|
||||
providerDataHex: cbtcCusdtPool ? encodeDodoPool(cbtcCusdtPool) : undefined,
|
||||
notes: ['Bitcoin monetary-unit route for the jewelry-box program.'],
|
||||
reason: cbtcCusdtPool ? undefined : 'Set CHAIN138_POOL_CBTC_CUSDT after the canonical cBTC/cUSDT PMM pool is created and funded.',
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cBTC',
|
||||
tokenAAddress: assets.cBTC.address,
|
||||
tokenBSymbol: 'cUSDC',
|
||||
tokenBAddress: assets.cUSDC.address,
|
||||
status: liveOrPlannedAddress(cbtcCusdcPool),
|
||||
target: dodoProvider,
|
||||
providerData: cbtcCusdcPool ? { poolAddress: cbtcCusdcPool } : undefined,
|
||||
providerDataHex: cbtcCusdcPool ? encodeDodoPool(cbtcCusdcPool) : undefined,
|
||||
notes: ['Bitcoin monetary-unit route for the jewelry-box program.'],
|
||||
reason: cbtcCusdcPool ? undefined : 'Set CHAIN138_POOL_CBTC_CUSDC after the canonical cBTC/cUSDC PMM pool is created and funded.',
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'cBTC',
|
||||
tokenAAddress: assets.cBTC.address,
|
||||
tokenBSymbol: 'cXAUC',
|
||||
tokenBAddress: assets.cXAUC.address,
|
||||
status: liveOrPlannedAddress(cbtcXaucPool),
|
||||
target: dodoProvider,
|
||||
providerData: cbtcXaucPool ? { poolAddress: cbtcXaucPool } : undefined,
|
||||
providerDataHex: cbtcXaucPool ? encodeDodoPool(cbtcXaucPool) : undefined,
|
||||
notes: ['Bitcoin-to-gold route for jewelry-box rebalances; excluded unless policy allows commodity intermediates.'],
|
||||
reason: cbtcXaucPool ? undefined : 'Set CHAIN138_POOL_CBTC_CXAUC after the canonical cBTC/cXAUC PMM pool is created and funded.',
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'WETH',
|
||||
tokenAAddress: assets.WETH.address,
|
||||
tokenBSymbol: 'USDT',
|
||||
tokenBAddress: assets.USDT.address,
|
||||
status: wethUsdtPool ? 'live' : 'planned',
|
||||
target: dodoProvider,
|
||||
providerData: wethUsdtPool ? { poolAddress: wethUsdtPool } : undefined,
|
||||
providerDataHex: wethUsdtPool ? encodeDodoPool(wethUsdtPool) : undefined,
|
||||
notes: ['Phase 1 WETH lane for router-v2 stable execution.'],
|
||||
reason: wethUsdtPool ? undefined : 'Set CHAIN138_POOL_WETH_USDT after the canonical WETH/USDT pool is created and funded.',
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
tokenASymbol: 'WETH',
|
||||
tokenAAddress: assets.WETH.address,
|
||||
tokenBSymbol: 'USDC',
|
||||
tokenBAddress: assets.USDC.address,
|
||||
status: wethUsdcPool ? 'live' : 'planned',
|
||||
target: dodoProvider,
|
||||
providerData: wethUsdcPool ? { poolAddress: wethUsdcPool } : undefined,
|
||||
providerDataHex: wethUsdcPool ? encodeDodoPool(wethUsdcPool) : undefined,
|
||||
notes: ['Phase 1 WETH lane for router-v2 stable execution.'],
|
||||
reason: wethUsdcPool ? undefined : 'Set CHAIN138_POOL_WETH_USDC after the canonical WETH/USDC pool is created and funded.',
|
||||
}),
|
||||
];
|
||||
|
||||
const livePairs = pairs.filter((pair) => pair.status === 'live');
|
||||
return {
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo',
|
||||
executionMode: 'onchain',
|
||||
live: livePairs.length > 0,
|
||||
quoteLive: livePairs.length > 0,
|
||||
executionLive: livePairs.length > 0,
|
||||
supportedLegTypes: ['swap'],
|
||||
pairs,
|
||||
notes: ['DODO is the first production executor for Chain 138 router-v2 rollout.'],
|
||||
};
|
||||
}
|
||||
|
||||
function chain138DodoV3Capabilities(): ProviderCapabilityRecord {
|
||||
const assets = getChain138RoutingAssets();
|
||||
const enabled = process.env.CHAIN138_ENABLE_DODO_V3_ROUTING !== '0';
|
||||
const proxy = normalizeAddress(process.env.CHAIN138_D3_PROXY_ADDRESS) || '0xc9a11abb7c63d88546be24d58a6d95e3762cb843';
|
||||
const pool = normalizeAddress(process.env.CHAIN138_D3_MM_ADDRESS) || '0x6550a3a59070061a262a893a1d6f3f490affdbda';
|
||||
const status = enabled && proxy && pool ? 'live' : 'planned';
|
||||
const executionLive = status === 'live' && isChain138DodoV3ExecutionLive();
|
||||
|
||||
const pairs = [
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo_v3',
|
||||
tokenASymbol: 'WETH10',
|
||||
tokenAAddress: assets.WETH10.address,
|
||||
tokenBSymbol: 'USDT',
|
||||
tokenBAddress: assets.USDT.address,
|
||||
status,
|
||||
target: proxy,
|
||||
providerData: status === 'live'
|
||||
? { poolAddress: pool, proxyAddress: proxy, quoteMethod: 'querySellTokens' }
|
||||
: undefined,
|
||||
providerDataHex: executionLive ? encodeChain138DodoV3ProviderData(pool) : undefined,
|
||||
notes: [
|
||||
'Canonical Chain 138 DODO v3 / D3MM pilot route.',
|
||||
executionLive
|
||||
? 'Planner visibility, quote selection, and EnhancedSwapRouterV2 execution are live for the canonical pilot pair.'
|
||||
: 'Planner visibility and quote selection are live; EnhancedSwapRouterV2 adapter support is still pending.',
|
||||
],
|
||||
reason: status === 'planned'
|
||||
? 'Set CHAIN138_ENABLE_DODO_V3_ROUTING=1 and ensure CHAIN138_D3_MM_ADDRESS / CHAIN138_D3_PROXY_ADDRESS are configured to expose the pilot venue.'
|
||||
: undefined,
|
||||
}),
|
||||
];
|
||||
|
||||
return {
|
||||
chainId: CHAIN_138,
|
||||
provider: 'dodo_v3',
|
||||
executionMode: 'onchain',
|
||||
live: status === 'live',
|
||||
quoteLive: status === 'live',
|
||||
executionLive,
|
||||
supportedLegTypes: ['swap'],
|
||||
pairs,
|
||||
notes: [
|
||||
executionLive
|
||||
? 'Private DODO v3 / D3MM Chain 138 pilot is live in planner-v2 visibility and internal execution-plan calldata.'
|
||||
: 'Private DODO v3 / D3MM Chain 138 pilot promoted into planner-v2 visibility.',
|
||||
executionLive
|
||||
? 'Route discovery and execution-plan generation are live for the canonical pilot pair.'
|
||||
: 'Route discovery is live, but internal execution-plan calldata is intentionally withheld until a dedicated D3 route executor adapter exists.',
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function chain138UniswapCapabilities(): ProviderCapabilityRecord {
|
||||
const assets = getChain138RoutingAssets();
|
||||
const router = normalizeAddress(process.env.UNISWAP_V3_ROUTER || CHAIN138_UNISWAP_V3_ROUTER);
|
||||
const quoter = normalizeAddress(process.env.UNISWAP_QUOTER_ADDRESS || process.env.UNISWAP_QUOTER || CHAIN138_UNISWAP_V3_QUOTER);
|
||||
const wethUsdtFee = Number(process.env.UNISWAP_V3_WETH_USDT_FEE || '500');
|
||||
const wethUsdcFee = Number(process.env.UNISWAP_V3_WETH_USDC_FEE || '500');
|
||||
const status = router && quoter ? 'live' : 'planned';
|
||||
|
||||
const pairs = [
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'uniswap_v3',
|
||||
tokenASymbol: 'WETH',
|
||||
tokenAAddress: assets.WETH.address,
|
||||
tokenBSymbol: 'USDT',
|
||||
tokenBAddress: assets.USDT.address,
|
||||
status,
|
||||
target: router,
|
||||
providerData: status === 'live' ? { fee: wethUsdtFee, quoter } : undefined,
|
||||
providerDataHex: status === 'live' ? encodeUniswapRoute(wethUsdtFee, quoter) : undefined,
|
||||
notes: ['Canonical Chain 138 upstream-native Uniswap v3 WETH/USDT venue.'],
|
||||
reason: status === 'planned' ? 'Configure UNISWAP_V3_ROUTER and UNISWAP_QUOTER_ADDRESS after Chain 138 native venue deployment.' : undefined,
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'uniswap_v3',
|
||||
tokenASymbol: 'WETH',
|
||||
tokenAAddress: assets.WETH.address,
|
||||
tokenBSymbol: 'USDC',
|
||||
tokenBAddress: assets.USDC.address,
|
||||
status,
|
||||
target: router,
|
||||
providerData: status === 'live' ? { fee: wethUsdcFee, quoter } : undefined,
|
||||
providerDataHex: status === 'live' ? encodeUniswapRoute(wethUsdcFee, quoter) : undefined,
|
||||
notes: ['Canonical Chain 138 upstream-native Uniswap v3 WETH/USDC venue.'],
|
||||
reason: status === 'planned' ? 'Configure UNISWAP_V3_ROUTER and UNISWAP_QUOTER_ADDRESS after Chain 138 native venue deployment.' : undefined,
|
||||
}),
|
||||
];
|
||||
|
||||
return {
|
||||
chainId: CHAIN_138,
|
||||
provider: 'uniswap_v3',
|
||||
executionMode: 'onchain',
|
||||
live: status === 'live',
|
||||
quoteLive: status === 'live',
|
||||
executionLive: status === 'live',
|
||||
supportedLegTypes: ['swap'],
|
||||
pairs,
|
||||
notes: ['Canonical Chain 138 upstream-native Uniswap v3 router/quoter path.'],
|
||||
};
|
||||
}
|
||||
|
||||
function chain138BalancerCapabilities(): ProviderCapabilityRecord {
|
||||
const assets = getChain138RoutingAssets();
|
||||
const vault = normalizeAddress(process.env.BALANCER_VAULT || CHAIN138_PILOT_BALANCER_VAULT);
|
||||
const wethUsdtPoolId = process.env.BALANCER_WETH_USDT_POOL_ID || CHAIN138_PILOT_BALANCER_WETH_USDT_POOL_ID;
|
||||
const wethUsdcPoolId = process.env.BALANCER_WETH_USDC_POOL_ID || CHAIN138_PILOT_BALANCER_WETH_USDC_POOL_ID;
|
||||
|
||||
const pairs = [
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'balancer',
|
||||
tokenASymbol: 'WETH',
|
||||
tokenAAddress: assets.WETH.address,
|
||||
tokenBSymbol: 'USDT',
|
||||
tokenBAddress: assets.USDT.address,
|
||||
status: vault && wethUsdtPoolId ? 'live' : 'planned',
|
||||
target: vault,
|
||||
providerData: vault && wethUsdtPoolId ? { poolId: wethUsdtPoolId } : undefined,
|
||||
providerDataHex: vault && wethUsdtPoolId ? encodeBalancerRoute(wethUsdtPoolId) : undefined,
|
||||
notes: ['Enabled only after Chain 138 Balancer pool IDs are set.'],
|
||||
reason: vault && wethUsdtPoolId ? undefined : 'Configure BALANCER_VAULT and BALANCER_WETH_USDT_POOL_ID once the pool exists.',
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'balancer',
|
||||
tokenASymbol: 'WETH',
|
||||
tokenAAddress: assets.WETH.address,
|
||||
tokenBSymbol: 'USDC',
|
||||
tokenBAddress: assets.USDC.address,
|
||||
status: vault && wethUsdcPoolId ? 'live' : 'planned',
|
||||
target: vault,
|
||||
providerData: vault && wethUsdcPoolId ? { poolId: wethUsdcPoolId } : undefined,
|
||||
providerDataHex: vault && wethUsdcPoolId ? encodeBalancerRoute(wethUsdcPoolId) : undefined,
|
||||
notes: ['Enabled only after Chain 138 Balancer pool IDs are set.'],
|
||||
reason: vault && wethUsdcPoolId ? undefined : 'Configure BALANCER_VAULT and BALANCER_WETH_USDC_POOL_ID once the pool exists.',
|
||||
}),
|
||||
];
|
||||
|
||||
return {
|
||||
chainId: CHAIN_138,
|
||||
provider: 'balancer',
|
||||
executionMode: 'onchain',
|
||||
live: pairs.some((pair) => pair.status === 'live'),
|
||||
quoteLive: pairs.some((pair) => pair.status === 'live'),
|
||||
executionLive: pairs.some((pair) => pair.status === 'live'),
|
||||
supportedLegTypes: ['swap'],
|
||||
pairs,
|
||||
notes: ['Balancer stays disabled until the minimum viable Chain 138 venue set exists.'],
|
||||
};
|
||||
}
|
||||
|
||||
function chain138CurveCapabilities(): ProviderCapabilityRecord {
|
||||
const assets = getChain138RoutingAssets();
|
||||
const curvePool = normalizeAddress(process.env.CURVE_3POOL || CHAIN138_PILOT_CURVE_3POOL);
|
||||
const status = liveOrPlannedAddress(curvePool);
|
||||
|
||||
const pairs = [
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'curve',
|
||||
tokenASymbol: 'USDT',
|
||||
tokenAAddress: assets.USDT.address,
|
||||
tokenBSymbol: 'USDC',
|
||||
tokenBAddress: assets.USDC.address,
|
||||
status,
|
||||
target: curvePool,
|
||||
providerData: status === 'live' ? { i: 0, j: 1, useUnderlying: false } : undefined,
|
||||
providerDataHex: status === 'live' ? encodeCurveRoute(0, 1, false) : undefined,
|
||||
notes: ['Curve is reserved for stable-stable legs; no direct WETH path is configured.'],
|
||||
reason: status === 'planned' ? 'Configure CURVE_3POOL once the Chain 138 stable-stable venue is live.' : undefined,
|
||||
}),
|
||||
];
|
||||
|
||||
return {
|
||||
chainId: CHAIN_138,
|
||||
provider: 'curve',
|
||||
executionMode: 'onchain',
|
||||
live: status === 'live',
|
||||
quoteLive: status === 'live',
|
||||
executionLive: status === 'live',
|
||||
supportedLegTypes: ['swap'],
|
||||
pairs,
|
||||
notes: ['Curve is intentionally constrained to stable-stable execution for router-v2.'],
|
||||
};
|
||||
}
|
||||
|
||||
function chain138PartnerCapabilities(): ProviderCapabilityRecord {
|
||||
return {
|
||||
chainId: CHAIN_138,
|
||||
provider: 'partner',
|
||||
executionMode: 'partner',
|
||||
live: false,
|
||||
quoteLive: false,
|
||||
executionLive: false,
|
||||
supportedLegTypes: ['swap', 'bridge'],
|
||||
pairs: [],
|
||||
notes: ['1inch, 0x, and LiFi remain partner payload adapters until explicit Chain 138 live support is verified.'],
|
||||
};
|
||||
}
|
||||
|
||||
function chain138OneInchCapabilities(): ProviderCapabilityRecord {
|
||||
const assets = getChain138RoutingAssets();
|
||||
const router = normalizeAddress(process.env.ONEINCH_ROUTER || CHAIN138_PILOT_ONEINCH_ROUTER);
|
||||
const status = router ? 'live' : 'planned';
|
||||
|
||||
const pairs = [
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'one_inch',
|
||||
tokenASymbol: 'WETH',
|
||||
tokenAAddress: assets.WETH.address,
|
||||
tokenBSymbol: 'USDT',
|
||||
tokenBAddress: assets.USDT.address,
|
||||
status,
|
||||
target: router,
|
||||
providerData: status === 'live' ? { executor: router, allowanceTarget: router } : undefined,
|
||||
providerDataHex: status === 'live' ? encodeOneInchRoute(router) : undefined,
|
||||
notes: ['Enabled after the Chain 138 pilot-compatible 1inch router is deployed and funded.'],
|
||||
reason: status === 'planned' ? 'Configure ONEINCH_ROUTER once the Chain 138 pilot-compatible router is live.' : undefined,
|
||||
}),
|
||||
...bidirectionalPair({
|
||||
chainId: CHAIN_138,
|
||||
provider: 'one_inch',
|
||||
tokenASymbol: 'WETH',
|
||||
tokenAAddress: assets.WETH.address,
|
||||
tokenBSymbol: 'USDC',
|
||||
tokenBAddress: assets.USDC.address,
|
||||
status,
|
||||
target: router,
|
||||
providerData: status === 'live' ? { executor: router, allowanceTarget: router } : undefined,
|
||||
providerDataHex: status === 'live' ? encodeOneInchRoute(router) : undefined,
|
||||
notes: ['Enabled after the Chain 138 pilot-compatible 1inch router is deployed and funded.'],
|
||||
reason: status === 'planned' ? 'Configure ONEINCH_ROUTER once the Chain 138 pilot-compatible router is live.' : undefined,
|
||||
}),
|
||||
];
|
||||
|
||||
return {
|
||||
chainId: CHAIN_138,
|
||||
provider: 'one_inch',
|
||||
executionMode: 'onchain',
|
||||
live: status === 'live',
|
||||
quoteLive: status === 'live',
|
||||
executionLive: status === 'live',
|
||||
supportedLegTypes: ['swap'],
|
||||
pairs,
|
||||
notes: ['1inch is promoted from partner-only placeholder to an executable Chain 138 pilot-compatible router when configured.'],
|
||||
};
|
||||
}
|
||||
|
||||
export function getProviderCapabilities(chainId: number): ProviderCapabilityRecord[] {
|
||||
if (chainId !== CHAIN_138) return [];
|
||||
return [
|
||||
chain138DodoCapabilities(),
|
||||
chain138DodoV3Capabilities(),
|
||||
chain138UniswapCapabilities(),
|
||||
chain138BalancerCapabilities(),
|
||||
chain138CurveCapabilities(),
|
||||
chain138OneInchCapabilities(),
|
||||
chain138PartnerCapabilities(),
|
||||
];
|
||||
}
|
||||
|
||||
export function findProviderPairCapability(
|
||||
chainId: number,
|
||||
provider: PlannerProvider,
|
||||
tokenInAddress: string,
|
||||
tokenOutAddress: string
|
||||
): ProviderPairCapability | undefined {
|
||||
const normalizedIn = normalizeAddress(tokenInAddress);
|
||||
const normalizedOut = normalizeAddress(tokenOutAddress);
|
||||
return getProviderCapabilities(chainId)
|
||||
.find((record) => record.provider === provider)
|
||||
?.pairs.find(
|
||||
(pair) =>
|
||||
pair.tokenInAddress === normalizedIn &&
|
||||
pair.tokenOutAddress === normalizedOut
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user