tapimo 0.7.2 → 0.8.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 (294) 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 +20434 -19484
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +62 -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 +168 -168
  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 +10305 -12785
  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 +24 -24
  35. package/dist/apps/data/routes/addresses.d.ts +288 -288
  36. package/dist/apps/data/routes/balances.d.ts +6 -6
  37. package/dist/apps/data/routes/blocks.d.ts +39 -39
  38. package/dist/apps/data/routes/coingecko.d.ts +170 -170
  39. package/dist/apps/data/routes/earn.d.ts +436 -6
  40. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  41. package/dist/apps/data/routes/earn.js +631 -4
  42. package/dist/apps/data/routes/earn.js.map +1 -1
  43. package/dist/apps/data/routes/exchanges.d.ts +10 -10
  44. package/dist/apps/data/routes/fee-amm.d.ts +2 -2
  45. package/dist/apps/data/routes/indexer.d.ts +6 -6
  46. package/dist/apps/data/routes/indexer.js +4 -1
  47. package/dist/apps/data/routes/indexer.js.map +1 -1
  48. package/dist/apps/data/routes/receipts.d.ts +235 -235
  49. package/dist/apps/data/routes/rpc.d.ts +1 -1
  50. package/dist/apps/data/routes/rpc.js +4 -1
  51. package/dist/apps/data/routes/rpc.js.map +1 -1
  52. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  53. package/dist/apps/data/routes/tokens.d.ts +6 -6
  54. package/dist/apps/data/routes/transactions.d.ts +2 -2
  55. package/dist/apps/data/routes/transfers.d.ts +1 -1
  56. package/dist/apps/data/routes/valuation.d.ts +1 -1
  57. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  58. package/dist/apps/data/routes/webhooks.d.ts +9 -9
  59. package/dist/apps/data/routes/zones.d.ts +14 -14
  60. package/dist/apps/funding/Access.d.ts +16 -0
  61. package/dist/apps/funding/Access.d.ts.map +1 -0
  62. package/dist/apps/funding/Access.js +22 -0
  63. package/dist/apps/funding/Access.js.map +1 -0
  64. package/dist/apps/{data/routes/funding.d.ts → funding/App.d.ts} +1717 -3035
  65. package/dist/apps/funding/App.d.ts.map +1 -0
  66. package/dist/apps/funding/App.js +46 -0
  67. package/dist/apps/funding/App.js.map +1 -0
  68. package/dist/apps/funding/Inventory.d.ts +5 -0
  69. package/dist/apps/funding/Inventory.d.ts.map +1 -0
  70. package/dist/apps/funding/Inventory.js +25 -0
  71. package/dist/apps/funding/Inventory.js.map +1 -0
  72. package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
  73. package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
  74. package/dist/apps/funding/ProviderAttempts.js +24 -0
  75. package/dist/apps/funding/ProviderAttempts.js.map +1 -0
  76. package/dist/apps/funding/index.d.ts +2 -0
  77. package/dist/apps/funding/index.d.ts.map +1 -0
  78. package/dist/apps/funding/index.js +2 -0
  79. package/dist/apps/funding/index.js.map +1 -0
  80. package/dist/apps/funding/routes/chains.d.ts +169 -0
  81. package/dist/apps/funding/routes/chains.d.ts.map +1 -0
  82. package/dist/apps/funding/routes/chains.js +80 -0
  83. package/dist/apps/funding/routes/chains.js.map +1 -0
  84. package/dist/apps/funding/routes/deposit-addresses.d.ts +2388 -0
  85. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
  86. package/dist/apps/funding/routes/deposit-addresses.js +860 -0
  87. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
  88. package/dist/apps/funding/routes/providers.d.ts +91 -0
  89. package/dist/apps/funding/routes/providers.d.ts.map +1 -0
  90. package/dist/apps/funding/routes/providers.js +56 -0
  91. package/dist/apps/funding/routes/providers.js.map +1 -0
  92. package/dist/apps/funding/routes/quotes.d.ts +426 -0
  93. package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
  94. package/dist/apps/funding/routes/quotes.js +188 -0
  95. package/dist/apps/funding/routes/quotes.js.map +1 -0
  96. package/dist/apps/funding/routes/transfers.d.ts +1999 -0
  97. package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
  98. package/dist/apps/funding/routes/transfers.js +614 -0
  99. package/dist/apps/funding/routes/transfers.js.map +1 -0
  100. package/dist/apps/index.d.ts +1 -0
  101. package/dist/apps/index.d.ts.map +1 -1
  102. package/dist/apps/index.js +1 -0
  103. package/dist/apps/index.js.map +1 -1
  104. package/dist/apps/management/App.d.ts +430 -165
  105. package/dist/apps/management/App.d.ts.map +1 -1
  106. package/dist/apps/management/routes/api-keys.d.ts +298 -21
  107. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  108. package/dist/apps/management/routes/api-keys.js +75 -1
  109. package/dist/apps/management/routes/api-keys.js.map +1 -1
  110. package/dist/apps/management/routes/billing.d.ts +6 -6
  111. package/dist/apps/management/routes/faucet.d.ts +35 -35
  112. package/dist/apps/management/routes/invitations.d.ts +48 -48
  113. package/dist/apps/management/routes/invite-links.d.ts +38 -38
  114. package/dist/apps/management/routes/members.d.ts +69 -69
  115. package/dist/apps/management/routes/orgs.d.ts +37 -37
  116. package/dist/apps/management/routes/projects.d.ts +25 -25
  117. package/dist/apps/management/routes/scopes.d.ts +1 -1
  118. package/dist/apps/management/routes/usage.d.ts +72 -72
  119. package/dist/apps/management/routes/verified-token-requests.d.ts +18 -18
  120. package/dist/apps/management/routes/webhooks.d.ts +128 -128
  121. package/dist/apps/mcp.d.ts +2 -3
  122. package/dist/apps/mcp.d.ts.map +1 -1
  123. package/dist/apps/mcp.js +16 -14
  124. package/dist/apps/mcp.js.map +1 -1
  125. package/dist/apps/metadata.d.ts +4 -0
  126. package/dist/apps/metadata.d.ts.map +1 -1
  127. package/dist/apps/metadata.js +10 -0
  128. package/dist/apps/metadata.js.map +1 -1
  129. package/dist/apps/mpp/App.d.ts +2 -2
  130. package/dist/cloudflare.d.ts +50 -0
  131. package/dist/cloudflare.d.ts.map +1 -1
  132. package/dist/cloudflare.js +178 -0
  133. package/dist/cloudflare.js.map +1 -1
  134. package/dist/db/Db.d.ts +2 -0
  135. package/dist/db/Db.d.ts.map +1 -1
  136. package/dist/db/Db.js +37 -0
  137. package/dist/db/Db.js.map +1 -1
  138. package/dist/db/Schema.d.ts +12 -0
  139. package/dist/db/Schema.d.ts.map +1 -1
  140. package/dist/db/Schema.js +20 -0
  141. package/dist/db/Schema.js.map +1 -1
  142. package/dist/db/tables/fundingDepositAddresses.d.ts +13 -0
  143. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
  144. package/dist/db/tables/fundingDepositAddresses.js +12 -0
  145. package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
  146. package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
  147. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
  148. package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
  149. package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
  150. package/dist/db/tables/fundingDeposits.d.ts +57 -1
  151. package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
  152. package/dist/db/tables/fundingDeposits.js +82 -0
  153. package/dist/db/tables/fundingDeposits.js.map +1 -1
  154. package/dist/internal/Auth.d.ts +11 -11
  155. package/dist/internal/Auth.d.ts.map +1 -1
  156. package/dist/internal/Auth.js +41 -15
  157. package/dist/internal/Auth.js.map +1 -1
  158. package/dist/internal/Cache.js +1 -1
  159. package/dist/internal/Cache.js.map +1 -1
  160. package/dist/internal/EdgeCache.d.ts +2 -2
  161. package/dist/internal/EdgeCache.d.ts.map +1 -1
  162. package/dist/internal/EdgeCache.js +10 -4
  163. package/dist/internal/EdgeCache.js.map +1 -1
  164. package/dist/internal/IpAllowlist.d.ts +15 -0
  165. package/dist/internal/IpAllowlist.d.ts.map +1 -0
  166. package/dist/internal/IpAllowlist.js +27 -0
  167. package/dist/internal/IpAllowlist.js.map +1 -0
  168. package/dist/internal/OpenApi.d.ts +1 -1
  169. package/dist/internal/OpenApi.js +1 -1
  170. package/dist/internal/OpenApi.js.map +1 -1
  171. package/dist/internal/Tidx.d.ts +6 -0
  172. package/dist/internal/Tidx.d.ts.map +1 -1
  173. package/dist/internal/Tidx.js +13 -3
  174. package/dist/internal/Tidx.js.map +1 -1
  175. package/dist/internal/Viem.d.ts +8 -0
  176. package/dist/internal/Viem.d.ts.map +1 -1
  177. package/dist/internal/Viem.js +12 -5
  178. package/dist/internal/Viem.js.map +1 -1
  179. package/dist/internal/funding/Deposit.d.ts +42 -0
  180. package/dist/internal/funding/Deposit.d.ts.map +1 -1
  181. package/dist/internal/funding/Deposit.js +37 -5
  182. package/dist/internal/funding/Deposit.js.map +1 -1
  183. package/dist/internal/funding/Evidence.d.ts +1 -5
  184. package/dist/internal/funding/Evidence.d.ts.map +1 -1
  185. package/dist/internal/funding/Evidence.js +4 -3
  186. package/dist/internal/funding/Evidence.js.map +1 -1
  187. package/dist/internal/funding/Provider.d.ts +102 -0
  188. package/dist/internal/funding/Provider.d.ts.map +1 -1
  189. package/dist/internal/funding/Provider.js +5 -0
  190. package/dist/internal/funding/Provider.js.map +1 -1
  191. package/dist/internal/funding/Reconciliation.d.ts +16 -2
  192. package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
  193. package/dist/internal/funding/Reconciliation.js +257 -48
  194. package/dist/internal/funding/Reconciliation.js.map +1 -1
  195. package/dist/internal/funding/SourceObservation.d.ts +108 -0
  196. package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
  197. package/dist/internal/funding/SourceObservation.js +143 -0
  198. package/dist/internal/funding/SourceObservation.js.map +1 -0
  199. package/dist/internal/funding/Subsidy.d.ts +2 -2
  200. package/dist/internal/funding/Subsidy.d.ts.map +1 -1
  201. package/dist/internal/funding/Subsidy.js +1 -1
  202. package/dist/internal/funding/Subsidy.js.map +1 -1
  203. package/dist/internal/funding/index.d.ts +1 -0
  204. package/dist/internal/funding/index.d.ts.map +1 -1
  205. package/dist/internal/funding/index.js +1 -0
  206. package/dist/internal/funding/index.js.map +1 -1
  207. package/dist/internal/funding/providers/relay.d.ts +43 -1
  208. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  209. package/dist/internal/funding/providers/relay.js +434 -27
  210. package/dist/internal/funding/providers/relay.js.map +1 -1
  211. package/package.json +3 -3
  212. package/src/ApiKey.test.ts +35 -0
  213. package/src/ApiKey.ts +27 -0
  214. package/src/ApiKeys.test.ts +29 -0
  215. package/src/ApiKeys.ts +11 -3
  216. package/src/App.test.ts +23 -5
  217. package/src/App.ts +76 -5
  218. package/src/Client.test-d.ts +171 -19
  219. package/src/admin/apps/api-keys.test.ts +25 -1
  220. package/src/admin/apps/api-keys.ts +6 -2
  221. package/src/admin/ui.generated.ts +67 -67
  222. package/src/analytics/Analytics.ts +4 -0
  223. package/src/apps/composition.test.ts +100 -2
  224. package/src/apps/data/App.test.ts +0 -12
  225. package/src/apps/data/App.ts +4 -8
  226. package/src/apps/data/routes/earn.test.ts +283 -0
  227. package/src/apps/data/routes/earn.ts +800 -3
  228. package/src/apps/data/routes/indexer.ts +2 -1
  229. package/src/apps/data/routes/rpc.ts +2 -0
  230. package/src/apps/data/routes/tokens.test.ts +6 -8
  231. package/src/apps/funding/Access.ts +26 -0
  232. package/src/apps/funding/App.test.ts +78 -0
  233. package/src/apps/funding/App.ts +63 -0
  234. package/src/apps/funding/Inventory.ts +30 -0
  235. package/src/apps/funding/ProviderAttempts.ts +26 -0
  236. package/src/apps/funding/index.ts +1 -0
  237. package/src/apps/funding/routes/chains.test.ts +78 -0
  238. package/src/apps/funding/routes/chains.ts +101 -0
  239. package/src/apps/funding/routes/deposit-addresses.test.ts +1628 -0
  240. package/src/apps/funding/routes/deposit-addresses.ts +1015 -0
  241. package/src/apps/funding/routes/providers.test.ts +165 -0
  242. package/src/apps/funding/routes/providers.ts +73 -0
  243. package/src/apps/funding/routes/quotes.test.ts +511 -0
  244. package/src/apps/funding/routes/quotes.ts +205 -0
  245. package/src/apps/funding/routes/transfers.test.ts +1255 -0
  246. package/src/apps/funding/routes/transfers.ts +704 -0
  247. package/src/apps/index.ts +1 -0
  248. package/src/apps/management/routes/api-keys.test.ts +98 -0
  249. package/src/apps/management/routes/api-keys.ts +96 -1
  250. package/src/apps/mcp.test.ts +17 -2
  251. package/src/apps/mcp.ts +18 -13
  252. package/src/apps/metadata.ts +12 -0
  253. package/src/apps/mpp/internal/Policy.test.ts +7 -1
  254. package/src/cloudflare.ts +279 -0
  255. package/src/db/Db.ts +62 -0
  256. package/src/db/Schema.ts +24 -0
  257. package/src/db/tables/fundingDepositAddresses.ts +28 -0
  258. package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
  259. package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
  260. package/src/db/tables/fundingDeposits.test.ts +138 -0
  261. package/src/db/tables/fundingDeposits.ts +145 -1
  262. package/src/internal/Auth.test.ts +177 -5
  263. package/src/internal/Auth.ts +48 -18
  264. package/src/internal/Cache.test.ts +37 -3
  265. package/src/internal/Cache.ts +1 -1
  266. package/src/internal/EdgeCache.test.ts +7 -7
  267. package/src/internal/EdgeCache.ts +13 -4
  268. package/src/internal/IpAllowlist.test.ts +52 -0
  269. package/src/internal/IpAllowlist.ts +39 -0
  270. package/src/internal/Log.test.ts +7 -1
  271. package/src/internal/OpenApi.ts +1 -1
  272. package/src/internal/Tidx.test.ts +28 -0
  273. package/src/internal/Tidx.ts +19 -3
  274. package/src/internal/Viem.test.ts +28 -0
  275. package/src/internal/Viem.ts +21 -5
  276. package/src/internal/funding/Deposit.ts +74 -5
  277. package/src/internal/funding/Evidence.test.ts +2 -2
  278. package/src/internal/funding/Evidence.ts +4 -9
  279. package/src/internal/funding/Provider.ts +115 -0
  280. package/src/internal/funding/Reconciliation.local.test.ts +7 -0
  281. package/src/internal/funding/Reconciliation.test.ts +1052 -35
  282. package/src/internal/funding/Reconciliation.ts +328 -56
  283. package/src/internal/funding/SourceObservation.test.ts +301 -0
  284. package/src/internal/funding/SourceObservation.ts +267 -0
  285. package/src/internal/funding/Subsidy.ts +2 -2
  286. package/src/internal/funding/index.test.ts +1 -0
  287. package/src/internal/funding/index.ts +1 -0
  288. package/src/internal/funding/providers/relay.test.ts +94 -4
  289. package/src/internal/funding/providers/relay.ts +528 -27
  290. package/dist/apps/data/routes/funding.d.ts.map +0 -1
  291. package/dist/apps/data/routes/funding.js +0 -1557
  292. package/dist/apps/data/routes/funding.js.map +0 -1
  293. package/src/apps/data/routes/funding.test.ts +0 -2994
  294. 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,273 @@ 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 (!deliveryObservationStatuses.includes(parameters.request.status))
