tapimo 0.6.0 → 0.7.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 (250) hide show
  1. package/dist/App.d.ts +8641 -8263
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +9 -1
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +4 -0
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +5 -0
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/admin/App.d.ts +112 -11
  10. package/dist/admin/App.d.ts.map +1 -1
  11. package/dist/admin/apps/api-keys.d.ts +2 -1
  12. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  13. package/dist/admin/apps/early-access.d.ts +2 -2
  14. package/dist/admin/apps/early-access.d.ts.map +1 -1
  15. package/dist/admin/apps/early-access.js +5 -5
  16. package/dist/admin/apps/early-access.js.map +1 -1
  17. package/dist/admin/apps/organizations.d.ts +124 -1
  18. package/dist/admin/apps/organizations.d.ts.map +1 -1
  19. package/dist/admin/apps/organizations.js +97 -6
  20. package/dist/admin/apps/organizations.js.map +1 -1
  21. package/dist/admin/ui.generated.d.ts +1 -1
  22. package/dist/admin/ui.generated.d.ts.map +1 -1
  23. package/dist/admin/ui.generated.js +95 -87
  24. package/dist/admin/ui.generated.js.map +1 -1
  25. package/dist/analytics/Analytics.d.ts.map +1 -1
  26. package/dist/analytics/Analytics.js +20 -0
  27. package/dist/analytics/Analytics.js.map +1 -1
  28. package/dist/analytics/tables/requestEvents.d.ts +18 -0
  29. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  30. package/dist/analytics/tables/requestEvents.js +36 -7
  31. package/dist/analytics/tables/requestEvents.js.map +1 -1
  32. package/dist/apps/data/App.d.ts +5016 -4985
  33. package/dist/apps/data/App.d.ts.map +1 -1
  34. package/dist/apps/data/FundingQuoteProviders.js +2 -15
  35. package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
  36. package/dist/apps/data/FxOracle.d.ts +1 -1
  37. package/dist/apps/data/FxOracle.d.ts.map +1 -1
  38. package/dist/apps/data/FxOracle.js +1 -1
  39. package/dist/apps/data/FxOracle.js.map +1 -1
  40. package/dist/apps/data/routes/activities.d.ts +27 -27
  41. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  42. package/dist/apps/data/routes/activities.js +21 -1
  43. package/dist/apps/data/routes/activities.js.map +1 -1
  44. package/dist/apps/data/routes/balances.d.ts +58 -58
  45. package/dist/apps/data/routes/blocks.d.ts +39 -39
  46. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  47. package/dist/apps/data/routes/exchanges.d.ts +113 -113
  48. package/dist/apps/data/routes/fee-amm.d.ts +105 -91
  49. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  50. package/dist/apps/data/routes/fee-amm.js +76 -91
  51. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  52. package/dist/apps/data/routes/funding.d.ts +72 -34
  53. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  54. package/dist/apps/data/routes/funding.js +34 -26
  55. package/dist/apps/data/routes/funding.js.map +1 -1
  56. package/dist/apps/data/routes/indexer.d.ts +5 -5
  57. package/dist/apps/data/routes/receipts.d.ts +6 -6
  58. package/dist/apps/data/routes/rpc.d.ts +31 -31
  59. package/dist/apps/data/routes/tokens.d.ts +2 -2
  60. package/dist/apps/data/routes/tokens.js +1 -1
  61. package/dist/apps/data/routes/transactions.d.ts +4 -4
  62. package/dist/apps/data/routes/transfers.d.ts +99 -88
  63. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  64. package/dist/apps/data/routes/transfers.js +226 -141
  65. package/dist/apps/data/routes/transfers.js.map +1 -1
  66. package/dist/apps/data/routes/valuation.d.ts +34 -34
  67. package/dist/apps/data/routes/valuation.js +3 -3
  68. package/dist/apps/data/routes/valuation.js.map +1 -1
  69. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  70. package/dist/apps/data/routes/webhooks.d.ts +366 -366
  71. package/dist/apps/index.d.ts +1 -0
  72. package/dist/apps/index.d.ts.map +1 -1
  73. package/dist/apps/index.js +1 -0
  74. package/dist/apps/index.js.map +1 -1
  75. package/dist/apps/management/App.d.ts +460 -523
  76. package/dist/apps/management/App.d.ts.map +1 -1
  77. package/dist/apps/management/routes/api-keys.d.ts +7 -23
  78. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  79. package/dist/apps/management/routes/api-keys.js +20 -8
  80. package/dist/apps/management/routes/api-keys.js.map +1 -1
  81. package/dist/apps/management/routes/billing.d.ts +106 -151
  82. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  83. package/dist/apps/management/routes/billing.js +28 -28
  84. package/dist/apps/management/routes/billing.js.map +1 -1
  85. package/dist/apps/management/routes/faucet.d.ts +40 -26
  86. package/dist/apps/management/routes/faucet.d.ts.map +1 -1
  87. package/dist/apps/management/routes/faucet.js +33 -18
  88. package/dist/apps/management/routes/faucet.js.map +1 -1
  89. package/dist/apps/management/routes/invitations.d.ts +131 -131
  90. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  91. package/dist/apps/management/routes/invitations.js +0 -3
  92. package/dist/apps/management/routes/invitations.js.map +1 -1
  93. package/dist/apps/management/routes/me.d.ts +6 -8
  94. package/dist/apps/management/routes/me.d.ts.map +1 -1
  95. package/dist/apps/management/routes/me.js +0 -4
  96. package/dist/apps/management/routes/me.js.map +1 -1
  97. package/dist/apps/management/routes/members.d.ts +45 -45
  98. package/dist/apps/management/routes/orgs.d.ts +94 -94
  99. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  100. package/dist/apps/management/routes/orgs.js +5 -0
  101. package/dist/apps/management/routes/orgs.js.map +1 -1
  102. package/dist/apps/management/routes/usage.d.ts +32 -32
  103. package/dist/apps/management/routes/verified-token-requests.d.ts +94 -94
  104. package/dist/apps/management/routes/webhooks.d.ts +1 -1
  105. package/dist/apps/mpp/App.d.ts +363 -0
  106. package/dist/apps/mpp/App.d.ts.map +1 -0
  107. package/dist/apps/mpp/App.js +72 -0
  108. package/dist/apps/mpp/App.js.map +1 -0
  109. package/dist/apps/mpp/Mpp.d.ts +178 -0
  110. package/dist/apps/mpp/Mpp.d.ts.map +1 -0
  111. package/dist/apps/mpp/Mpp.js +183 -0
  112. package/dist/apps/mpp/Mpp.js.map +1 -0
  113. package/dist/apps/mpp/index.d.ts +3 -0
  114. package/dist/apps/mpp/index.d.ts.map +1 -0
  115. package/dist/apps/mpp/index.js +3 -0
  116. package/dist/apps/mpp/index.js.map +1 -0
  117. package/dist/apps/relay/App.d.ts.map +1 -1
  118. package/dist/apps/relay/App.js +56 -9
  119. package/dist/apps/relay/App.js.map +1 -1
  120. package/dist/db/Db.d.ts +2 -0
  121. package/dist/db/Db.d.ts.map +1 -1
  122. package/dist/db/Db.js +27 -0
  123. package/dist/db/Db.js.map +1 -1
  124. package/dist/db/Schema.d.ts +12 -0
  125. package/dist/db/Schema.d.ts.map +1 -1
  126. package/dist/db/Schema.js +11 -0
  127. package/dist/db/Schema.js.map +1 -1
  128. package/dist/db/tables/enabledBillingSources.d.ts +73 -0
  129. package/dist/db/tables/enabledBillingSources.d.ts.map +1 -0
  130. package/dist/db/tables/enabledBillingSources.js +69 -0
  131. package/dist/db/tables/enabledBillingSources.js.map +1 -0
  132. package/dist/db/tables/invitations.d.ts +1 -12
  133. package/dist/db/tables/invitations.d.ts.map +1 -1
  134. package/dist/db/tables/invitations.js +3 -23
  135. package/dist/db/tables/invitations.js.map +1 -1
  136. package/dist/db/tables/organizations.d.ts +5 -2
  137. package/dist/db/tables/organizations.d.ts.map +1 -1
  138. package/dist/db/tables/organizations.js +13 -2
  139. package/dist/db/tables/organizations.js.map +1 -1
  140. package/dist/handlers/internal/utils.d.ts +1 -16
  141. package/dist/handlers/internal/utils.d.ts.map +1 -1
  142. package/dist/handlers/internal/utils.js +17 -6
  143. package/dist/handlers/internal/utils.js.map +1 -1
  144. package/dist/handlers/relay.d.ts.map +1 -1
  145. package/dist/handlers/relay.js +37 -5
  146. package/dist/handlers/relay.js.map +1 -1
  147. package/dist/internal/Auth.d.ts +0 -32
  148. package/dist/internal/Auth.d.ts.map +1 -1
  149. package/dist/internal/Auth.js +3 -51
  150. package/dist/internal/Auth.js.map +1 -1
  151. package/dist/internal/FundingQuotes.d.ts +78 -32
  152. package/dist/internal/FundingQuotes.d.ts.map +1 -1
  153. package/dist/internal/FundingQuotes.js +45 -8
  154. package/dist/internal/FundingQuotes.js.map +1 -1
  155. package/dist/internal/Log.d.ts +18 -4
  156. package/dist/internal/Log.d.ts.map +1 -1
  157. package/dist/internal/Log.js +50 -3
  158. package/dist/internal/Log.js.map +1 -1
  159. package/dist/internal/MetricSink.d.ts.map +1 -1
  160. package/dist/internal/MetricSink.js +15 -0
  161. package/dist/internal/MetricSink.js.map +1 -1
  162. package/dist/internal/Response.d.ts +2 -2
  163. package/dist/internal/Response.d.ts.map +1 -1
  164. package/dist/internal/Response.js +12 -4
  165. package/dist/internal/Response.js.map +1 -1
  166. package/dist/internal/Viem.d.ts +1 -1
  167. package/package.json +7 -2
  168. package/src/App.test.ts +9 -4
  169. package/src/App.ts +14 -2
  170. package/src/Client.test-d.ts +109 -28
  171. package/src/Scope.ts +5 -0
  172. package/src/admin/App.test.ts +4 -0
  173. package/src/admin/apps/early-access.ts +6 -5
  174. package/src/admin/apps/organizations.test.ts +86 -2
  175. package/src/admin/apps/organizations.ts +155 -31
  176. package/src/admin/apps/scopes.test.ts +5 -0
  177. package/src/admin/ui/src/lib/organizations.test.ts +81 -1
  178. package/src/admin/ui/src/lib/organizations.ts +92 -0
  179. package/src/admin/ui/src/routes/api-keys.tsx +1 -1
  180. package/src/admin/ui/src/routes/early-access.tsx +8 -3
  181. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +103 -0
  182. package/src/admin/ui.generated.ts +95 -109
  183. package/src/analytics/Analytics.test.ts +83 -0
  184. package/src/analytics/Analytics.ts +20 -0
  185. package/src/analytics/tables/requestEvents.ts +60 -7
  186. package/src/apps/composition.test.ts +11 -2
  187. package/src/apps/data/App.test.ts +1 -1
  188. package/src/apps/data/FundingQuoteProviders.test.ts +0 -5
  189. package/src/apps/data/FundingQuoteProviders.ts +2 -29
  190. package/src/apps/data/FxOracle.test.ts +2 -2
  191. package/src/apps/data/FxOracle.ts +2 -2
  192. package/src/apps/data/routes/activities.test.ts +6 -2
  193. package/src/apps/data/routes/activities.ts +39 -1
  194. package/src/apps/data/routes/balances.test.ts +6 -2
  195. package/src/apps/data/routes/exchanges.test.ts +7 -3
  196. package/src/apps/data/routes/fee-amm.test.ts +47 -23
  197. package/src/apps/data/routes/fee-amm.ts +98 -119
  198. package/src/apps/data/routes/funding.test.ts +29 -8
  199. package/src/apps/data/routes/funding.ts +42 -48
  200. package/src/apps/data/routes/receipts.test.ts +6 -2
  201. package/src/apps/data/routes/tokens.ts +1 -1
  202. package/src/apps/data/routes/transfers.test.ts +203 -35
  203. package/src/apps/data/routes/transfers.ts +294 -172
  204. package/src/apps/data/routes/valuation.test.ts +18 -3
  205. package/src/apps/data/routes/valuation.ts +4 -4
  206. package/src/apps/index.ts +1 -0
  207. package/src/apps/management/routes/api-keys.test.ts +2 -75
  208. package/src/apps/management/routes/api-keys.ts +30 -7
  209. package/src/apps/management/routes/billing.test.ts +106 -85
  210. package/src/apps/management/routes/billing.ts +32 -21
  211. package/src/apps/management/routes/faucet.test.ts +11 -3
  212. package/src/apps/management/routes/faucet.ts +46 -30
  213. package/src/apps/management/routes/invitations.test.ts +0 -172
  214. package/src/apps/management/routes/invitations.ts +0 -3
  215. package/src/apps/management/routes/me.test.ts +3 -145
  216. package/src/apps/management/routes/me.ts +0 -6
  217. package/src/apps/management/routes/orgs.test.ts +45 -2
  218. package/src/apps/management/routes/orgs.ts +6 -0
  219. package/src/apps/management/routes/scopes.test.ts +1 -0
  220. package/src/apps/mpp/App.ts +86 -0
  221. package/src/apps/mpp/Mpp.test.ts +193 -0
  222. package/src/apps/mpp/Mpp.ts +353 -0
  223. package/src/apps/mpp/index.ts +2 -0
  224. package/src/apps/mpp/internal/Adapter.ts +11 -0
  225. package/src/apps/mpp/internal/Error.ts +9 -0
  226. package/src/apps/mpp/internal/Idempotency.ts +7 -0
  227. package/src/apps/mpp/internal/Screening.ts +10 -0
  228. package/src/apps/relay/App.test.ts +88 -6
  229. package/src/apps/relay/App.ts +61 -9
  230. package/src/db/Db.test.ts +2 -2
  231. package/src/db/Db.ts +30 -0
  232. package/src/db/Schema.ts +15 -0
  233. package/src/db/tables/enabledBillingSources.test.ts +303 -0
  234. package/src/db/tables/enabledBillingSources.ts +118 -0
  235. package/src/db/tables/invitations.ts +4 -26
  236. package/src/db/tables/organizations.test.ts +103 -0
  237. package/src/db/tables/organizations.ts +18 -2
  238. package/src/handlers/internal/utils.ts +22 -6
  239. package/src/handlers/relay.errors.test.ts +153 -0
  240. package/src/handlers/relay.ts +42 -7
  241. package/src/internal/Auth.test.ts +3 -3
  242. package/src/internal/Auth.ts +3 -63
  243. package/src/internal/FundingQuotes.test.ts +18 -5
  244. package/src/internal/FundingQuotes.ts +46 -11
  245. package/src/internal/Log.test.ts +91 -0
  246. package/src/internal/Log.ts +69 -5
  247. package/src/internal/MetricSink.test.ts +55 -0
  248. package/src/internal/MetricSink.ts +14 -0
  249. package/src/internal/Response.test.ts +28 -0
  250. package/src/internal/Response.ts +12 -4
