tapimo 0.3.0 → 0.4.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 (153) hide show
  1. package/README.md +5 -5
  2. package/dist/App.d.ts +31 -9
  3. package/dist/App.d.ts.map +1 -1
  4. package/dist/App.js +3 -3
  5. package/dist/App.js.map +1 -1
  6. package/dist/admin/App.d.ts +116 -116
  7. package/dist/apps/data/App.d.ts +30 -5
  8. package/dist/apps/data/App.d.ts.map +1 -1
  9. package/dist/apps/data/App.js +1 -1
  10. package/dist/apps/data/App.js.map +1 -1
  11. package/dist/apps/data/FundingRouteProviders.d.ts +54 -0
  12. package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -0
  13. package/dist/apps/data/FundingRouteProviders.js +355 -0
  14. package/dist/apps/data/FundingRouteProviders.js.map +1 -0
  15. package/dist/apps/data/index.d.ts +1 -0
  16. package/dist/apps/data/index.d.ts.map +1 -1
  17. package/dist/apps/data/index.js +1 -0
  18. package/dist/apps/data/index.js.map +1 -1
  19. package/dist/apps/data/routes/funding.d.ts +79 -1
  20. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  21. package/dist/apps/data/routes/funding.js +68 -8
  22. package/dist/apps/data/routes/funding.js.map +1 -1
  23. package/dist/apps/data/routes/indexer.d.ts +5 -5
  24. package/dist/apps/docs/App.d.ts +1 -1
  25. package/dist/apps/docs/App.js +1 -1
  26. package/dist/apps/index.d.ts +2 -2
  27. package/dist/apps/index.d.ts.map +1 -1
  28. package/dist/apps/index.js +2 -2
  29. package/dist/apps/index.js.map +1 -1
  30. package/dist/apps/{platform → management}/App.d.ts +11 -11
  31. package/dist/apps/{platform → management}/App.d.ts.map +1 -1
  32. package/dist/apps/{platform → management}/App.js +7 -7
  33. package/dist/apps/management/App.js.map +1 -0
  34. package/dist/apps/management/Billing.d.ts.map +1 -0
  35. package/dist/apps/management/Billing.js.map +1 -0
  36. package/dist/apps/management/index.d.ts +3 -0
  37. package/dist/apps/management/index.d.ts.map +1 -0
  38. package/dist/apps/management/index.js +3 -0
  39. package/dist/apps/management/index.js.map +1 -0
  40. package/dist/apps/management/routes/api-keys.d.ts.map +1 -0
  41. package/dist/apps/{platform → management}/routes/api-keys.js +1 -1
  42. package/dist/apps/management/routes/api-keys.js.map +1 -0
  43. package/dist/apps/{platform → management}/routes/billing.d.ts.map +1 -1
  44. package/dist/apps/management/routes/billing.js.map +1 -0
  45. package/dist/apps/{platform → management}/routes/invitations.d.ts +1 -1
  46. package/dist/apps/management/routes/invitations.d.ts.map +1 -0
  47. package/dist/apps/{platform → management}/routes/invitations.js +1 -1
  48. package/dist/apps/management/routes/invitations.js.map +1 -0
  49. package/dist/apps/management/routes/me.d.ts.map +1 -0
  50. package/dist/apps/management/routes/me.js.map +1 -0
  51. package/dist/apps/management/routes/members.d.ts.map +1 -0
  52. package/dist/apps/management/routes/members.js.map +1 -0
  53. package/dist/apps/management/routes/orgs.d.ts.map +1 -0
  54. package/dist/apps/management/routes/orgs.js.map +1 -0
  55. package/dist/apps/{platform → management}/routes/projects.d.ts.map +1 -1
  56. package/dist/apps/management/routes/projects.js.map +1 -0
  57. package/dist/apps/management/routes/scopes.d.ts.map +1 -0
  58. package/dist/apps/management/routes/scopes.js.map +1 -0
  59. package/dist/apps/management/routes/usage.d.ts.map +1 -0
  60. package/dist/apps/management/routes/usage.js.map +1 -0
  61. package/dist/apps/mcp.js +1 -1
  62. package/dist/apps/relay/App.js +1 -1
  63. package/dist/apps/relay/App.js.map +1 -1
  64. package/dist/internal/Auth.js +1 -1
  65. package/dist/internal/Auth.js.map +1 -1
  66. package/dist/internal/EdgeCache.js +1 -1
  67. package/dist/internal/EdgeCache.js.map +1 -1
  68. package/dist/internal/FundingRoutes.d.ts +748 -16
  69. package/dist/internal/FundingRoutes.d.ts.map +1 -1
  70. package/dist/internal/FundingRoutes.js +166 -31
  71. package/dist/internal/FundingRoutes.js.map +1 -1
  72. package/package.json +5 -5
  73. package/src/App.ts +10 -10
  74. package/src/Client.test-d.ts +4 -4
  75. package/src/apps/composition.test.ts +26 -13
  76. package/src/apps/data/App.ts +3 -1
  77. package/src/apps/data/FundingRouteProviders.test.ts +281 -0
  78. package/src/apps/data/FundingRouteProviders.ts +459 -0
  79. package/src/apps/data/index.ts +1 -0
  80. package/src/apps/data/routes/funding.test.ts +102 -2
  81. package/src/apps/data/routes/funding.ts +122 -8
  82. package/src/apps/docs/App.ts +1 -1
  83. package/src/apps/index.ts +2 -2
  84. package/src/apps/{platform → management}/App.ts +14 -14
  85. package/src/apps/{platform → management}/index.ts +1 -1
  86. package/src/apps/{platform → management}/routes/api-keys.ts +1 -1
  87. package/src/apps/{platform → management}/routes/invitations.ts +1 -1
  88. package/src/apps/mcp.test.ts +1 -1
  89. package/src/apps/mcp.ts +1 -1
  90. package/src/apps/relay/App.test.ts +1 -1
  91. package/src/apps/relay/App.ts +1 -1
  92. package/src/internal/Auth.ts +1 -1
  93. package/src/internal/EdgeCache.ts +1 -1
  94. package/src/internal/FundingRoutes.test.ts +213 -0
  95. package/src/internal/FundingRoutes.ts +347 -32
  96. package/dist/apps/platform/App.js.map +0 -1
  97. package/dist/apps/platform/Billing.d.ts.map +0 -1
  98. package/dist/apps/platform/Billing.js.map +0 -1
  99. package/dist/apps/platform/index.d.ts +0 -3
  100. package/dist/apps/platform/index.d.ts.map +0 -1
  101. package/dist/apps/platform/index.js +0 -3
  102. package/dist/apps/platform/index.js.map +0 -1
  103. package/dist/apps/platform/routes/api-keys.d.ts.map +0 -1
  104. package/dist/apps/platform/routes/api-keys.js.map +0 -1
  105. package/dist/apps/platform/routes/billing.js.map +0 -1
  106. package/dist/apps/platform/routes/invitations.d.ts.map +0 -1
  107. package/dist/apps/platform/routes/invitations.js.map +0 -1
  108. package/dist/apps/platform/routes/me.d.ts.map +0 -1
  109. package/dist/apps/platform/routes/me.js.map +0 -1
  110. package/dist/apps/platform/routes/members.d.ts.map +0 -1
  111. package/dist/apps/platform/routes/members.js.map +0 -1
  112. package/dist/apps/platform/routes/orgs.d.ts.map +0 -1
  113. package/dist/apps/platform/routes/orgs.js.map +0 -1
  114. package/dist/apps/platform/routes/projects.js.map +0 -1
  115. package/dist/apps/platform/routes/scopes.d.ts.map +0 -1
  116. package/dist/apps/platform/routes/scopes.js.map +0 -1
  117. package/dist/apps/platform/routes/usage.d.ts.map +0 -1
  118. package/dist/apps/platform/routes/usage.js.map +0 -1
  119. /package/dist/apps/{platform → management}/Billing.d.ts +0 -0
  120. /package/dist/apps/{platform → management}/Billing.js +0 -0
  121. /package/dist/apps/{platform → management}/routes/api-keys.d.ts +0 -0
  122. /package/dist/apps/{platform → management}/routes/billing.d.ts +0 -0
  123. /package/dist/apps/{platform → management}/routes/billing.js +0 -0
  124. /package/dist/apps/{platform → management}/routes/me.d.ts +0 -0
  125. /package/dist/apps/{platform → management}/routes/me.js +0 -0
  126. /package/dist/apps/{platform → management}/routes/members.d.ts +0 -0
  127. /package/dist/apps/{platform → management}/routes/members.js +0 -0
  128. /package/dist/apps/{platform → management}/routes/orgs.d.ts +0 -0
  129. /package/dist/apps/{platform → management}/routes/orgs.js +0 -0
  130. /package/dist/apps/{platform → management}/routes/projects.d.ts +0 -0
  131. /package/dist/apps/{platform → management}/routes/projects.js +0 -0
  132. /package/dist/apps/{platform → management}/routes/scopes.d.ts +0 -0
  133. /package/dist/apps/{platform → management}/routes/scopes.js +0 -0
  134. /package/dist/apps/{platform → management}/routes/usage.d.ts +0 -0
  135. /package/dist/apps/{platform → management}/routes/usage.js +0 -0
  136. /package/src/apps/{platform → management}/Billing.test.ts +0 -0
  137. /package/src/apps/{platform → management}/Billing.ts +0 -0
  138. /package/src/apps/{platform → management}/routes/api-keys.test.ts +0 -0
  139. /package/src/apps/{platform → management}/routes/billing.test.ts +0 -0
  140. /package/src/apps/{platform → management}/routes/billing.ts +0 -0
  141. /package/src/apps/{platform → management}/routes/invitations.test.ts +0 -0
  142. /package/src/apps/{platform → management}/routes/me.test.ts +0 -0
  143. /package/src/apps/{platform → management}/routes/me.ts +0 -0
  144. /package/src/apps/{platform → management}/routes/members.test.ts +0 -0
  145. /package/src/apps/{platform → management}/routes/members.ts +0 -0
  146. /package/src/apps/{platform → management}/routes/orgs.test.ts +0 -0
  147. /package/src/apps/{platform → management}/routes/orgs.ts +0 -0
  148. /package/src/apps/{platform → management}/routes/projects.test.ts +0 -0
  149. /package/src/apps/{platform → management}/routes/projects.ts +0 -0
  150. /package/src/apps/{platform → management}/routes/scopes.test.ts +0 -0
  151. /package/src/apps/{platform → management}/routes/scopes.ts +0 -0
  152. /package/src/apps/{platform → management}/routes/usage.test.ts +0 -0
  153. /package/src/apps/{platform → management}/routes/usage.ts +0 -0
