tapimo 0.7.2 → 0.9.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 (300) hide show
  1. package/README.md +10 -7
  2. package/dist/ApiKey.d.ts +3 -0
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +10 -0
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +7 -3
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +9 -2
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +20117 -19183
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +61 -4
  13. package/dist/App.js.map +1 -1
  14. package/dist/admin/App.d.ts +84 -74
  15. package/dist/admin/App.d.ts.map +1 -1
  16. package/dist/admin/apps/api-keys.d.ts +14 -1
  17. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  18. package/dist/admin/apps/api-keys.js +6 -2
  19. package/dist/admin/apps/api-keys.js.map +1 -1
  20. package/dist/admin/apps/organizations.d.ts +2 -0
  21. package/dist/admin/apps/organizations.d.ts.map +1 -1
  22. package/dist/admin/apps/verified-tokens.d.ts +11 -11
  23. package/dist/admin/ui.generated.d.ts +1 -1
  24. package/dist/admin/ui.generated.js +67 -67
  25. package/dist/admin/ui.generated.js.map +1 -1
  26. package/dist/analytics/Analytics.d.ts +2 -0
  27. package/dist/analytics/Analytics.d.ts.map +1 -1
  28. package/dist/analytics/Analytics.js +3 -0
  29. package/dist/analytics/Analytics.js.map +1 -1
  30. package/dist/apps/data/App.d.ts +11696 -13937
  31. package/dist/apps/data/App.d.ts.map +1 -1
  32. package/dist/apps/data/App.js +4 -8
  33. package/dist/apps/data/App.js.map +1 -1
  34. package/dist/apps/data/routes/activities.d.ts +271 -271
  35. package/dist/apps/data/routes/addresses.d.ts +398 -167
  36. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  37. package/dist/apps/data/routes/balances.d.ts +278 -8
  38. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  39. package/dist/apps/data/routes/balances.js +114 -2
  40. package/dist/apps/data/routes/balances.js.map +1 -1
  41. package/dist/apps/data/routes/blocks.d.ts +123 -123
  42. package/dist/apps/data/routes/coingecko.d.ts +170 -170
  43. package/dist/apps/data/routes/earn.d.ts +498 -6
  44. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  45. package/dist/apps/data/routes/earn.js +876 -4
  46. package/dist/apps/data/routes/earn.js.map +1 -1
  47. package/dist/apps/data/routes/exchanges.d.ts +10 -10
  48. package/dist/apps/data/routes/fee-amm.d.ts +2 -2
  49. package/dist/apps/data/routes/indexer.d.ts +6 -6
  50. package/dist/apps/data/routes/indexer.js +4 -1
  51. package/dist/apps/data/routes/indexer.js.map +1 -1
  52. package/dist/apps/data/routes/receipts.d.ts +235 -235
  53. package/dist/apps/data/routes/rpc.d.ts +31 -31
  54. package/dist/apps/data/routes/rpc.js +4 -1
  55. package/dist/apps/data/routes/rpc.js.map +1 -1
  56. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  57. package/dist/apps/data/routes/tokens.d.ts +449 -449
  58. package/dist/apps/data/routes/transactions.d.ts +2 -2
  59. package/dist/apps/data/routes/transfers.d.ts +81 -81
  60. package/dist/apps/data/routes/valuation.d.ts +1 -1
  61. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  62. package/dist/apps/data/routes/webhooks.d.ts +9 -9
  63. package/dist/apps/data/routes/zones.d.ts +14 -14
  64. package/dist/apps/funding/Access.d.ts +16 -0
  65. package/dist/apps/funding/Access.d.ts.map +1 -0
  66. package/dist/apps/funding/Access.js +22 -0
  67. package/dist/apps/funding/Access.js.map +1 -0
  68. package/dist/apps/{data/routes/funding.d.ts → funding/App.d.ts} +1640 -3217
  69. package/dist/apps/funding/App.d.ts.map +1 -0
  70. package/dist/apps/funding/App.js +61 -0
  71. package/dist/apps/funding/App.js.map +1 -0
  72. package/dist/apps/funding/Inventory.d.ts +5 -0
  73. package/dist/apps/funding/Inventory.d.ts.map +1 -0
  74. package/dist/apps/funding/Inventory.js +25 -0
  75. package/dist/apps/funding/Inventory.js.map +1 -0
  76. package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
  77. package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
  78. package/dist/apps/funding/ProviderAttempts.js +24 -0
  79. package/dist/apps/funding/ProviderAttempts.js.map +1 -0
  80. package/dist/apps/funding/index.d.ts +2 -0
  81. package/dist/apps/funding/index.d.ts.map +1 -0
  82. package/dist/apps/funding/index.js +2 -0
  83. package/dist/apps/funding/index.js.map +1 -0
  84. package/dist/apps/funding/routes/chains.d.ts +169 -0
  85. package/dist/apps/funding/routes/chains.d.ts.map +1 -0
  86. package/dist/apps/funding/routes/chains.js +80 -0
  87. package/dist/apps/funding/routes/chains.js.map +1 -0
  88. package/dist/apps/funding/routes/deposit-addresses.d.ts +1997 -0
  89. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
  90. package/dist/apps/funding/routes/deposit-addresses.js +778 -0
  91. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
  92. package/dist/apps/funding/routes/providers.d.ts +91 -0
  93. package/dist/apps/funding/routes/providers.d.ts.map +1 -0
  94. package/dist/apps/funding/routes/providers.js +56 -0
  95. package/dist/apps/funding/routes/providers.js.map +1 -0
  96. package/dist/apps/funding/routes/quotes.d.ts +426 -0
  97. package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
  98. package/dist/apps/funding/routes/quotes.js +188 -0
  99. package/dist/apps/funding/routes/quotes.js.map +1 -0
  100. package/dist/apps/funding/routes/transfers.d.ts +1999 -0
  101. package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
  102. package/dist/apps/funding/routes/transfers.js +614 -0
  103. package/dist/apps/funding/routes/transfers.js.map +1 -0
  104. package/dist/apps/index.d.ts +1 -0
  105. package/dist/apps/index.d.ts.map +1 -1
  106. package/dist/apps/index.js +1 -0
  107. package/dist/apps/index.js.map +1 -1
  108. package/dist/apps/management/App.d.ts +430 -165
  109. package/dist/apps/management/App.d.ts.map +1 -1
  110. package/dist/apps/management/routes/api-keys.d.ts +298 -21
  111. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  112. package/dist/apps/management/routes/api-keys.js +75 -1
  113. package/dist/apps/management/routes/api-keys.js.map +1 -1
  114. package/dist/apps/management/routes/billing.d.ts +6 -6
  115. package/dist/apps/management/routes/faucet.d.ts +35 -35
  116. package/dist/apps/management/routes/invitations.d.ts +122 -122
  117. package/dist/apps/management/routes/invite-links.d.ts +38 -38
  118. package/dist/apps/management/routes/members.d.ts +69 -69
  119. package/dist/apps/management/routes/orgs.d.ts +99 -99
  120. package/dist/apps/management/routes/projects.d.ts +25 -25
  121. package/dist/apps/management/routes/scopes.d.ts +1 -1
  122. package/dist/apps/management/routes/usage.d.ts +72 -72
  123. package/dist/apps/management/routes/verified-token-requests.d.ts +77 -77
  124. package/dist/apps/management/routes/webhooks.d.ts +128 -128
  125. package/dist/apps/mcp.d.ts +2 -3
  126. package/dist/apps/mcp.d.ts.map +1 -1
  127. package/dist/apps/mcp.js +16 -14
  128. package/dist/apps/mcp.js.map +1 -1
  129. package/dist/apps/metadata.d.ts +4 -0
  130. package/dist/apps/metadata.d.ts.map +1 -1
  131. package/dist/apps/metadata.js +10 -0
  132. package/dist/apps/metadata.js.map +1 -1
  133. package/dist/apps/mpp/App.d.ts +44 -44
  134. package/dist/cloudflare.d.ts +50 -0
  135. package/dist/cloudflare.d.ts.map +1 -1
  136. package/dist/cloudflare.js +178 -0
  137. package/dist/cloudflare.js.map +1 -1
  138. package/dist/db/Db.d.ts +2 -0
  139. package/dist/db/Db.d.ts.map +1 -1
  140. package/dist/db/Db.js +45 -0
  141. package/dist/db/Db.js.map +1 -1
  142. package/dist/db/Schema.d.ts +12 -0
  143. package/dist/db/Schema.d.ts.map +1 -1
  144. package/dist/db/Schema.js +20 -0
  145. package/dist/db/Schema.js.map +1 -1
  146. package/dist/db/tables/fundingDepositAddresses.d.ts +13 -0
  147. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
  148. package/dist/db/tables/fundingDepositAddresses.js +12 -0
  149. package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
  150. package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
  151. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
  152. package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
  153. package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
  154. package/dist/db/tables/fundingDeposits.d.ts +74 -1
  155. package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
  156. package/dist/db/tables/fundingDeposits.js +126 -0
  157. package/dist/db/tables/fundingDeposits.js.map +1 -1
  158. package/dist/internal/Auth.d.ts +15 -11
  159. package/dist/internal/Auth.d.ts.map +1 -1
  160. package/dist/internal/Auth.js +52 -17
  161. package/dist/internal/Auth.js.map +1 -1
  162. package/dist/internal/Cache.js +1 -1
  163. package/dist/internal/Cache.js.map +1 -1
  164. package/dist/internal/EdgeCache.d.ts +2 -2
  165. package/dist/internal/EdgeCache.d.ts.map +1 -1
  166. package/dist/internal/EdgeCache.js +10 -4
  167. package/dist/internal/EdgeCache.js.map +1 -1
  168. package/dist/internal/IpAllowlist.d.ts +15 -0
  169. package/dist/internal/IpAllowlist.d.ts.map +1 -0
  170. package/dist/internal/IpAllowlist.js +27 -0
  171. package/dist/internal/IpAllowlist.js.map +1 -0
  172. package/dist/internal/OpenApi.d.ts +1 -1
  173. package/dist/internal/OpenApi.js +1 -1
  174. package/dist/internal/OpenApi.js.map +1 -1
  175. package/dist/internal/Tidx.d.ts +6 -0
  176. package/dist/internal/Tidx.d.ts.map +1 -1
  177. package/dist/internal/Tidx.js +13 -3
  178. package/dist/internal/Tidx.js.map +1 -1
  179. package/dist/internal/Viem.d.ts +8 -0
  180. package/dist/internal/Viem.d.ts.map +1 -1
  181. package/dist/internal/Viem.js +12 -5
  182. package/dist/internal/Viem.js.map +1 -1
  183. package/dist/internal/funding/Deposit.d.ts +42 -0
  184. package/dist/internal/funding/Deposit.d.ts.map +1 -1
  185. package/dist/internal/funding/Deposit.js +37 -5
  186. package/dist/internal/funding/Deposit.js.map +1 -1
  187. package/dist/internal/funding/Evidence.d.ts +1 -5
  188. package/dist/internal/funding/Evidence.d.ts.map +1 -1
  189. package/dist/internal/funding/Evidence.js +4 -3
  190. package/dist/internal/funding/Evidence.js.map +1 -1
  191. package/dist/internal/funding/Provider.d.ts +102 -0
  192. package/dist/internal/funding/Provider.d.ts.map +1 -1
  193. package/dist/internal/funding/Provider.js +5 -0
  194. package/dist/internal/funding/Provider.js.map +1 -1
  195. package/dist/internal/funding/Reconciliation.d.ts +16 -2
  196. package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
  197. package/dist/internal/funding/Reconciliation.js +348 -54
  198. package/dist/internal/funding/Reconciliation.js.map +1 -1
  199. package/dist/internal/funding/SourceObservation.d.ts +108 -0
  200. package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
  201. package/dist/internal/funding/SourceObservation.js +143 -0
  202. package/dist/internal/funding/SourceObservation.js.map +1 -0
  203. package/dist/internal/funding/Subsidy.d.ts +2 -2
  204. package/dist/internal/funding/Subsidy.d.ts.map +1 -1
  205. package/dist/internal/funding/Subsidy.js +1 -1
  206. package/dist/internal/funding/Subsidy.js.map +1 -1
  207. package/dist/internal/funding/index.d.ts +1 -0
  208. package/dist/internal/funding/index.d.ts.map +1 -1
  209. package/dist/internal/funding/index.js +1 -0
  210. package/dist/internal/funding/index.js.map +1 -1
  211. package/dist/internal/funding/providers/relay.d.ts +43 -1
  212. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  213. package/dist/internal/funding/providers/relay.js +438 -27
  214. package/dist/internal/funding/providers/relay.js.map +1 -1
  215. package/package.json +3 -3
  216. package/src/ApiKey.test.ts +35 -0
  217. package/src/ApiKey.ts +27 -0
  218. package/src/ApiKeys.test.ts +29 -0
  219. package/src/ApiKeys.ts +11 -3
  220. package/src/App.test.ts +55 -5
  221. package/src/App.ts +79 -5
  222. package/src/Client.test-d.ts +203 -28
  223. package/src/admin/apps/api-keys.test.ts +25 -1
  224. package/src/admin/apps/api-keys.ts +6 -2
  225. package/src/admin/ui.generated.ts +67 -67
  226. package/src/analytics/Analytics.ts +4 -0
  227. package/src/apps/composition.test.ts +130 -3
  228. package/src/apps/data/App.test.ts +0 -12
  229. package/src/apps/data/App.ts +4 -8
  230. package/src/apps/data/routes/balances.test.ts +42 -0
  231. package/src/apps/data/routes/balances.ts +218 -85
  232. package/src/apps/data/routes/earn.test.ts +363 -0
  233. package/src/apps/data/routes/earn.ts +1141 -3
  234. package/src/apps/data/routes/indexer.ts +2 -1
  235. package/src/apps/data/routes/rpc.ts +2 -0
  236. package/src/apps/data/routes/tokens.test.ts +6 -8
  237. package/src/apps/funding/Access.ts +26 -0
  238. package/src/apps/funding/App.test.ts +78 -0
  239. package/src/apps/funding/App.ts +78 -0
  240. package/src/apps/funding/Inventory.ts +30 -0
  241. package/src/apps/funding/ProviderAttempts.ts +26 -0
  242. package/src/apps/funding/index.ts +1 -0
  243. package/src/apps/funding/routes/chains.test.ts +78 -0
  244. package/src/apps/funding/routes/chains.ts +101 -0
  245. package/src/apps/funding/routes/deposit-addresses.test.ts +1649 -0
  246. package/src/apps/funding/routes/deposit-addresses.ts +932 -0
  247. package/src/apps/funding/routes/providers.test.ts +165 -0
  248. package/src/apps/funding/routes/providers.ts +73 -0
  249. package/src/apps/funding/routes/quotes.test.ts +511 -0
  250. package/src/apps/funding/routes/quotes.ts +205 -0
  251. package/src/apps/funding/routes/transfers.test.ts +1236 -0
  252. package/src/apps/funding/routes/transfers.ts +704 -0
  253. package/src/apps/index.ts +1 -0
  254. package/src/apps/management/routes/api-keys.test.ts +98 -0
  255. package/src/apps/management/routes/api-keys.ts +96 -1
  256. package/src/apps/mcp.test.ts +17 -2
  257. package/src/apps/mcp.ts +18 -13
  258. package/src/apps/metadata.ts +12 -0
  259. package/src/apps/mpp/internal/Policy.test.ts +7 -1
  260. package/src/cloudflare.ts +279 -0
  261. package/src/db/Db.ts +71 -0
  262. package/src/db/Schema.ts +24 -0
  263. package/src/db/tables/fundingDepositAddresses.ts +28 -0
  264. package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
  265. package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
  266. package/src/db/tables/fundingDeposits.test.ts +239 -0
  267. package/src/db/tables/fundingDeposits.ts +214 -1
  268. package/src/internal/Auth.test.ts +205 -5
  269. package/src/internal/Auth.ts +63 -19
  270. package/src/internal/Cache.test.ts +37 -3
  271. package/src/internal/Cache.ts +1 -1
  272. package/src/internal/EdgeCache.test.ts +7 -7
  273. package/src/internal/EdgeCache.ts +13 -4
  274. package/src/internal/IpAllowlist.test.ts +52 -0
  275. package/src/internal/IpAllowlist.ts +39 -0
  276. package/src/internal/Log.test.ts +7 -1
  277. package/src/internal/OpenApi.ts +1 -1
  278. package/src/internal/Tidx.test.ts +28 -0
  279. package/src/internal/Tidx.ts +19 -3
  280. package/src/internal/Viem.test.ts +28 -0
  281. package/src/internal/Viem.ts +21 -5
  282. package/src/internal/funding/Deposit.ts +74 -5
  283. package/src/internal/funding/Evidence.test.ts +2 -2
  284. package/src/internal/funding/Evidence.ts +4 -9
  285. package/src/internal/funding/Provider.ts +115 -0
  286. package/src/internal/funding/Reconciliation.local.test.ts +7 -0
  287. package/src/internal/funding/Reconciliation.test.ts +1364 -109
  288. package/src/internal/funding/Reconciliation.ts +467 -88
  289. package/src/internal/funding/SourceObservation.test.ts +301 -0
  290. package/src/internal/funding/SourceObservation.ts +267 -0
  291. package/src/internal/funding/Subsidy.ts +2 -2
  292. package/src/internal/funding/index.test.ts +1 -0
  293. package/src/internal/funding/index.ts +1 -0
  294. package/src/internal/funding/providers/relay.test.ts +94 -4
  295. package/src/internal/funding/providers/relay.ts +533 -27
  296. package/dist/apps/data/routes/funding.d.ts.map +0 -1
  297. package/dist/apps/data/routes/funding.js +0 -1557
  298. package/dist/apps/data/routes/funding.js.map +0 -1
  299. package/src/apps/data/routes/funding.test.ts +0 -2994
  300. package/src/apps/data/routes/funding.ts +0 -1821
