@exagent/agent 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/chunk-7UGLJO6W.js +6392 -0
  2. package/dist/chunk-EHAOPCTJ.js +6406 -0
  3. package/dist/chunk-FGMXTW5I.js +6540 -0
  4. package/dist/chunk-IVA2SCSN.js +6756 -0
  5. package/dist/chunk-JHXCSGPC.js +6352 -0
  6. package/dist/chunk-V6O4UXVN.js +6345 -0
  7. package/dist/chunk-ZRAOPQQW.js +6406 -0
  8. package/dist/cli.js +40 -98
  9. package/dist/index.d.ts +24 -2
  10. package/dist/index.js +1 -1
  11. package/package.json +17 -14
  12. package/.turbo/turbo-build.log +0 -17
  13. package/src/bridge/across.ts +0 -240
  14. package/src/bridge/bridge-manager.ts +0 -87
  15. package/src/bridge/index.ts +0 -9
  16. package/src/bridge/types.ts +0 -77
  17. package/src/chains.ts +0 -105
  18. package/src/cli.ts +0 -244
  19. package/src/config.ts +0 -499
  20. package/src/diagnostics.ts +0 -335
  21. package/src/index.ts +0 -98
  22. package/src/llm/anthropic.ts +0 -63
  23. package/src/llm/base.ts +0 -264
  24. package/src/llm/deepseek.ts +0 -48
  25. package/src/llm/google.ts +0 -63
  26. package/src/llm/groq.ts +0 -48
  27. package/src/llm/index.ts +0 -42
  28. package/src/llm/mistral.ts +0 -48
  29. package/src/llm/ollama.ts +0 -52
  30. package/src/llm/openai.ts +0 -51
  31. package/src/llm/together.ts +0 -48
  32. package/src/llm-providers.ts +0 -100
  33. package/src/logger.ts +0 -137
  34. package/src/paper/executor.ts +0 -201
  35. package/src/paper/index.ts +0 -1
  36. package/src/perp/client.ts +0 -200
  37. package/src/perp/index.ts +0 -12
  38. package/src/perp/msgpack.ts +0 -272
  39. package/src/perp/orders.ts +0 -234
  40. package/src/perp/positions.ts +0 -126
  41. package/src/perp/signer.ts +0 -277
  42. package/src/perp/types.ts +0 -192
  43. package/src/perp/websocket.ts +0 -274
  44. package/src/position-tracker.ts +0 -243
  45. package/src/prediction/client.ts +0 -281
  46. package/src/prediction/index.ts +0 -3
  47. package/src/prediction/order-manager.ts +0 -297
  48. package/src/prediction/types.ts +0 -151
  49. package/src/relay.ts +0 -254
  50. package/src/runtime.ts +0 -1755
  51. package/src/scrub-secrets.ts +0 -39
  52. package/src/setup.ts +0 -384
  53. package/src/signal.ts +0 -212
  54. package/src/spot/aerodrome.ts +0 -158
  55. package/src/spot/client.ts +0 -138
  56. package/src/spot/index.ts +0 -11
  57. package/src/spot/swap-manager.ts +0 -219
  58. package/src/spot/types.ts +0 -203
  59. package/src/spot/uniswap.ts +0 -150
  60. package/src/store.ts +0 -50
  61. package/src/strategy/index.ts +0 -2
  62. package/src/strategy/loader.ts +0 -191
  63. package/src/strategy/templates.ts +0 -125
  64. package/src/trading/index.ts +0 -2
  65. package/src/trading/market.ts +0 -120
  66. package/src/trading/risk.ts +0 -107
  67. package/src/ui.ts +0 -75
  68. package/test-bridge-arb-to-base.mjs +0 -223
  69. package/test-funded-check.mjs +0 -79
  70. package/test-funded-phase19.mjs +0 -933
  71. package/test-hl-deposit-recover.mjs +0 -281
  72. package/test-hl-withdraw.mjs +0 -372
  73. package/test-live-signing.mjs +0 -374
  74. package/test-phase7.mjs +0 -416
  75. package/test-recover-arb.mjs +0 -206
  76. package/test-spot-bridge.mjs +0 -248
  77. package/test-wallet-setup.mjs +0 -126
  78. package/tsconfig.json +0 -8
