@mento-protocol/mento-sdk 3.2.7 → 3.2.8

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 (302) hide show
  1. package/dist/cache/routes.d.ts +13 -0
  2. package/dist/cache/routes.js +14649 -0
  3. package/dist/cache/tokens.d.ts +68 -0
  4. package/dist/cache/tokens.js +488 -0
  5. package/dist/core/abis/activePool.d.ts +2 -0
  6. package/dist/core/abis/activePool.js +14 -0
  7. package/dist/core/abis/addressesRegistry.d.ts +2 -0
  8. package/dist/core/abis/addressesRegistry.js +26 -0
  9. package/dist/core/abis/bipoolmanager.d.ts +34 -0
  10. package/dist/core/abis/bipoolmanager.js +72 -0
  11. package/dist/core/abis/borrowerOperations.d.ts +9 -0
  12. package/dist/core/abis/borrowerOperations.js +89 -0
  13. package/dist/core/abis/breakerbox.d.ts +13 -0
  14. package/dist/core/abis/breakerbox.js +8 -0
  15. package/dist/core/abis/broker.d.ts +2 -0
  16. package/dist/core/abis/broker.js +9 -0
  17. package/dist/core/abis/erc20.d.ts +9 -0
  18. package/dist/core/abis/erc20.js +21 -0
  19. package/dist/core/abis/fpmm.d.ts +270 -0
  20. package/dist/core/abis/fpmm.js +49 -0
  21. package/dist/core/abis/fpmmFactory.d.ts +85 -0
  22. package/dist/core/abis/fpmmFactory.js +26 -0
  23. package/dist/core/abis/hintHelpers.d.ts +2 -0
  24. package/dist/core/abis/hintHelpers.js +14 -0
  25. package/dist/core/abis/index.d.ts +22 -0
  26. package/dist/core/abis/index.js +38 -0
  27. package/dist/core/abis/liquidityStrategy.d.ts +132 -0
  28. package/dist/core/abis/liquidityStrategy.js +10 -0
  29. package/dist/core/abis/multiTroveGetter.d.ts +8 -0
  30. package/dist/core/abis/multiTroveGetter.js +15 -0
  31. package/dist/core/abis/priceFeed.d.ts +7 -0
  32. package/dist/core/abis/priceFeed.js +16 -0
  33. package/dist/core/abis/pricingmodule.d.ts +2 -0
  34. package/dist/core/abis/pricingmodule.js +6 -0
  35. package/dist/core/abis/reserve.d.ts +3 -0
  36. package/dist/core/abis/reserve.js +18 -0
  37. package/dist/core/abis/router.d.ts +521 -0
  38. package/dist/core/abis/router.js +45 -0
  39. package/dist/core/abis/sortedTroves.d.ts +2 -0
  40. package/dist/core/abis/sortedTroves.js +15 -0
  41. package/dist/core/abis/systemParams.d.ts +2 -0
  42. package/dist/core/abis/systemParams.js +14 -0
  43. package/dist/core/abis/troveManager.d.ts +2 -0
  44. package/dist/core/abis/troveManager.js +27 -0
  45. package/dist/core/abis/troveNFT.d.ts +2 -0
  46. package/dist/core/abis/troveNFT.js +9 -0
  47. package/dist/core/abis/virtualPool.d.ts +50 -0
  48. package/dist/core/abis/virtualPool.js +11 -0
  49. package/dist/core/abis/virtualPoolFactory.d.ts +59 -0
  50. package/dist/core/abis/virtualPoolFactory.js +17 -0
  51. package/dist/core/constants/addresses.d.ts +18 -0
  52. package/dist/core/constants/addresses.js +113 -0
  53. package/dist/core/constants/borrowConstants.d.ts +10 -0
  54. package/dist/core/constants/borrowConstants.js +16 -0
  55. package/dist/core/constants/borrowRegistries.d.ts +7 -0
  56. package/dist/core/constants/borrowRegistries.js +34 -0
  57. package/dist/core/constants/chainId.d.ts +7 -0
  58. package/dist/core/constants/chainId.js +11 -0
  59. package/dist/core/constants/contractNames.d.ts +21 -0
  60. package/dist/core/constants/contractNames.js +24 -0
  61. package/dist/core/constants/index.d.ts +6 -0
  62. package/dist/core/constants/index.js +22 -0
  63. package/dist/core/errors/base.d.ts +8 -0
  64. package/dist/core/errors/base.js +17 -0
  65. package/dist/core/errors/index.d.ts +4 -0
  66. package/dist/core/errors/index.js +20 -0
  67. package/dist/core/errors/oracle.d.ts +9 -0
  68. package/dist/core/errors/oracle.js +15 -0
  69. package/dist/core/errors/router.d.ts +14 -0
  70. package/dist/core/errors/router.js +24 -0
  71. package/dist/core/types/borrow.d.ts +87 -0
  72. package/dist/core/types/borrow.js +3 -0
  73. package/dist/core/types/contractAddresses.d.ts +42 -0
  74. package/dist/core/types/contractAddresses.js +3 -0
  75. package/dist/core/types/index.d.ts +10 -0
  76. package/dist/core/types/index.js +26 -0
  77. package/dist/core/types/liquidity.d.ts +194 -0
  78. package/dist/core/types/liquidity.js +3 -0
  79. package/dist/core/types/pool.d.ts +208 -0
  80. package/dist/core/types/pool.js +14 -0
  81. package/dist/core/types/provider.d.ts +45 -0
  82. package/dist/core/types/provider.js +3 -0
  83. package/dist/core/types/route.d.ts +62 -0
  84. package/dist/core/types/route.js +3 -0
  85. package/dist/core/types/token.d.ts +21 -0
  86. package/dist/core/types/token.js +3 -0
  87. package/dist/core/types/tradingLimits.d.ts +91 -0
  88. package/dist/core/types/tradingLimits.js +3 -0
  89. package/dist/core/types/tradingMode.d.ts +24 -0
  90. package/dist/core/types/tradingMode.js +31 -0
  91. package/dist/core/types/transaction.d.ts +45 -0
  92. package/dist/core/types/transaction.js +3 -0
  93. package/dist/esm/cache/routes.js +14644 -0
  94. package/dist/esm/cache/tokens.js +480 -0
  95. package/dist/esm/core/abis/activePool.js +10 -0
  96. package/dist/esm/core/abis/addressesRegistry.js +22 -0
  97. package/dist/esm/core/abis/bipoolmanager.js +68 -0
  98. package/dist/esm/core/abis/borrowerOperations.js +85 -0
  99. package/dist/esm/core/abis/breakerbox.js +4 -0
  100. package/dist/esm/core/abis/broker.js +5 -0
  101. package/dist/esm/core/abis/erc20.js +17 -0
  102. package/dist/esm/core/abis/fpmm.js +45 -0
  103. package/dist/esm/core/abis/fpmmFactory.js +22 -0
  104. package/dist/esm/core/abis/hintHelpers.js +10 -0
  105. package/dist/esm/core/abis/index.js +21 -0
  106. package/dist/esm/core/abis/liquidityStrategy.js +6 -0
  107. package/dist/esm/core/abis/multiTroveGetter.js +11 -0
  108. package/dist/esm/core/abis/priceFeed.js +12 -0
  109. package/dist/esm/core/abis/pricingmodule.js +2 -0
  110. package/dist/esm/core/abis/reserve.js +14 -0
  111. package/dist/esm/core/abis/router.js +41 -0
  112. package/dist/esm/core/abis/sortedTroves.js +11 -0
  113. package/dist/esm/core/abis/systemParams.js +10 -0
  114. package/dist/esm/core/abis/troveManager.js +23 -0
  115. package/dist/esm/core/abis/troveNFT.js +5 -0
  116. package/dist/esm/core/abis/virtualPool.js +7 -0
  117. package/dist/esm/core/abis/virtualPoolFactory.js +13 -0
  118. package/dist/esm/core/constants/addresses.js +107 -0
  119. package/dist/esm/core/constants/borrowConstants.js +12 -0
  120. package/dist/esm/core/constants/borrowRegistries.js +29 -0
  121. package/dist/esm/core/constants/chainId.js +7 -0
  122. package/dist/esm/core/constants/contractNames.js +20 -0
  123. package/dist/esm/core/constants/index.js +5 -0
  124. package/dist/esm/core/errors/base.js +12 -0
  125. package/dist/esm/core/errors/index.js +3 -0
  126. package/dist/esm/core/errors/oracle.js +10 -0
  127. package/dist/esm/core/errors/router.js +18 -0
  128. package/dist/esm/core/types/borrow.js +1 -0
  129. package/dist/esm/core/types/contractAddresses.js +1 -0
  130. package/dist/esm/core/types/index.js +9 -0
  131. package/dist/esm/core/types/liquidity.js +1 -0
  132. package/dist/esm/core/types/pool.js +10 -0
  133. package/dist/esm/core/types/provider.js +1 -0
  134. package/dist/esm/core/types/route.js +1 -0
  135. package/dist/esm/core/types/token.js +1 -0
  136. package/dist/esm/core/types/tradingLimits.js +1 -0
  137. package/dist/esm/core/types/tradingMode.js +26 -0
  138. package/dist/esm/core/types/transaction.js +1 -0
  139. package/dist/esm/index.js +139 -0
  140. package/dist/esm/package.json +1 -0
  141. package/dist/esm/services/borrow/BorrowService.js +455 -0
  142. package/dist/esm/services/borrow/borrowHelpers.js +3 -0
  143. package/dist/esm/services/borrow/borrowMath.js +127 -0
  144. package/dist/esm/services/borrow/index.js +3 -0
  145. package/dist/esm/services/borrow/internal/borrowApprovalService.js +48 -0
  146. package/dist/esm/services/borrow/internal/borrowContextStore.js +35 -0
  147. package/dist/esm/services/borrow/internal/borrowErc20.js +38 -0
  148. package/dist/esm/services/borrow/internal/borrowHints.js +27 -0
  149. package/dist/esm/services/borrow/internal/borrowPositionParser.js +82 -0
  150. package/dist/esm/services/borrow/internal/borrowReadService.js +271 -0
  151. package/dist/esm/services/borrow/internal/borrowRegistryReader.js +108 -0
  152. package/dist/esm/services/borrow/internal/borrowTransactionService.js +271 -0
  153. package/dist/esm/services/borrow/internal/borrowTypes.js +1 -0
  154. package/dist/esm/services/borrow/internal/borrowValidation.js +89 -0
  155. package/dist/esm/services/index.js +8 -0
  156. package/dist/esm/services/liquidity/LiquidityService.js +163 -0
  157. package/dist/esm/services/liquidity/basicLiquidity.js +162 -0
  158. package/dist/esm/services/liquidity/index.js +1 -0
  159. package/dist/esm/services/liquidity/liquidityHelpers.js +95 -0
  160. package/dist/esm/services/liquidity/rebalance.js +59 -0
  161. package/dist/esm/services/liquidity/zapHelpers.js +181 -0
  162. package/dist/esm/services/liquidity/zapIn.js +131 -0
  163. package/dist/esm/services/liquidity/zapOut.js +248 -0
  164. package/dist/esm/services/pools/PoolService.js +204 -0
  165. package/dist/esm/services/pools/index.js +1 -0
  166. package/dist/esm/services/pools/poolDetails.js +209 -0
  167. package/dist/esm/services/pools/poolDiscovery.js +112 -0
  168. package/dist/esm/services/pools/rebalancePreview.js +181 -0
  169. package/dist/esm/services/quotes/QuoteService.js +85 -0
  170. package/dist/esm/services/quotes/index.js +1 -0
  171. package/dist/esm/services/routes/RouteService.js +268 -0
  172. package/dist/esm/services/routes/index.js +1 -0
  173. package/dist/esm/services/swap/SwapService.js +247 -0
  174. package/dist/esm/services/swap/index.js +1 -0
  175. package/dist/esm/services/tokens/index.js +1 -0
  176. package/dist/esm/services/tokens/tokenService.js +285 -0
  177. package/dist/esm/services/trading/TradingLimitsService.js +154 -0
  178. package/dist/esm/services/trading/TradingService.js +222 -0
  179. package/dist/esm/services/trading/index.js +2 -0
  180. package/dist/esm/utils/chainConfig.js +118 -0
  181. package/dist/esm/utils/costUtils.js +56 -0
  182. package/dist/esm/utils/deadline.js +22 -0
  183. package/dist/esm/utils/index.js +9 -0
  184. package/dist/esm/utils/multicall.js +47 -0
  185. package/dist/esm/utils/pathEncoder.js +69 -0
  186. package/dist/esm/utils/rateFeed.js +23 -0
  187. package/dist/esm/utils/retry.js +24 -0
  188. package/dist/esm/utils/routeUtils.js +361 -0
  189. package/dist/esm/utils/routes.js +2 -0
  190. package/dist/esm/utils/sortUtils.js +33 -0
  191. package/dist/esm/utils/tokens.js +2 -0
  192. package/dist/esm/utils/tradingLimits.js +163 -0
  193. package/dist/esm/utils/validation.js +30 -0
  194. package/dist/index.d.ts +101 -0
  195. package/dist/index.js +158 -0
  196. package/dist/services/borrow/BorrowService.d.ts +381 -0
  197. package/dist/services/borrow/BorrowService.js +460 -0
  198. package/dist/services/borrow/borrowHelpers.d.ts +4 -0
  199. package/dist/services/borrow/borrowHelpers.js +13 -0
  200. package/dist/services/borrow/borrowMath.d.ts +21 -0
  201. package/dist/services/borrow/borrowMath.js +137 -0
  202. package/dist/services/borrow/index.d.ts +4 -0
  203. package/dist/services/borrow/index.js +20 -0
  204. package/dist/services/borrow/internal/borrowApprovalService.d.ts +14 -0
  205. package/dist/services/borrow/internal/borrowApprovalService.js +53 -0
  206. package/dist/services/borrow/internal/borrowContextStore.d.ts +11 -0
  207. package/dist/services/borrow/internal/borrowContextStore.js +40 -0
  208. package/dist/services/borrow/internal/borrowErc20.d.ts +5 -0
  209. package/dist/services/borrow/internal/borrowErc20.js +43 -0
  210. package/dist/services/borrow/internal/borrowHints.d.ts +7 -0
  211. package/dist/services/borrow/internal/borrowHints.js +31 -0
  212. package/dist/services/borrow/internal/borrowPositionParser.d.ts +4 -0
  213. package/dist/services/borrow/internal/borrowPositionParser.js +87 -0
  214. package/dist/services/borrow/internal/borrowReadService.d.ts +31 -0
  215. package/dist/services/borrow/internal/borrowReadService.js +276 -0
  216. package/dist/services/borrow/internal/borrowRegistryReader.d.ts +5 -0
  217. package/dist/services/borrow/internal/borrowRegistryReader.js +113 -0
  218. package/dist/services/borrow/internal/borrowTransactionService.d.ts +23 -0
  219. package/dist/services/borrow/internal/borrowTransactionService.js +276 -0
  220. package/dist/services/borrow/internal/borrowTypes.d.ts +15 -0
  221. package/dist/services/borrow/internal/borrowTypes.js +3 -0
  222. package/dist/services/borrow/internal/borrowValidation.d.ts +14 -0
  223. package/dist/services/borrow/internal/borrowValidation.js +104 -0
  224. package/dist/services/index.d.ts +9 -0
  225. package/dist/services/index.js +25 -0
  226. package/dist/services/liquidity/LiquidityService.d.ts +139 -0
  227. package/dist/services/liquidity/LiquidityService.js +168 -0
  228. package/dist/services/liquidity/basicLiquidity.d.ts +11 -0
  229. package/dist/services/liquidity/basicLiquidity.js +172 -0
  230. package/dist/services/liquidity/index.d.ts +2 -0
  231. package/dist/services/liquidity/index.js +18 -0
  232. package/dist/services/liquidity/liquidityHelpers.d.ts +19 -0
  233. package/dist/services/liquidity/liquidityHelpers.js +104 -0
  234. package/dist/services/liquidity/rebalance.d.ts +6 -0
  235. package/dist/services/liquidity/rebalance.js +64 -0
  236. package/dist/services/liquidity/zapHelpers.d.ts +100 -0
  237. package/dist/services/liquidity/zapHelpers.js +192 -0
  238. package/dist/services/liquidity/zapIn.d.ts +18 -0
  239. package/dist/services/liquidity/zapIn.js +138 -0
  240. package/dist/services/liquidity/zapOut.d.ts +9 -0
  241. package/dist/services/liquidity/zapOut.js +255 -0
  242. package/dist/services/pools/PoolService.d.ts +69 -0
  243. package/dist/services/pools/PoolService.js +209 -0
  244. package/dist/services/pools/index.d.ts +2 -0
  245. package/dist/services/pools/index.js +18 -0
  246. package/dist/services/pools/poolDetails.d.ts +13 -0
  247. package/dist/services/pools/poolDetails.js +216 -0
  248. package/dist/services/pools/poolDiscovery.d.ts +12 -0
  249. package/dist/services/pools/poolDiscovery.js +117 -0
  250. package/dist/services/pools/rebalancePreview.d.ts +5 -0
  251. package/dist/services/pools/rebalancePreview.js +186 -0
  252. package/dist/services/quotes/QuoteService.d.ts +51 -0
  253. package/dist/services/quotes/QuoteService.js +91 -0
  254. package/dist/services/quotes/index.d.ts +2 -0
  255. package/dist/services/quotes/index.js +18 -0
  256. package/dist/services/routes/RouteService.d.ts +117 -0
  257. package/dist/services/routes/RouteService.js +306 -0
  258. package/dist/services/routes/index.d.ts +2 -0
  259. package/dist/services/routes/index.js +18 -0
  260. package/dist/services/swap/SwapService.d.ts +198 -0
  261. package/dist/services/swap/SwapService.js +252 -0
  262. package/dist/services/swap/index.d.ts +2 -0
  263. package/dist/services/swap/index.js +18 -0
  264. package/dist/services/tokens/index.d.ts +2 -0
  265. package/dist/services/tokens/index.js +18 -0
  266. package/dist/services/tokens/tokenService.d.ts +55 -0
  267. package/dist/services/tokens/tokenService.js +290 -0
  268. package/dist/services/trading/TradingLimitsService.d.ts +38 -0
  269. package/dist/services/trading/TradingLimitsService.js +159 -0
  270. package/dist/services/trading/TradingService.d.ts +115 -0
  271. package/dist/services/trading/TradingService.js +227 -0
  272. package/dist/services/trading/index.d.ts +3 -0
  273. package/dist/services/trading/index.js +19 -0
  274. package/dist/utils/chainConfig.d.ts +16 -0
  275. package/dist/utils/chainConfig.js +123 -0
  276. package/dist/utils/costUtils.d.ts +12 -0
  277. package/dist/utils/costUtils.js +60 -0
  278. package/dist/utils/deadline.d.ts +21 -0
  279. package/dist/utils/deadline.js +26 -0
  280. package/dist/utils/index.d.ts +10 -0
  281. package/dist/utils/index.js +26 -0
  282. package/dist/utils/multicall.d.ts +30 -0
  283. package/dist/utils/multicall.js +52 -0
  284. package/dist/utils/pathEncoder.d.ts +34 -0
  285. package/dist/utils/pathEncoder.js +73 -0
  286. package/dist/utils/rateFeed.d.ts +18 -0
  287. package/dist/utils/rateFeed.js +27 -0
  288. package/dist/utils/retry.d.ts +12 -0
  289. package/dist/utils/retry.js +28 -0
  290. package/dist/utils/routeUtils.d.ts +295 -0
  291. package/dist/utils/routeUtils.js +371 -0
  292. package/dist/utils/routes.d.ts +3 -0
  293. package/dist/utils/routes.js +8 -0
  294. package/dist/utils/sortUtils.d.ts +24 -0
  295. package/dist/utils/sortUtils.js +39 -0
  296. package/dist/utils/tokens.d.ts +2 -0
  297. package/dist/utils/tokens.js +13 -0
  298. package/dist/utils/tradingLimits.d.ts +41 -0
  299. package/dist/utils/tradingLimits.js +171 -0
  300. package/dist/utils/validation.d.ts +19 -0
  301. package/dist/utils/validation.js +34 -0
  302. package/package.json +1 -1