@@ -1,13 +1,45 @@
1
- import { Address, Hash, Hex } from 'ox';
1
+ import { AbiEvent, AbiFunction, Address, Hash, Hex, RpcTransport } from 'ox';
2
+ import { createClient, custom, fallback } from 'viem';
3
+ import { getBlockNumber, getLogs, getTransaction, getTransactionReceipt } from 'viem/actions';
2
4
  import * as z from 'zod/mini';
3
5
  import * as Db from '../../../db/Db.js';
4
6
  import * as FundingDepositAddresses from '../../../db/tables/fundingDepositAddresses.js';
7
+ import * as FundingDepositRequestObservations from '../../../db/tables/fundingDepositRequestObservations.js';
8
+ import * as Viem from '../../Viem.js';
9
+ import * as FundingCatalog from '../Catalog.js';
5
10
  import * as FundingChain from '../Chain.js';
11
+ import * as Evidence from '../Evidence.js';
6
12
  import * as FundingProvider from '../Provider.js';
7
13
  import * as Subsidy from '../Subsidy.js';
8
14
  const defaultBaseUrl = 'https://api.relay.link';
9
15
  const defaultFetch = (input, init) => globalThis.fetch(input, init);
10
- const webhookSignatureToleranceSeconds = 300;
16
+ const deliveryBackfillBlocks = 256n;
17
+ const relayChainsCacheTtlMs = 60_000;
18
+ const sourceSweepForwardBlocks = 256n;
19
+ const depositErc20FullAllowance = AbiFunction.from('function depositErc20(address depositor,address token,bytes32 id)');
20
+ const depositErc20WithAmount = AbiFunction.from('function depositErc20(address depositor,address token,uint256 amount,bytes32 id)');
21
+ const relayErc20DepositEvent = AbiEvent.from('event RelayErc20Deposit(address from, address token, uint256 amount, bytes32 id)');
22
+ const relayTransfer = AbiFunction.from('function transfer(address recipient,uint256 amount)');
23
+ const transferEvent = AbiEvent.from('event Transfer(address indexed from, address indexed to, uint256 value)');
24
+ const webhookSignatureToleranceMs = 300_000;
25
+ const deliveryObservationStatuses = [
26
+ 'depositing',
27
+ 'pending',
28
+ 'submitted',
29
+ 'success',
30
+ 'waiting',
31
+ ];
32
+ // Bound provider statuses before using them as metric tags.
33
+ const webhookStatuses = [
34
+ 'depositing',
35
+ 'failure',
36
+ 'fallback',
37
+ 'pending',
38
+ 'refund',
39
+ 'submitted',
40
+ 'success',
41
+ 'waiting',
42
+ ];
11
43
  var schema;
