amped-defi 1.0.0

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 (189) hide show
  1. package/README.md +757 -0
  2. package/dist/__mocks__/@sodax/sdk.d.ts +24 -0
  3. package/dist/__mocks__/@sodax/sdk.d.ts.map +1 -0
  4. package/dist/__mocks__/@sodax/sdk.js +24 -0
  5. package/dist/__mocks__/@sodax/sdk.js.map +1 -0
  6. package/dist/__tests__/setup.d.ts +4 -0
  7. package/dist/__tests__/setup.d.ts.map +1 -0
  8. package/dist/__tests__/setup.js +32 -0
  9. package/dist/__tests__/setup.js.map +1 -0
  10. package/dist/index.d.ts +66 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +281 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/policy/policyEngine.d.ts +119 -0
  15. package/dist/policy/policyEngine.d.ts.map +1 -0
  16. package/dist/policy/policyEngine.js +322 -0
  17. package/dist/policy/policyEngine.js.map +1 -0
  18. package/dist/providers/spokeProviderFactory.d.ts +38 -0
  19. package/dist/providers/spokeProviderFactory.d.ts.map +1 -0
  20. package/dist/providers/spokeProviderFactory.js +212 -0
  21. package/dist/providers/spokeProviderFactory.js.map +1 -0
  22. package/dist/sodax/client.d.ts +34 -0
  23. package/dist/sodax/client.d.ts.map +1 -0
  24. package/dist/sodax/client.js +99 -0
  25. package/dist/sodax/client.js.map +1 -0
  26. package/dist/tools/bridge.d.ts +105 -0
  27. package/dist/tools/bridge.d.ts.map +1 -0
  28. package/dist/tools/bridge.js +334 -0
  29. package/dist/tools/bridge.js.map +1 -0
  30. package/dist/tools/discovery.d.ts +141 -0
  31. package/dist/tools/discovery.d.ts.map +1 -0
  32. package/dist/tools/discovery.js +777 -0
  33. package/dist/tools/discovery.js.map +1 -0
  34. package/dist/tools/moneyMarket.d.ts +227 -0
  35. package/dist/tools/moneyMarket.d.ts.map +1 -0
  36. package/dist/tools/moneyMarket.js +867 -0
  37. package/dist/tools/moneyMarket.js.map +1 -0
  38. package/dist/tools/portfolio.d.ts +43 -0
  39. package/dist/tools/portfolio.d.ts.map +1 -0
  40. package/dist/tools/portfolio.js +538 -0
  41. package/dist/tools/portfolio.js.map +1 -0
  42. package/dist/tools/swap.d.ts +71 -0
  43. package/dist/tools/swap.d.ts.map +1 -0
  44. package/dist/tools/swap.js +762 -0
  45. package/dist/tools/swap.js.map +1 -0
  46. package/dist/tools/walletManagement.d.ts +80 -0
  47. package/dist/tools/walletManagement.d.ts.map +1 -0
  48. package/dist/tools/walletManagement.js +289 -0
  49. package/dist/tools/walletManagement.js.map +1 -0
  50. package/dist/types.d.ts +205 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +5 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/utils/errorUtils.d.ts +2 -0
  55. package/dist/utils/errorUtils.d.ts.map +1 -0
  56. package/dist/utils/errorUtils.js +19 -0
  57. package/dist/utils/errorUtils.js.map +1 -0
  58. package/dist/utils/errors.d.ts +144 -0
  59. package/dist/utils/errors.d.ts.map +1 -0
  60. package/dist/utils/errors.js +310 -0
  61. package/dist/utils/errors.js.map +1 -0
  62. package/dist/utils/positionAggregator.d.ts +122 -0
  63. package/dist/utils/positionAggregator.d.ts.map +1 -0
  64. package/dist/utils/positionAggregator.js +377 -0
  65. package/dist/utils/positionAggregator.js.map +1 -0
  66. package/dist/utils/priceService.d.ts +45 -0
  67. package/dist/utils/priceService.d.ts.map +1 -0
  68. package/dist/utils/priceService.js +108 -0
  69. package/dist/utils/priceService.js.map +1 -0
  70. package/dist/utils/sodaxApi.d.ts +92 -0
  71. package/dist/utils/sodaxApi.d.ts.map +1 -0
  72. package/dist/utils/sodaxApi.js +143 -0
  73. package/dist/utils/sodaxApi.js.map +1 -0
  74. package/dist/utils/tokenResolver.d.ts +54 -0
  75. package/dist/utils/tokenResolver.d.ts.map +1 -0
  76. package/dist/utils/tokenResolver.js +252 -0
  77. package/dist/utils/tokenResolver.js.map +1 -0
  78. package/dist/wallet/backendConfig.d.ts +37 -0
  79. package/dist/wallet/backendConfig.d.ts.map +1 -0
  80. package/dist/wallet/backendConfig.js +125 -0
  81. package/dist/wallet/backendConfig.js.map +1 -0
  82. package/dist/wallet/backends/BankrBackend.d.ts +73 -0
  83. package/dist/wallet/backends/BankrBackend.d.ts.map +1 -0
  84. package/dist/wallet/backends/BankrBackend.js +315 -0
  85. package/dist/wallet/backends/BankrBackend.js.map +1 -0
  86. package/dist/wallet/backends/BankrWalletProvider.d.ts +75 -0
  87. package/dist/wallet/backends/BankrWalletProvider.d.ts.map +1 -0
  88. package/dist/wallet/backends/BankrWalletProvider.js +243 -0
  89. package/dist/wallet/backends/BankrWalletProvider.js.map +1 -0
  90. package/dist/wallet/backends/EnvBackend.d.ts +50 -0
  91. package/dist/wallet/backends/EnvBackend.d.ts.map +1 -0
  92. package/dist/wallet/backends/EnvBackend.js +114 -0
  93. package/dist/wallet/backends/EnvBackend.js.map +1 -0
  94. package/dist/wallet/backends/EvmWalletSkillBackend.d.ts +40 -0
  95. package/dist/wallet/backends/EvmWalletSkillBackend.d.ts.map +1 -0
  96. package/dist/wallet/backends/EvmWalletSkillBackend.js +81 -0
  97. package/dist/wallet/backends/EvmWalletSkillBackend.js.map +1 -0
  98. package/dist/wallet/backends/index.d.ts +10 -0
  99. package/dist/wallet/backends/index.d.ts.map +1 -0
  100. package/dist/wallet/backends/index.js +10 -0
  101. package/dist/wallet/backends/index.js.map +1 -0
  102. package/dist/wallet/index.d.ts +9 -0
  103. package/dist/wallet/index.d.ts.map +1 -0
  104. package/dist/wallet/index.js +12 -0
  105. package/dist/wallet/index.js.map +1 -0
  106. package/dist/wallet/providers/AmpedWalletProvider.d.ts +107 -0
  107. package/dist/wallet/providers/AmpedWalletProvider.d.ts.map +1 -0
  108. package/dist/wallet/providers/AmpedWalletProvider.js +208 -0
  109. package/dist/wallet/providers/AmpedWalletProvider.js.map +1 -0
  110. package/dist/wallet/providers/BankrBackend.d.ts +105 -0
  111. package/dist/wallet/providers/BankrBackend.d.ts.map +1 -0
  112. package/dist/wallet/providers/BankrBackend.js +327 -0
  113. package/dist/wallet/providers/BankrBackend.js.map +1 -0
  114. package/dist/wallet/providers/LocalKeyBackend.d.ts +62 -0
  115. package/dist/wallet/providers/LocalKeyBackend.d.ts.map +1 -0
  116. package/dist/wallet/providers/LocalKeyBackend.js +152 -0
  117. package/dist/wallet/providers/LocalKeyBackend.js.map +1 -0
  118. package/dist/wallet/providers/chainConfig.d.ts +209 -0
  119. package/dist/wallet/providers/chainConfig.d.ts.map +1 -0
  120. package/dist/wallet/providers/chainConfig.js +175 -0
  121. package/dist/wallet/providers/chainConfig.js.map +1 -0
  122. package/dist/wallet/providers/index.d.ts +30 -0
  123. package/dist/wallet/providers/index.d.ts.map +1 -0
  124. package/dist/wallet/providers/index.js +32 -0
  125. package/dist/wallet/providers/index.js.map +1 -0
  126. package/dist/wallet/providers/types.d.ts +156 -0
  127. package/dist/wallet/providers/types.d.ts.map +1 -0
  128. package/dist/wallet/providers/types.js +11 -0
  129. package/dist/wallet/providers/types.js.map +1 -0
  130. package/dist/wallet/skillWalletAdapter.d.ts +96 -0
  131. package/dist/wallet/skillWalletAdapter.d.ts.map +1 -0
  132. package/dist/wallet/skillWalletAdapter.js +280 -0
  133. package/dist/wallet/skillWalletAdapter.js.map +1 -0
  134. package/dist/wallet/types.d.ts +134 -0
  135. package/dist/wallet/types.d.ts.map +1 -0
  136. package/dist/wallet/types.js +138 -0
  137. package/dist/wallet/types.js.map +1 -0
  138. package/dist/wallet/walletManager.d.ts +111 -0
  139. package/dist/wallet/walletManager.d.ts.map +1 -0
  140. package/dist/wallet/walletManager.js +476 -0
  141. package/dist/wallet/walletManager.js.map +1 -0
  142. package/dist/wallet/walletRegistry.d.ts +95 -0
  143. package/dist/wallet/walletRegistry.d.ts.map +1 -0
  144. package/dist/wallet/walletRegistry.js +184 -0
  145. package/dist/wallet/walletRegistry.js.map +1 -0
  146. package/index.js +2 -0
  147. package/openclaw.plugin.json +37 -0
  148. package/package.json +69 -0
  149. package/src/__mocks__/@sodax/sdk.ts +28 -0
  150. package/src/__tests__/errors.test.ts +238 -0
  151. package/src/__tests__/policyEngine.test.ts +354 -0
  152. package/src/__tests__/positionAggregator.test.ts +271 -0
  153. package/src/__tests__/setup.ts +35 -0
  154. package/src/__tests__/sodaxApi.test.ts +203 -0
  155. package/src/__tests__/walletRegistry.test.ts +155 -0
  156. package/src/index.ts +376 -0
  157. package/src/policy/policyEngine.ts +389 -0
  158. package/src/providers/spokeProviderFactory.ts +283 -0
  159. package/src/sodax/client.ts +113 -0
  160. package/src/tools/bridge.ts +425 -0
  161. package/src/tools/discovery.ts +989 -0
  162. package/src/tools/moneyMarket.ts +1265 -0
  163. package/src/tools/portfolio.ts +697 -0
  164. package/src/tools/swap.ts +926 -0
  165. package/src/tools/walletManagement.ts +359 -0
  166. package/src/types.ts +228 -0
  167. package/src/utils/errorUtils.ts +16 -0
  168. package/src/utils/errors.ts +396 -0
  169. package/src/utils/positionAggregator.ts +559 -0
  170. package/src/utils/priceService.ts +153 -0
  171. package/src/utils/sodaxApi.ts +261 -0
  172. package/src/utils/tokenResolver.ts +286 -0
  173. package/src/wallet/backendConfig.ts +151 -0
  174. package/src/wallet/backends/BankrBackend.ts +399 -0
  175. package/src/wallet/backends/BankrWalletProvider.ts +329 -0
  176. package/src/wallet/backends/EnvBackend.ts +149 -0
  177. package/src/wallet/backends/EvmWalletSkillBackend.ts +110 -0
  178. package/src/wallet/backends/index.ts +10 -0
  179. package/src/wallet/index.ts +14 -0
  180. package/src/wallet/providers/AmpedWalletProvider.ts +267 -0
  181. package/src/wallet/providers/BankrBackend.ts +407 -0
  182. package/src/wallet/providers/LocalKeyBackend.ts +184 -0
  183. package/src/wallet/providers/chainConfig.ts +194 -0
  184. package/src/wallet/providers/index.ts +62 -0
  185. package/src/wallet/providers/types.ts +186 -0
  186. package/src/wallet/skillWalletAdapter.ts +335 -0
  187. package/src/wallet/types.ts +248 -0
  188. package/src/wallet/walletManager.ts +561 -0
  189. package/src/wallet/walletRegistry.ts +216 -0
