tapimo 0.4.3 → 0.5.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 (192) hide show
  1. package/dist/App.d.ts +7222 -6316
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +4 -0
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +18 -1
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +24 -3
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/admin/apps/verified-tokens.d.ts +116 -116
  10. package/dist/apps/data/App.d.ts +2541 -2607
  11. package/dist/apps/data/App.d.ts.map +1 -1
  12. package/dist/apps/data/App.js +135 -44
  13. package/dist/apps/data/App.js.map +1 -1
  14. package/dist/apps/data/FundingQuoteProviders.d.ts +36 -0
  15. package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -0
  16. package/dist/apps/data/FundingQuoteProviders.js +190 -0
  17. package/dist/apps/data/FundingQuoteProviders.js.map +1 -0
  18. package/dist/apps/data/index.d.ts +1 -1
  19. package/dist/apps/data/index.js +1 -1
  20. package/dist/apps/data/routes/activities.d.ts +242 -242
  21. package/dist/apps/data/routes/addresses.d.ts +227 -227
  22. package/dist/apps/data/routes/balances.d.ts +12 -12
  23. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  24. package/dist/apps/data/routes/balances.js +67 -149
  25. package/dist/apps/data/routes/balances.js.map +1 -1
  26. package/dist/apps/data/routes/blocks.d.ts +125 -126
  27. package/dist/apps/data/routes/blocks.d.ts.map +1 -1
  28. package/dist/apps/data/routes/blocks.js +3 -5
  29. package/dist/apps/data/routes/blocks.js.map +1 -1
  30. package/dist/apps/data/routes/coingecko.d.ts +55 -54
  31. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  32. package/dist/apps/data/routes/coingecko.js +3 -8
  33. package/dist/apps/data/routes/coingecko.js.map +1 -1
  34. package/dist/apps/data/routes/exchanges.d.ts +455 -455
  35. package/dist/apps/data/routes/fee-amm.d.ts +92 -92
  36. package/dist/apps/data/routes/funding.d.ts +91 -864
  37. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  38. package/dist/apps/data/routes/funding.js +138 -450
  39. package/dist/apps/data/routes/funding.js.map +1 -1
  40. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  41. package/dist/apps/data/routes/rpc.js +86 -16
  42. package/dist/apps/data/routes/rpc.js.map +1 -1
  43. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  44. package/dist/apps/data/routes/webhooks.d.ts +340 -0
  45. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  46. package/dist/apps/data/routes/webhooks.js +13 -1
  47. package/dist/apps/data/routes/webhooks.js.map +1 -1
  48. package/dist/apps/index.d.ts +1 -1
  49. package/dist/apps/index.js +1 -1
  50. package/dist/apps/management/App.d.ts +1843 -861
  51. package/dist/apps/management/App.d.ts.map +1 -1
  52. package/dist/apps/management/App.js +4 -0
  53. package/dist/apps/management/App.js.map +1 -1
  54. package/dist/apps/management/routes/api-keys.d.ts +121 -121
  55. package/dist/apps/management/routes/api-keys.js +2 -2
  56. package/dist/apps/management/routes/api-keys.js.map +1 -1
  57. package/dist/apps/management/routes/billing.d.ts +2 -2
  58. package/dist/apps/management/routes/invite-links.d.ts +1100 -0
  59. package/dist/apps/management/routes/invite-links.d.ts.map +1 -0
  60. package/dist/apps/management/routes/invite-links.js +411 -0
  61. package/dist/apps/management/routes/invite-links.js.map +1 -0
  62. package/dist/apps/management/routes/me.d.ts +13 -13
  63. package/dist/apps/management/routes/members.d.ts +77 -77
  64. package/dist/apps/management/routes/orgs.d.ts +96 -96
  65. package/dist/apps/management/routes/projects.d.ts +62 -62
  66. package/dist/apps/management/routes/scopes.d.ts +11 -11
  67. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  68. package/dist/apps/management/routes/scopes.js +2 -2
  69. package/dist/apps/management/routes/scopes.js.map +1 -1
  70. package/dist/apps/management/routes/usage.d.ts +79 -79
  71. package/dist/apps/mcp.d.ts.map +1 -1
  72. package/dist/apps/mcp.js +3 -1
  73. package/dist/apps/mcp.js.map +1 -1
  74. package/dist/apps/relay/App.d.ts.map +1 -1
  75. package/dist/apps/relay/App.js +42 -4
  76. package/dist/apps/relay/App.js.map +1 -1
  77. package/dist/cloudflare.d.ts +2 -0
  78. package/dist/cloudflare.d.ts.map +1 -1
  79. package/dist/cloudflare.js +4 -0
  80. package/dist/cloudflare.js.map +1 -1
  81. package/dist/db/Db.d.ts +3 -0
  82. package/dist/db/Db.d.ts.map +1 -1
  83. package/dist/db/Db.js +57 -0
  84. package/dist/db/Db.js.map +1 -1
  85. package/dist/db/Schema.d.ts +31 -0
  86. package/dist/db/Schema.d.ts.map +1 -1
  87. package/dist/db/Schema.js +31 -0
  88. package/dist/db/Schema.js.map +1 -1
  89. package/dist/db/tables/inviteLinks.d.ts +42 -0
  90. package/dist/db/tables/inviteLinks.d.ts.map +1 -0
  91. package/dist/db/tables/inviteLinks.js +148 -0
  92. package/dist/db/tables/inviteLinks.js.map +1 -0
  93. package/dist/db/tables/organizations.d.ts.map +1 -1
  94. package/dist/db/tables/organizations.js +2 -0
  95. package/dist/db/tables/organizations.js.map +1 -1
  96. package/dist/handlers/relay.d.ts.map +1 -1
  97. package/dist/handlers/relay.js +13 -3
  98. package/dist/handlers/relay.js.map +1 -1
  99. package/dist/internal/Auth.d.ts +42 -23
  100. package/dist/internal/Auth.d.ts.map +1 -1
  101. package/dist/internal/Auth.js +169 -20
  102. package/dist/internal/Auth.js.map +1 -1
  103. package/dist/internal/EdgeCache.d.ts +3 -1
  104. package/dist/internal/EdgeCache.d.ts.map +1 -1
  105. package/dist/internal/EdgeCache.js +17 -2
  106. package/dist/internal/EdgeCache.js.map +1 -1
  107. package/dist/internal/Errors.d.ts +6 -0
  108. package/dist/internal/Errors.d.ts.map +1 -0
  109. package/dist/internal/Errors.js +16 -0
  110. package/dist/internal/Errors.js.map +1 -0
  111. package/dist/internal/FundingQuotes.d.ts +644 -0
  112. package/dist/internal/FundingQuotes.d.ts.map +1 -0
  113. package/dist/internal/{FundingRoutes.js → FundingQuotes.js} +93 -289
  114. package/dist/internal/FundingQuotes.js.map +1 -0
  115. package/dist/internal/Store.d.ts +61 -16
  116. package/dist/internal/Store.d.ts.map +1 -1
  117. package/dist/internal/Store.js +72 -17
  118. package/dist/internal/Store.js.map +1 -1
  119. package/dist/internal/Viem.d.ts +2 -0
  120. package/dist/internal/Viem.d.ts.map +1 -1
  121. package/dist/internal/Viem.js +11 -2
  122. package/dist/internal/Viem.js.map +1 -1
  123. package/dist/internal/index.d.ts +27 -0
  124. package/dist/internal/index.d.ts.map +1 -0
  125. package/dist/internal/index.js +27 -0
  126. package/dist/internal/index.js.map +1 -0
  127. package/package.json +8 -3
  128. package/src/ApiKeys.test.ts +23 -0
  129. package/src/App.test-d.ts +2 -0
  130. package/src/App.test.ts +60 -4
  131. package/src/App.ts +6 -0
  132. package/src/Client.test-d.ts +16 -54
  133. package/src/Scope.test.ts +1 -0
  134. package/src/Scope.ts +30 -4
  135. package/src/apps/data/App.test.ts +354 -0
  136. package/src/apps/data/App.ts +159 -52
  137. package/src/apps/data/FundingQuoteProviders.test.ts +151 -0
  138. package/src/apps/data/FundingQuoteProviders.ts +251 -0
  139. package/src/apps/data/index.ts +1 -1
  140. package/src/apps/data/routes/balances.test.ts +64 -18
  141. package/src/apps/data/routes/balances.ts +76 -177
  142. package/src/apps/data/routes/blocks.ts +3 -5
  143. package/src/apps/data/routes/coingecko.ts +3 -8
  144. package/src/apps/data/routes/funding.test.ts +149 -306
  145. package/src/apps/data/routes/funding.ts +154 -604
  146. package/src/apps/data/routes/indexer.test.ts +99 -3
  147. package/src/apps/data/routes/rpc.test.ts +219 -0
  148. package/src/apps/data/routes/rpc.ts +92 -17
  149. package/src/apps/data/routes/webhooks.test.ts +23 -2
  150. package/src/apps/data/routes/webhooks.ts +13 -1
  151. package/src/apps/index.ts +1 -1
  152. package/src/apps/management/App.ts +4 -0
  153. package/src/apps/management/routes/api-keys.test.ts +47 -3
  154. package/src/apps/management/routes/api-keys.ts +2 -2
  155. package/src/apps/management/routes/invite-links.test.ts +385 -0
  156. package/src/apps/management/routes/invite-links.ts +570 -0
  157. package/src/apps/management/routes/scopes.test.ts +3 -2
  158. package/src/apps/management/routes/scopes.ts +4 -2
  159. package/src/apps/mcp.test.ts +55 -14
  160. package/src/apps/mcp.ts +3 -1
  161. package/src/apps/relay/App.test.ts +324 -4
  162. package/src/apps/relay/App.ts +40 -4
  163. package/src/cloudflare.ts +5 -0
  164. package/src/db/Db.ts +65 -0
  165. package/src/db/Schema.ts +39 -0
  166. package/src/db/tables/inviteLinks.ts +187 -0
  167. package/src/db/tables/organizations.ts +2 -0
  168. package/src/handlers/relay.ts +16 -4
  169. package/src/internal/Auth.test.ts +528 -37
  170. package/src/internal/Auth.ts +246 -45
  171. package/src/internal/EdgeCache.test.ts +24 -3
  172. package/src/internal/EdgeCache.ts +18 -2
  173. package/src/internal/FundingQuotes.test.ts +174 -0
  174. package/src/internal/FundingQuotes.ts +735 -0
  175. package/src/internal/Log.test.ts +4 -4
  176. package/src/internal/Store.test-d.ts +15 -0
  177. package/src/internal/Store.test.ts +153 -2
  178. package/src/internal/Store.ts +157 -41
  179. package/src/internal/Viem.ts +14 -5
  180. package/src/internal/index.test.ts +34 -0
  181. package/src/internal/index.ts +26 -0
  182. package/dist/apps/data/FundingRouteProviders.d.ts +0 -143
  183. package/dist/apps/data/FundingRouteProviders.d.ts.map +0 -1
  184. package/dist/apps/data/FundingRouteProviders.js +0 -546
  185. package/dist/apps/data/FundingRouteProviders.js.map +0 -1
  186. package/dist/internal/FundingRoutes.d.ts +0 -1085
  187. package/dist/internal/FundingRoutes.d.ts.map +0 -1
  188. package/dist/internal/FundingRoutes.js.map +0 -1
  189. package/src/apps/data/FundingRouteProviders.test.ts +0 -485
  190. package/src/apps/data/FundingRouteProviders.ts +0 -744
  191. package/src/internal/FundingRoutes.test.ts +0 -239
  192. package/src/internal/FundingRoutes.ts +0 -1067