12
44
  (function (schema) {
13
45
  const Decimal = z.union([
@@ -67,8 +99,35 @@ var schema;
67
99
  requestId: z.optional(z.string()),
68
100
  })),
69
101
  });
102
+ schema.Chains = z.object({
103
+ chains: z.array(z.object({
104
+ id: Integer,
105
+ protocol: z.optional(z.object({
106
+ v2: z.optional(z.object({
107
+ depository: z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/)),
108
+ })),
109
+ })),
110
+ solverAddresses: z.optional(z.array(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/)))),
111
+ })),
112
+ });
113
+ schema.DeliveryObservation = z.object({
114
+ relay: z.object({
115
+ orderId: z.string().check(z.regex(/^0x[0-9a-fA-F]{64}$/)),
116
+ sourceDepository: z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/)),
117
+ sourceObservedAt: z.iso.datetime(),
118
+ sourceSweepTransactionHash: z.string().check(z.regex(/^0x[0-9a-fA-F]{64}$/)),
119
+ }),
120
+ });
121
+ schema.RequestCorrelation = z.object({
122
+ relay: z.object({
123
+ depositor: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
124
+ depository: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
125
+ orderId: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{64}$/))),
126
+ solver: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
127
+ }),
128
+ });
70
129
  const RequestTransaction = z.object({
71
- hash: z.string().check(z.minLength(1)),
130
+ txHash: z.string().check(z.minLength(1)),
72
131
  });