@@ -0,0 +1,281 @@
1
+ import * as FundingRoutes from '../../internal/FundingRoutes.js'
2
+ import * as FundingRouteProviders from './FundingRouteProviders.js'
3
+
4
+ const now = () => new Date('2026-01-01T00:00:00.000Z')
5
+
6
+ describe('FundingRouteProviders', () => {
7
+ test('maps Across suggested-fees responses into quote probes', async () => {
8
+ const requests: RecordedRequest[] = []
9
+ const fetch = recordingFetch(requests, async () =>
10
+ jsonResponse({
11
+ estimatedFillTimeSec: 2,
12
+ limits: {
13
+ maxDeposit: '2000000',
14
+ maxDepositInstant: '1500000',
15
+ minDeposit: '500000',
16
+ },
17
+ outputAmount: '990000',
18
+ relayFeePct: '1200000000000000',
19
+ timestamp: '1767225600',
20
+ }),
21
+ )
22
+ const adapter = FundingRouteProviders.across({
23
+ apiKey: 'across-api-key',
24
+ baseUrl: 'https://across.test/api/',
25
+ fetch,
26
+ integratorId: 'tempo',
27
+ now,
28
+ })
29
+
30
+ const result = await adapter.probeQuote(
31
+ await probeInput('across'),
32
+ new AbortController().signal,
33
+ )
34
+
35
+ expect(requests).toHaveLength(1)
36
+ expect(requests[0]?.url.pathname).toBe('/api/suggested-fees')
37
+ expect(requests[0]?.url.searchParams.get('originChainId')).toBe('8453')
38
+ expect(requests[0]?.url.searchParams.get('destinationChainId')).toBe('4217')
39
+ expect(requests[0]?.url.searchParams.get('amount')).toBe('1000000')
40
+ expect(requests[0]?.url.searchParams.get('integratorId')).toBe('tempo')
41
+ expect(requests[0]?.url.searchParams.get('allowUnmatchedDecimals')).toBe('true')
42
+ expect(new Headers(requests[0]?.init?.headers).get('authorization')).toBe(
43
+ 'Bearer across-api-key',
44
+ )
45
+ expect(result).toMatchObject({
46
+ expiresAt: '2026-01-01T00:10:00.000Z',
47
+ limits: {
48
+ instantSourceAmount: '1500000',
49
+ maxSourceAmount: '2000000',
50
+ minSourceAmount: '500000',
51
+ },
52
+ outputAmount: '990000',
53
+ outputAmountFormatted: '0.99',
54
+ quality: {
55
+ availableVolumeUsd: '2',
56
+ estimatedSeconds: 2,
57
+ feeBps: 12,
58
+ instantVolumeUsd: '1.5',
59
+ liquiditySource: 'providerLimit',
60
+ sourceDetail: 'across:suggested-fees',
61
+ tier: 'liquid',
62
+ },
63
+ sampledAt: '2026-01-01T00:00:00.000Z',
64
+ status: 'available',
65
+ })
66
+ })
67
+
68
+ test('maps recognized Across quote errors to unavailable probes', async () => {
69
+ const fetch = recordingFetch([], async () =>
70
+ jsonResponse({ code: 'AMOUNT_TOO_HIGH', message: 'Amount exceeds max deposit' }, 400),
71
+ )
72
+ const adapter = FundingRouteProviders.across({
73
+ apiKey: 'across-api-key',
74
+ fetch,
75
+ integratorId: 'tempo',
76
+ now,
77
+ })
78
+
79
+ const result = await adapter.probeQuote(
80
+ await probeInput('across'),
81
+ new AbortController().signal,
82
+ )
83
+
84
+ expect(result).toMatchObject({
85
+ quality: {
86
+ liquiditySource: 'providerLimit',
87
+ score: 0,
88
+ sourceDetail: 'across:suggested-fees:AMOUNT_TOO_HIGH',
89
+ tier: 'unavailable',
90
+ },
91
+ sampledAt: '2026-01-01T00:00:00.000Z',
92
+ status: 'unavailable',
93
+ })
94
+ })
95
+
96
+ test('maps Relay liquidity and quote responses into quote probes', async () => {
97
+ const requests: RecordedRequest[] = []
98
+ const route = await staticRoute('relay')
99
+ const fetch = recordingFetch(requests, async (request) => {
100
+ if (request.url.pathname === '/quote/v2')
101
+ return jsonResponse({
102
+ details: {
103
+ currencyOut: {
104
+ amount: '990000',
105
+ minimumAmount: '980000',
106
+ },
107
+ timeEstimate: 15,
108
+ totalImpact: { percent: '-1.20' },
109
+ },
110
+ })
111
+ return jsonResponse({
112
+ liquidity: [
113
+ {
114
+ address: route.destinationToken.address.toUpperCase(),
115
+ amountUsd: '5.00',
116
+ balance: '5000000',
117
+ chainId: 4217,
118
+ },
119
+ ],
120
+ })
121
+ })
122
+ const adapter = FundingRouteProviders.relay({
123
+ apiKey: 'relay-api-key',
124
+ fetch,
125
+ now,
126
+ userAddress: '0x1111111111111111111111111111111111111111',
127
+ })
128
+
129
+ const result = await adapter.probeQuote(
130
+ probeInputFromRoute(route),
131
+ new AbortController().signal,
132
+ )
133
+
134
+ expect(requests.map((request) => request.url.pathname)).toEqual([
135
+ '/quote/v2',
136
+ '/chains/liquidity',
137
+ ])
138
+ expect(requests[1]?.url.searchParams.get('chainId')).toBe('4217')
139
+ expect(new Headers(requests[0]?.init?.headers).get('x-api-key')).toBe('relay-api-key')
140
+ expect(jsonRequestBody(requests[0])).toMatchObject({
141
+ amount: '1000000',
142
+ destinationChainId: 4217,
143
+ destinationCurrency: route.destinationToken.address,
144
+ originChainId: 8453,
145
+ originCurrency: route.sourceToken.address,
146
+ tradeType: 'EXACT_INPUT',
147
+ user: '0x1111111111111111111111111111111111111111',
148
+ })
149
+ expect(result).toMatchObject({
150
+ limits: null,
151
+ minimumOutputAmount: '980000',
152
+ outputAmount: '990000',
153
+ outputAmountFormatted: '0.99',
154
+ quality: {
155
+ availableVolumeUsd: '5.00',
156
+ estimatedSeconds: 15,
157
+ feeBps: null,
158
+ instantVolumeUsd: '5.00',
159
+ liquiditySource: 'providerQuote',
160
+ sourceDetail: 'relay:quote-v2',
161
+ tier: 'liquid',
162
+ },
163
+ sampledAt: '2026-01-01T00:00:00.000Z',
164
+ status: 'available',
165
+ })
166
+ })
167
+
168
+ test('keeps Relay quotes when the liquidity probe fails', async () => {
169
+ const route = await staticRoute('relay')
170
+ const fetch = recordingFetch([], async (request) => {
171
+ if (request.url.pathname === '/quote/v2')
172
+ return jsonResponse({
173
+ details: {
174
+ currencyOut: {
175
+ amount: '990000',
176
+ minimumAmount: '980000',
177
+ },
178
+ timeEstimate: 15,
179
+ },
180
+ })
181
+ return jsonResponse({ message: 'temporarily unavailable' }, 503)
182
+ })
183
+ const adapter = FundingRouteProviders.relay({
184
+ fetch,
185
+ now,
186
+ userAddress: '0x1111111111111111111111111111111111111111',
187
+ })
188
+
189
+ const result = await adapter.probeQuote(
190
+ probeInputFromRoute(route),
191
+ new AbortController().signal,
192
+ )
193
+
194
+ expect(result).toMatchObject({
195
+ outputAmount: '990000',
196
+ quality: {
197
+ availableVolumeUsd: null,
198
+ instantVolumeUsd: null,
199
+ liquiditySource: 'providerQuote',
200
+ tier: 'unknown',
201
+ },
202
+ status: 'unknown',
203
+ })
204
+ })
205
+ })
206
+
207
+ type RecordedRequest = {
208
+ init: RequestInit | undefined
209
+ url: URL
210
+ }
211
+
212
+ async function staticRoute(
213
+ provider: FundingRoutes.ProviderId,
214
+ ): Promise<FundingRoutes.QuoteProbeInput['route']> {
215
+ const [route] = await FundingRoutes.listRoutes({
216
+ limit: 10,
217
+ provider,
218
+ sourceAmount: '1000000',
219
+ sourceAmountUnits: 'baseUnits',
220
+ sourceToken: 'USDC',
221
+ sourceChain: 'base',
222
+ })
223
+ if (!route) throw new Error(`Missing ${provider} route`)
224
+ return route as FundingRoutes.QuoteProbeInput['route']
225
+ }
226
+
227
+ async function probeInput(provider: FundingRoutes.ProviderId) {
228
+ return probeInputFromRoute(await staticRoute(provider))
229
+ }
230
+
231
+ function probeInputFromRoute(
232
+ route: FundingRoutes.QuoteProbeInput['route'],
233
+ ): FundingRoutes.QuoteProbeInput {
234
+ return {
235
+ cache: { bypass: false },
236
+ destinationChain: route.destinationChain,
237
+ destinationToken: route.destinationToken,
238
+ include: ['quotes'],
239
+ request: {
240
+ include: ['quotes'],
241
+ limit: 10,
242
+ provider: route.provider.id,
243
+ sourceAmount: route.sourceAmount.amount,
244
+ sourceAmountUnits: 'baseUnits',
245
+ sourceChain: route.sourceChain.id,
246
+ sourceToken: route.sourceToken.symbol,
247
+ },
248
+ route,
249
+ sourceAmount: route.sourceAmount,
250
+ sourceChain: route.sourceChain,
251
+ sourceToken: route.sourceToken,
252
+ }
253
+ }
254
+
255
+ function recordingFetch(
256
+ requests: RecordedRequest[],
257
+ respond: (request: RecordedRequest) => Response | Promise<Response>,
258
+ ): typeof globalThis.fetch {
259
+ return async (input, init) => {
260
+ const request = { init, url: toUrl(input) }
261
+ requests.push(request)
262
+ return respond(request)
263
+ }
264
+ }
265
+
266
+ function toUrl(input: Parameters<typeof globalThis.fetch>[0]) {
267
+ if (input instanceof Request) return new URL(input.url)
268
+ return new URL(String(input))
269
+ }
270
+
271
+ function jsonResponse(body: unknown, status = 200) {
272
+ return new Response(JSON.stringify(body), {
273
+ headers: { 'content-type': 'application/json' },
274
+ status,
275
+ })
276
+ }
277
+
278
+ function jsonRequestBody(request: RecordedRequest | undefined) {
279
+ if (typeof request?.init?.body !== 'string') throw new Error('Expected JSON request body')
280
+ return JSON.parse(request.init.body) as unknown
281
+ }