@@ -1,3 +1,5 @@
1
+ import * as Value from './Value.js'
2
+
1
3
  /** Tempo mainnet as a CAIP-2 chain reference. */
2
4
  const destinationChain = {
3
5
  addressFormat: 'evm',
@@ -27,6 +29,7 @@ const sourceChains = [
27
29
  {
28
30
  address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
29
31
  coingeckoId: 'usd-coin',
32
+ currency: 'USD',
30
33
  decimals: 6,
31
34
  name: 'USD Coin',
32
35
  standard: 'ERC-20',
@@ -35,6 +38,7 @@ const sourceChains = [
35
38
  {
36
39
  address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
37
40
  coingeckoId: 'tether',
41
+ currency: 'USD',
38
42
  decimals: 6,
39
43
  name: 'Tether USD',
40
44
  standard: 'ERC-20',
@@ -43,6 +47,7 @@ const sourceChains = [
43
47
  {
44
48
  address: '0x6c3ea9036406852006290770bedfcaba0e23a0e8',
45
49
  coingeckoId: 'paypal-usd',
50
+ currency: 'USD',
46
51
  decimals: 6,
47
52
  name: 'PayPal USD',
48
53
  standard: 'ERC-20',
@@ -51,6 +56,7 @@ const sourceChains = [
51
56
  {
52
57
  address: '0x1abaea1f7c830bd89acc67ec4af516284b1bc33c',
53
58
  coingeckoId: 'euro-coin',
59
+ currency: 'EUR',
54
60
  decimals: 6,
55
61
  name: 'Euro Coin',
56
62
  standard: 'ERC-20',
@@ -59,6 +65,7 @@ const sourceChains = [
59
65
  {
60
66
  address: '0x6b175474e89094c44da98b954eedeac495271d0f',
61
67
  coingeckoId: 'dai',
68
+ currency: 'USD',
62
69
  decimals: 18,
63
70
  name: 'Dai Stablecoin',
64
71
  standard: 'ERC-20',
@@ -83,6 +90,7 @@ const sourceChains = [
83
90
  {
84
91
  address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
85
92
  coingeckoId: 'usd-coin',
93
+ currency: 'USD',
86
94
  decimals: 6,
87
95
  name: 'USD Coin',
88
96
  standard: 'ERC-20',
@@ -91,6 +99,7 @@ const sourceChains = [
91
99
  {
92
100
  address: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
93
101
  coingeckoId: 'tether',
102
+ currency: 'USD',
94
103
  decimals: 6,
95
104
  name: 'Tether USD',
96
105
  standard: 'ERC-20',
@@ -99,6 +108,7 @@ const sourceChains = [
99
108
  {
100
109
  address: '0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42',
101
110
  coingeckoId: 'euro-coin',
111
+ currency: 'EUR',
102
112
  decimals: 6,
103
113
  name: 'Euro Coin',
104
114
  standard: 'ERC-20',
@@ -122,6 +132,7 @@ const sourceChains = [
122
132
  {
123
133
  address: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
124
134
  coingeckoId: 'usd-coin',
135
+ currency: 'USD',
125
136
  decimals: 18,
126
137
  name: 'Binance-Peg USD Coin',
127
138
  standard: 'BEP-20',
@@ -130,6 +141,7 @@ const sourceChains = [
130
141
  {
131
142
  address: '0x55d398326f99059ff775485246999027b3197955',
132
143
  coingeckoId: 'tether',
144
+ currency: 'USD',
133
145
  decimals: 18,
134
146
  name: 'Tether USD',
135
147
  standard: 'BEP-20',
@@ -151,6 +163,7 @@ const sourceChains = [
151
163
  {
152
164
  address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
153
165
  coingeckoId: 'usd-coin',
166
+ currency: 'USD',
154
167
  decimals: 6,
155
168
  name: 'USD Coin',
156
169
  standard: 'SPL',
@@ -173,6 +186,7 @@ const sourceChains = [
173
186
  {
174
187
  address: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
175
188
  coingeckoId: 'tether',
189
+ currency: 'USD',
176
190
  decimals: 6,
177
191
  name: 'Tether USD',
178
192
  standard: 'TRC-20',
@@ -186,6 +200,7 @@ const sourceChains = [
186
200
  const destinationTokens = [
187
201
  {
188
202
  address: '0x20c0000000000000000000000000000000000000',
203
+ currency: 'USD',
189
204
  decimals: 6,
190
205
  name: 'pathUSD',
191
206
  slug: 'pathusd',
@@ -194,6 +209,7 @@ const destinationTokens = [
194
209
  },
195
210
  {
196
211
  address: '0x20c000000000000000000000b9537d11c60e8b50',
212
+ currency: 'USD',
197
213
  decimals: 6,
198
214
  name: 'Bridged USDC (Stargate)',
199
215
  slug: 'usdce',
@@ -202,6 +218,7 @@ const destinationTokens = [
202
218
  },
203
219
  {
204
220
  address: '0x20c0000000000000000000001621e21f71cf12fb',
221
+ currency: 'EUR',
205
222
  decimals: 6,
206
223
  name: 'Bridged EURC',
207
224
  slug: 'eurce',
@@ -210,6 +227,7 @@ const destinationTokens = [
210
227
  },
211
228
  {
212
229
  address: '0x20c00000000000000000000014f22ca97301eb73',
230
+ currency: 'USD',
213
231
  decimals: 6,
214
232
  name: 'USDT0',
215
233
  slug: 'usdt0',
@@ -218,6 +236,7 @@ const destinationTokens = [
218
236
  },
219
237
  {
220
238
  address: '0x20c0000000000000000000003554d28269e0f3c2',
239
+ currency: 'USD',
221
240
  decimals: 6,
222
241
  name: 'frxUSD',
223
242
  slug: 'frxusd',
@@ -346,8 +365,6 @@ export type QuoteProbeResult = {
346
365
  minimumOutputAmount?: string | undefined
347
366
  /** Expected destination amount in base units. */
348
367
  outputAmount?: string | undefined
349
- /** Expected destination amount rendered with destination token decimals. */
350
- outputAmountFormatted?: string | undefined
351
368
  /** Timestamp for the live quote probe. */
352
369
  sampledAt: string
353
370
  /** Provider-specific quote status for the requested amount. */
@@ -659,6 +676,7 @@ function formatChain(chain: SourceChainInput) {
659
676
  function formatDestinationToken(token: DestinationTokenInput) {
660
677
  return {
661
678
  address: token.address,
679
+ currency: token.currency,
662
680
  decimals: token.decimals,
663
681
  metadata: {},
664
682
  name: token.name,
@@ -706,6 +724,7 @@ function formatQuoteCandidate(options: {
706
724
  function formatSourceToken(chain: SourceChainInput, token: TokenInput) {
707
725
  return {
708
726
  address: token.address,
727
+ currency: token.currency,
709
728
  decimals: token.decimals,
710
729
  metadata: { coingeckoId: token.coingeckoId },
711
730
  name: token.name,
@@ -741,7 +760,24 @@ type SuccessfulQuoteProbe = QuoteProbeResult & {
741
760
 
742
761
  function formatFundingQuote(candidate: QuoteCandidate, quote: SuccessfulQuoteProbe) {
743
762
  return {
744
- ...candidate,
763
+ destinationAmount: Value.tokenAmount({
764
+ baseUnits: quote.outputAmount,
765
+ currency: candidate.destinationToken.currency,
766
+ decimals: candidate.destinationToken.decimals,
767
+ }),
768
+ ...(quote.minimumOutputAmount !== undefined
769
+ ? {
770
+ destinationAmountMin: Value.tokenAmount({
771
+ baseUnits: quote.minimumOutputAmount,
772
+ currency: candidate.destinationToken.currency,
773
+ decimals: candidate.destinationToken.decimals,
774
+ }),
775
+ }
776
+ : {}),
777
+ destinationChain: candidate.destinationChain,
778
+ destinationToken: candidate.destinationToken,
779
+ id: candidate.id,
780
+ provider: candidate.provider,
745
781
  quality: {
746
782
  ...(quote.quality?.estimatedSeconds !== undefined
747
783
  ? { estimatedSeconds: quote.quality.estimatedSeconds }
@@ -754,16 +790,15 @@ function formatFundingQuote(candidate: QuoteCandidate, quote: SuccessfulQuotePro
754
790
  },
755
791
  quote: {
756
792
  ...(quote.expiresAt !== undefined ? { expiresAt: quote.expiresAt } : {}),
757
- inputAmount: candidate.sourceAmount.amount,
758
- ...(quote.minimumOutputAmount !== undefined
759
- ? { minimumOutputAmount: quote.minimumOutputAmount }
760
- : {}),
761
- outputAmount: quote.outputAmount,
762
- outputAmountFormatted:
763
- quote.outputAmountFormatted ??
764
- decimalFromBaseUnits(quote.outputAmount, candidate.destinationToken.decimals),
765
793
  sampledAt: quote.sampledAt,
766
794
  },
795
+ sourceAmount: Value.tokenAmount({
796
+ baseUnits: candidate.sourceAmount.amount,
797
+ currency: candidate.sourceToken.currency,
798
+ decimals: candidate.sourceToken.decimals,
799
+ }),
800
+ sourceChain: candidate.sourceChain,
801
+ sourceToken: candidate.sourceToken,
767
802
  } as const
768
803
  }
769
804
 
@@ -138,6 +138,97 @@ describe('middleware', () => {
138
138
  `)
139
139
  })
140
140
 
141
+ test('records a JSON-RPC error returned with HTTP 200', async () => {
142
+ const entries: Log.Entry[] = []
143
+ const api = app({ emit: (entry) => void entries.push(entry) })
144
+ api.post('/rpc/sponsor', (c) => {
145
+ const body = {
146
+ error: {
147
+ code: -32602,
148
+ data: { code: 'billing_required', retryable: false },
149
+ message: 'Sponsorship requires an active billing source.',
150
+ },
151
+ id: 1,
152
+ jsonrpc: '2.0',
153
+ }
154
+ c.set('rpcResponse', Log.rpcErrors(body))
155
+ return c.json(body)
156
+ })
157
+
158
+ const response = await api.request('/rpc/sponsor', { method: 'POST' })
159
+
160
+ expect(response.status).toBe(200)
161
+ expect(stable(entries[0]!)).toMatchInlineSnapshot(`
162
+ {
163
+ "level": "warn",
164
+ "method": "POST",
165
+ "path": "/rpc/sponsor",
166
+ "route": "/rpc/sponsor",
167
+ "rpc": {
168
+ "code": -32602,
169
+ "dataCode": "billing_required",
170
+ "errors": 1,
171
+ },
172
+ "status": 200,
173
+ }
174
+ `)
175
+ })
176
+
177
+ test('logs JSON-RPC server errors at error level', async () => {
178
+ const entries: Log.Entry[] = []
179
+ const api = app({ emit: (entry) => void entries.push(entry) })
180
+ api.post('/rpc/sponsor', (c) => {
181
+ const body = {
182
+ error: {
183
+ code: -32603,
184
+ data: { code: 'internal_error' },
185
+ message: 'Internal error',
186
+ },
187
+ id: 1,
188
+ jsonrpc: '2.0',
189
+ }
190
+ c.set('rpcResponse', Log.rpcErrors(body))
191
+ return c.json(body)
192
+ })
193
+
194
+ await api.request('/rpc/sponsor', { method: 'POST' })
195
+
196
+ expect(entries[0]?.level).toBe('error')
197
+ })
198
+
199
+ test('counts batch JSON-RPC errors without retaining mixed codes', async () => {
200
+ const entries: Log.Entry[] = []
201
+ const api = app({ emit: (entry) => void entries.push(entry) })
202
+ api.post('/rpc/relay', (c) => {
203
+ const body = [
204
+ { id: 1, jsonrpc: '2.0', result: '0x1' },
205
+ { error: { code: -32602, message: 'Invalid params.' }, id: 2, jsonrpc: '2.0' },
206
+ {
207
+ error: { code: -32603, data: { code: 'Internal Error' }, message: 'Internal error.' },
208
+ id: 3,
209
+ jsonrpc: '2.0',
210
+ },
211
+ ]
212
+ c.set('rpcResponse', Log.rpcErrors(body))
213
+ return c.json(body)
214
+ })
215
+
216
+ await api.request('/rpc/relay', { method: 'POST' })
217
+
218
+ expect(stable(entries[0]!)).toMatchInlineSnapshot(`
219
+ {
220
+ "level": "warn",
221
+ "method": "POST",
222
+ "path": "/rpc/relay",
223
+ "route": "/rpc/relay",
224
+ "rpc": {
225
+ "errors": 2,
226
+ },
227
+ "status": 200,
228
+ }
229
+ `)
230
+ })
231
+
141
232
  test('includes redacted provider failure details', async () => {
142
233
  const entries: Log.Entry[] = []
143
234
  const api = app({ emit: (entry) => void entries.push(entry) })
@@ -17,6 +17,8 @@ export type Variables = {
17
17
  providerFailure?: Entry['provider'] | undefined
18
18
  /** Redacted provider failures when multiple upstream adapters fail. */
19
19
  providerFailures?: Entry['providerFailures'] | undefined
20
+ /** Bounded JSON-RPC failure metadata recorded by a composed RPC handler. */
21
+ rpcResponse?: Entry['rpc'] | undefined
20
22
  }
21
23
 
22
24
  /** Hono environment shape used by the request logger. */
@@ -71,7 +73,7 @@ export type Entry = {
71
73
  errorCode?: string | undefined
72
74
  /** Bounded funding provider attempts made while serving the request. */
73
75
  fundingProviderAttempts?: readonly FundingProviderAttempt[] | undefined
74
- /** Log level derived from the response status: 5xx `error`, 4xx `warn`, else `info`. */
76
+ /** Log level: 5xx `error`; 4xx or JSON-RPC failure `warn`; otherwise `info`. */
75
77
  level: Level
76
78
  /** Request method. */
77
79
  method: string
@@ -121,7 +123,16 @@ export type Entry = {
121
123
  requestId: string
122
124
  /** Matched handler path (bounded cardinality), e.g. `/v1/tokens/:address`; `/*` when a middleware (e.g. an edge-cache hit) short-circuited routing. */
123
125
  route: string
124
- /** Response status code. */
126
+ /** JSON-RPC failures returned in an otherwise successful HTTP response. */
127
+ rpc?: {
128
+ /** Numeric JSON-RPC error code when every failed response shares one code. */
129
+ code?: number | undefined
130
+ /** Stable application error code from `error.data.code`, when shared by every failed response. */
131
+ dataCode?: string | undefined
132
+ /** Number of failed responses in the single or batch JSON-RPC response. */
133
+ errors: number
134
+ }
135
+ /** HTTP response status code. JSON-RPC failures retain their protocol-native `200`. */
125
136
  status: number
126
137
  /** Upstream operation durations in milliseconds, from the request's Server-Timing metrics (e.g. RPC and TIDX calls). */
127
138
  timings?: Record<string, number> | undefined
@@ -138,8 +149,9 @@ export type Emit = (entry: Entry) => Promise<void> | void
138
149
  *
139
150
  * The entry derives everything from state other layers already produce —
140
151
  * `requestId`, the auth principal, `RateLimit-*` headers, Server-Timing
141
- * metrics, the `errorCode` recorded by `Response.error`, and the edge-cache
142
- * hit marker so handlers never log per-request lines themselves.
152
+ * metrics, the `errorCode` recorded by `Response.error`, JSON-RPC response
153
+ * errors, and the edge-cache hit marker, so handlers never log per-request
154
+ * lines themselves.
143
155
  */
144
156
  export function middleware<environment extends Environment = Environment>(
145
157
  options: middleware.Options = {},
@@ -164,6 +176,7 @@ export function middleware<environment extends Environment = Environment>(
164
176
  | undefined
165
177
  const provider = c.get('providerFailure' as never) as Entry['provider']
166
178
  const providerFailures = c.get('providerFailures' as never) as Entry['providerFailures']
179
+ const rpc = c.get('rpcResponse' as never) as Entry['rpc']
167
180
 
168
181
  // The `request` total metric duplicates `duration` (measured here, one
169
182
  // layer further out), so only operation timings are kept.
@@ -193,7 +206,12 @@ export function middleware<environment extends Environment = Environment>(
193
206
  duration: round(duration),
194
207
  ...(errorCode === undefined ? {} : { errorCode }),
195
208
  ...(fundingProviderAttempts === undefined ? {} : { fundingProviderAttempts }),
196
- level: status >= 500 ? 'error' : status >= 400 ? 'warn' : 'info',
209
+ level:
210
+ status >= 500 || rpcServerError(rpc?.code)
211
+ ? 'error'
212
+ : status >= 400 || rpc
213
+ ? 'warn'
214
+ : 'info',
197
215
  method: c.req.method,
198
216
  path: url.pathname,
199
217
  ...(principal?.payment === undefined ? {} : { payment: principal.payment.reason }),
@@ -220,6 +238,7 @@ export function middleware<environment extends Environment = Environment>(
220
238
  ...(rateLimit === undefined ? {} : { rateLimit }),
221
239
  requestId: c.get('requestId' as never) as string,
222
240
  route: c.req.routePath,
241
+ ...(rpc === undefined ? {} : { rpc }),
223
242
  status,
224
243
  ...(timings === undefined ? {} : { timings }),
225
244
  })
@@ -238,6 +257,51 @@ export function middleware<environment extends Environment = Environment>(
238
257
  }
239
258
  }
240
259
 
260
+ const rpcDataCode = /^[a-z][a-z0-9_]{0,63}$/
261
+
262
+ /** Extracts bounded error metadata from an already-parsed JSON-RPC response. */
263
+ export function rpcErrors(body: unknown): Entry['rpc'] {
264
+ const values = Array.isArray(body) ? body : [body]
265
+ const errors = values.flatMap((value) => {
266
+ if (!value || typeof value !== 'object') return []
267
+ const record = value as Record<string, unknown>
268
+ if (record['jsonrpc'] !== '2.0' || !record['error'] || typeof record['error'] !== 'object')
269
+ return []
270
+ const error = record['error'] as Record<string, unknown>
271
+ if (
272
+ !Number.isInteger(error['code']) ||
273
+ Number(error['code']) < -2_147_483_648 ||
274
+ Number(error['code']) > 2_147_483_647
275
+ )
276
+ return []
277
+ const data = error['data']
278
+ const dataCode_value =
279
+ data && typeof data === 'object' ? (data as Record<string, unknown>)['code'] : undefined
280
+ const dataCode = typeof dataCode_value === 'string' ? dataCode_value : undefined
281
+ return [
282
+ {
283
+ code: error['code'] as number,
284
+ ...(dataCode && rpcDataCode.test(dataCode) ? { dataCode } : {}),
285
+ },
286
+ ]
287
+ })
288
+ if (errors.length === 0) return undefined
289
+
290
+ const codes = new Set(errors.map((error) => error.code))
291
+ const dataCodes = new Set(errors.map((error) => error.dataCode).filter(Boolean))
292
+ return {
293
+ ...(codes.size === 1 ? { code: errors[0]!.code } : {}),
294
+ ...(dataCodes.size === 1 && errors.every((error) => error.dataCode !== undefined)
295
+ ? { dataCode: errors[0]!.dataCode }
296
+ : {}),
297
+ errors: Math.min(errors.length, 65_535),
298
+ }
299
+ }
300
+
301
+ function rpcServerError(code: number | undefined) {
302
+ return code === -32603 || (code !== undefined && code >= -32099 && code <= -32000)
303
+ }
304
+
241
305
  export declare namespace middleware {
242
306
  /** Options for the request logger middleware. */
243
307
  type Options = {
@@ -10,6 +10,61 @@ type MetricEvent = {
10
10
  }
11
11
 
12
12
  describe('requests', () => {
13
+ test('records JSON-RPC errors with bounded code tags', () => {
14
+ const events: MetricEvent[] = []
15
+ const metrics = Metrics.from({
16
+ count(name, value, tags) {
17
+ events.push({ name, tags, type: 'count', value })
18
+ },
19
+ flush() {},
20
+ gauge() {},
21
+ histogram() {},
22
+ })
23
+ const record = MetricSink.requests(metrics)
24
+
25
+ record({
26
+ duration: 10,
27
+ level: 'warn',
28
+ method: 'POST',
29
+ path: '/rpc/relay',
30
+ requestId: 'request_private',
31
+ route: '/rpc/relay',
32
+ rpc: { code: -32602, dataCode: 'billing_required', errors: 2 },
33
+ status: 200,
34
+ })
35
+
36
+ expect(events).toMatchInlineSnapshot(`
37
+ [
38
+ {
39
+ "name": "http_response_count",
40
+ "tags": {
41
+ "cache": "miss",
42
+ "method": "POST",
43
+ "principal_environment": "none",
44
+ "principal_type": "unknown",
45
+ "route": "/rpc/relay",
46
+ "status": 200,
47
+ },
48
+ "type": "count",
49
+ "value": 1,
50
+ },
51
+ {
52
+ "name": "rpc_response_error_count",
53
+ "tags": {
54
+ "cache": "miss",
55
+ "method": "POST",
56
+ "principal_environment": "none",
57
+ "principal_type": "unknown",
58
+ "route": "/rpc/relay",
59
+ "rpc_error_code": "-32602",
60
+ },
61
+ "type": "count",
62
+ "value": 2,
63
+ },
64
+ ]
65
+ `)
66
+ })
67
+
13
68
  test('records bounded funding provider attempts without caller data', () => {
14
69
  const events: MetricEvent[] = []
15
70
  let flushes = 0
@@ -85,6 +85,12 @@ export function requests(backend: Metrics): RequestSink {
85
85
  })
86
86
  backend.histogram('http_response_duration_ms', entry.duration, tags)
87
87
 
88
+ if (entry.rpc)
89
+ backend.count('rpc_response_error_count', entry.rpc.errors, {
90
+ ...tags,
91
+ rpc_error_code: rpcErrorCode(entry.rpc.code),
92
+ })
93
+
88
94
  if (entry.status === 429 || entry.errorCode === 'rate_limit_exceeded')
89
95
  backend.count('http_rate_limit_count', 1, {
90
96
  ...tags,
@@ -109,6 +115,14 @@ export function requests(backend: Metrics): RequestSink {
109
115
  }
110
116
  }
111
117
 
118
+ /** Maps arbitrary upstream JSON-RPC codes into bounded metric tags. */
119
+ function rpcErrorCode(code: number | undefined) {
120
+ if (code === undefined) return 'mixed'
121
+ if ([-32700, -32600, -32601, -32602, -32603].includes(code)) return String(code)
122
+ if (code >= -32099 && code <= -32000) return 'server'
123
+ return 'other'
124
+ }
125
+
112
126
  function routeTags(entry: Log.Entry): RouteTags {
113
127
  return {
114
128
  cache: entry.cache ?? 'miss',
@@ -0,0 +1,28 @@
1
+ import { Hono } from 'hono'
2
+
3
+ import * as Response from './Response.js'
4
+
5
+ describe('upstream', () => {
6
+ test('returns a stable message and preserves the cause on context', async () => {
7
+ const app = new Hono()
8
+ const cause = new Error('provider URL contains secret credentials')
9
+ let captured: Error | undefined
10
+
11
+ app.use(async (c, next) => {
12
+ await next()
13
+ captured = c.error
14
+ })
15
+ app.get('/', (c) => Response.upstream(c, cause))
16
+
17
+ const response = await app.request('/')
18
+ expect(await response.json()).toMatchInlineSnapshot(`
19
+ {
20
+ "error": {
21
+ "code": "upstream_error",
22
+ "message": "Upstream service could not complete the request",
23
+ },
24
+ }
25
+ `)
26
+ expect(captured).toBe(cause)
27
+ })
28
+ })
@@ -39,12 +39,20 @@ export function unsupportedChainId(c: Context, chainId: number, supported: Itera
39
39
  }
40
40
 
41
41
  /**
42
- * Maps an unexpected upstream failure to a 502 JSON error envelope. Rethrows
43
- * non-`Error` causes so the global handler can convert them.
42
+ * Maps an unexpected upstream failure to a stable 502 JSON error envelope.
43
+ * Preserves `Error` causes for reporting and rethrows non-`Error` causes.
44
44
  */
45
45
  export function upstream(c: Context, cause: unknown) {
46
- if (cause instanceof Error)
47
- return error(c, { code: 'upstream_error', message: cause.message, status: 502 })
46
+ if (cause instanceof Error) {
47
+ // Preserve the original error for error-reporting middleware without
48
+ // exposing provider URLs, payloads, or credentials in the response.
49
+ c.error = cause
50
+ return error(c, {
51
+ code: 'upstream_error',
52
+ message: 'Upstream service could not complete the request',
53
+ status: 502,
54
+ })
55
+ }
48
56
  throw cause
49
57
  }
50
58