@@ -0,0 +1,735 @@
1
+ /** Tempo mainnet as a CAIP-2 chain reference. */
2
+ const destinationChain = {
3
+ addressFormat: 'evm',
4
+ id: 'eip155:4217',
5
+ kind: 'evm',
6
+ name: 'Tempo',
7
+ } as const
8
+
9
+ /** Tempo token used when the caller omits `destinationToken`. */
10
+ const defaultDestinationToken = 'usdce'
11
+
12
+ /** Source chains considered by funding quote providers. */
13
+ const sourceChains = [
14
+ {
15
+ addressFormat: 'evm',
16
+ aliases: ['ethereum', 'eth', 'eip155:1'],
17
+ id: 'eip155:1',
18
+ kind: 'evm',
19
+ name: 'Ethereum',
20
+ providerIds: {
21
+ relay: '1',
22
+ },
23
+ slug: 'ethereum',
24
+ tokens: [
25
+ {
26
+ address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
27
+ coingeckoId: 'usd-coin',
28
+ decimals: 6,
29
+ name: 'USD Coin',
30
+ standard: 'ERC-20',
31
+ symbol: 'USDC',
32
+ },
33
+ {
34
+ address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
35
+ coingeckoId: 'tether',
36
+ decimals: 6,
37
+ name: 'Tether USD',
38
+ standard: 'ERC-20',
39
+ symbol: 'USDT',
40
+ },
41
+ {
42
+ address: '0x6c3ea9036406852006290770bedfcaba0e23a0e8',
43
+ coingeckoId: 'paypal-usd',
44
+ decimals: 6,
45
+ name: 'PayPal USD',
46
+ standard: 'ERC-20',
47
+ symbol: 'PYUSD',
48
+ },
49
+ {
50
+ address: '0x1abaea1f7c830bd89acc67ec4af516284b1bc33c',
51
+ coingeckoId: 'euro-coin',
52
+ decimals: 6,
53
+ name: 'Euro Coin',
54
+ standard: 'ERC-20',
55
+ symbol: 'EURC',
56
+ },
57
+ {
58
+ address: '0x6b175474e89094c44da98b954eedeac495271d0f',
59
+ coingeckoId: 'dai',
60
+ decimals: 18,
61
+ name: 'Dai Stablecoin',
62
+ standard: 'ERC-20',
63
+ symbol: 'DAI',
64
+ },
65
+ ],
66
+ },
67
+ {
68
+ addressFormat: 'evm',
69
+ aliases: ['base', 'eip155:8453'],
70
+ id: 'eip155:8453',
71
+ kind: 'evm',
72
+ name: 'Base',
73
+ providerIds: {
74
+ relay: '8453',
75
+ },
76
+ slug: 'base',
77
+ tokens: [
78
+ {
79
+ address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
80
+ coingeckoId: 'usd-coin',
81
+ decimals: 6,
82
+ name: 'USD Coin',
83
+ standard: 'ERC-20',
84
+ symbol: 'USDC',
85
+ },
86
+ {
87
+ address: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
88
+ coingeckoId: 'tether',
89
+ decimals: 6,
90
+ name: 'Tether USD',
91
+ standard: 'ERC-20',
92
+ symbol: 'USDT',
93
+ },
94
+ {
95
+ address: '0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42',
96
+ coingeckoId: 'euro-coin',
97
+ decimals: 6,
98
+ name: 'Euro Coin',
99
+ standard: 'ERC-20',
100
+ symbol: 'EURC',
101
+ },
102
+ ],
103
+ },
104
+ {
105
+ addressFormat: 'evm',
106
+ aliases: ['bnb', 'bsc', 'eip155:56'],
107
+ id: 'eip155:56',
108
+ kind: 'evm',
109
+ name: 'BNB Smart Chain',
110
+ providerIds: {
111
+ relay: '56',
112
+ },
113
+ slug: 'bnb',
114
+ tokens: [
115
+ {
116
+ address: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
117
+ coingeckoId: 'usd-coin',
118
+ decimals: 18,
119
+ name: 'Binance-Peg USD Coin',
120
+ standard: 'BEP-20',
121
+ symbol: 'USDC',
122
+ },
123
+ {
124
+ address: '0x55d398326f99059ff775485246999027b3197955',
125
+ coingeckoId: 'tether',
126
+ decimals: 18,
127
+ name: 'Tether USD',
128
+ standard: 'BEP-20',
129
+ symbol: 'USDT',
130
+ },
131
+ ],
132
+ },
133
+ ] as const
134
+
135
+ /** Tempo destination tokens supported by funding quote providers. */
136
+ const destinationTokens = [
137
+ {
138
+ address: '0x20c0000000000000000000000000000000000000',
139
+ decimals: 6,
140
+ name: 'pathUSD',
141
+ slug: 'pathusd',
142
+ standard: 'TIP-20',
143
+ symbol: 'pathUSD',
144
+ },
145
+ {
146
+ address: '0x20c000000000000000000000b9537d11c60e8b50',
147
+ decimals: 6,
148
+ name: 'Bridged USDC (Stargate)',
149
+ slug: 'usdce',
150
+ standard: 'TIP-20',
151
+ symbol: 'USDC.e',
152
+ },
153
+ {
154
+ address: '0x20c0000000000000000000001621e21f71cf12fb',
155
+ decimals: 6,
156
+ name: 'Bridged EURC',
157
+ slug: 'eurce',
158
+ standard: 'TIP-20',
159
+ symbol: 'EURC.e',
160
+ },
161
+ {
162
+ address: '0x20c00000000000000000000014f22ca97301eb73',
163
+ decimals: 6,
164
+ name: 'USDT0',
165
+ slug: 'usdt0',
166
+ standard: 'TIP-20',
167
+ symbol: 'USDT0',
168
+ },
169
+ {
170
+ address: '0x20c0000000000000000000003554d28269e0f3c2',
171
+ decimals: 6,
172
+ name: 'frxUSD',
173
+ slug: 'frxusd',
174
+ standard: 'TIP-20',
175
+ symbol: 'frxUSD',
176
+ },
177
+ ] as const
178
+
179
+ /** Providers supported by live funding quotes. */
180
+ const providers = [
181
+ {
182
+ id: 'relay',
183
+ kind: 'solverNetwork',
184
+ name: 'Relay',
185
+ requiresApiKey: false,
186
+ status: 'unconfigured',
187
+ },
188
+ ] as const
189
+
190
+ type TokenInput = (typeof sourceChains)[number]['tokens'][number]
191
+ type DestinationTokenInput = (typeof destinationTokens)[number]
192
+ type ProviderInput = (typeof providers)[number]
193
+ type SourceChainInput = (typeof sourceChains)[number]
194
+
195
+ /** Provider ids accepted by funding quote requests. */
196
+ export type ProviderId = ProviderInput['id']
197
+
198
+ /** Provider-specific quote status for the requested amount. */
199
+ export type QuoteStatus = 'available' | 'thin' | 'unknown' | 'unavailable'
200
+
201
+ /** Machine-readable quote quality tier. */
202
+ export type QualityTier = 'liquid' | 'thin' | 'unknown' | 'unavailable'
203
+
204
+ /** Normalized chain reference passed to provider adapters. */
205
+ export type ChainRef = ReturnType<typeof formatChain> | typeof destinationChain
206
+
207
+ /** Normalized token amount passed to provider adapters. */
208
+ export type TokenAmount = ReturnType<typeof resolveSourceAmount>
209
+
210
+ /** Normalized token reference passed to provider adapters. */
211
+ export type TokenRef =
212
+ | ReturnType<typeof formatDestinationToken>
213
+ | ReturnType<typeof formatSourceToken>
214
+
215
+ /** A normalized internal candidate passed to funding quote providers. */
216
+ export type QuoteCandidate = ReturnType<typeof formatQuoteCandidate>
217
+
218
+ /** A successful normalized funding quote returned to callers. */
219
+ export type FundingQuote = ReturnType<typeof formatFundingQuote>
220
+
221
+ /** Cache policy for a single funding quote request. */
222
+ export type CachePolicy = {
223
+ /** Whether provider adapters should bypass their own quote or inventory caches. */
224
+ bypass: boolean
225
+ }
226
+
227
+ /** Normalized caller request passed to provider adapters. */
228
+ export type QuoteRequest = {
229
+ /** Optional Tempo destination token symbol, address, or token key. */
230
+ destinationToken?: string | undefined
231
+ /** Optional provider id filter. */
232
+ provider?: string | undefined
233
+ /** Source amount as supplied by the caller. */
234
+ sourceAmount: string
235
+ /** Unit used by {@link sourceAmount}. */
236
+ sourceAmountUnits: SourceAmountUnits
237
+ /** Chain alias or CAIP-2 source chain id. */
238
+ sourceChain: string
239
+ /** Source token symbol, contract address, mint, or Tempo token key. */
240
+ sourceToken: string
241
+ }
242
+
243
+ /** Input passed to a provider adapter for one curated quote candidate. */
244
+ export type QuoteProbeInput = {
245
+ /** Cache policy for provider-side discovery and quote lookups. */
246
+ cache: CachePolicy
247
+ /** Destination chain, always Tempo mainnet in V1. */
248
+ destinationChain: ChainRef
249
+ /** Tempo token expected from this quote. */
250
+ destinationToken: TokenRef
251
+ /** Normalized caller request that produced this quote candidate. */
252
+ request: QuoteRequest
253
+ /** Curated candidate selected for live provider quoting. */
254
+ candidate: QuoteCandidate
255
+ /** Source amount in base units and display form. */
256
+ sourceAmount: TokenAmount
257
+ /** Chain where the caller currently holds funds. */
258
+ sourceChain: ChainRef
259
+ /** Token the caller currently holds. */
260
+ sourceToken: TokenRef
261
+ }
262
+
263
+ /** Quote probe data returned by a provider adapter. */
264
+ export type QuoteProbeResult = {
265
+ /** Slippage-adjusted minimum destination amount in base units. */
266
+ minimumOutputAmount?: string | undefined
267
+ /** Expected destination amount in base units. */
268
+ outputAmount?: string | undefined
269
+ /** Expected destination amount rendered with destination token decimals. */
270
+ outputAmountFormatted?: string | undefined
271
+ /** Timestamp for the live quote probe. */
272
+ sampledAt: string
273
+ /** Provider-specific quote status for the requested amount. */
274
+ status: QuoteStatus
275
+ /** Provider-backed quote quality metadata. */
276
+ quality?: {
277
+ /** Estimated settlement time in seconds, when known. */
278
+ estimatedSeconds?: number | undefined
279
+ /** Where the live liquidity or support signal came from. */
280
+ liquiditySource: string
281
+ /** Provider-specific quote source or endpoint detail. */
282
+ sourceDetail?: string | undefined
283
+ /** Machine-readable quote quality tier. */
284
+ tier?: QualityTier | undefined
285
+ }
286
+ }
287
+
288
+ /** Adapter used to request a live provider quote. */
289
+ export type ProviderAdapter = {
290
+ /** Probes provider-backed quote information for one static candidate. */
291
+ probeQuote(input: QuoteProbeInput, signal: AbortSignal): Promise<QuoteProbeResult>
292
+ }
293
+
294
+ /** Provider adapter registry, keyed by provider id. */
295
+ export type ProviderAdapters = Partial<Record<ProviderId, ProviderAdapter>>
296
+
297
+ /** Curated provider support rows used to select live quote probes. */
298
+ const inventory: readonly InventoryEntry[] = [
299
+ { destinationToken: 'usdce', provider: 'relay', sourceChains: ['ethereum', 'base', 'bnb'], sourceTokens: ['USDC', 'USDT'] }, // prettier-ignore
300
+ ]
301
+
302
+ type InventoryEntry = {
303
+ destinationToken: string
304
+ provider: ProviderInput['id']
305
+ sourceChains: readonly string[]
306
+ sourceTokens: readonly string[]
307
+ }
308
+
309
+ const defaultAdapterTimeoutMs = 2_000
310
+
311
+ /** Lists normalized chains and source tokens supported by quote providers. */
312
+ export function listChains() {
313
+ return sourceChains.flatMap((chain) => {
314
+ const symbols = new Set(
315
+ inventory
316
+ .filter((entry) => entry.sourceChains.includes(chain.slug))
317
+ .flatMap((entry) => entry.sourceTokens),
318
+ )
319
+ if (symbols.size === 0) return []
320
+ return [
321
+ {
322
+ ...formatChain(chain),
323
+ tokens: chain.tokens
324
+ .filter((token) => symbols.has(token.symbol))
325
+ .map((token) => formatSourceToken(chain, token)),
326
+ },
327
+ ]
328
+ })
329
+ }
330
+
331
+ /** Lists funding quote providers and their deployment status. */
332
+ export function listProviders(options: listProviders.Options = {}) {
333
+ return providers.map((provider) => ({
334
+ ...formatProvider(provider),
335
+ status: options.adapters?.[provider.id] ? ('configured' as const) : provider.status,
336
+ }))
337
+ }
338
+
339
+ export declare namespace listProviders {
340
+ /** Options for listing providers. */
341
+ type Options = {
342
+ /** Provider adapters configured for this deployment. */
343
+ adapters?: ProviderAdapters | undefined
344
+ }
345
+ }
346
+
347
+ /** Returns successful live funding quotes from configured providers. */
348
+ export async function listQuotes(options: listQuotes.Options) {
349
+ const candidates = listQuoteCandidates(options)
350
+ const results = await Promise.allSettled(
351
+ candidates.map((candidate) =>
352
+ quoteCandidate(candidate, {
353
+ adapters: options.adapters ?? {},
354
+ cache: options.cache ?? { bypass: false },
355
+ request: {
356
+ destinationToken: options.destinationToken,
357
+ provider: options.provider,
358
+ sourceAmount: options.sourceAmount,
359
+ sourceAmountUnits: options.sourceAmountUnits,
360
+ sourceChain: options.sourceChain,
361
+ sourceToken: options.sourceToken,
362
+ },
363
+ timeoutMs: options.adapterTimeoutMs ?? defaultAdapterTimeoutMs,
364
+ }),
365
+ ),
366
+ )
367
+ const quotes = results.flatMap((result) =>
368
+ result.status === 'fulfilled' && result.value ? [result.value] : [],
369
+ )
370
+ if (quotes.length === 0 && results.some((result) => result.status === 'rejected'))
371
+ throw new ProviderQuoteError()
372
+ return quotes
373
+ }
374
+
375
+ /** Lists internal quote candidates for a normalized caller request. */
376
+ export function listQuoteCandidates(options: listQuoteCandidates.Options) {
377
+ const sourceChain = resolveSourceChain(options.sourceChain)
378
+ const sourceToken = resolveSourceToken(sourceChain, options.sourceToken)
379
+ const destinationToken = resolveDestinationToken(options.destinationToken)
380
+ const provider = options.provider ? resolveProvider(options.provider) : undefined
381
+
382
+ if (!sourceToken) {
383
+ validateSourceAmountSyntax(options.sourceAmount, options.sourceAmountUnits)
384
+ return []
385
+ }
386
+
387
+ const sourceAmount = resolveSourceAmount({
388
+ amount: options.sourceAmount,
389
+ token: sourceToken,
390
+ units: options.sourceAmountUnits,
391
+ })
392
+
393
+ return inventory
394
+ .filter((entry) => entry.sourceChains.includes(sourceChain.slug))
395
+ .filter((entry) => entry.sourceTokens.includes(sourceToken.symbol))
396
+ .filter((entry) => entry.destinationToken === destinationToken.slug)
397
+ .filter((entry) => provider === undefined || entry.provider === provider.id)
398
+ .map((entry) =>
399
+ formatQuoteCandidate({
400
+ destinationToken,
401
+ provider: providers.find((candidate) => candidate.id === entry.provider)!,
402
+ sourceAmount,
403
+ sourceChain,
404
+ sourceToken,
405
+ }),
406
+ )
407
+ }
408
+
409
+ export declare namespace listQuoteCandidates {
410
+ /** Options for selecting funding quote candidates. */
411
+ type Options = {
412
+ /** Optional Tempo destination token symbol, address, or token key. */
413
+ destinationToken?: string | undefined
414
+ /** Optional provider id filter. */
415
+ provider?: string | undefined
416
+ /** Source amount as supplied by the caller. */
417
+ sourceAmount: string
418
+ /** Unit used by {@link sourceAmount}. */
419
+ sourceAmountUnits: SourceAmountUnits
420
+ /** Chain alias or CAIP-2 source chain id. */
421
+ sourceChain: string
422
+ /** Source token symbol, contract address, mint, or Tempo token key. */
423
+ sourceToken: string
424
+ }
425
+ }
426
+
427
+ export declare namespace listQuotes {
428
+ /** Options for requesting live funding quotes. */
429
+ type Options = listQuoteCandidates.Options & {
430
+ /** Provider adapters used to request live quotes. */
431
+ adapters?: ProviderAdapters | undefined
432
+ /** Per-provider quote timeout in milliseconds. */
433
+ adapterTimeoutMs?: number | undefined
434
+ /** Cache policy for provider-side discovery and quote lookups. */
435
+ cache?: CachePolicy | undefined
436
+ }
437
+ }
438
+
439
+ /** Source amount unit accepted by the funding quote request. */
440
+ export type SourceAmountUnits = 'baseUnits' | 'decimal'
441
+
442
+ function tokenKey(chain: { id: string }, token: TokenInput | DestinationTokenInput) {
443
+ return `${chain.id}/erc20:${token.address}`
444
+ }
445
+
446
+ function decimalFromBaseUnits(baseUnits: string, decimals: number) {
447
+ const padded = baseUnits.padStart(decimals + 1, '0')
448
+ const whole = decimals === 0 ? padded : padded.slice(0, -decimals)
449
+ const fraction = decimals === 0 ? '' : padded.slice(-decimals).replace(/0+$/, '')
450
+ return fraction ? `${whole}.${fraction}` : whole
451
+ }
452
+
453
+ async function quoteCandidate(
454
+ candidate: QuoteCandidate,
455
+ options: {
456
+ adapters: ProviderAdapters
457
+ cache: CachePolicy
458
+ request: QuoteRequest
459
+ timeoutMs: number
460
+ },
461
+ ) {
462
+ const adapter = options.adapters[candidate.provider.id as ProviderId]
463
+ if (!adapter) return undefined
464
+
465
+ const quote = await runWithTimeout(
466
+ (signal) =>
467
+ adapter.probeQuote(
468
+ {
469
+ cache: options.cache,
470
+ destinationChain: candidate.destinationChain,
471
+ destinationToken: candidate.destinationToken,
472
+ request: options.request,
473
+ candidate,
474
+ sourceAmount: candidate.sourceAmount,
475
+ sourceChain: candidate.sourceChain,
476
+ sourceToken: candidate.sourceToken,
477
+ },
478
+ signal,
479
+ ),
480
+ options.timeoutMs,
481
+ )
482
+ if (quote.status === 'unavailable') return undefined
483
+ if (
484
+ (quote.status !== 'available' && quote.status !== 'thin') ||
485
+ quote.outputAmount === undefined ||
486
+ quote.outputAmount === null
487
+ )
488
+ throw new ProviderQuoteError()
489
+ return formatFundingQuote(candidate, {
490
+ ...quote,
491
+ outputAmount: quote.outputAmount,
492
+ status: quote.status,
493
+ })
494
+ }
495
+
496
+ function formatChain(chain: SourceChainInput) {
497
+ const { addressFormat, id, kind, name, providerIds } = chain
498
+ return { addressFormat, id, kind, name, providerIds }
499
+ }
500
+
501
+ function formatDestinationToken(token: DestinationTokenInput) {
502
+ return {
503
+ address: token.address,
504
+ decimals: token.decimals,
505
+ metadata: {},
506
+ name: token.name,
507
+ standard: token.standard,
508
+ symbol: token.symbol,
509
+ tokenKey: tokenKey(destinationChain, token),
510
+ verified: true,
511
+ }
512
+ }
513
+
514
+ function formatProvider(provider: ProviderInput) {
515
+ const { id, kind, name, requiresApiKey, status } = provider
516
+ return { id, kind, name, requiresApiKey, status }
517
+ }
518
+
519
+ function formatProviderRef(provider: ProviderInput) {
520
+ const { id, kind, name, requiresApiKey } = provider
521
+ return { id, kind, name, requiresApiKey }
522
+ }
523
+
524
+ function formatQuoteCandidate(options: {
525
+ destinationToken: DestinationTokenInput
526
+ provider: ProviderInput
527
+ sourceAmount: ReturnType<typeof resolveSourceAmount>
528
+ sourceChain: SourceChainInput
529
+ sourceToken: TokenInput
530
+ }) {
531
+ return {
532
+ destinationChain,
533
+ destinationToken: formatDestinationToken(options.destinationToken),
534
+ id: [
535
+ options.sourceChain.slug,
536
+ options.sourceToken.symbol.toLowerCase(),
537
+ 'tempo',
538
+ options.destinationToken.slug,
539
+ options.provider.id,
540
+ ].join('-'),
541
+ provider: formatProviderRef(options.provider),
542
+ sourceAmount: options.sourceAmount,
543
+ sourceChain: formatChain(options.sourceChain),
544
+ sourceToken: formatSourceToken(options.sourceChain, options.sourceToken),
545
+ } as const
546
+ }
547
+
548
+ function formatSourceToken(chain: SourceChainInput, token: TokenInput) {
549
+ return {
550
+ address: token.address,
551
+ decimals: token.decimals,
552
+ metadata: { coingeckoId: token.coingeckoId },
553
+ name: token.name,
554
+ standard: token.standard,
555
+ symbol: token.symbol,
556
+ tokenKey: tokenKey(chain, token),
557
+ verified: true,
558
+ }
559
+ }
560
+
561
+ function normalizeInput(value: string) {
562
+ return value.trim().toLowerCase()
563
+ }
564
+
565
+ function parseBaseUnits(value: string) {
566
+ if (!/^\d+$/.test(value) || BigInt(value) <= 0n) throw new InvalidAmountError()
567
+ return BigInt(value).toString()
568
+ }
569
+
570
+ function parseDecimalUnits(value: string, decimals: number) {
571
+ if (!/^\d+(\.\d+)?$/.test(value)) throw new InvalidAmountError()
572
+ const [whole, fraction = ''] = value.split('.')
573
+ if (fraction.length > decimals) throw new InvalidAmountError()
574
+ const baseUnits = BigInt(`${whole}${fraction.padEnd(decimals, '0')}`)
575
+ if (baseUnits <= 0n) throw new InvalidAmountError()
576
+ return baseUnits.toString()
577
+ }
578
+
579
+ type SuccessfulQuoteProbe = QuoteProbeResult & {
580
+ outputAmount: string
581
+ status: 'available' | 'thin'
582
+ }
583
+
584
+ function formatFundingQuote(candidate: QuoteCandidate, quote: SuccessfulQuoteProbe) {
585
+ return {
586
+ ...candidate,
587
+ quality: {
588
+ ...(quote.quality?.estimatedSeconds !== undefined
589
+ ? { estimatedSeconds: quote.quality.estimatedSeconds }
590
+ : {}),
591
+ liquiditySource: quote.quality?.liquiditySource ?? 'providerQuote',
592
+ ...(quote.quality?.sourceDetail !== undefined
593
+ ? { sourceDetail: quote.quality.sourceDetail }
594
+ : {}),
595
+ tier: quote.status === 'thin' ? ('thin' as const) : ('liquid' as const),
596
+ },
597
+ quote: {
598
+ inputAmount: candidate.sourceAmount.amount,
599
+ ...(quote.minimumOutputAmount !== undefined
600
+ ? { minimumOutputAmount: quote.minimumOutputAmount }
601
+ : {}),
602
+ outputAmount: quote.outputAmount,
603
+ outputAmountFormatted:
604
+ quote.outputAmountFormatted ??
605
+ decimalFromBaseUnits(quote.outputAmount, candidate.destinationToken.decimals),
606
+ sampledAt: quote.sampledAt,
607
+ },
608
+ } as const
609
+ }
610
+
611
+ function runWithTimeout<result>(fn: (signal: AbortSignal) => Promise<result>, timeoutMs: number) {
612
+ const controller = new AbortController()
613
+ const timeout = setTimeout(() => controller.abort(), timeoutMs)
614
+ return Promise.race([
615
+ fn(controller.signal),
616
+ new Promise<never>((_, reject) => {
617
+ if (controller.signal.aborted) {
618
+ reject(new ProviderAdapterTimeoutError())
619
+ return
620
+ }
621
+ controller.signal.addEventListener('abort', () => reject(new ProviderAdapterTimeoutError()), {
622
+ once: true,
623
+ })
624
+ }),
625
+ ]).finally(() => clearTimeout(timeout))
626
+ }
627
+
628
+ function resolveDestinationToken(input: string | undefined) {
629
+ if (input === undefined) return destinationTokens.find((token) => token.slug === defaultDestinationToken)! // prettier-ignore
630
+ const normalized = normalizeInput(input)
631
+ const token = destinationTokens.find(
632
+ (candidate) =>
633
+ candidate.slug === normalized ||
634
+ candidate.symbol.toLowerCase() === normalized ||
635
+ candidate.address === normalized ||
636
+ tokenKey(destinationChain, candidate).toLowerCase() === normalized,
637
+ )
638
+ if (!token) throw new UnsupportedDestinationTokenError()
639
+ return token
640
+ }
641
+
642
+ function resolveProvider(input: string) {
643
+ const provider = providers.find((candidate) => candidate.id === normalizeInput(input))
644
+ if (!provider) throw new UnsupportedProviderError()
645
+ return provider
646
+ }
647
+
648
+ function resolveSourceAmount(options: resolveSourceAmount.Options) {
649
+ const amount =
650
+ options.units === 'baseUnits'
651
+ ? parseBaseUnits(options.amount)
652
+ : parseDecimalUnits(options.amount, options.token.decimals)
653
+ return {
654
+ amount,
655
+ formatted: decimalFromBaseUnits(amount, options.token.decimals),
656
+ }
657
+ }
658
+
659
+ declare namespace resolveSourceAmount {
660
+ type Options = {
661
+ amount: string
662
+ token: TokenInput
663
+ units: SourceAmountUnits
664
+ }
665
+ }
666
+
667
+ function resolveSourceToken(chain: SourceChainInput, input: string) {
668
+ const trimmed = input.trim()
669
+ const normalized = trimmed.toLowerCase()
670
+ return chain.tokens.find(
671
+ (token) =>
672
+ token.symbol.toLowerCase() === normalized ||
673
+ matchesTokenIdentifier(chain, token.address, trimmed, normalized) ||
674
+ matchesTokenIdentifier(chain, tokenKey(chain, token), trimmed, normalized),
675
+ )
676
+ }
677
+
678
+ function matchesTokenIdentifier(
679
+ chain: SourceChainInput,
680
+ candidate: string,
681
+ trimmed: string,
682
+ normalized: string,
683
+ ) {
684
+ return chain.kind === 'evm' ? candidate.toLowerCase() === normalized : candidate === trimmed
685
+ }
686
+
687
+ function resolveSourceChain(input: string) {
688
+ const normalized = normalizeInput(input)
689
+ const chain = sourceChains.find(
690
+ (candidate) =>
691
+ candidate.id === normalized || (candidate.aliases as readonly string[]).includes(normalized),
692
+ )
693
+ if (!chain) throw new UnsupportedSourceChainError()
694
+ return chain
695
+ }
696
+
697
+ function validateSourceAmountSyntax(value: string, units: SourceAmountUnits) {
698
+ if (units === 'baseUnits') {
699
+ parseBaseUnits(value)
700
+ return
701
+ }
702
+ if (!/^\d+(\.\d+)?$/.test(value)) throw new InvalidAmountError()
703
+ const [whole, fraction = ''] = value.split('.')
704
+ if (BigInt(`${whole}${fraction}`) <= 0n) throw new InvalidAmountError()
705
+ }
706
+
707
+ /** Error thrown when the caller supplies an unsupported destination token. */
708
+ export class UnsupportedDestinationTokenError extends Error {
709
+ override name = 'FundingQuotes.UnsupportedDestinationTokenError'
710
+ }
711
+
712
+ /** Error thrown when the caller supplies an invalid source amount. */
713
+ export class InvalidAmountError extends Error {
714
+ override name = 'FundingQuotes.InvalidAmountError'
715
+ }
716
+
717
+ /** Error thrown internally when a provider adapter exceeds its quote timeout. */
718
+ export class ProviderAdapterTimeoutError extends Error {
719
+ override name = 'FundingQuotes.ProviderAdapterTimeoutError'
720
+ }
721
+
722
+ /** Error thrown when every matching provider fails to produce a quote. */
723
+ export class ProviderQuoteError extends Error {
724
+ override name = 'FundingQuotes.ProviderQuoteError'
725
+ }
726
+
727
+ /** Error thrown when the caller filters to an unknown provider. */
728
+ export class UnsupportedProviderError extends Error {
729
+ override name = 'FundingQuotes.UnsupportedProviderError'
730
+ }
731
+
732
+ /** Error thrown when the caller supplies an unsupported source chain. */
733
+ export class UnsupportedSourceChainError extends Error {
734
+ override name = 'FundingQuotes.UnsupportedSourceChainError'
735
+ }