73
132
  schema.Requests = z.object({
74
133
  continuation: z.optional(z.string().check(z.minLength(1))),
@@ -77,10 +136,22 @@ var schema;
77
136
  data: z.optional(z.object({
78
137
  outTxs: z.optional(z.array(RequestTransaction)),
79
138
  })),
80
- depositAddress: z.optional(z.object({
81
- depositTxHash: z.optional(z.string().check(z.minLength(1))),
82
- })),
139
+ depositAddress: z.optional(z.nullable(z.object({
140
+ depositTxHash: z.optional(z.nullable(z.string().check(z.minLength(1)))),
141
+ }))),
83
142
  id: z.string().check(z.minLength(1)),
143
+ protocol: z.optional(z.nullable(z.object({
144
+ deposit: z.optional(z.object({
145
+ origin: z.optional(z.object({
146
+ depositor: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
147
+ depository: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
148
+ })),
149
+ })),
150
+ orderId: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{64}$/))),
151
+ solver: z.optional(z.object({
152
+ address: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
153
+ })),
154
+ }))),
84
155
  status: z.string().check(z.minLength(1)),
85
156
  updatedAt: z.iso.datetime(),
86
157
  })),
@@ -89,10 +160,19 @@ var schema;
89
160
  data: z.object({
90
161
  depositAddress: z.nullable(z.object({ address: z.string().check(z.minLength(1)) })),
91
162
  requestId: z.optional(z.string().check(z.minLength(1))),
163
+ status: z.string().check(z.minLength(1)),
164
+ updatedAt: z.number().check(z.int(), z.nonnegative()),
92
165
  }),