@@ -0,0 +1,867 @@
1
+ /**
2
+ * Money Market Tools for Amped DeFi Plugin
3
+ *
4
+ * Provides advanced supply, withdraw, borrow, and repay operations for the SODAX money market.
5
+ * Supports both same-chain and cross-chain operations (e.g., supply on Chain A, borrow to Chain B).
6
+ *
7
+ * Key capabilities:
8
+ * - Supply: Deposit tokens as collateral on any supported chain
9
+ * - Borrow: Borrow tokens to any chain (cross-chain capable)
10
+ * - Withdraw: Withdraw supplied tokens from any chain
11
+ * - Repay: Repay borrowed tokens from any chain
12
+ * - Intent-based operations: Create intents for custom flows
13
+ *
14
+ * Cross-chain flows:
15
+ * 1. Supply on Chain A → Borrow to Chain B (different destination)
16
+ * 2. Supply on Chain A → Borrow on Chain A (same chain)
17
+ * 3. Cross-chain repay: Repay debt from any chain
18
+ * 4. Cross-chain withdraw: Withdraw collateral to any chain
19
+ */
20
+ import { Type } from "@sinclair/typebox";
21
+ import { getSodaxClient } from "../sodax/client";
22
+ import { getSpokeProvider } from "../providers/spokeProviderFactory";
23
+ import { PolicyEngine } from "../policy/policyEngine";
24
+ import { getWalletManager } from '../wallet/walletManager';
25
+ import { serializeError } from '../utils/errorUtils';
26
+ import { resolveToken, getTokenInfo } from '../utils/tokenResolver';
27
+ import { toSodaxChainId } from '../wallet/types';
28
+ // ============================================================================
29
+ // TypeBox Schemas
30
+ // ============================================================================
31
+ /**
32
+ * Base schema for money market operations
33
+ */
34
+ const MoneyMarketBaseSchema = Type.Object({
35
+ walletId: Type.String({
36
+ description: "Unique identifier for the wallet"
37
+ }),
38
+ chainId: Type.String({
39
+ description: "Source SODAX spoke chain ID where the operation originates (e.g., 'ethereum', 'arbitrum', 'sonic')"
40
+ }),
41
+ token: Type.String({
42
+ description: "Token address or symbol to supply/borrow/withdraw/repay",
43
+ }),
44
+ amount: Type.String({
45
+ description: "Amount in human-readable units (e.g., '100.5' for 100.5 USDC). Use '-1' for max repay (repay full debt).",
46
+ }),
47
+ timeoutMs: Type.Optional(Type.Number({
48
+ description: "Operation timeout in milliseconds",
49
+ default: 180000,
50
+ })),
51
+ policyId: Type.Optional(Type.String({ description: "Optional policy profile identifier for custom limits" })),
52
+ skipSimulation: Type.Optional(Type.Boolean({
53
+ description: "Skip transaction simulation (not recommended)",
54
+ default: false,
55
+ })),
56
+ });
57
+ /**
58
+ * Supply operation schema
59
+ * Supply tokens as collateral to the money market on the specified chain
60
+ */
61
+ const MoneyMarketSupplySchema = Type.Composite([
62
+ MoneyMarketBaseSchema,
63
+ Type.Object({
64
+ useAsCollateral: Type.Optional(Type.Boolean({
65
+ description: "Whether to use the supplied tokens as collateral for borrowing (default: true)",
66
+ default: true,
67
+ })),
68
+ // Cross-chain supply options
69
+ dstChainId: Type.Optional(Type.String({
70
+ description: "Optional destination chain for the supply operation. If different from chainId, performs cross-chain supply.",
71
+ })),
72
+ recipient: Type.Optional(Type.String({
73
+ description: "Optional recipient address for the supplied position (defaults to wallet address)",
74
+ })),
75
+ }),
76
+ ]);
77
+ /**
78
+ * Withdraw operation schema
79
+ * Withdraw supplied tokens from the money market
80
+ */
81
+ const MoneyMarketWithdrawSchema = Type.Composite([
82
+ MoneyMarketBaseSchema,
83
+ Type.Object({
84
+ withdrawType: Type.Optional(Type.Union([
85
+ Type.Literal('default'),
86
+ Type.Literal('collateral'),
87
+ Type.Literal('all'),
88
+ ], {
89
+ description: "Withdraw type: 'default' (standard), 'collateral' (withdraw collateral only), 'all' (withdraw maximum)",
90
+ default: 'default',
91
+ })),
92
+ // Cross-chain withdraw options
93
+ dstChainId: Type.Optional(Type.String({
94
+ description: "Optional destination chain to receive withdrawn tokens. If different from chainId, performs cross-chain withdraw.",
95
+ })),
96
+ recipient: Type.Optional(Type.String({
97
+ description: "Optional recipient address to receive withdrawn tokens (defaults to wallet address)",
98
+ })),
99
+ }),
100
+ ]);
101
+ /**
102
+ * Borrow operation schema
103
+ * Borrow tokens from the money market
104
+ *
105
+ * Key feature: Can borrow to a DIFFERENT chain than where collateral is supplied!
106
+ * Example: Supply USDC on Ethereum, borrow USDT to Arbitrum
107
+ */
108
+ const MoneyMarketBorrowSchema = Type.Composite([
109
+ MoneyMarketBaseSchema,
110
+ Type.Object({
111
+ interestRateMode: Type.Optional(Type.Union([
112
+ Type.Literal(1, { description: "Stable interest rate" }),
113
+ Type.Literal(2, { description: "Variable interest rate (recommended)" }),
114
+ ], {
115
+ description: "Interest rate mode: 1 = Stable, 2 = Variable",
116
+ default: 2,
117
+ })),
118
+ referralCode: Type.Optional(Type.String({
119
+ description: "Optional referral code for the borrow operation",
120
+ })),
121
+ // Cross-chain borrow options (key feature!)
122
+ dstChainId: Type.Optional(Type.String({
123
+ description: "Destination chain to receive borrowed tokens. If different from chainId, performs cross-chain borrow (supply on chainId, receive borrowed tokens on dstChainId).",
124
+ })),
125
+ recipient: Type.Optional(Type.String({
126
+ description: "Optional recipient address to receive borrowed tokens (defaults to wallet address on dstChainId or chainId)",
127
+ })),
128
+ }),
129
+ ]);
130
+ /**
131
+ * Repay operation schema
132
+ * Repay borrowed tokens to the money market
133
+ */
134
+ const MoneyMarketRepaySchema = Type.Composite([
135
+ MoneyMarketBaseSchema,
136
+ Type.Object({
137
+ interestRateMode: Type.Optional(Type.Union([
138
+ Type.Literal(1, { description: "Stable interest rate" }),
139
+ Type.Literal(2, { description: "Variable interest rate" }),
140
+ ], {
141
+ description: "Interest rate mode of the debt to repay: 1 = Stable, 2 = Variable",
142
+ default: 2,
143
+ })),
144
+ repayAll: Type.Optional(Type.Boolean({
145
+ description: "If true, repays the full debt amount (useful for closing position)",
146
+ default: false,
147
+ })),
148
+ // Cross-chain repay options
149
+ collateralChainId: Type.Optional(Type.String({
150
+ description: "Optional chain ID where collateral is held (for cross-chain repay scenarios)",
151
+ })),
152
+ }),
153
+ ]);
154
+ /**
155
+ * Create Intent schemas for advanced users
156
+ * These allow building custom multi-step flows
157
+ */
158
+ const CreateSupplyIntentSchema = Type.Composite([
159
+ MoneyMarketSupplySchema,
160
+ Type.Object({
161
+ raw: Type.Optional(Type.Boolean({
162
+ description: "If true, returns raw transaction data instead of executing (for custom signing flows)",
163
+ default: false,
164
+ })),
165
+ }),
166
+ ]);
167
+ const CreateBorrowIntentSchema = Type.Composite([
168
+ MoneyMarketBorrowSchema,
169
+ Type.Object({
170
+ raw: Type.Optional(Type.Boolean({
171
+ description: "If true, returns raw transaction data instead of executing (for custom signing flows)",
172
+ default: false,
173
+ })),
174
+ }),
175
+ ]);
176
+ const CreateWithdrawIntentSchema = Type.Composite([
177
+ MoneyMarketWithdrawSchema,
178
+ Type.Object({
179
+ raw: Type.Optional(Type.Boolean({
180
+ description: "If true, returns raw transaction data instead of executing (for custom signing flows)",
181
+ default: false,
182
+ })),
183
+ }),
184
+ ]);
185
+ const CreateRepayIntentSchema = Type.Composite([
186
+ MoneyMarketRepaySchema,
187
+ Type.Object({
188
+ raw: Type.Optional(Type.Boolean({
189
+ description: "If true, returns raw transaction data instead of executing (for custom signing flows)",
190
+ default: false,
191
+ })),
192
+ }),
193
+ ]);
194
+ // ============================================================================
195
+ // Helper Functions
196
+ // ============================================================================
197
+ /**
198
+ * Converts human-readable amount to token units (wei)
199
+ */
200
+ function parseTokenAmount(amount, decimals = 18) {
201
+ // Handle special case for max repay
202
+ if (amount === '-1') {
203
+ return BigInt(-1);
204
+ }
205
+ const parsed = parseFloat(amount);
206
+ if (isNaN(parsed)) {
207
+ throw new Error(`Invalid amount: ${amount}`);
208
+ }
209
+ const multiplier = Math.pow(10, decimals);
210
+ return BigInt(Math.floor(parsed * multiplier));
211
+ }
212
+ /**
213
+ * Resolves wallet and creates spoke provider for the operation
214
+ */
215
+ async function resolveWalletAndProvider(walletId, chainId) {
216
+ const walletManager = getWalletManager();
217
+ const wallet = await walletManager.resolve(walletId);
218
+ const walletAddress = await wallet.getAddress();
219
+ const spokeProvider = await getSpokeProvider(walletId, chainId);
220
+ return { walletAddress, spokeProvider };
221
+ }
222
+ /**
223
+ * Common pre-operation checks and allowance handling
224
+ */
225
+ async function prepareMoneyMarketOperation(walletId, chainId, token, amount, operation, policyId) {
226
+ // ============================================================================
227
+ // Hub Chain Validation
228
+ // ============================================================================
229
+ // SODAX architecture: Money market operations must be initiated from spoke chains,
230
+ // not the hub chain (Sonic). The hub chain is the settlement layer where contracts
231
+ // live, but users interact via spoke chains that relay operations to the hub.
232
+ // Reference: sodax-tests/tests/crossChainSdk.test.ts explicitly omits SONIC_MAINNET_CHAIN_ID
233
+ const isHubChainSource = chainId.toLowerCase() === 'sonic' || chainId === '146';
234
+ if (isHubChainSource) {
235
+ throw new Error(`Money market operations cannot be initiated from the hub chain (Sonic). ` +
236
+ `Please use a spoke chain (base, arbitrum, ethereum, optimism, avalanche, bsc, polygon) as the source chain.`);
237
+ }
238
+ // Ensure sodax client is initialized
239
+ const _sodaxClient = getSodaxClient(); // Just verify it's ready
240
+ void _sodaxClient;
241
+ // Normalize chain ID to SDK format for token resolution
242
+ const sdkChainId = toSodaxChainId(chainId);
243
+ // Resolve token symbol to address
244
+ const tokenAddr = await resolveToken(sdkChainId, token);
245
+ // Resolve wallet and create spoke provider
246
+ const { walletAddress, spokeProvider } = await resolveWalletAndProvider(walletId, chainId);
247
+ // Policy check
248
+ const policyEngine = new PolicyEngine();
249
+ const policyResult = await policyEngine.checkMoneyMarket({
250
+ walletId,
251
+ chainId,
252
+ token,
253
+ amount, // Add required amount parameter
254
+ amountUsd: parseFloat(amount), // Simplified - would need actual price lookup
255
+ operation,
256
+ policyId,
257
+ });
258
+ if (!policyResult.allowed) {
259
+ throw new Error(`Policy check failed: ${policyResult.reason || "Operation not permitted"}.`);
260
+ }
261
+ return { walletAddress, spokeProvider, policyResult, tokenAddr };
262
+ }
263
+ /**
264
+ * Resolves token and returns its decimals
265
+ * Falls back to 18 decimals if token info not found
266
+ */
267
+ async function getTokenDecimals(chainId, token) {
268
+ try {
269
+ const sdkChainId = toSodaxChainId(chainId);
270
+ const tokenInfo = await getTokenInfo(sdkChainId, token);
271
+ return tokenInfo?.decimals ?? 18;
272
+ }
273
+ catch {
274
+ // If token info lookup fails, fall back to 18 decimals
275
+ return 18;
276
+ }
277
+ }
278
+ /**
279
+ * Checks and handles token approval if needed
280
+ */
281
+ async function ensureAllowance(params, spokeProvider, raw = false) {
282
+ const sodaxClient = await getSodaxClient();
283
+ // Check if allowance is sufficient
284
+ const isAllowanceValid = await sodaxClient.moneyMarket.isAllowanceValid(params, spokeProvider);
285
+ if (!isAllowanceValid.ok || !isAllowanceValid.value) {
286
+ if (raw) {
287
+ // Return raw approval transaction
288
+ const rawApproval = await sodaxClient.moneyMarket.approve(params, spokeProvider, true // raw mode
289
+ );
290
+ return { rawApproval };
291
+ }
292
+ else {
293
+ // Execute approval transaction
294
+ const approvalResult = await sodaxClient.moneyMarket.approve(params, spokeProvider, false);
295
+ // Handle Result type from SDK
296
+ const txHash = approvalResult.ok
297
+ ? approvalResult.value
298
+ : approvalResult.txHash || approvalResult;
299
+ return { approvalTxHash: String(txHash) };
300
+ }
301
+ }
302
+ return {};
303
+ }
304
+ /**
305
+ * Determine if operation is cross-chain
306
+ */
307
+ function isCrossChainOperation(srcChainId, dstChainId) {
308
+ return !!dstChainId && dstChainId !== srcChainId;
309
+ }
310
+ // ============================================================================
311
+ // Tool Handlers
312
+ // ============================================================================
313
+ /**
314
+ * Supply tokens to the money market
315
+ *
316
+ * Supports cross-chain supply: supply tokens on chainId, collateral is recorded on dstChainId (if different)
317
+ */
318
+ async function handleSupply(params) {
319
+ const { walletId, chainId, token, amount, timeoutMs = 180000, policyId, useAsCollateral = true, dstChainId, recipient, skipSimulation = false } = params;
320
+ const crossChain = isCrossChainOperation(chainId, dstChainId);
321
+ const warnings = [];
322
+ try {
323
+ // Pre-operation checks
324
+ const { walletAddress, spokeProvider, tokenAddr } = await prepareMoneyMarketOperation(walletId, chainId, token, amount, "supply", policyId);
325
+ // Parse amount with actual token decimals
326
+ const decimals = await getTokenDecimals(chainId, token);
327
+ const amountBigInt = parseTokenAmount(amount, decimals);
328
+ // Check allowance for supply
329
+ const { approvalTxHash } = await ensureAllowance({ token: tokenAddr, amount: amountBigInt, action: 'supply' }, spokeProvider);
330
+ if (approvalTxHash) {
331
+ warnings.push(`Approval transaction executed: ${approvalTxHash}`);
332
+ }
333
+ const sodaxClient = await getSodaxClient();
334
+ // Build supply parameters
335
+ const supplyParams = {
336
+ action: 'supply',
337
+ token: tokenAddr,
338
+ amount: amountBigInt,
339
+ toAddress: recipient || walletAddress,
340
+ };
341
+ // Add cross-chain parameters if applicable
342
+ if (crossChain && dstChainId) {
343
+ supplyParams.toChainId = toSodaxChainId(dstChainId);
344
+ warnings.push(`Cross-chain supply: tokens supplied on ${chainId}, collateral recorded on ${dstChainId}`);
345
+ }
346
+ // Check and handle allowance (required for ALL supply operations)
347
+ // Reference: sodax-frontend moneymarket-ops.ts - supply ALWAYS checks allowance
348
+ try {
349
+ const allowanceResult = await sodaxClient.moneyMarket.isAllowanceValid({ token: tokenAddr, amount: amountBigInt, action: 'supply' }, spokeProvider);
350
+ if (allowanceResult.ok && !allowanceResult.value) {
351
+ console.log('[mm:supply] Approval needed, approving...');
352
+ const approveResult = await sodaxClient.moneyMarket.approve({ token: tokenAddr, amount: amountBigInt, action: 'supply' }, spokeProvider);
353
+ if (!approveResult.ok) {
354
+ throw new Error(`Approval failed: ${serializeError(approveResult.error)}`);
355
+ }
356
+ // Wait for approval confirmation
357
+ const approvalTxHash = approveResult.value;
358
+ if (approvalTxHash && spokeProvider.walletProvider?.waitForTransactionReceipt) {
359
+ await spokeProvider.walletProvider.waitForTransactionReceipt(approvalTxHash);
360
+ console.log('[mm:supply] Approval confirmed');
361
+ }
362
+ }
363
+ }
364
+ catch (allowanceError) {
365
+ console.warn('[mm:supply] Allowance check failed, proceeding anyway:', allowanceError);
366
+ }
367
+ // Execute supply
368
+ const supplyResult = await sodaxClient.moneyMarket.supply(supplyParams, spokeProvider, timeoutMs);
369
+ // Handle Result type from SDK
370
+ if (supplyResult.ok === false) {
371
+ throw new Error(`Supply failed: ${serializeError(supplyResult.error)}`);
372
+ }
373
+ const value = supplyResult.ok ? supplyResult.value : supplyResult;
374
+ // SDK may return [spokeTxHash, hubTxHash] tuple
375
+ const [solverResponse, intent, deliveryInfo] = Array.isArray(value) ? value : [value, undefined, undefined];
376
+ // Extract tx hashes from deliveryInfo (SDK returns 3-element tuple)
377
+ const spokeTxHash = deliveryInfo?.srcTxHash || solverResponse?.txHash || solverResponse;
378
+ const hubTxHash = deliveryInfo?.hubTxHash || intent?.hubTxHash;
379
+ const dstTxHash = deliveryInfo?.dstTxHash;
380
+ return {
381
+ success: true,
382
+ txHash: String(spokeTxHash),
383
+ status: "success",
384
+ spokeTxHash: String(spokeTxHash),
385
+ hubTxHash: hubTxHash ? String(hubTxHash) : undefined,
386
+ intentHash: undefined,
387
+ operation: "supply",
388
+ chainId,
389
+ dstChainId: dstChainId || chainId,
390
+ token,
391
+ amount,
392
+ isCrossChain: crossChain,
393
+ message: crossChain
394
+ ? `Successfully supplied ${amount} ${token} on ${chainId}. Collateral available on ${dstChainId || chainId}.`
395
+ : `Successfully supplied ${amount} ${token} to money market on ${chainId}`,
396
+ warnings: warnings.length > 0 ? warnings : undefined,
397
+ };
398
+ }
399
+ catch (error) {
400
+ const errorMessage = error instanceof Error ? error.message : "Unknown error during supply";
401
+ return {
402
+ success: false,
403
+ status: "failed",
404
+ operation: "supply",
405
+ chainId,
406
+ dstChainId: dstChainId || chainId,
407
+ token,
408
+ amount,
409
+ isCrossChain: crossChain,
410
+ message: `Money market supply failed: ${errorMessage}`,
411
+ };
412
+ }
413
+ }
414
+ /**
415
+ * Withdraw tokens from the money market
416
+ *
417
+ * Supports cross-chain withdraw: withdraw collateral from chainId, receive tokens on dstChainId
418
+ */
419
+ async function handleWithdraw(params) {
420
+ const { walletId, chainId, token, amount, timeoutMs = 180000, policyId, withdrawType = 'default', dstChainId, recipient, skipSimulation = false } = params;
421
+ const crossChain = isCrossChainOperation(chainId, dstChainId);
422
+ const warnings = [];
423
+ try {
424
+ // Pre-operation checks
425
+ const { walletAddress, spokeProvider, tokenAddr } = await prepareMoneyMarketOperation(walletId, chainId, token, amount, "withdraw", policyId);
426
+ // Parse amount with actual token decimals
427
+ const decimals = await getTokenDecimals(chainId, token);
428
+ const amountBigInt = parseTokenAmount(amount, decimals);
429
+ const sodaxClient = await getSodaxClient();
430
+ // Build withdraw parameters
431
+ const withdrawParams = {
432
+ action: 'withdraw',
433
+ token: tokenAddr,
434
+ amount: amountBigInt,
435
+ toAddress: recipient || walletAddress,
436
+ };
437
+ // Add cross-chain parameters if applicable
438
+ if (crossChain && dstChainId) {
439
+ withdrawParams.toChainId = toSodaxChainId(dstChainId);
440
+ warnings.push(`Cross-chain withdraw: withdrawing from ${chainId}, receiving tokens on ${dstChainId}`);
441
+ }
442
+ // Check and handle allowance (required for hub chain operations)
443
+ // Reference: sodax-frontend moneymarket-ops.ts
444
+ const isHubChain = chainId === 'sonic' || chainId === '146';
445
+ if (isHubChain) {
446
+ try {
447
+ const allowanceResult = await sodaxClient.moneyMarket.isAllowanceValid({ token: tokenAddr, amount: amountBigInt, action: 'withdraw' }, spokeProvider);
448
+ if (allowanceResult.ok && !allowanceResult.value) {
449
+ console.log('[mm:withdraw] Approval needed, approving...');
450
+ const approveResult = await sodaxClient.moneyMarket.approve({ token: tokenAddr, amount: amountBigInt, action: 'withdraw' }, spokeProvider);
451
+ if (!approveResult.ok) {
452
+ throw new Error(`Approval failed: ${serializeError(approveResult.error)}`);
453
+ }
454
+ // Wait for approval confirmation
455
+ const approvalTxHash = approveResult.value;
456
+ if (approvalTxHash && spokeProvider.walletProvider?.waitForTransactionReceipt) {
457
+ await spokeProvider.walletProvider.waitForTransactionReceipt(approvalTxHash);
458
+ console.log('[mm:withdraw] Approval confirmed');
459
+ }
460
+ }
461
+ }
462
+ catch (allowanceError) {
463
+ console.warn('[mm:withdraw] Allowance check failed, proceeding anyway:', allowanceError);
464
+ }
465
+ }
466
+ // Execute withdraw
467
+ const withdrawResult = await sodaxClient.moneyMarket.withdraw(withdrawParams, spokeProvider, timeoutMs);
468
+ // Handle Result type from SDK
469
+ if (withdrawResult.ok === false) {
470
+ throw new Error(`Withdraw failed: ${serializeError(withdrawResult.error)}`);
471
+ }
472
+ const value = withdrawResult.ok ? withdrawResult.value : withdrawResult;
473
+ const [solverResponse, intent, deliveryInfo] = Array.isArray(value) ? value : [value, undefined, undefined];
474
+ // Extract tx hashes from deliveryInfo (SDK returns 3-element tuple)
475
+ const spokeTxHash = deliveryInfo?.srcTxHash || solverResponse?.txHash || solverResponse;
476
+ const hubTxHash = deliveryInfo?.hubTxHash || intent?.hubTxHash;
477
+ const dstTxHash = deliveryInfo?.dstTxHash;
478
+ return {
479
+ success: true,
480
+ txHash: String(spokeTxHash),
481
+ status: "success",
482
+ spokeTxHash: String(spokeTxHash),
483
+ hubTxHash: hubTxHash ? String(hubTxHash) : undefined,
484
+ intentHash: undefined,
485
+ operation: "withdraw",
486
+ chainId,
487
+ dstChainId: dstChainId || chainId,
488
+ token,
489
+ amount,
490
+ isCrossChain: crossChain,
491
+ message: crossChain
492
+ ? `Successfully withdrew ${amount} ${token} from ${chainId} to ${dstChainId}`
493
+ : `Successfully withdrew ${amount} ${token} from money market on ${chainId}`,
494
+ warnings: warnings.length > 0 ? warnings : undefined,
495
+ };
496
+ }
497
+ catch (error) {
498
+ const errorMessage = error instanceof Error ? error.message : "Unknown error during withdraw";
499
+ return {
500
+ success: false,
501
+ status: "failed",
502
+ operation: "withdraw",
503
+ chainId,
504
+ dstChainId: dstChainId || chainId,
505
+ token,
506
+ amount,
507
+ isCrossChain: crossChain,
508
+ message: `Money market withdraw failed: ${errorMessage}`,
509
+ };
510
+ }
511
+ }
512
+ /**
513
+ * Borrow tokens from the money market
514
+ *
515
+ * KEY FEATURE: Can borrow to a DIFFERENT chain than where collateral is supplied!
516
+ * Example: Supply USDC on Ethereum (chainId), borrow USDT to Arbitrum (dstChainId)
517
+ *
518
+ * This is a powerful cross-chain DeFi primitive that allows:
519
+ * 1. Accessing liquidity without moving collateral
520
+ * 2. Arbitraging interest rates across chains
521
+ * 3. Efficient capital utilization across the entire SODAX network
522
+ */
523
+ async function handleBorrow(params) {
524
+ const { walletId, chainId, token, amount, timeoutMs = 180000, policyId, interestRateMode = 2, referralCode, dstChainId, // KEY: This can be different from chainId for cross-chain borrow!
525
+ recipient, skipSimulation = false } = params;
526
+ const crossChain = isCrossChainOperation(chainId, dstChainId);
527
+ const warnings = [];
528
+ try {
529
+ // Pre-operation checks
530
+ const { walletAddress, spokeProvider, tokenAddr } = await prepareMoneyMarketOperation(walletId, chainId, token, amount, "borrow", policyId);
531
+ // Parse amount with actual token decimals
532
+ const decimals = await getTokenDecimals(chainId, token);
533
+ const amountBigInt = parseTokenAmount(amount, decimals);
534
+ // Get user's positions to check health factor (best practice)
535
+ const sodaxClient = await getSodaxClient();
536
+ // For cross-chain borrow, resolve token on DESTINATION chain
537
+ // SDK expects: getMoneyMarketToken(toChainId, params.token)
538
+ // So params.token must be the destination chain's token address
539
+ let borrowTokenAddr = tokenAddr;
540
+ if (crossChain && dstChainId) {
541
+ borrowTokenAddr = await resolveToken(dstChainId, token);
542
+ console.log('[mm:borrow] Cross-chain: resolved token on destination chain', {
543
+ srcChain: chainId,
544
+ dstChain: dstChainId,
545
+ srcTokenAddr: tokenAddr,
546
+ dstTokenAddr: borrowTokenAddr,
547
+ });
548
+ }
549
+ // Build borrow parameters
550
+ const borrowParams = {
551
+ action: 'borrow',
552
+ token: borrowTokenAddr,
553
+ amount: amountBigInt,
554
+ toAddress: recipient || walletAddress,
555
+ };
556
+ // KEY CROSS-CHAIN FEATURE:
557
+ // If dstChainId is provided and different from chainId, the borrowed tokens
558
+ // will be delivered to dstChainId instead of chainId where the borrow is initiated
559
+ if (crossChain && dstChainId) {
560
+ borrowParams.toChainId = toSodaxChainId(dstChainId);
561
+ warnings.push(`Cross-chain borrow: Using collateral on ${chainId}, receiving borrowed tokens on ${dstChainId}`);
562
+ warnings.push(`Ensure you have sufficient collateral on ${chainId} to support this borrow`);
563
+ }
564
+ // Check and handle allowance (required for hub chain operations)
565
+ // Reference: sodax-frontend moneymarket-ops.ts
566
+ const isHubChain = chainId === 'sonic' || chainId === '146';
567
+ if (isHubChain) {
568
+ try {
569
+ const allowanceResult = await sodaxClient.moneyMarket.isAllowanceValid({ token: borrowTokenAddr, amount: amountBigInt, action: 'borrow' }, spokeProvider);
570
+ if (allowanceResult.ok && !allowanceResult.value) {
571
+ console.log('[mm:borrow] Approval needed, approving...');
572
+ const approveResult = await sodaxClient.moneyMarket.approve({ token: borrowTokenAddr, amount: amountBigInt, action: 'borrow' }, spokeProvider);
573
+ if (!approveResult.ok) {
574
+ throw new Error(`Approval failed: ${serializeError(approveResult.error)}`);
575
+ }
576
+ // Wait for approval confirmation
577
+ const approvalTxHash = approveResult.value;
578
+ if (approvalTxHash && spokeProvider.walletProvider?.waitForTransactionReceipt) {
579
+ await spokeProvider.walletProvider.waitForTransactionReceipt(approvalTxHash);
580
+ console.log('[mm:borrow] Approval confirmed');
581
+ }
582
+ }
583
+ }
584
+ catch (allowanceError) {
585
+ console.warn('[mm:borrow] Allowance check failed, proceeding anyway:', allowanceError);
586
+ }
587
+ }
588
+ // Execute borrow
589
+ const borrowResult = await sodaxClient.moneyMarket.borrow(borrowParams, spokeProvider, timeoutMs);
590
+ // Handle Result type from SDK
591
+ if (borrowResult.ok === false) {
592
+ throw new Error(`Borrow failed: ${serializeError(borrowResult.error)}`);
593
+ }
594
+ const value = borrowResult.ok ? borrowResult.value : borrowResult;
595
+ const [solverResponse, intent, deliveryInfo] = Array.isArray(value) ? value : [value, undefined, undefined];
596
+ // Extract tx hashes from deliveryInfo (SDK returns 3-element tuple)
597
+ const spokeTxHash = deliveryInfo?.srcTxHash || solverResponse?.txHash || solverResponse;
598
+ const hubTxHash = deliveryInfo?.hubTxHash || intent?.hubTxHash;
599
+ const dstTxHash = deliveryInfo?.dstTxHash;
600
+ return {
601
+ success: true,
602
+ txHash: String(spokeTxHash),
603
+ status: "success",
604
+ spokeTxHash: String(spokeTxHash),
605
+ hubTxHash: hubTxHash ? String(hubTxHash) : undefined,
606
+ intentHash: undefined,
607
+ operation: "borrow",
608
+ chainId,
609
+ dstChainId: dstChainId || chainId,
610
+ token,
611
+ amount,
612
+ isCrossChain: crossChain,
613
+ message: crossChain
614
+ ? `Successfully borrowed ${amount} ${token} on ${dstChainId} using collateral from ${chainId}. Interest rate mode: ${interestRateMode === 1 ? 'Stable' : 'Variable'}`
615
+ : `Successfully borrowed ${amount} ${token} from money market on ${chainId}. Interest rate mode: ${interestRateMode === 1 ? 'Stable' : 'Variable'}`,
616
+ warnings: warnings.length > 0 ? warnings : undefined,
617
+ };
618
+ }
619
+ catch (error) {
620
+ const errorMessage = error instanceof Error ? error.message : "Unknown error during borrow";
621
+ return {
622
+ success: false,
623
+ status: "failed",
624
+ operation: "borrow",
625
+ chainId,
626
+ dstChainId: dstChainId || chainId,
627
+ token,
628
+ amount,
629
+ isCrossChain: crossChain,
630
+ message: `Money market borrow failed: ${errorMessage}`,
631
+ };
632
+ }
633
+ }
634
+ /**
635
+ * Repay borrowed tokens to the money market
636
+ *
637
+ * Supports cross-chain repay: repay debt using tokens from a different chain
638
+ */
639
+ async function handleRepay(params) {
640
+ const { walletId, chainId, token, amount, timeoutMs = 180000, policyId, interestRateMode = 2, repayAll = false, collateralChainId, skipSimulation = false } = params;
641
+ const crossChain = !!collateralChainId && collateralChainId !== chainId;
642
+ const warnings = [];
643
+ try {
644
+ // Pre-operation checks
645
+ const { walletAddress, spokeProvider, tokenAddr } = await prepareMoneyMarketOperation(walletId, chainId, token, amount, "repay", policyId);
646
+ // Parse amount with actual token decimals (use -1 for max repay if repayAll is true)
647
+ const decimals = await getTokenDecimals(chainId, token);
648
+ const amountBigInt = repayAll ? BigInt(-1) : parseTokenAmount(amount, decimals);
649
+ // Check allowance for repay
650
+ const { approvalTxHash } = await ensureAllowance({ token: tokenAddr, amount: amountBigInt === BigInt(-1) ? BigInt(0) : amountBigInt, action: 'repay' }, spokeProvider);
651
+ if (approvalTxHash) {
652
+ warnings.push(`Approval transaction executed: ${approvalTxHash}`);
653
+ }
654
+ const sodaxClient = await getSodaxClient();
655
+ // Build repay parameters
656
+ const repayParams = {
657
+ action: 'repay',
658
+ token: tokenAddr,
659
+ amount: amountBigInt,
660
+ };
661
+ // Add cross-chain parameters if applicable
662
+ if (crossChain && collateralChainId) {
663
+ repayParams.toChainId = collateralChainId;
664
+ warnings.push(`Cross-chain repay: Repaying debt on ${collateralChainId} using tokens from ${chainId}`);
665
+ }
666
+ // Check and handle allowance (required for ALL repay operations)
667
+ // Reference: sodax-frontend moneymarket-ops.ts - repay ALWAYS checks allowance
668
+ try {
669
+ const allowanceResult = await sodaxClient.moneyMarket.isAllowanceValid({ token: tokenAddr, amount: amountBigInt, action: 'repay' }, spokeProvider);
670
+ if (allowanceResult.ok && !allowanceResult.value) {
671
+ console.log('[mm:repay] Approval needed, approving...');
672
+ const approveResult = await sodaxClient.moneyMarket.approve({ token: tokenAddr, amount: amountBigInt, action: 'repay' }, spokeProvider);
673
+ if (!approveResult.ok) {
674
+ throw new Error(`Approval failed: ${serializeError(approveResult.error)}`);
675
+ }
676
+ // Wait for approval confirmation
677
+ const approvalTxHash = approveResult.value;
678
+ if (approvalTxHash && spokeProvider.walletProvider?.waitForTransactionReceipt) {
679
+ await spokeProvider.walletProvider.waitForTransactionReceipt(approvalTxHash);
680
+ console.log('[mm:repay] Approval confirmed');
681
+ }
682
+ }
683
+ }
684
+ catch (allowanceError) {
685
+ console.warn('[mm:repay] Allowance check failed, proceeding anyway:', allowanceError);
686
+ }
687
+ // Execute repay
688
+ const repayResult = await sodaxClient.moneyMarket.repay(repayParams, spokeProvider, timeoutMs);
689
+ // Handle Result type from SDK
690
+ if (repayResult.ok === false) {
691
+ throw new Error(`Repay failed: ${serializeError(repayResult.error)}`);
692
+ }
693
+ const value = repayResult.ok ? repayResult.value : repayResult;
694
+ const [solverResponse, intent, deliveryInfo] = Array.isArray(value) ? value : [value, undefined, undefined];
695
+ // Extract tx hashes from deliveryInfo (SDK returns 3-element tuple)
696
+ const spokeTxHash = deliveryInfo?.srcTxHash || solverResponse?.txHash || solverResponse;
697
+ const hubTxHash = deliveryInfo?.hubTxHash || intent?.hubTxHash;
698
+ const dstTxHash = deliveryInfo?.dstTxHash;
699
+ return {
700
+ success: true,
701
+ txHash: String(spokeTxHash),
702
+ status: "success",
703
+ spokeTxHash: String(spokeTxHash),
704
+ hubTxHash: hubTxHash ? String(hubTxHash) : undefined,
705
+ intentHash: undefined,
706
+ operation: "repay",
707
+ chainId,
708
+ token,
709
+ amount: repayAll ? "max (full debt)" : amount,
710
+ isCrossChain: crossChain,
711
+ message: repayAll
712
+ ? `Successfully repaid full debt for ${token} on ${chainId}`
713
+ : `Successfully repaid ${amount} ${token} to money market on ${chainId}`,
714
+ warnings: warnings.length > 0 ? warnings : undefined,
715
+ };
716
+ }
717
+ catch (error) {
718
+ const errorMessage = error instanceof Error ? error.message : "Unknown error during repay";
719
+ return {
720
+ success: false,
721
+ status: "failed",
722
+ operation: "repay",
723
+ chainId,
724
+ token,
725
+ amount: repayAll ? "max (full debt)" : amount,
726
+ isCrossChain: crossChain,
727
+ message: `Money market repay failed: ${errorMessage}`,
728
+ };
729
+ }
730
+ }
731
+ // ============================================================================
732
+ // Intent Creation Handlers (Advanced)
733
+ // ============================================================================
734
+ /**
735
+ * Create a supply intent without executing (for custom flows)
736
+ */
737
+ async function handleCreateSupplyIntent(params) {
738
+ const { walletId, chainId, token, amount, useAsCollateral = true, dstChainId, recipient, raw = true } = params;
739
+ try {
740
+ const { walletAddress, spokeProvider, tokenAddr } = await prepareMoneyMarketOperation(walletId, chainId, token, amount, "supply");
741
+ const decimals = await getTokenDecimals(chainId, token);
742
+ const amountBigInt = parseTokenAmount(amount, decimals);
743
+ const sodaxClient = await getSodaxClient();
744
+ const supplyParams = {
745
+ action: 'supply',
746
+ token: tokenAddr,
747
+ amount: amountBigInt,
748
+ toAddress: recipient || walletAddress,
749
+ };
750
+ if (dstChainId) {
751
+ supplyParams.toChainId = toSodaxChainId(dstChainId);
752
+ }
753
+ const intentData = await sodaxClient.moneyMarket.createSupplyIntent(supplyParams, spokeProvider, raw);
754
+ return {
755
+ success: true,
756
+ status: "pending",
757
+ operation: "createSupplyIntent",
758
+ chainId,
759
+ dstChainId: dstChainId || chainId,
760
+ token,
761
+ amount,
762
+ intentData,
763
+ requiresSubmission: true,
764
+ message: "Supply intent created. Submit this intent to execute the supply operation.",
765
+ };
766
+ }
767
+ catch (error) {
768
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
769
+ throw new Error(`Create supply intent failed: ${errorMessage}`);
770
+ }
771
+ }
772
+ /**
773
+ * Create a borrow intent without executing (for custom flows)
774
+ */
775
+ async function handleCreateBorrowIntent(params) {
776
+ const { walletId, chainId, token, amount, interestRateMode = 2, dstChainId, recipient, raw = true } = params;
777
+ try {
778
+ const { walletAddress, spokeProvider, tokenAddr } = await prepareMoneyMarketOperation(walletId, chainId, token, amount, "borrow");
779
+ const decimals = await getTokenDecimals(chainId, token);
780
+ const amountBigInt = parseTokenAmount(amount, decimals);
781
+ const sodaxClient = await getSodaxClient();
782
+ const borrowParams = {
783
+ action: 'borrow',
784
+ token: tokenAddr,
785
+ amount: amountBigInt,
786
+ toAddress: recipient || walletAddress,
787
+ };
788
+ if (dstChainId) {
789
+ borrowParams.toChainId = toSodaxChainId(dstChainId);
790
+ }
791
+ const intentData = await sodaxClient.moneyMarket.createBorrowIntent(borrowParams, spokeProvider, raw);
792
+ return {
793
+ success: true,
794
+ status: "pending",
795
+ operation: "createBorrowIntent",
796
+ chainId,
797
+ dstChainId: dstChainId || chainId,
798
+ token,
799
+ amount,
800
+ intentData,
801
+ requiresSubmission: true,
802
+ message: dstChainId && dstChainId !== chainId
803
+ ? `Cross-chain borrow intent created. Collateral on ${chainId}, borrowed tokens to ${dstChainId}. Submit this intent to execute.`
804
+ : "Borrow intent created. Submit this intent to execute the borrow operation.",
805
+ };
806
+ }
807
+ catch (error) {
808
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
809
+ throw new Error(`Create borrow intent failed: ${errorMessage}`);
810
+ }
811
+ }
812
+ // ============================================================================
813
+ // Tool Registration
814
+ // ============================================================================
815
+ /**
816
+ * Registers all money market tools with the agent tools registry
817
+ */
818
+ export function registerMoneyMarketTools(agentTools) {
819
+ // Supply
820
+ agentTools.register({
821
+ name: "amped_mm_supply",
822
+ summary: "Supply tokens as collateral to the SODAX money market. Supports same-chain and cross-chain supply (supply on chain A, collateral available on chain B).",
823
+ schema: MoneyMarketSupplySchema,
824
+ handler: handleSupply,
825
+ });
826
+ // Withdraw
827
+ agentTools.register({
828
+ name: "amped_mm_withdraw",
829
+ summary: "Withdraw supplied tokens from the SODAX money market. Supports cross-chain withdraw (withdraw from chain A, receive tokens on chain B).",
830
+ schema: MoneyMarketWithdrawSchema,
831
+ handler: handleWithdraw,
832
+ });
833
+ // Borrow
834
+ agentTools.register({
835
+ name: "amped_mm_borrow",
836
+ summary: "Borrow tokens from the SODAX money market. KEY FEATURE: Can borrow to a different chain than collateral! Example: Supply on Ethereum, borrow to Arbitrum using dstChainId parameter.",
837
+ schema: MoneyMarketBorrowSchema,
838
+ handler: handleBorrow,
839
+ });
840
+ // Repay
841
+ agentTools.register({
842
+ name: "amped_mm_repay",
843
+ summary: "Repay borrowed tokens to the SODAX money market. Use amount='-1' or repayAll=true to repay full debt. Supports cross-chain repay.",
844
+ schema: MoneyMarketRepaySchema,
845
+ handler: handleRepay,
846
+ });
847
+ // Advanced: Create Intent variants for custom flows
848
+ agentTools.register({
849
+ name: "amped_mm_create_supply_intent",
850
+ summary: "[Advanced] Create a supply intent without executing. Returns raw intent data for custom signing or multi-step flows.",
851
+ schema: CreateSupplyIntentSchema,
852
+ handler: handleCreateSupplyIntent,
853
+ });
854
+ agentTools.register({
855
+ name: "amped_mm_create_borrow_intent",
856
+ summary: "[Advanced] Create a borrow intent without executing. Supports cross-chain borrow intents. Returns raw intent data for custom signing or multi-step flows.",
857
+ schema: CreateBorrowIntentSchema,
858
+ handler: handleCreateBorrowIntent,
859
+ });
860
+ }
861
+ // ============================================================================
862
+ // Re-exports for testing and direct usage
863
+ // ============================================================================
864
+ export { MoneyMarketBaseSchema, MoneyMarketSupplySchema, MoneyMarketWithdrawSchema, MoneyMarketBorrowSchema, MoneyMarketRepaySchema, CreateSupplyIntentSchema, CreateWithdrawIntentSchema, CreateBorrowIntentSchema, CreateRepayIntentSchema, handleSupply, handleWithdraw, handleBorrow, handleRepay, handleCreateSupplyIntent, handleCreateBorrowIntent, };
865
+ // Aliases for index.ts compatibility
866
+ export { MoneyMarketSupplySchema as MmSupplySchema, MoneyMarketWithdrawSchema as MmWithdrawSchema, MoneyMarketBorrowSchema as MmBorrowSchema, MoneyMarketRepaySchema as MmRepaySchema, handleSupply as handleMmSupply, handleWithdraw as handleMmWithdraw, handleBorrow as handleMmBorrow, handleRepay as handleMmRepay, };
867
+ //# sourceMappingURL=moneyMarket.js.map