@@ -1,158 +0,0 @@
1
- import { getChainConfig } from '../chains.js';
2
- import type { SpotDEXClient } from './client.js';
3
- import {
4
- AERODROME_ROUTER_ABI,
5
- AERODROME_DEFAULT_FACTORY,
6
- type SpotSwapParams,
7
- type SpotQuoteResult,
8
- type SpotSwapResult,
9
- } from './types.js';
10
-
11
- export class AerodromeAdapter {
12
- private client: SpotDEXClient;
13
-
14
- constructor(client: SpotDEXClient) {
15
- this.client = client;
16
- }
17
-
18
- async quote(params: SpotSwapParams): Promise<SpotQuoteResult> {
19
- if (params.chain !== 'base') {
20
- throw new Error('Aerodrome is only available on Base');
21
- }
22
-
23
- const chainConfig = getChainConfig('base')!;
24
- const routerAddress = chainConfig.dexRouters.aerodrome;
25
- if (!routerAddress) {
26
- throw new Error('No Aerodrome router configured for Base');
27
- }
28
-
29
- const { publicClient } = this.client.getClients('base');
30
-
31
- let bestAmountOut = 0n;
32
- let bestStable = false;
33
-
34
- // Try both stable and volatile pools
35
- for (const stable of [true, false]) {
36
- try {
37
- const route = [
38
- {
39
- from: params.tokenIn,
40
- to: params.tokenOut,
41
- stable,
42
- factory: AERODROME_DEFAULT_FACTORY,
43
- },
44
- ];
45
-
46
- const amounts = await publicClient.readContract({
47
- address: routerAddress,
48
- abi: AERODROME_ROUTER_ABI,
49
- functionName: 'getAmountsOut',
50
- args: [params.amountIn, route],
51
- }) as bigint[];
52
-
53
- const amountOut = amounts[amounts.length - 1];
54
- if (amountOut > bestAmountOut) {
55
- bestAmountOut = amountOut;
56
- bestStable = stable;
57
- }
58
- } catch {
59
- // Pool doesn't exist for this type — skip
60
- }
61
- }
62
-
63
- if (bestAmountOut === 0n) {
64
- throw new Error(`No Aerodrome pool found for ${params.tokenIn}/${params.tokenOut}`);
65
- }
66
-
67
- const tokenInSymbol = await this.client.getSymbol(params.tokenIn, 'base');
68
- const tokenOutSymbol = await this.client.getSymbol(params.tokenOut, 'base');
69
-
70
- return {
71
- amountOut: bestAmountOut,
72
- stable: bestStable,
73
- route: `${tokenInSymbol} → ${tokenOutSymbol} (${bestStable ? 'stable' : 'volatile'})`,
74
- };
75
- }
76
-
77
- async swap(params: SpotSwapParams): Promise<SpotSwapResult> {
78
- if (params.chain !== 'base') {
79
- throw new Error('Aerodrome is only available on Base');
80
- }
81
-
82
- const chainConfig = getChainConfig('base')!;
83
- const routerAddress = chainConfig.dexRouters.aerodrome;
84
- if (!routerAddress) {
85
- throw new Error('No Aerodrome router configured for Base');
86
- }
87
-
88
- // Get quote for pool type selection and slippage calc
89
- const quoteResult = await this.quote(params);
90
- const amountOutMin = (quoteResult.amountOut * BigInt(10000 - params.slippageBps)) / 10000n;
91
-
92
- // Ensure approval
93
- await this.client.ensureApproval(params.tokenIn, routerAddress, params.amountIn, 'base');
94
-
95
- const { publicClient, walletClient } = this.client.getClients('base');
96
- const recipient = params.recipient ?? this.client.address;
97
-
98
- const route = [
99
- {
100
- from: params.tokenIn,
101
- to: params.tokenOut,
102
- stable: quoteResult.stable!,
103
- factory: AERODROME_DEFAULT_FACTORY,
104
- },
105
- ];
106
-
107
- // Deadline: 5 minutes from now
108
- const deadline = BigInt(Math.floor(Date.now() / 1000) + 300);
109
-
110
- try {
111
- const hash = await walletClient.writeContract({
112
- address: routerAddress,
113
- abi: AERODROME_ROUTER_ABI,
114
- functionName: 'swapExactTokensForTokens',
115
- args: [params.amountIn, amountOutMin, route, recipient, deadline],
116
- });
117
-
118
- const receipt = await publicClient.waitForTransactionReceipt({ hash });
119
-
120
- // Calculate effective price
121
- const tokenInDecimals = await this.client.getDecimals(params.tokenIn, 'base');
122
- const tokenOutDecimals = await this.client.getDecimals(params.tokenOut, 'base');
123
- const amountInFloat = Number(params.amountIn) / 10 ** tokenInDecimals;
124
- const amountOutFloat = Number(quoteResult.amountOut) / 10 ** tokenOutDecimals;
125
- const effectivePrice = amountOutFloat / amountInFloat;
126
-
127
- const gasUsed = receipt.gasUsed ?? 0n;
128
- const gasPrice = receipt.effectiveGasPrice ?? 0n;
129
-
130
- return {
131
- success: true,
132
- txHash: hash,
133
- amountIn: params.amountIn,
134
- amountOut: quoteResult.amountOut,
135
- tokenIn: params.tokenIn,
136
- tokenOut: params.tokenOut,
137
- effectivePrice,
138
- gasCost: gasUsed * gasPrice,
139
- chain: 'base',
140
- dex: 'aerodrome',
141
- };
142
- } catch (err) {
143
- return {
144
- success: false,
145
- txHash: '0x0' as `0x${string}`,
146
- amountIn: params.amountIn,
147
- amountOut: 0n,
148
- tokenIn: params.tokenIn,
149
- tokenOut: params.tokenOut,
150
- effectivePrice: 0,
151
- gasCost: 0n,
152
- chain: 'base',
153
- dex: 'aerodrome',
154
- error: (err as Error).message,
155
- };
156
- }
157
- }
158
- }
@@ -1,138 +0,0 @@
1
- import {
2
- createPublicClient,
3
- createWalletClient,
4
- http,
5
- maxUint256,
6
- type PublicClient,
7
- type WalletClient,
8
- type Transport,
9
- type Chain,
10
- type Account,
11
- } from 'viem';
12
- import { privateKeyToAccount } from 'viem/accounts';
13
- import { getChainConfig, type ChainConfig } from '../chains.js';
14
- import { ERC20_ABI } from './types.js';
15
-
16
- interface ChainClients {
17
- publicClient: PublicClient<Transport, Chain>;
18
- walletClient: WalletClient<Transport, Chain, Account>;
19
- chainConfig: ChainConfig;
20
- }
21
-
22
- export class SpotDEXClient {
23
- private account: ReturnType<typeof privateKeyToAccount>;
24
- private clients: Map<string, ChainClients> = new Map();
25
- private decimalsCache: Map<string, number> = new Map();
26
- private symbolCache: Map<string, string> = new Map();
27
-
28
- constructor(privateKey: string) {
29
- this.account = privateKeyToAccount(privateKey as `0x${string}`);
30
- }
31
-
32
- get address(): `0x${string}` {
33
- return this.account.address;
34
- }
35
-
36
- getClients(chainName: string): ChainClients {
37
- const cached = this.clients.get(chainName);
38
- if (cached) return cached;
39
-
40
- const chainConfig = getChainConfig(chainName);
41
- if (!chainConfig) {
42
- throw new Error(`Unknown chain: ${chainName}`);
43
- }
44
-
45
- const publicClient = createPublicClient({
46
- chain: chainConfig.viemChain,
47
- transport: http(chainConfig.rpcUrl),
48
- }) as PublicClient<Transport, Chain>;
49
-
50
- const walletClient = createWalletClient({
51
- account: this.account,
52
- chain: chainConfig.viemChain,
53
- transport: http(chainConfig.rpcUrl),
54
- }) as WalletClient<Transport, Chain, Account>;
55
-
56
- const entry: ChainClients = { publicClient, walletClient, chainConfig };
57
- this.clients.set(chainName, entry);
58
- return entry;
59
- }
60
-
61
- async getDecimals(token: `0x${string}`, chainName: string): Promise<number> {
62
- const key = `${chainName}:${token}`;
63
- const cached = this.decimalsCache.get(key);
64
- if (cached !== undefined) return cached;
65
-
66
- const { publicClient } = this.getClients(chainName);
67
- const decimals = await publicClient.readContract({
68
- address: token,
69
- abi: ERC20_ABI,
70
- functionName: 'decimals',
71
- }) as number;
72
-
73
- this.decimalsCache.set(key, decimals);
74
- return decimals;
75
- }
76
-
77
- async getSymbol(token: `0x${string}`, chainName: string): Promise<string> {
78
- const key = `${chainName}:${token}`;
79
- const cached = this.symbolCache.get(key);
80
- if (cached) return cached;
81
-
82
- const { publicClient } = this.getClients(chainName);
83
- const symbol = await publicClient.readContract({
84
- address: token,
85
- abi: ERC20_ABI,
86
- functionName: 'symbol',
87
- }) as string;
88
-
89
- this.symbolCache.set(key, symbol);
90
- return symbol;
91
- }
92
-
93
- async getBalance(token: `0x${string}`, chainName: string): Promise<bigint> {
94
- const { publicClient } = this.getClients(chainName);
95
- return await publicClient.readContract({
96
- address: token,
97
- abi: ERC20_ABI,
98
- functionName: 'balanceOf',
99
- args: [this.account.address],
100
- }) as bigint;
101
- }
102
-
103
- /** Get the native token balance (ETH, POL, etc.) on a chain */
104
- async getNativeBalance(chainName: string): Promise<bigint> {
105
- const { publicClient } = this.getClients(chainName);
106
- return await publicClient.getBalance({ address: this.account.address });
107
- }
108
-
109
- async ensureApproval(
110
- token: `0x${string}`,
111
- spender: `0x${string}`,
112
- amount: bigint,
113
- chainName: string,
114
- ): Promise<`0x${string}` | null> {
115
- const { publicClient, walletClient } = this.getClients(chainName);
116
-
117
- const allowance = await publicClient.readContract({
118
- address: token,
119
- abi: ERC20_ABI,
120
- functionName: 'allowance',
121
- args: [this.account.address, spender],
122
- }) as bigint;
123
-
124
- if (allowance >= amount) return null;
125
-
126
- // Approve max to avoid repeated approvals
127
- const hash = await walletClient.writeContract({
128
- address: token,
129
- abi: ERC20_ABI,
130
- functionName: 'approve',
131
- args: [spender, maxUint256],
132
- });
133
-
134
- await publicClient.waitForTransactionReceipt({ hash });
135
- console.log(`[spot] Approved ${token} for ${spender} on ${chainName} (tx: ${hash})`);
136
- return hash;
137
- }
138
- }
package/src/spot/index.ts DELETED
@@ -1,11 +0,0 @@
1
- export { SpotDEXClient } from './client.js';
2
- export { UniswapAdapter } from './uniswap.js';
3
- export { AerodromeAdapter } from './aerodrome.js';
4
- export { SpotSwapManager } from './swap-manager.js';
5
- export type {
6
- SpotConfig,
7
- SpotSwapParams,
8
- SpotQuoteResult,
9
- SpotSwapResult,
10
- } from './types.js';
11
- export { DEFAULT_SPOT_CONFIG } from './types.js';
@@ -1,219 +0,0 @@
1
- import { getChainConfig } from '../chains.js';
2
- import { SpotDEXClient } from './client.js';
3
- import { UniswapAdapter } from './uniswap.js';
4
- import { AerodromeAdapter } from './aerodrome.js';
5
- import type { SpotConfig, SpotSwapParams, SpotQuoteResult, SpotSwapResult } from './types.js';
6
-
7
- /** Well-known token symbols → addresses per chain */
8
- const TOKEN_ALIASES: Record<string, Record<string, `0x${string}`>> = {
9
- base: {
10
- ETH: '0x4200000000000000000000000000000000000006',
11
- WETH: '0x4200000000000000000000000000000000000006',
12
- USDC: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
13
- USDbC: '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA',
14
- DAI: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
15
- },
16
- ethereum: {
17
- ETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
18
- WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
19
- USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
20
- USDT: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
21
- DAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
22
- },
23
- arbitrum: {
24
- ETH: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
25
- WETH: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
26
- USDC: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
27
- 'USDC.e': '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',
28
- USDT: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
29
- },
30
- polygon: {
31
- POL: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
32
- WPOL: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
33
- WMATIC: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
34
- USDC: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
35
- 'USDC.e': '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
36
- USDT: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
37
- },
38
- };
39
-
40
- export class SpotSwapManager {
41
- private dexClient: SpotDEXClient;
42
- private uniswap: UniswapAdapter;
43
- private aerodrome: AerodromeAdapter;
44
- private config: SpotConfig;
45
-
46
- constructor(privateKey: string, config: SpotConfig) {
47
- this.config = config;
48
- this.dexClient = new SpotDEXClient(privateKey);
49
- this.uniswap = new UniswapAdapter(this.dexClient);
50
- this.aerodrome = new AerodromeAdapter(this.dexClient);
51
- }
52
-
53
- get client(): SpotDEXClient {
54
- return this.dexClient;
55
- }
56
-
57
- /** Resolve a token symbol or address to a hex address */
58
- resolveToken(tokenOrSymbol: string, chain: string): `0x${string}` {
59
- if (tokenOrSymbol.startsWith('0x') && tokenOrSymbol.length === 42) {
60
- return tokenOrSymbol as `0x${string}`;
61
- }
62
-
63
- const chainAliases = TOKEN_ALIASES[chain];
64
- if (chainAliases) {
65
- const upper = tokenOrSymbol.toUpperCase();
66
- const address = chainAliases[upper] ?? chainAliases[tokenOrSymbol];
67
- if (address) return address;
68
- }
69
-
70
- // Try WETH/USDC from chain config
71
- const chainConfig = getChainConfig(chain);
72
- if (chainConfig) {
73
- const upper = tokenOrSymbol.toUpperCase();
74
- if (upper === 'WETH' || upper === 'ETH') return chainConfig.wethAddress;
75
- if (upper === 'USDC') return chainConfig.usdcAddress;
76
- }
77
-
78
- throw new Error(`Cannot resolve token "${tokenOrSymbol}" on ${chain}. Provide contract address.`);
79
- }
80
-
81
- /** Select DEX: explicit choice, or auto-detect */
82
- private selectDex(chain: string, dex?: string): 'uniswap' | 'aerodrome' {
83
- if (dex) return dex as 'uniswap' | 'aerodrome';
84
-
85
- // Prefer Aerodrome on Base (lower fees, deeper liquidity for common pairs)
86
- const chainConfig = getChainConfig(chain);
87
- if (chain === 'base' && chainConfig?.dexRouters.aerodrome) {
88
- return 'aerodrome';
89
- }
90
- return 'uniswap';
91
- }
92
-
93
- async getQuote(params: {
94
- tokenIn: string;
95
- tokenOut: string;
96
- amountIn: bigint;
97
- chain?: string;
98
- dex?: string;
99
- }): Promise<SpotQuoteResult> {
100
- const chain = params.chain ?? this.config.defaultChain;
101
- const dex = this.selectDex(chain, params.dex);
102
-
103
- const swapParams: SpotSwapParams = {
104
- tokenIn: this.resolveToken(params.tokenIn, chain),
105
- tokenOut: this.resolveToken(params.tokenOut, chain),
106
- amountIn: params.amountIn,
107
- slippageBps: this.config.maxSlippageBps,
108
- chain,
109
- dex,
110
- };
111
-
112
- if (dex === 'aerodrome') {
113
- return this.aerodrome.quote(swapParams);
114
- }
115
- return this.uniswap.quote(swapParams);
116
- }
117
-
118
- async executeSwap(params: {
119
- tokenIn: string;
120
- tokenOut: string;
121
- amountIn: bigint;
122
- chain?: string;
123
- dex?: string;
124
- slippageBps?: number;
125
- }): Promise<SpotSwapResult> {
126
- const chain = params.chain ?? this.config.defaultChain;
127
- const dex = this.selectDex(chain, params.dex);
128
-
129
- // Enforce max swap value cap
130
- // amountIn is in token decimals — we need to estimate USD value.
131
- // For stablecoins (6 decimals), amountIn / 1e6 ≈ USD value.
132
- // For non-stables, we can't precisely compute USD without a price feed,
133
- // so we use a conservative estimate: assume the token could be worth up to $100k/unit.
134
- // The risk manager's filterSignals() already checks signal-level USD values before
135
- // we get here, so this is a belt-and-suspenders check using the config cap.
136
- const tokenInAddress = this.resolveToken(params.tokenIn, chain);
137
- try {
138
- const decimals = await this.dexClient.getDecimals(tokenInAddress, chain);
139
- const amountFloat = Number(params.amountIn) / 10 ** decimals;
140
- // For stablecoins, 1 token ≈ $1. For others, this is approximate.
141
- // A more precise check would need a price oracle, but the runtime's risk filter
142
- // already validates USD values before calling executeSwap().
143
- if (amountFloat > this.config.maxSwapValueUSD) {
144
- return {
145
- success: false,
146
- txHash: '0x0' as `0x${string}`,
147
- amountIn: params.amountIn,
148
- amountOut: 0n,
149
- tokenIn: '0x0' as `0x${string}`,
150
- tokenOut: '0x0' as `0x${string}`,
151
- effectivePrice: 0,
152
- gasCost: 0n,
153
- chain,
154
- dex,
155
- error: `Swap value ${amountFloat.toFixed(2)} tokens exceeds max $${this.config.maxSwapValueUSD}. Reduce size or increase maxSwapValueUSD.`,
156
- };
157
- }
158
- } catch {
159
- // If we can't check decimals, proceed — let the swap itself validate
160
- }
161
-
162
- if (!this.config.chains.includes(chain)) {
163
- return {
164
- success: false,
165
- txHash: '0x0' as `0x${string}`,
166
- amountIn: params.amountIn,
167
- amountOut: 0n,
168
- tokenIn: '0x0' as `0x${string}`,
169
- tokenOut: '0x0' as `0x${string}`,
170
- effectivePrice: 0,
171
- gasCost: 0n,
172
- chain,
173
- dex,
174
- error: `Chain "${chain}" not enabled in spot config (enabled: ${this.config.chains.join(', ')})`,
175
- };
176
- }
177
-
178
- // Pre-check native gas balance — swaps revert with opaque errors if wallet has 0 gas
179
- const MIN_GAS_WEI = 100_000_000_000_000n; // 0.0001 ETH/POL
180
- try {
181
- const nativeBalance = await this.dexClient.getNativeBalance(chain);
182
- if (nativeBalance < MIN_GAS_WEI) {
183
- const chainConfig = getChainConfig(chain);
184
- const nativeName = chainConfig?.nativeCurrency ?? 'ETH';
185
- return {
186
- success: false,
187
- txHash: '0x0' as `0x${string}`,
188
- amountIn: params.amountIn,
189
- amountOut: 0n,
190
- tokenIn: '0x0' as `0x${string}`,
191
- tokenOut: '0x0' as `0x${string}`,
192
- effectivePrice: 0,
193
- gasCost: 0n,
194
- chain,
195
- dex,
196
- error: `Insufficient ${nativeName} for gas on ${chain}. Balance: ${nativeBalance} wei. Need at least 0.0001 ${nativeName}. Bridge gas to ${chain} before swapping.`,
197
- };
198
- }
199
- } catch {
200
- // Non-critical — proceed and let the swap itself surface the error
201
- }
202
-
203
- const swapParams: SpotSwapParams = {
204
- tokenIn: this.resolveToken(params.tokenIn, chain),
205
- tokenOut: this.resolveToken(params.tokenOut, chain),
206
- amountIn: params.amountIn,
207
- slippageBps: params.slippageBps ?? this.config.maxSlippageBps,
208
- chain,
209
- dex,
210
- };
211
-
212
- console.log(`[spot] Swapping on ${dex}/${chain}: ${params.tokenIn} → ${params.tokenOut} (${params.amountIn})`);
213
-
214
- if (dex === 'aerodrome') {
215
- return this.aerodrome.swap(swapParams);
216
- }
217
- return this.uniswap.swap(swapParams);
218
- }
219
- }