93
166
  event: z.literal('request.status.updated'),
94
167
  });
95
168
  })(schema || (schema = {}));
169
+ /** Validates a Relay request-list response and reports payload drift with field-level details. */
170
+ export function parseDepositAddressRequests(value) {
171
+ const parsed = schema.Requests.safeParse(value);
172
+ if (!parsed.success)
173
+ throw new FundingProvider.ProviderPayloadError(z.prettifyError(parsed.error));
174
+ return parsed.data;
175
+ }
96
176
  /** Creates a Relay funding provider backed by `/quote/v2`. */
97
177
  export function relay(options = {}) {
98
178
  if ((options.userAddress !== undefined &&
@@ -103,6 +183,15 @@ export function relay(options = {}) {
103
183
  const endpoint = quoteEndpoint(options);
104
184
  const fetch = options.fetch ?? defaultFetch;
105
185
  const now = options.now ?? (() => new Date());
186
+ const observeDepositAddressRequest = options.observation
187
+ ? createDepositAddressObserver({
188
+ ...options.observation,
189
+ ...(options.apiKey ? { apiKey: options.apiKey } : {}),
190
+ ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),
191
+ fetch,
192
+ now,
193
+ })
194
+ : undefined;
106
195
  const webhook = (() => {
107
196
  if (!options.apiKey || !options.webhook)
108
197
  return undefined;
@@ -110,6 +199,7 @@ export function relay(options = {}) {
110
199
  const verify = (input) => verifyWebhook(webhookOptions(input, apiKey));
111
200
  return {
112
201
  async receive(input) {
202
+ const receivedAt = now().toISOString();
113
203
  const hint = (() => {
114
204
  try {
115
205
  return verify(input);
@@ -125,7 +215,8 @@ export function relay(options = {}) {
125
215
  webhook.onResult?.('ignored');
126
216
  return { type: 'ignored' };
127
217
  }
128
- const address = await FundingDepositAddresses.getByProviderAddress(Db.get(webhook.db), {
218
+ const database = Db.get(webhook.db);
219
+ const address = await FundingDepositAddresses.getByProviderAddress(database, {
129
220
  address: hint.address,
130
221
  providerId: 'relay',
131
222
  });
@@ -133,11 +224,33 @@ export function relay(options = {}) {
133
224
  webhook.onResult?.('unknown_address');
134
225
  return { type: 'unknown_address' };
135
226
  }
227
+ if (hint.requestId)
228
+ await FundingDepositRequestObservations.observeWebhook(database, {
229
+ depositAddressId: address.id,
230
+ providerRequestId: hint.requestId,
231
+ receivedAt,
232
+ }).catch((cause) => {
233
+ webhook.onResult?.('observation_failed');
234
+ throw cause;
235
+ });
136
236
  try {
237
+ const webhookTiming = (() => {
238
+ if (!hint.providerStatus || !hint.providerUpdatedAt || !hint.requestId || !hint.sentAt)
239
+ return undefined;
240
+ return {
241
+ enqueuedAt: now().toISOString(),
242
+ providerStatus: hint.providerStatus,
243
+ providerUpdatedAt: hint.providerUpdatedAt,
244
+ receivedAt,
245
+ sentAt: hint.sentAt,
246
+ };
247
+ })();
137
248
  await webhook.dispatch({
138
249
  addressId: address.id,
139
250
  ...(hint.requestId ? { requestId: hint.requestId } : {}),
251
+ trigger: 'webhook',
140
252
  type: 'funding:deposit-address:reconcile',
253
+ ...(webhookTiming ? { webhookTiming } : {}),
141
254
  });
142
255
  }
143
256
  catch (cause) {
@@ -280,10 +393,9 @@ export function relay(options = {}) {
280
393
  const requestUrl = requestsEndpoint(options);
281
394
  if (requestId)
282
395
  requestUrl.searchParams.set('id', requestId);
283
- else {
396
+ else
284
397
  requestUrl.searchParams.set('depositAddress', address);
285
- requestUrl.searchParams.set('includeChildRequests', 'true');
286
- }
398
+ requestUrl.searchParams.set('includeChildRequests', 'true');
287
399
  requestUrl.searchParams.set('limit', '50');
288
400
  requestUrl.searchParams.set('sortBy', 'updatedAt');
289
401
  requestUrl.searchParams.set('sortDirection', 'desc');
@@ -295,23 +407,43 @@ export function relay(options = {}) {
295
407
  method: 'GET',
296
408
  signal,
297
409
  });
298
- const parsed = schema.Requests.safeParse(response);
299
- if (!parsed.success)
300
- throw new FundingProvider.ProviderPayloadError();
410
+ const parsed = parseDepositAddressRequests(response);
301
411
  return {
302
- ...(parsed.data.continuation ? { continuation: parsed.data.continuation } : {}),
303
- requests: parsed.data.requests.map((request) => ({
304
- createdAt: new Date(request.createdAt).toISOString(),
305
- destinationTransactionHashes: request.data?.outTxs?.map((tx) => tx.hash) ?? [],
306
- id: request.id,
307
- sourceTransactionHashes: request.depositAddress?.depositTxHash
308
- ? [request.depositAddress.depositTxHash]
309
- : [],
310
- status: request.status,
311
- updatedAt: new Date(request.updatedAt).toISOString(),
312
- })),
412
+ ...(parsed.continuation ? { continuation: parsed.continuation } : {}),
413
+ requests: parsed.requests.map((request) => {
414
+ const correlation = (() => {
415
+ const { protocol } = request;
416
+ if (!protocol)
417
+ return undefined;
418
+ const relay = {
419
+ ...(protocol.deposit?.origin?.depositor
420
+ ? { depositor: protocol.deposit.origin.depositor }
421
+ : {}),
422
+ ...(protocol.deposit?.origin?.depository
423
+ ? { depository: protocol.deposit.origin.depository }
424
+ : {}),
425
+ ...(protocol.orderId ? { orderId: protocol.orderId } : {}),
426
+ ...(protocol.solver?.address ? { solver: protocol.solver.address } : {}),
427
+ };
428
+ if (Object.keys(relay).length === 0)
429
+ return undefined;
430
+ return schema.RequestCorrelation.parse({ relay });
431
+ })();
432
+ return {
433
+ createdAt: new Date(request.createdAt).toISOString(),
434
+ destinationTransactionHashes: request.data?.outTxs?.map((tx) => tx.txHash) ?? [],
435
+ id: request.id,
436
+ ...(correlation ? { providerState: correlation } : {}),
437
+ sourceTransactionHashes: request.depositAddress?.depositTxHash
438
+ ? [request.depositAddress.depositTxHash]
439
+ : [],
440
+ status: request.status,
441
+ updatedAt: new Date(request.updatedAt).toISOString(),
442
+ };
443
+ }),
313
444
  };
314
445
  },
446
+ ...(observeDepositAddressRequest ? { observeDepositAddressRequest } : {}),
315
447
  async prepareTransfer({ candidate, method, mode, recipient, sender, slippageBps }, signal) {
316
448
  // Catalog capabilities gate deposit addresses and exact destination
317
449
  // off this adapter; refuse them defensively if one slips through.
@@ -390,12 +522,277 @@ export function relay(options = {}) {
390
522
  },
391
523
  });
392
524
  }
525
+ function createDepositAddressObserver(options) {
526
+ let chainsExpiresAt = 0;
527
+ let chainsPromise;
528
+ let urlsExpiresAt = 0;
529
+ let urlsPromise;
530
+ const getChains = (signal) => {
531
+ if (chainsPromise && options.now().getTime() < chainsExpiresAt)
532
+ return chainsPromise;
533
+ chainsExpiresAt = options.now().getTime() + relayChainsCacheTtlMs;
534
+ chainsPromise = FundingProvider.requestJson(chainsEndpoint(options), {
535
+ fetch: options.fetch,
536
+ headers: options.apiKey ? { 'x-api-key': options.apiKey } : undefined,
537
+ signal,
538
+ })
539
+ .then((value) => schema.Chains.parse(value).chains)
540
+ .catch((cause) => {
541
+ chainsExpiresAt = 0;
542
+ chainsPromise = undefined;
543
+ throw cause;
544
+ });
545
+ return chainsPromise;
546
+ };
547
+ const getUrls = () => {
548
+ if (urlsPromise && options.now().getTime() < urlsExpiresAt)
549
+ return urlsPromise;
550
+ urlsExpiresAt = options.now().getTime() + relayChainsCacheTtlMs;
551
+ urlsPromise = FundingCatalog.read(Db.get(options.db))
552
+ .then((catalog) => new Map([...catalog.chainsByKey.values()].map((chain) => [
553
+ chain.id,
554
+ Evidence.resolveRpcUrls({
555
+ chain,
556
+ defaultChainId: options.defaultChainId ?? Viem.defaultChainId,
557
+ rpcUrlEnv: options.rpcUrlEnv,
558
+ }),
559
+ ])))
560
+ .catch((cause) => {
561
+ urlsExpiresAt = 0;
562
+ urlsPromise = undefined;
563
+ throw cause;
564
+ });
565
+ return urlsPromise;
566
+ };
567
+ return async (parameters, signal) => {
568
+ if (parameters.request &&
569
+ !deliveryObservationStatuses.includes(parameters.request.status))
570
+ return { type: 'unsupported' };
571
+ if (parameters.sourceChain.kind !== 'evm' || parameters.destinationChain.kind !== 'evm')
572
+ return { type: 'unsupported' };
573
+ if (!Address.validate(parameters.address, { strict: false }) ||
574
+ !Address.validate(parameters.destinationToken.address, { strict: false }) ||
575
+ !Address.validate(parameters.recipient, { strict: false }) ||
576
+ !Address.validate(parameters.refundAddress, { strict: false }) ||
577
+ !Address.validate(parameters.sourceToken.address, { strict: false }) ||
578
+ !Hash.validate(parameters.source.transactionHash))
579
+ return { type: 'unsupported' };
580
+ const [chains, urls] = await Promise.all([getChains(signal), getUrls()]);
581
+ const sourceChain = relayEvmChain(chains, parameters.sourceChain);
582
+ const destinationChain = relayEvmChain(chains, parameters.destinationChain);
583
+ const sourceUrls = urls.get(parameters.sourceChain.id);
584
+ const destinationUrls = urls.get(parameters.destinationChain.id);
585
+ const depository = sourceChain?.protocol?.v2?.depository;
586
+ const solvers = destinationChain?.solverAddresses;
587
+ if (!depository ||
588
+ !solvers ||
589
+ solvers.length === 0 ||
590
+ !sourceUrls?.length ||
591
+ !destinationUrls?.length)
592
+ return { type: 'unsupported' };
593
+ const parsedCorrelation = schema.RequestCorrelation.safeParse(parameters.request?.providerState);
594
+ const correlation = parsedCorrelation.success ? parsedCorrelation.data.relay : undefined;
595
+ if ((correlation?.depository &&
596
+ !Address.isEqual(correlation.depository, depository)) ||
597
+ (correlation?.solver &&
598
+ !solvers.some((solver) => Address.isEqual(solver, correlation.solver))))
599
+ return { type: 'unsupported' };
600
+ const depositor = (correlation?.depositor ?? parameters.refundAddress);
601
+ const stored = schema.DeliveryObservation.safeParse(parameters.providerState);
602
+ const sourceObservation = await (async () => {
603
+ if (stored.success)
604
+ return stored.data.relay;
605
+ const sourceClient = jsonRpcClient({ fetch: options.fetch, urls: sourceUrls });
606
+ const sourceReceipt = await getTransactionReceipt(sourceClient, {
607
+ hash: parameters.source.transactionHash,
608
+ });
609
+ if (sourceReceipt.status !== 'success' || sourceReceipt.blockNumber === null)
610
+ return undefined;
611
+ const sourceHead = await getBlockNumber(sourceClient, { cacheTime: 0 });
612
+ const sweepLogs = await getLogs(sourceClient, {
613
+ address: depository,
614
+ event: relayErc20DepositEvent,
615
+ fromBlock: sourceReceipt.blockNumber,
616
+ toBlock: minimum(sourceHead, sourceReceipt.blockNumber + sourceSweepForwardBlocks),
617
+ });
618
+ const observations = [];
619
+ for (const log of sweepLogs) {
620
+ if (!log.transactionHash)
621
+ continue;
622
+ const event = decodeEvent(relayErc20DepositEvent, log);
623
+ if (!event ||
624
+ !Address.isEqual(event.from, depositor) ||
625
+ !Address.isEqual(event.token, parameters.sourceToken.address) ||
626
+ event.amount !== BigInt(parameters.source.amount) ||
627
+ (correlation?.orderId && !Hex.isEqual(event.id, correlation.orderId)))
628
+ continue;
629
+ const transaction = await getTransaction(sourceClient, { hash: log.transactionHash });
630
+ const call = decodeRelayErc20Deposit(transaction.input);
631
+ if (!call ||
632
+ !Address.isEqual(transaction.from, parameters.address) ||
633
+ !transaction.to ||
634
+ !Address.isEqual(transaction.to, depository) ||
635
+ !Address.isEqual(call.depositor, event.from) ||
636
+ !Address.isEqual(call.token, event.token) ||
637
+ (call.amount !== undefined && call.amount !== event.amount) ||
638
+ !Hex.isEqual(call.id, event.id))
639
+ continue;
640
+ observations.push({
641
+ orderId: event.id,
642
+ sourceDepository: depository,
643
+ sourceObservedAt: options.now().toISOString(),
644
+ sourceSweepTransactionHash: log.transactionHash,
645
+ });
646
+ }
647
+ return observations.length === 1 ? observations[0] : undefined;
648
+ })();
649
+ if (!sourceObservation)
650
+ return { type: 'pending' };
651
+ const destinationClient = jsonRpcClient({ fetch: options.fetch, urls: destinationUrls });
652
+ const destinationHead = await getBlockNumber(destinationClient, { cacheTime: 0 });
653
+ const destinationLogs = await getLogs(destinationClient, {
654
+ address: parameters.destinationToken.address,
655
+ args: { to: parameters.recipient },
656
+ event: transferEvent,
657
+ fromBlock: destinationHead > deliveryBackfillBlocks ? destinationHead - deliveryBackfillBlocks : 0n,
658
+ toBlock: destinationHead,
659
+ });
660
+ const solverAddresses = (correlation?.solver ? [correlation.solver] : solvers).map((address) => address);
661
+ const correlatedTransactionHashes = [];
662
+ const uncorrelatedTransactionHashes = [];
663
+ for (const log of destinationLogs) {
664
+ if (!log.transactionHash ||
665
+ correlatedTransactionHashes.includes(log.transactionHash) ||
666
+ uncorrelatedTransactionHashes.includes(log.transactionHash))
667
+ continue;
668
+ const event = decodeEvent(transferEvent, log);
669
+ if (!event ||
670
+ event.value === 0n ||
671
+ !Address.isEqual(event.to, parameters.recipient) ||
672
+ !solverAddresses.some((address) => Address.isEqual(address, event.from)))
673
+ continue;
674
+ const transaction = await getTransaction(destinationClient, { hash: log.transactionHash });
675
+ const call = decodeRelayTransfer(transaction.input);
676
+ if (!call ||
677
+ !solverAddresses.some((address) => Address.isEqual(address, transaction.from)) ||
678
+ !Address.isEqual(event.from, transaction.from) ||
679
+ !transaction.to ||
680
+ !Address.isEqual(transaction.to, parameters.destinationToken.address) ||
681
+ !Address.isEqual(call.recipient, event.to) ||
682
+ call.amount !== event.value ||
683
+ (call.orderId && !Hex.isEqual(call.orderId, sourceObservation.orderId)))
684
+ continue;
685
+ if (call.orderId)
686
+ correlatedTransactionHashes.push(log.transactionHash);
687
+ else
688
+ uncorrelatedTransactionHashes.push(log.transactionHash);
689
+ }
690
+ const destinationTransactionHashes = (() => {
691
+ if (correlatedTransactionHashes.length > 0)
692
+ return correlatedTransactionHashes;
693
+ // Provider-independent completion requires the Relay order id to bind both chain legs.
694
+ if (!parameters.request)
695
+ return [];
696
+ if (uncorrelatedTransactionHashes.length === 1)
697
+ return uncorrelatedTransactionHashes;
698
+ return [];
699
+ })();
700
+ const providerState = { relay: sourceObservation };
701
+ if (destinationTransactionHashes.length === 0)
702
+ return {
703
+ providerState,
704
+ providerTransactionHashes: [sourceObservation.sourceSweepTransactionHash],
705
+ type: 'pending',
706
+ };
707
+ return {
708
+ destinationTransactionHashes,
709
+ providerState: {
710
+ relay: {
711
+ ...sourceObservation,
712
+ destinationObservedAt: options.now().toISOString(),
713
+ },
714
+ },
715
+ providerTransactionHashes: [
716
+ sourceObservation.sourceSweepTransactionHash,
717
+ ...destinationTransactionHashes,
718
+ ],
719
+ type: 'verified',
720
+ };
721
+ };
722
+ }
723
+ function chainsEndpoint(options) {
724
+ const endpoint = new URL(options.baseUrl ?? defaultBaseUrl);
725
+ endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/chains`;
726
+ return endpoint;
727
+ }
728
+ function decodeRelayErc20Deposit(data) {
729
+ const fullAllowanceLength = 2 + 2 * (4 + 32 * 3);
730
+ const withAmountLength = 2 + 2 * (4 + 32 * 4);
731
+ try {
732
+ if (data.length === withAmountLength) {
733
+ const [depositor, token, amount, id] = AbiFunction.decodeData(depositErc20WithAmount, data);
734
+ return { amount, depositor, id, token };
735
+ }
736
+ if (data.length === fullAllowanceLength) {
737
+ const [depositor, token, id] = AbiFunction.decodeData(depositErc20FullAllowance, data);
738
+ return { depositor, id, token };
739
+ }
740
+ return undefined;
741
+ }
742
+ catch {
743
+ return undefined;
744
+ }
745
+ }
746
+ function decodeRelayTransfer(data) {
747
+ const encodedLength = 2 + 2 * (4 + 32 * 2);
748
+ if (data.length !== encodedLength && data.length !== encodedLength + 64)
749
+ return undefined;
750
+ try {
751
+ const [recipient, amount] = AbiFunction.decodeData(relayTransfer, data.slice(0, encodedLength));
752
+ return {
753
+ amount,
754
+ ...(data.length > encodedLength
755
+ ? { orderId: `0x${data.slice(encodedLength)}` }
756
+ : {}),
757
+ recipient,
758
+ };
759
+ }
760
+ catch {
761
+ return undefined;
762
+ }
763
+ }
764
+ function decodeEvent(abiEvent, log) {
765
+ try {
766
+ return AbiEvent.decode(abiEvent, log);
767
+ }
768
+ catch {
769
+ return undefined;
770
+ }
771
+ }
772
+ function jsonRpcClient(options) {
773
+ return createClient({
774
+ transport: fallback(options.urls.map((url) => {
775
+ const transport = RpcTransport.fromHttp(url, { fetchFn: options.fetch });
776
+ return custom(transport, { retryCount: 0 });
777
+ })),
778
+ });
779
+ }
780
+ function minimum(left, right) {
781
+ return left < right ? left : right;
782
+ }
783
+ function relayEvmChain(chains, chain) {
784
+ if (chain.kind !== 'evm')
785
+ return undefined;
786
+ const match = /^eip155:(\d+)$/.exec(chain.id);
787
+ if (!match?.[1])
788
+ return undefined;
789
+ return chains.find((candidate) => candidate.id === Number(match[1]));
790
+ }
393
791
  function verifyWebhook(options) {
394
792
  const timestamp = Number(options.timestamp);
395
793
  if (!Number.isSafeInteger(timestamp))
396
794
  throw new WebhookSignatureError();
397
- const now = Math.floor(Date.now() / 1_000);
398
- if (Math.abs(now - timestamp) > webhookSignatureToleranceSeconds)
795
+ if (Math.abs(Date.now() - timestamp) > webhookSignatureToleranceMs)
399
796
  throw new WebhookSignatureError();
400
797
  if (!/^[0-9a-fA-F]{64}$/.test(options.signature))
401
798
  throw new WebhookSignatureError();
@@ -415,7 +812,10 @@ function verifyWebhook(options) {
415
812
  throw new WebhookPayloadError();
416
813
  return {
417
814
  address: parsed.data.data.depositAddress?.address,
815
+ providerStatus: webhookStatus(parsed.data.data.status),
816
+ providerUpdatedAt: webhookTimestamp(parsed.data.data.updatedAt),
418
817
  requestId: parsed.data.data.requestId,
818
+ sentAt: webhookTimestamp(timestamp),
419
819
  };
420
820
  }
421
821
  function webhookOptions(input, secret) {
@@ -432,7 +832,7 @@ function quoteEndpoint(options) {
432
832
  }
433
833
  function requestsEndpoint(options) {
434
834
  const endpoint = new URL(options.baseUrl ?? defaultBaseUrl);
435
- endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/requests/v2`;
835
+ endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/requests/v3`;
436
836
  return endpoint;
437
837
  }
438
838
  function timingSafeEqual(left, right) {
@@ -443,6 +843,17 @@ function timingSafeEqual(left, right) {
443
843
  mismatch |= left.charCodeAt(index) ^ right.charCodeAt(index);
444
844
  return mismatch === 0;
445
845
  }
846
+ function webhookStatus(status) {
847
+ return webhookStatuses.includes(status) ? status : 'unknown';
848
+ }
849
+ function webhookTimestamp(value) {
850
+ try {
851
+ return new Date(value).toISOString();
852
+ }
853
+ catch {
854
+ throw new WebhookPayloadError();
855
+ }
856
+ }
446
857
  function relayChainId(chain) {
447
858
  if (chain.id.startsWith('eip155:'))
448
859
  return Number(FundingChain.eip155Id(chain));