569
+ return { type: 'unsupported' };
570
+ if (parameters.sourceChain.kind !== 'evm' || parameters.destinationChain.kind !== 'evm')
571
+ return { type: 'unsupported' };
572
+ if (!Address.validate(parameters.address, { strict: false }) ||
573
+ !Address.validate(parameters.destinationToken.address, { strict: false }) ||
574
+ !Address.validate(parameters.recipient, { strict: false }) ||
575
+ !Address.validate(parameters.refundAddress, { strict: false }) ||
576
+ !Address.validate(parameters.sourceToken.address, { strict: false }) ||
577
+ !Hash.validate(parameters.source.transactionHash))
578
+ return { type: 'unsupported' };
579
+ const [chains, urls] = await Promise.all([getChains(signal), getUrls()]);
580
+ const sourceChain = relayEvmChain(chains, parameters.sourceChain);
581
+ const destinationChain = relayEvmChain(chains, parameters.destinationChain);
582
+ const sourceUrls = urls.get(parameters.sourceChain.id);
583
+ const destinationUrls = urls.get(parameters.destinationChain.id);
584
+ const depository = sourceChain?.protocol?.v2?.depository;
585
+ const solvers = destinationChain?.solverAddresses;
586
+ if (!depository ||
587
+ !solvers ||
588
+ solvers.length === 0 ||
589
+ !sourceUrls?.length ||
590
+ !destinationUrls?.length)
591
+ return { type: 'unsupported' };
592
+ const parsedCorrelation = schema.RequestCorrelation.safeParse(parameters.request.providerState);
593
+ const correlation = parsedCorrelation.success ? parsedCorrelation.data.relay : undefined;
594
+ if ((correlation?.depository &&
595
+ !Address.isEqual(correlation.depository, depository)) ||
596
+ (correlation?.solver &&
597
+ !solvers.some((solver) => Address.isEqual(solver, correlation.solver))))
598
+ return { type: 'unsupported' };
599
+ const depositor = (correlation?.depositor ?? parameters.refundAddress);
600
+ const stored = schema.DeliveryObservation.safeParse(parameters.providerState);
601
+ const sourceObservation = await (async () => {
602
+ if (stored.success)
603
+ return stored.data.relay;
604
+ const sourceClient = jsonRpcClient({ fetch: options.fetch, urls: sourceUrls });
605
+ const sourceReceipt = await getTransactionReceipt(sourceClient, {
606
+ hash: parameters.source.transactionHash,
607
+ });
608
+ if (sourceReceipt.status !== 'success' || sourceReceipt.blockNumber === null)
609
+ return undefined;
610
+ const sourceHead = await getBlockNumber(sourceClient, { cacheTime: 0 });
611
+ const sweepLogs = await getLogs(sourceClient, {
612
+ address: depository,
613
+ event: relayErc20DepositEvent,
614
+ fromBlock: sourceReceipt.blockNumber,
615
+ toBlock: minimum(sourceHead, sourceReceipt.blockNumber + sourceSweepForwardBlocks),
616
+ });
617
+ const observations = [];
618
+ for (const log of sweepLogs) {
619
+ if (!log.transactionHash)
620
+ continue;
621
+ const event = decodeEvent(relayErc20DepositEvent, log);
622
+ if (!event ||
623
+ !Address.isEqual(event.from, depositor) ||
624
+ !Address.isEqual(event.token, parameters.sourceToken.address) ||
625
+ event.amount !== BigInt(parameters.source.amount) ||
626
+ (correlation?.orderId && !Hex.isEqual(event.id, correlation.orderId)))
627
+ continue;
628
+ const transaction = await getTransaction(sourceClient, { hash: log.transactionHash });
629
+ const call = decodeRelayErc20Deposit(transaction.input);
630
+ if (!call ||
631
+ !Address.isEqual(transaction.from, parameters.address) ||
632
+ !transaction.to ||
633
+ !Address.isEqual(transaction.to, depository) ||
634
+ !Address.isEqual(call.depositor, event.from) ||
635
+ !Address.isEqual(call.token, event.token) ||
636
+ (call.amount !== undefined && call.amount !== event.amount) ||
637
+ !Hex.isEqual(call.id, event.id))
638
+ continue;
639
+ observations.push({
640
+ orderId: event.id,
641
+ sourceDepository: depository,
642
+ sourceObservedAt: options.now().toISOString(),
643
+ sourceSweepTransactionHash: log.transactionHash,
644
+ });
645
+ }
646
+ return observations.length === 1 ? observations[0] : undefined;
647
+ })();
648
+ if (!sourceObservation)
649
+ return { type: 'pending' };
650
+ const destinationClient = jsonRpcClient({ fetch: options.fetch, urls: destinationUrls });
651
+ const destinationHead = await getBlockNumber(destinationClient, { cacheTime: 0 });
652
+ const destinationLogs = await getLogs(destinationClient, {
653
+ address: parameters.destinationToken.address,
654
+ args: { to: parameters.recipient },
655
+ event: transferEvent,
656
+ fromBlock: destinationHead > deliveryBackfillBlocks ? destinationHead - deliveryBackfillBlocks : 0n,
657
+ toBlock: destinationHead,
658
+ });
659
+ const solverAddresses = (correlation?.solver ? [correlation.solver] : solvers).map((address) => address);
660
+ const correlatedTransactionHashes = [];
661
+ const uncorrelatedTransactionHashes = [];
662
+ for (const log of destinationLogs) {
663
+ if (!log.transactionHash ||
664
+ correlatedTransactionHashes.includes(log.transactionHash) ||
665
+ uncorrelatedTransactionHashes.includes(log.transactionHash))
666
+ continue;
667
+ const event = decodeEvent(transferEvent, log);
668
+ if (!event ||
669
+ event.value === 0n ||
670
+ !Address.isEqual(event.to, parameters.recipient) ||
671
+ !solverAddresses.some((address) => Address.isEqual(address, event.from)))
672
+ continue;
673
+ const transaction = await getTransaction(destinationClient, { hash: log.transactionHash });
674
+ const call = decodeRelayTransfer(transaction.input);
675
+ if (!call ||
676
+ !solverAddresses.some((address) => Address.isEqual(address, transaction.from)) ||
677
+ !Address.isEqual(event.from, transaction.from) ||
678
+ !transaction.to ||
679
+ !Address.isEqual(transaction.to, parameters.destinationToken.address) ||
680
+ !Address.isEqual(call.recipient, event.to) ||
681
+ call.amount !== event.value ||
682
+ (call.orderId && !Hex.isEqual(call.orderId, sourceObservation.orderId)))
683
+ continue;
684
+ if (call.orderId)
685
+ correlatedTransactionHashes.push(log.transactionHash);
686
+ else
687
+ uncorrelatedTransactionHashes.push(log.transactionHash);
688
+ }
689
+ const destinationTransactionHashes = (() => {
690
+ if (correlatedTransactionHashes.length > 0)
691
+ return correlatedTransactionHashes;
692
+ if (uncorrelatedTransactionHashes.length === 1)
693
+ return uncorrelatedTransactionHashes;
694
+ return [];
695
+ })();
696
+ const providerState = { relay: sourceObservation };
697
+ if (destinationTransactionHashes.length === 0)
698
+ return {
699
+ providerState,
700
+ providerTransactionHashes: [sourceObservation.sourceSweepTransactionHash],
701
+ type: 'pending',
702
+ };
703
+ return {
704
+ destinationTransactionHashes,
705
+ providerState: {
706
+ relay: {
707
+ ...sourceObservation,
708
+ destinationObservedAt: options.now().toISOString(),
709
+ },
710
+ },
711
+ providerTransactionHashes: [
712
+ sourceObservation.sourceSweepTransactionHash,
713
+ ...destinationTransactionHashes,
714
+ ],
715
+ type: 'verified',
716
+ };
717
+ };
718
+ }
719
+ function chainsEndpoint(options) {
720
+ const endpoint = new URL(options.baseUrl ?? defaultBaseUrl);
721
+ endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/chains`;
722
+ return endpoint;
723
+ }
724
+ function decodeRelayErc20Deposit(data) {
725
+ const fullAllowanceLength = 2 + 2 * (4 + 32 * 3);
726
+ const withAmountLength = 2 + 2 * (4 + 32 * 4);
727
+ try {
728
+ if (data.length === withAmountLength) {
729
+ const [depositor, token, amount, id] = AbiFunction.decodeData(depositErc20WithAmount, data);
730
+ return { amount, depositor, id, token };
731
+ }
732
+ if (data.length === fullAllowanceLength) {
733
+ const [depositor, token, id] = AbiFunction.decodeData(depositErc20FullAllowance, data);
734
+ return { depositor, id, token };
735
+ }
736
+ return undefined;
737
+ }
738
+ catch {
739
+ return undefined;
740
+ }
741
+ }
742
+ function decodeRelayTransfer(data) {
743
+ const encodedLength = 2 + 2 * (4 + 32 * 2);
744
+ if (data.length !== encodedLength && data.length !== encodedLength + 64)
745
+ return undefined;
746
+ try {
747
+ const [recipient, amount] = AbiFunction.decodeData(relayTransfer, data.slice(0, encodedLength));
748
+ return {
749
+ amount,
750
+ ...(data.length > encodedLength
751
+ ? { orderId: `0x${data.slice(encodedLength)}` }
752
+ : {}),
753
+ recipient,
754
+ };
755
+ }
756
+ catch {
757
+ return undefined;
758
+ }
759
+ }
760
+ function decodeEvent(abiEvent, log) {
761
+ try {
762
+ return AbiEvent.decode(abiEvent, log);
763
+ }
764
+ catch {
765
+ return undefined;
766
+ }
767
+ }
768
+ function jsonRpcClient(options) {
769
+ return createClient({
770
+ transport: fallback(options.urls.map((url) => {
771
+ const transport = RpcTransport.fromHttp(url, { fetchFn: options.fetch });
772
+ return custom(transport, { retryCount: 0 });
773
+ })),
774
+ });
775
+ }
776
+ function minimum(left, right) {
777
+ return left < right ? left : right;
778
+ }
779
+ function relayEvmChain(chains, chain) {
780
+ if (chain.kind !== 'evm')
781
+ return undefined;
782
+ const match = /^eip155:(\d+)$/.exec(chain.id);
783
+ if (!match?.[1])
784
+ return undefined;
785
+ return chains.find((candidate) => candidate.id === Number(match[1]));
786
+ }
393
787
  function verifyWebhook(options) {
394
788
  const timestamp = Number(options.timestamp);
395
789
  if (!Number.isSafeInteger(timestamp))
396
790
  throw new WebhookSignatureError();
397
- const now = Math.floor(Date.now() / 1_000);
398
- if (Math.abs(now - timestamp) > webhookSignatureToleranceSeconds)
791
+ if (Math.abs(Date.now() - timestamp) > webhookSignatureToleranceMs)
399
792
  throw new WebhookSignatureError();
400
793
  if (!/^[0-9a-fA-F]{64}$/.test(options.signature))
401
794
  throw new WebhookSignatureError();
@@ -415,7 +808,10 @@ function verifyWebhook(options) {
415
808
  throw new WebhookPayloadError();
416
809
  return {
417
810
  address: parsed.data.data.depositAddress?.address,
811
+ providerStatus: webhookStatus(parsed.data.data.status),
812
+ providerUpdatedAt: webhookTimestamp(parsed.data.data.updatedAt),
418
813
  requestId: parsed.data.data.requestId,
814
+ sentAt: webhookTimestamp(timestamp),
419
815
  };
420
816
  }
421
817
  function webhookOptions(input, secret) {
@@ -432,7 +828,7 @@ function quoteEndpoint(options) {
432
828
  }
433
829
  function requestsEndpoint(options) {
434
830
  const endpoint = new URL(options.baseUrl ?? defaultBaseUrl);
435
- endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/requests/v2`;
831
+ endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/requests/v3`;
436
832
  return endpoint;
437
833
  }
438
834
  function timingSafeEqual(left, right) {
@@ -443,6 +839,17 @@ function timingSafeEqual(left, right) {
443
839
  mismatch |= left.charCodeAt(index) ^ right.charCodeAt(index);
444
840
  return mismatch === 0;
445
841
  }
842
+ function webhookStatus(status) {
843
+ return webhookStatuses.includes(status) ? status : 'unknown';
844
+ }
845
+ function webhookTimestamp(value) {
846
+ try {
847
+ return new Date(value).toISOString();
848
+ }
849
+ catch {
850
+ throw new WebhookPayloadError();
851
+ }
852
+ }
446
853
  function relayChainId(chain) {
447
854
  if (chain.id.startsWith('eip155:'))
448
855
  return Number(FundingChain.eip155Id(chain));