@@ -0,0 +1,361 @@
1
+ import { canonicalSymbolKey } from './sortUtils';
2
+ /**
3
+ * Builds the connectivity data structures needed for route generation.
4
+ *
5
+ * Transforms a list of direct trading pairs into our ConnectivityData
6
+ * that allow us to quickly find trading routes.
7
+ *
8
+ * **Construction Process:**
9
+ *
10
+ * ```
11
+ * Input: TradablePairs = [
12
+ * { id: 'USDm-CELO', assets: [USDm, CELO], path: [exchange1_CELO_USDm] },
13
+ * { id: 'CELO-EURm', assets: [CELO, EURm], path: [exchange2_CELO_EURm] }
14
+ * ]
15
+ *
16
+ * Step 1 - Build addrToSymbol map:
17
+ * USDm.address → 'USDm'
18
+ * CELO.address → 'CELO'
19
+ * EURm.address → 'EURm'
20
+ *
21
+ * Step 2 - Build directPathMap (sorted alphabetically for consistency):
22
+ * 'CELO_addr-EURm_addr' → exchange2_CELO_EURm
23
+ * 'CELO_addr-USDm_addr' → exchange1_CELO_USDm
24
+ *
25
+ * Step 3 - Build bidirectional tokenGraph:
26
+ * USDm.address → Set([CELO.address])
27
+ * CELO.address → Set([USDm.address, EURm.address])
28
+ * EURm.address → Set([CELO.address])
29
+ * ```
30
+ *
31
+ * **Result**: We can now efficiently answer:
32
+ * - "What's the symbol for address X?" → addrToSymbol.get(addr)
33
+ * - "What exchange connects tokens X and Y?" → directPathMap.get(sortedAddressPairKey)
34
+ * - "What tokens can I reach from token X?" → tokenGraph.get(X)
35
+ *
36
+ * @param directRoutes - Array of direct trading pairs
37
+ * @returns Connectivity data structure for efficient route generation
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const directPairs = [
42
+ * { id: 'USDm-CELO', assets: [USDm, CELO], path: [exchange1] },
43
+ * { id: 'CELO-EURm', assets: [CELO, EURm], path: [exchange2] }
44
+ * ]
45
+ *
46
+ * const connectivityData = buildConnectivityStructures(directPairs)
47
+ *
48
+ * // Now we can efficiently find routes:
49
+ * // 1. Check if USDm connects to anything: connectivityData.tokenGraph.get(USDm.address) → [CELO.address]
50
+ * // 2. Check if CELO connects to EURm: connectivityData.tokenGraph.get(CELO.address) → [USDm.address, EURm.address] ✓
51
+ * // 3. Get exchange details: connectivityData.directPathMap.get('CELO_addr-EURm_addr') → exchange2_CELO_EURm
52
+ * // Result: Found route USDm → CELO → EURm with exchange details
53
+ * ```
54
+ */
55
+ export function buildConnectivityStructures(directRoutes) {
56
+ const addrToSymbol = new Map();
57
+ const directRouteMap = new Map();
58
+ const tokenGraph = new Map();
59
+ for (const route of directRoutes) {
60
+ const [tokenA, tokenB] = route.tokens;
61
+ // Build address-to-symbol map for quick symbol lookups
62
+ addrToSymbol.set(tokenA.address, tokenA.symbol);
63
+ addrToSymbol.set(tokenB.address, tokenB.symbol);
64
+ // Build direct path map (sorted addresses as key for consistency)
65
+ // for quick lookup of exchange details for any token pair
66
+ const routeId = canonicalSymbolKey(tokenA.symbol, tokenB.symbol);
67
+ if (!directRouteMap.has(routeId)) {
68
+ directRouteMap.set(routeId, route.path[0]);
69
+ }
70
+ // Build bidirectional connectivity graph for route traversal
71
+ // Each token can reach its directly connected tokens
72
+ if (!tokenGraph.has(tokenA.address))
73
+ tokenGraph.set(tokenA.address, new Set());
74
+ if (!tokenGraph.has(tokenB.address))
75
+ tokenGraph.set(tokenB.address, new Set());
76
+ tokenGraph.get(tokenA.address).add(tokenB.address);
77
+ tokenGraph.get(tokenB.address).add(tokenA.address);
78
+ }
79
+ return { addrToSymbol, directRouteMap, tokenGraph, directRoutes };
80
+ }
81
+ /**
82
+ * Generates all possible routes (direct + two-hop) using connectivity data.
83
+ *
84
+ * This function implements a route discovery algorithm that:
85
+ *
86
+ * 1. **Adds all direct routes** (single-hop routes)
87
+ * 2. **Discovers two-hop routes** using graph traversal:
88
+ * - For each token A, find its neighbors (tokens directly connected)
89
+ * - For each neighbor B, find B's neighbors
90
+ * - If B connects to token C (C ≠ A), then A->B->C is a valid route
91
+ *
92
+ * **Route Deduplication**: Multiple routes between the same token pair
93
+ * are collected in arrays, allowing the selection algorithm to choose
94
+ * the best one based on cost data or heuristics.
95
+ *
96
+ * **Canonical Route IDs**: All routes use alphabetically sorted symbols
97
+ * (e.g., 'EURm-USDm' not 'USDm-EURm') for consistent identification.
98
+ *
99
+ * @param connectivityData - The connectivity data from buildConnectivityStructures()
100
+ * @returns Map of route ID -> array of possible routes for that token pair
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * // Given direct routes: USDm-CELO, CELO-EURm, USDm-USDC
105
+ * const allRoutes = generateAllRoutes(connectivityData)
106
+ *
107
+ * // Results might include:
108
+ * // 'USDm-CELO' -> [{ path: [USDm->CELO] }] // direct route
109
+ * // 'EURm-USDm' -> [
110
+ * // { path: [USDm->USDC, USDC->EURm] } // two-hop via USDC
111
+ * // { path: [USDm->CELO, CELO->EURm] } // two-hop via CELO
112
+ * // ]
113
+ * ```
114
+ */
115
+ export function generateAllRoutes(connectivityData) {
116
+ const { addrToSymbol, directRouteMap, tokenGraph, directRoutes } = connectivityData;
117
+ const allRoutes = new Map();
118
+ // Step 1: Add all direct pairs (single-hop routes)
119
+ for (const route of directRoutes) {
120
+ if (!allRoutes.has(route.id)) {
121
+ allRoutes.set(route.id, []);
122
+ }
123
+ allRoutes.get(route.id).push(route);
124
+ }
125
+ // Step 2: Generate two-hop routes using graph traversal
126
+ // Algorithm: For each token, explore all paths of length 2
127
+ // OUTER LOOP: "For each starting token..." (e.g., USDm, CELO, EURm, etc.)
128
+ for (const [start, neighbors] of tokenGraph.entries()) {
129
+ // MIDDLE LOOP: "Where can I go from the starting token?" (first hop)
130
+ // Example: If start = USDm, neighbors might be [CELO, USDC, KESm]
131
+ for (const intermediate of neighbors) {
132
+ // Get all tokens reachable from this intermediate token (second hop destinations)
133
+ const secondHopNeighbors = tokenGraph.get(intermediate);
134
+ if (!secondHopNeighbors)
135
+ continue;
136
+ // INNER LOOP: "From the intermediate token, where can I go?" (second hop)
137
+ // Example: If intermediate = CELO, secondHopNeighbors might be [USDm, EURm, BRLm]
138
+ for (const end of secondHopNeighbors) {
139
+ // Skip circular routes like USDm → CELO → USDm (pointless)
140
+ if (end === start)
141
+ continue;
142
+ // At this point we have a potential route: start → intermediate → end
143
+ // Example: USDm → CELO → EURm
144
+ // Try to create a valid two-hop trading pair from this route
145
+ const twoHopRoute = createTwoHopRoute(start, intermediate, end, addrToSymbol, directRouteMap);
146
+ // If we successfully created the pair, add it to our collection
147
+ if (twoHopRoute) {
148
+ if (!allRoutes.has(twoHopRoute.id)) {
149
+ allRoutes.set(twoHopRoute.id, []);
150
+ }
151
+ allRoutes.get(twoHopRoute.id).push(twoHopRoute);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ return allRoutes;
157
+ }
158
+ /**
159
+ * Creates a two-hop tradable pair if valid exchange hops exist.
160
+ *
161
+ * 1. **Validates tokens exist** in the asset map
162
+ * 2. **Finds exchange hops** for both segments of the route
163
+ * 3. **Creates canonical pair structure** with sorted symbols
164
+ *
165
+ * **Route Structure**: The resulting pair represents trading from start->end
166
+ * via intermediate token, but the assets are ordered alphabetically by symbol
167
+ * for consistency (canonical form).
168
+ *
169
+ * **Path Representation**: The path array contains the actual exchange hops
170
+ * needed to execute the trade, preserving the routing information.
171
+ *
172
+ * @param startToken - Starting token address
173
+ * @param intermediate - Intermediate token address for routing
174
+ * @param end - Destination token address
175
+ * @param assetMap - Map of token address -> Asset details
176
+ * @param directPathMap - Map of token pairs -> exchange hop details
177
+ * @returns Route if valid route exists, null otherwise
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * // Create route: USDm -> CELO -> EURm
182
+ * const pair = createTwoHopPair(
183
+ * '0x765D...', // USDm address
184
+ * '0x471E...', // CELO address
185
+ * '0xD876...', // EURm address
186
+ * addrToSymbol,
187
+ * directPathMap
188
+ * )
189
+ *
190
+ * // Result:
191
+ * // {
192
+ * // id: 'EURm-USDm', // alphabetical order
193
+ * // assets: [EURm, USDm], // alphabetical order
194
+ * // path: [ // actual routing path
195
+ * // { USDm->CELO exchange },
196
+ * // { CELO->EURm exchange }
197
+ * // ]
198
+ * // }
199
+ * ```
200
+ */
201
+ export function createTwoHopRoute(startAddr, intermediateAddr, endAddr, addrToSymbol, directRouteMap) {
202
+ // Validate that both all tokens exist in our address-to-symbol map
203
+ const startSymbol = addrToSymbol.get(startAddr);
204
+ const intermediateSymbol = addrToSymbol.get(intermediateAddr);
205
+ const endSymbol = addrToSymbol.get(endAddr);
206
+ if (!startSymbol || !intermediateSymbol || !endSymbol)
207
+ return null;
208
+ // Find exchange hops for both segments of the two-hop route
209
+ // Keys are sorted token addresses for consistent lookup
210
+ const hop1Key = canonicalSymbolKey(startSymbol, intermediateSymbol);
211
+ const hop2Key = canonicalSymbolKey(intermediateSymbol, endSymbol);
212
+ const hop1 = directRouteMap.get(hop1Key);
213
+ const hop2 = directRouteMap.get(hop2Key);
214
+ // If either hop doesn't exist, this route is invalid
215
+ if (!hop1 || !hop2)
216
+ return null;
217
+ // Create canonical pair structure (alphabetical symbol ordering)
218
+ const routeId = canonicalSymbolKey(startSymbol, endSymbol);
219
+ // Create Token objects from address and symbol
220
+ const startToken = { address: startAddr, symbol: startSymbol };
221
+ const endToken = { address: endAddr, symbol: endSymbol };
222
+ // Token array follows alphabetical ordering for consistency
223
+ const tokens = startSymbol <= endSymbol ? [startToken, endToken] : [endToken, startToken];
224
+ return {
225
+ id: routeId,
226
+ tokens,
227
+ path: [hop1, hop2], // Preserves actual routing path for execution
228
+ };
229
+ }
230
+ /**
231
+ * Selects optimal routes from all candidates based on spread data or heuristics.
232
+ *
233
+ * This is the route optimization engine that implements the following logic:
234
+ *
235
+ * **For Single Route**: Use it directly (no optimization needed)
236
+ *
237
+ * **For Multiple Routes**:
238
+ * - If `returnAllRoutes=true`: Return all routes (used for cache generation)
239
+ * - If `returnAllRoutes=false`: Apply optimization to select the best route
240
+ *
241
+ * **Route Selection Strategy**: Delegates to `selectBestRoute()` which uses
242
+ * a multi-tier approach prioritizing cost efficiency and reliability.
243
+ *
244
+ * @param allRoutes - Map of pair ID -> array of possible routes
245
+ * @param returnAllRoutes - Whether to return all routes or optimize selection
246
+ * @param assetMap - Asset map for token symbol lookups during optimization
247
+ * @returns Array of selected optimal routes
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * // Multiple routes for USDm-EURm pair
252
+ * const candidates = new Map([
253
+ * ['EURm-USDm', [
254
+ * { path: [USDm->CELO->EURm], costData: { totalCostPercent: 0.5 } },
255
+ * { path: [USDm->BRLm->EURm], costData: { totalCostPercent: 0.3 } },
256
+ * { path: [USDm->EURm] } // direct route, no cost data
257
+ * ]]
258
+ * ])
259
+ *
260
+ * const optimal = selectOptimalRoutes(candidates, false, assetMap)
261
+ * // Returns the USDm->BRLm->EURm route (lowest cost: 0.3%)
262
+ * ```
263
+ */
264
+ export function selectOptimalRoutes(allRoutes, returnAllRoutes, addrToSymbol) {
265
+ const result = new Map();
266
+ for (const [routeId, routes] of allRoutes) {
267
+ if (routes.length === 1) {
268
+ // Only one route available - use it directly
269
+ result.set(routeId, routes[0]);
270
+ }
271
+ else if (returnAllRoutes) {
272
+ // Return all routes with unique keys (used for cache generation)
273
+ routes.forEach((route, index) => {
274
+ result.set(`${routeId}_${index}`, route);
275
+ });
276
+ }
277
+ else {
278
+ // Multiple routes - select the best one using optimization logic
279
+ const bestRoute = selectBestRoute(routes, addrToSymbol);
280
+ result.set(routeId, bestRoute);
281
+ }
282
+ }
283
+ return Array.from(result.values());
284
+ }
285
+ /**
286
+ * Selects the best route from candidates using cost data or fallback heuristics.
287
+ *
288
+ * This function implements a sophisticated route selection algorithm with
289
+ * multiple optimization tiers:
290
+ *
291
+ * **Tier 1 - Cost-Based Optimization** (Preferred):
292
+ * - Use routes with cost data (actual cost information)
293
+ * - Select route with lowest `totalCostPercent`
294
+ * - This provides the most cost-efficient trading
295
+ *
296
+ * **Tier 2 - Direct Route Preference** (Fallback):
297
+ * - If no cost data available, prefer direct (single-hop) routes
298
+ * - Direct routes have lower execution risk and gas costs
299
+ *
300
+ * **Tier 3 - Major Stablecoin Preference** (Final Fallback):
301
+ * - For two-hop routes, prefer those going through major stablecoins
302
+ * - Major FX currencies like USDm and EURm typically have better liquidity
303
+ *
304
+ * **Tier 4 - First Available** (Last Resort):
305
+ * - If no other heuristics apply, use the first route found
306
+ *
307
+ * @param candidates - Array of possible routes for the same token pair
308
+ * @param assetMap - Asset map for token symbol lookups
309
+ * @returns The optimal route selected using the tier system
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * const candidates = [
314
+ * { path: [A->B->C], costData: { totalCostPercent: 0.8 } },
315
+ * { path: [A->D->C], costData: { totalCostPercent: 0.4 } }, // Winner: lowest cost
316
+ * { path: [A->C] }, // direct route, no cost data
317
+ * ]
318
+ *
319
+ * const best = selectBestRoute(candidates, assetMap)
320
+ * // Returns the A->D->C route (0.4% cost)
321
+ * ```
322
+ */
323
+ export function selectBestRoute(candidates, addrToSymbol) {
324
+ // Tier 1: Prefer routes with cost data (lowest cost wins)
325
+ const candidatesWithCost = candidates.filter(hasCostData);
326
+ if (candidatesWithCost.length > 0) {
327
+ return candidatesWithCost.reduce((best, current) => current.costData.totalCostPercent < best.costData.totalCostPercent ? current : best);
328
+ }
329
+ // Tier 2: Prefer direct routes (single-hop, lower risk)
330
+ const directRoute = candidates.find((c) => c.path.length === 1);
331
+ if (directRoute)
332
+ return directRoute;
333
+ // Tier 3: Prefer routes through major stablecoins (better liquidity)
334
+ const stablecoins = ['USDm', 'EURm', 'USDC', 'USDT'];
335
+ const routeWithStablecoin = candidates.find((candidate) => {
336
+ const intermediateToken = getIntermediateToken(candidate);
337
+ if (!intermediateToken)
338
+ return false;
339
+ const symbol = addrToSymbol.get(intermediateToken);
340
+ return symbol && stablecoins.includes(symbol);
341
+ });
342
+ // Tier 4: Use first available route as last resort
343
+ return routeWithStablecoin || candidates[0];
344
+ }
345
+ /**
346
+ * Extracts the intermediate token address from a two-hop route.
347
+ * In a two-hop route A->B->C, this function finds token B (the intermediate).
348
+ */
349
+ export function getIntermediateToken(route) {
350
+ // Find the common token between the two hops
351
+ const [hop1, hop2] = route.path;
352
+ const hop1Tokens = [hop1.token0, hop1.token1];
353
+ const hop2Tokens = [hop2.token0, hop2.token1];
354
+ return hop1Tokens.find((addr) => hop2Tokens.includes(addr));
355
+ }
356
+ /**
357
+ * Type guard to check if a Route has cost data.
358
+ */
359
+ export function hasCostData(pair) {
360
+ return 'costData' in pair && pair.costData !== undefined;
361
+ }
@@ -0,0 +1,2 @@
1
+ // Re-export from consolidated cache
2
+ export { getCachedRoutes, cachedRoutes } from '../cache/routes';
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Sorts two token addresses to match the smart contract's _sortTokens behavior.
3
+ * Compares addresses as numeric values (BigInt), matching Solidity's address comparison.
4
+ *
5
+ * Solidity reference:
6
+ * ```solidity
7
+ * function _sortTokens(address a, address b) private pure returns (address, address) {
8
+ * return (a < b) ? (a, b) : (b, a);
9
+ * }
10
+ * ```
11
+ */
12
+ export function sortTokenAddresses(tokenA, tokenB) {
13
+ return BigInt(tokenA) < BigInt(tokenB)
14
+ ? [tokenA, tokenB]
15
+ : [tokenB, tokenA];
16
+ }
17
+ /**
18
+ * Creates a canonical route ID from two addresses.
19
+ * Uses numeric address comparison to match contract behavior.
20
+ */
21
+ export function canonicalAddressKey(addressA, addressB, separator = '-') {
22
+ const [first, second] = sortTokenAddresses(addressA, addressB);
23
+ return `${first}${separator}${second}`;
24
+ }
25
+ /**
26
+ * Creates a canonical pair key from two symbols.
27
+ * Uses standard string comparison for human-readable IDs.
28
+ */
29
+ export function canonicalSymbolKey(symbolA, symbolB, separator = '-') {
30
+ return symbolA < symbolB
31
+ ? `${symbolA}${separator}${symbolB}`
32
+ : `${symbolB}${separator}${symbolA}`;
33
+ }
@@ -0,0 +1,2 @@
1
+ // Re-export from consolidated cache
2
+ export { TokenSymbol, cachedTokens, getCachedTokens, getCachedTokensSync, getTokenAddress, findTokenBySymbol, TOKEN_ADDRESSES_BY_CHAIN, } from '../cache/tokens';
@@ -0,0 +1,163 @@
1
+ import { pad, hexToBigInt, numberToHex } from 'viem';
2
+ /** V2 trading limits use 15 decimals internally */
3
+ const V2_INTERNAL_DECIMALS = 15;
4
+ /** V2 L0 window is fixed at 5 minutes */
5
+ const V2_L0_TIMESTEP = 5 * 60; // 300 seconds
6
+ /** V2 L1 window is fixed at 1 day */
7
+ const V2_L1_TIMESTEP = 24 * 60 * 60; // 86400 seconds
8
+ /** Far future timestamp for global limits (year 2030) */
9
+ const TIMESTAMP_FAR_FUTURE = 1893456000;
10
+ /**
11
+ * Compute limit ID for Broker (V1) trading limits.
12
+ * limitId = exchangeId XOR bytes32(uint256(uint160(token)))
13
+ *
14
+ * @param exchangeId - The exchange ID (bytes32)
15
+ * @param token - The token address
16
+ * @returns The limit ID as hex string
17
+ */
18
+ export function computeLimitId(exchangeId, token) {
19
+ // Pad token address to 32 bytes (left-padded with zeros)
20
+ const tokenBytes32 = pad(token, { size: 32 });
21
+ // Convert both to BigInt for XOR operation
22
+ const exchangeIdBigInt = hexToBigInt(exchangeId);
23
+ const tokenBigInt = hexToBigInt(tokenBytes32);
24
+ // XOR operation
25
+ const limitIdBigInt = exchangeIdBigInt ^ tokenBigInt;
26
+ // Convert back to hex with proper padding
27
+ return pad(numberToHex(limitIdBigInt), { size: 32 });
28
+ }
29
+ /**
30
+ * Calculate trading limits from V1 config and state (Broker/Virtual pools).
31
+ * Returns human-friendly TradingLimit objects with maxIn/maxOut/until.
32
+ *
33
+ * @param config - V1 trading limits configuration
34
+ * @param state - V1 trading limits state
35
+ * @param asset - Token address
36
+ * @param tokenDecimals - Token decimals for consumer reference
37
+ * @returns Array of TradingLimit objects
38
+ */
39
+ export function calculateTradingLimitsV1(config, state, asset, tokenDecimals) {
40
+ const nowEpoch = Math.floor(Date.now() / 1000);
41
+ const limits = [];
42
+ // Check if L0 is enabled (bit 0 of flags)
43
+ const isL0Enabled = (config.flags & 0x01) !== 0;
44
+ // Check if L1 is enabled (bit 1 of flags)
45
+ const isL1Enabled = (config.flags & 0x02) !== 0;
46
+ // Check if LG is enabled (bit 2 of flags)
47
+ const isLGEnabled = (config.flags & 0x04) !== 0;
48
+ // Reset netflows if time windows have passed
49
+ const isL0Outdated = isL0Enabled && nowEpoch > state.lastUpdated0 + config.timestep0;
50
+ const isL1Outdated = isL1Enabled && nowEpoch > state.lastUpdated1 + config.timestep1;
51
+ const effectiveNetflow0 = isL0Outdated ? 0n : state.netflow0;
52
+ const effectiveNetflow1 = isL1Outdated ? 0n : state.netflow1;
53
+ const effectiveLastUpdated0 = isL0Outdated ? nowEpoch : state.lastUpdated0;
54
+ const effectiveLastUpdated1 = isL1Outdated ? nowEpoch : state.lastUpdated1;
55
+ // Add L0 limit if configured
56
+ if (isL0Enabled && config.limit0 > 0n) {
57
+ limits.push({
58
+ asset,
59
+ maxIn: config.limit0 - effectiveNetflow0,
60
+ maxOut: config.limit0 + effectiveNetflow0,
61
+ until: effectiveLastUpdated0 + config.timestep0,
62
+ decimals: tokenDecimals,
63
+ });
64
+ }
65
+ // Add L1 limit if configured
66
+ if (isL1Enabled && config.limit1 > 0n) {
67
+ limits.push({
68
+ asset,
69
+ maxIn: config.limit1 - effectiveNetflow1,
70
+ maxOut: config.limit1 + effectiveNetflow1,
71
+ until: effectiveLastUpdated1 + config.timestep1,
72
+ decimals: tokenDecimals,
73
+ });
74
+ }
75
+ // Add LG (global) limit if configured
76
+ if (isLGEnabled && config.limitGlobal > 0n) {
77
+ limits.push({
78
+ asset,
79
+ maxIn: config.limitGlobal - state.netflowGlobal,
80
+ maxOut: config.limitGlobal + state.netflowGlobal,
81
+ until: TIMESTAMP_FAR_FUTURE,
82
+ decimals: tokenDecimals,
83
+ });
84
+ }
85
+ // Apply cascading restrictions: limits with larger timeframes restrict smaller ones
86
+ // e.g., if maxIn is 0 in LG, it should also be 0 in L1 and L0
87
+ for (let i = limits.length - 1; i > 0; i--) {
88
+ if (limits[i - 1].maxIn > limits[i].maxIn) {
89
+ limits[i - 1] = { ...limits[i - 1], maxIn: limits[i].maxIn };
90
+ }
91
+ if (limits[i - 1].maxOut > limits[i].maxOut) {
92
+ limits[i - 1] = { ...limits[i - 1], maxOut: limits[i].maxOut };
93
+ }
94
+ }
95
+ return limits;
96
+ }
97
+ /**
98
+ * Calculate trading limits from V2 config and state (FPMM pools).
99
+ * V2 uses fixed timeframes: 5 min for L0, 1 day for L1.
100
+ * Values are stored with 15 decimals precision.
101
+ *
102
+ * @param config - V2 trading limits configuration
103
+ * @param state - V2 trading limits state
104
+ * @param asset - Token address
105
+ * @returns Array of TradingLimit objects
106
+ */
107
+ export function calculateTradingLimitsV2(config, state, asset) {
108
+ const nowEpoch = Math.floor(Date.now() / 1000);
109
+ const limits = [];
110
+ // Check if L0 is configured (limit0 > 0)
111
+ const isL0Enabled = config.limit0 > 0n;
112
+ // Check if L1 is configured (limit1 > 0)
113
+ const isL1Enabled = config.limit1 > 0n;
114
+ // Reset netflows if time windows have passed
115
+ const isL0Outdated = isL0Enabled && nowEpoch > state.lastUpdated0 + V2_L0_TIMESTEP;
116
+ const isL1Outdated = isL1Enabled && nowEpoch > state.lastUpdated1 + V2_L1_TIMESTEP;
117
+ const effectiveNetflow0 = isL0Outdated ? 0n : state.netflow0;
118
+ const effectiveNetflow1 = isL1Outdated ? 0n : state.netflow1;
119
+ const effectiveLastUpdated0 = isL0Outdated ? nowEpoch : state.lastUpdated0;
120
+ const effectiveLastUpdated1 = isL1Outdated ? nowEpoch : state.lastUpdated1;
121
+ // Add L0 limit if configured
122
+ if (isL0Enabled) {
123
+ limits.push({
124
+ asset,
125
+ maxIn: config.limit0 - effectiveNetflow0,
126
+ maxOut: config.limit0 + effectiveNetflow0,
127
+ until: effectiveLastUpdated0 + V2_L0_TIMESTEP,
128
+ decimals: V2_INTERNAL_DECIMALS, // V2 returns 15 decimal values
129
+ });
130
+ }
131
+ // Add L1 limit if configured
132
+ if (isL1Enabled) {
133
+ limits.push({
134
+ asset,
135
+ maxIn: config.limit1 - effectiveNetflow1,
136
+ maxOut: config.limit1 + effectiveNetflow1,
137
+ until: effectiveLastUpdated1 + V2_L1_TIMESTEP,
138
+ decimals: V2_INTERNAL_DECIMALS, // V2 returns 15 decimal values
139
+ });
140
+ }
141
+ // Apply cascading restrictions
142
+ for (let i = limits.length - 1; i > 0; i--) {
143
+ if (limits[i - 1].maxIn > limits[i].maxIn) {
144
+ limits[i - 1] = { ...limits[i - 1], maxIn: limits[i].maxIn };
145
+ }
146
+ if (limits[i - 1].maxOut > limits[i].maxOut) {
147
+ limits[i - 1] = { ...limits[i - 1], maxOut: limits[i].maxOut };
148
+ }
149
+ }
150
+ return limits;
151
+ }
152
+ /**
153
+ * Check if any trading limits are configured (flags > 0 for V1, or limit0/limit1 > 0 for V2)
154
+ */
155
+ export function hasConfiguredLimitsV1(config) {
156
+ return config.flags > 0;
157
+ }
158
+ /**
159
+ * Check if any trading limits are configured for V2
160
+ */
161
+ export function hasConfiguredLimitsV2(config) {
162
+ return config.limit0 > 0n || config.limit1 > 0n;
163
+ }
@@ -0,0 +1,30 @@
1
+ import { isAddress } from 'viem';
2
+ /**
3
+ * Validates an Ethereum address format using viem's isAddress utility
4
+ *
5
+ * @param address - The address string to validate
6
+ * @param paramName - Optional parameter name for better error messages
7
+ * @returns True if valid (type guard for `0x${string}`)
8
+ * @throws Error if address is invalid
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * validateAddress('0x765DE816845861e75A25fCA122bb6898B8B1282a')
13
+ * // Returns true
14
+ *
15
+ * validateAddress('invalid', 'tokenIn')
16
+ * // Throws: "Invalid address for tokenIn: invalid"
17
+ * ```
18
+ */
19
+ export function validateAddress(address, paramName) {
20
+ if (typeof address !== 'string') {
21
+ const param = paramName ? ` for ${paramName}` : '';
22
+ throw new Error(`Address${param} must be a string, got ${typeof address}`);
23
+ }
24
+ // Use viem's isAddress with strict: false to allow both checksummed and non-checksummed addresses
25
+ if (!isAddress(address, { strict: false })) {
26
+ const param = paramName ? ` for ${paramName}` : '';
27
+ throw new Error(`Invalid address${param}: ${address}. Expected 0x followed by 40 hex characters`);
28
+ }
29
+ return true;
30
+ }