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.
- package/README.md +10 -7
- package/dist/ApiKey.d.ts +3 -0
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +10 -0
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +7 -3
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +9 -2
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +20434 -19484
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +62 -4
- package/dist/App.js.map +1 -1
- package/dist/admin/App.d.ts +84 -74
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +14 -1
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +6 -2
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +2 -0
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +168 -168
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +67 -67
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +2 -0
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +3 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/apps/data/App.d.ts +10305 -12785
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +4 -8
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +24 -24
- package/dist/apps/data/routes/addresses.d.ts +288 -288
- package/dist/apps/data/routes/balances.d.ts +6 -6
- package/dist/apps/data/routes/blocks.d.ts +39 -39
- package/dist/apps/data/routes/coingecko.d.ts +170 -170
- package/dist/apps/data/routes/earn.d.ts +436 -6
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +631 -4
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +10 -10
- package/dist/apps/data/routes/fee-amm.d.ts +2 -2
- package/dist/apps/data/routes/indexer.d.ts +6 -6
- package/dist/apps/data/routes/indexer.js +4 -1
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +235 -235
- package/dist/apps/data/routes/rpc.d.ts +1 -1
- package/dist/apps/data/routes/rpc.js +4 -1
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +36 -36
- package/dist/apps/data/routes/tokens.d.ts +6 -6
- package/dist/apps/data/routes/transactions.d.ts +2 -2
- package/dist/apps/data/routes/transfers.d.ts +1 -1
- package/dist/apps/data/routes/valuation.d.ts +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
- package/dist/apps/data/routes/webhooks.d.ts +9 -9
- package/dist/apps/data/routes/zones.d.ts +14 -14
- package/dist/apps/funding/Access.d.ts +16 -0
- package/dist/apps/funding/Access.d.ts.map +1 -0
- package/dist/apps/funding/Access.js +22 -0
- package/dist/apps/funding/Access.js.map +1 -0
- package/dist/apps/{data/routes/funding.d.ts → funding/App.d.ts} +1717 -3035
- package/dist/apps/funding/App.d.ts.map +1 -0
- package/dist/apps/funding/App.js +46 -0
- package/dist/apps/funding/App.js.map +1 -0
- package/dist/apps/funding/Inventory.d.ts +5 -0
- package/dist/apps/funding/Inventory.d.ts.map +1 -0
- package/dist/apps/funding/Inventory.js +25 -0
- package/dist/apps/funding/Inventory.js.map +1 -0
- package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
- package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
- package/dist/apps/funding/ProviderAttempts.js +24 -0
- package/dist/apps/funding/ProviderAttempts.js.map +1 -0
- package/dist/apps/funding/index.d.ts +2 -0
- package/dist/apps/funding/index.d.ts.map +1 -0
- package/dist/apps/funding/index.js +2 -0
- package/dist/apps/funding/index.js.map +1 -0
- package/dist/apps/funding/routes/chains.d.ts +169 -0
- package/dist/apps/funding/routes/chains.d.ts.map +1 -0
- package/dist/apps/funding/routes/chains.js +80 -0
- package/dist/apps/funding/routes/chains.js.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts +2388 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.js +860 -0
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
- package/dist/apps/funding/routes/providers.d.ts +91 -0
- package/dist/apps/funding/routes/providers.d.ts.map +1 -0
- package/dist/apps/funding/routes/providers.js +56 -0
- package/dist/apps/funding/routes/providers.js.map +1 -0
- package/dist/apps/funding/routes/quotes.d.ts +426 -0
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
- package/dist/apps/funding/routes/quotes.js +188 -0
- package/dist/apps/funding/routes/quotes.js.map +1 -0
- package/dist/apps/funding/routes/transfers.d.ts +1999 -0
- package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
- package/dist/apps/funding/routes/transfers.js +614 -0
- package/dist/apps/funding/routes/transfers.js.map +1 -0
- package/dist/apps/index.d.ts +1 -0
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -0
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +430 -165
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +298 -21
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +75 -1
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +6 -6
- package/dist/apps/management/routes/faucet.d.ts +35 -35
- package/dist/apps/management/routes/invitations.d.ts +48 -48
- package/dist/apps/management/routes/invite-links.d.ts +38 -38
- package/dist/apps/management/routes/members.d.ts +69 -69
- package/dist/apps/management/routes/orgs.d.ts +37 -37
- package/dist/apps/management/routes/projects.d.ts +25 -25
- package/dist/apps/management/routes/scopes.d.ts +1 -1
- package/dist/apps/management/routes/usage.d.ts +72 -72
- package/dist/apps/management/routes/verified-token-requests.d.ts +18 -18
- package/dist/apps/management/routes/webhooks.d.ts +128 -128
- package/dist/apps/mcp.d.ts +2 -3
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +16 -14
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/metadata.d.ts +4 -0
- package/dist/apps/metadata.d.ts.map +1 -1
- package/dist/apps/metadata.js +10 -0
- package/dist/apps/metadata.js.map +1 -1
- package/dist/apps/mpp/App.d.ts +2 -2
- package/dist/cloudflare.d.ts +50 -0
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +178 -0
- package/dist/cloudflare.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +37 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +12 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +20 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.d.ts +13 -0
- package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.js +12 -0
- package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
- package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
- package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
- package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
- package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
- package/dist/db/tables/fundingDeposits.d.ts +57 -1
- package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
- package/dist/db/tables/fundingDeposits.js +82 -0
- package/dist/db/tables/fundingDeposits.js.map +1 -1
- package/dist/internal/Auth.d.ts +11 -11
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +41 -15
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.js +1 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts +2 -2
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +10 -4
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/IpAllowlist.d.ts +15 -0
- package/dist/internal/IpAllowlist.d.ts.map +1 -0
- package/dist/internal/IpAllowlist.js +27 -0
- package/dist/internal/IpAllowlist.js.map +1 -0
- package/dist/internal/OpenApi.d.ts +1 -1
- package/dist/internal/OpenApi.js +1 -1
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Tidx.d.ts +6 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +13 -3
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Viem.d.ts +8 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +12 -5
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/funding/Deposit.d.ts +42 -0
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +37 -5
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/Evidence.d.ts +1 -5
- package/dist/internal/funding/Evidence.d.ts.map +1 -1
- package/dist/internal/funding/Evidence.js +4 -3
- package/dist/internal/funding/Evidence.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +102 -0
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +5 -0
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts +16 -2
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +257 -48
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/SourceObservation.d.ts +108 -0
- package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
- package/dist/internal/funding/SourceObservation.js +143 -0
- package/dist/internal/funding/SourceObservation.js.map +1 -0
- package/dist/internal/funding/Subsidy.d.ts +2 -2
- package/dist/internal/funding/Subsidy.d.ts.map +1 -1
- package/dist/internal/funding/Subsidy.js +1 -1
- package/dist/internal/funding/Subsidy.js.map +1 -1
- package/dist/internal/funding/index.d.ts +1 -0
- package/dist/internal/funding/index.d.ts.map +1 -1
- package/dist/internal/funding/index.js +1 -0
- package/dist/internal/funding/index.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts +43 -1
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +434 -27
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/package.json +3 -3
- package/src/ApiKey.test.ts +35 -0
- package/src/ApiKey.ts +27 -0
- package/src/ApiKeys.test.ts +29 -0
- package/src/ApiKeys.ts +11 -3
- package/src/App.test.ts +23 -5
- package/src/App.ts +76 -5
- package/src/Client.test-d.ts +171 -19
- package/src/admin/apps/api-keys.test.ts +25 -1
- package/src/admin/apps/api-keys.ts +6 -2
- package/src/admin/ui.generated.ts +67 -67
- package/src/analytics/Analytics.ts +4 -0
- package/src/apps/composition.test.ts +100 -2
- package/src/apps/data/App.test.ts +0 -12
- package/src/apps/data/App.ts +4 -8
- package/src/apps/data/routes/earn.test.ts +283 -0
- package/src/apps/data/routes/earn.ts +800 -3
- package/src/apps/data/routes/indexer.ts +2 -1
- package/src/apps/data/routes/rpc.ts +2 -0
- package/src/apps/data/routes/tokens.test.ts +6 -8
- package/src/apps/funding/Access.ts +26 -0
- package/src/apps/funding/App.test.ts +78 -0
- package/src/apps/funding/App.ts +63 -0
- package/src/apps/funding/Inventory.ts +30 -0
- package/src/apps/funding/ProviderAttempts.ts +26 -0
- package/src/apps/funding/index.ts +1 -0
- package/src/apps/funding/routes/chains.test.ts +78 -0
- package/src/apps/funding/routes/chains.ts +101 -0
- package/src/apps/funding/routes/deposit-addresses.test.ts +1628 -0
- package/src/apps/funding/routes/deposit-addresses.ts +1015 -0
- package/src/apps/funding/routes/providers.test.ts +165 -0
- package/src/apps/funding/routes/providers.ts +73 -0
- package/src/apps/funding/routes/quotes.test.ts +511 -0
- package/src/apps/funding/routes/quotes.ts +205 -0
- package/src/apps/funding/routes/transfers.test.ts +1255 -0
- package/src/apps/funding/routes/transfers.ts +704 -0
- package/src/apps/index.ts +1 -0
- package/src/apps/management/routes/api-keys.test.ts +98 -0
- package/src/apps/management/routes/api-keys.ts +96 -1
- package/src/apps/mcp.test.ts +17 -2
- package/src/apps/mcp.ts +18 -13
- package/src/apps/metadata.ts +12 -0
- package/src/apps/mpp/internal/Policy.test.ts +7 -1
- package/src/cloudflare.ts +279 -0
- package/src/db/Db.ts +62 -0
- package/src/db/Schema.ts +24 -0
- package/src/db/tables/fundingDepositAddresses.ts +28 -0
- package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
- package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
- package/src/db/tables/fundingDeposits.test.ts +138 -0
- package/src/db/tables/fundingDeposits.ts +145 -1
- package/src/internal/Auth.test.ts +177 -5
- package/src/internal/Auth.ts +48 -18
- package/src/internal/Cache.test.ts +37 -3
- package/src/internal/Cache.ts +1 -1
- package/src/internal/EdgeCache.test.ts +7 -7
- package/src/internal/EdgeCache.ts +13 -4
- package/src/internal/IpAllowlist.test.ts +52 -0
- package/src/internal/IpAllowlist.ts +39 -0
- package/src/internal/Log.test.ts +7 -1
- package/src/internal/OpenApi.ts +1 -1
- package/src/internal/Tidx.test.ts +28 -0
- package/src/internal/Tidx.ts +19 -3
- package/src/internal/Viem.test.ts +28 -0
- package/src/internal/Viem.ts +21 -5
- package/src/internal/funding/Deposit.ts +74 -5
- package/src/internal/funding/Evidence.test.ts +2 -2
- package/src/internal/funding/Evidence.ts +4 -9
- package/src/internal/funding/Provider.ts +115 -0
- package/src/internal/funding/Reconciliation.local.test.ts +7 -0
- package/src/internal/funding/Reconciliation.test.ts +1052 -35
- package/src/internal/funding/Reconciliation.ts +328 -56
- package/src/internal/funding/SourceObservation.test.ts +301 -0
- package/src/internal/funding/SourceObservation.ts +267 -0
- package/src/internal/funding/Subsidy.ts +2 -2
- package/src/internal/funding/index.test.ts +1 -0
- package/src/internal/funding/index.ts +1 -0
- package/src/internal/funding/providers/relay.test.ts +94 -4
- package/src/internal/funding/providers/relay.ts +528 -27
- package/dist/apps/data/routes/funding.d.ts.map +0 -1
- package/dist/apps/data/routes/funding.js +0 -1557
- package/dist/apps/data/routes/funding.js.map +0 -1
- package/src/apps/data/routes/funding.test.ts +0 -2994
- package/src/apps/data/routes/funding.ts +0 -1821
|
@@ -1,15 +1,55 @@
|
|
|
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
|
|
|
4
6
|
import * as Db from '../../../db/Db.js'
|
|
5
7
|
import * as FundingDepositAddresses from '../../../db/tables/fundingDepositAddresses.js'
|
|
8
|
+
import * as FundingDepositRequestObservations from '../../../db/tables/fundingDepositRequestObservations.js'
|
|
9
|
+
import * as Viem from '../../Viem.js'
|
|
10
|
+
import * as FundingCatalog from '../Catalog.js'
|
|
6
11
|
import * as FundingChain from '../Chain.js'
|
|
12
|
+
import * as Evidence from '../Evidence.js'
|
|
7
13
|
import * as FundingProvider from '../Provider.js'
|
|
8
14
|
import * as Subsidy from '../Subsidy.js'
|
|
9
15
|
|
|
10
16
|
const defaultBaseUrl = 'https://api.relay.link'
|
|
11
17
|
const defaultFetch: FundingProvider.Fetch = (input, init) => globalThis.fetch(input, init)
|
|
12
|
-
const
|
|
18
|
+
const deliveryBackfillBlocks = 256n
|
|
19
|
+
const relayChainsCacheTtlMs = 60_000
|
|
20
|
+
const sourceSweepForwardBlocks = 256n
|
|
21
|
+
const depositErc20FullAllowance = AbiFunction.from(
|
|
22
|
+
'function depositErc20(address depositor,address token,bytes32 id)',
|
|
23
|
+
)
|
|
24
|
+
const depositErc20WithAmount = AbiFunction.from(
|
|
25
|
+
'function depositErc20(address depositor,address token,uint256 amount,bytes32 id)',
|
|
26
|
+
)
|
|
27
|
+
const relayErc20DepositEvent = AbiEvent.from(
|
|
28
|
+
'event RelayErc20Deposit(address from, address token, uint256 amount, bytes32 id)',
|
|
29
|
+
)
|
|
30
|
+
const relayTransfer = AbiFunction.from('function transfer(address recipient,uint256 amount)')
|
|
31
|
+
const transferEvent = AbiEvent.from(
|
|
32
|
+
'event Transfer(address indexed from, address indexed to, uint256 value)',
|
|
33
|
+
)
|
|
34
|
+
const webhookSignatureToleranceMs = 300_000
|
|
35
|
+
const deliveryObservationStatuses = [
|
|
36
|
+
'depositing',
|
|
37
|
+
'pending',
|
|
38
|
+
'submitted',
|
|
39
|
+
'success',
|
|
40
|
+
'waiting',
|
|
41
|
+
] as const
|
|
42
|
+
// Bound provider statuses before using them as metric tags.
|
|
43
|
+
const webhookStatuses = [
|
|
44
|
+
'depositing',
|
|
45
|
+
'failure',
|
|
46
|
+
'fallback',
|
|
47
|
+
'pending',
|
|
48
|
+
'refund',
|
|
49
|
+
'submitted',
|
|
50
|
+
'success',
|
|
51
|
+
'waiting',
|
|
52
|
+
] as const
|
|
13
53
|
|
|
14
54
|
namespace schema {
|
|
15
55
|
const Decimal = z.union([
|
|
@@ -98,8 +138,44 @@ namespace schema {
|
|
|
98
138
|
),
|
|
99
139
|
})
|
|
100
140
|
|
|
141
|
+
export const Chains = z.object({
|
|
142
|
+
chains: z.array(
|
|
143
|
+
z.object({
|
|
144
|
+
id: Integer,
|
|
145
|
+
protocol: z.optional(
|
|
146
|
+
z.object({
|
|
147
|
+
v2: z.optional(
|
|
148
|
+
z.object({
|
|
149
|
+
depository: z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/)),
|
|
150
|
+
}),
|
|
151
|
+
),
|
|
152
|
+
}),
|
|
153
|
+
),
|
|
154
|
+
solverAddresses: z.optional(z.array(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/)))),
|
|
155
|
+
}),
|
|
156
|
+
),
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
export const DeliveryObservation = z.object({
|
|
160
|
+
relay: z.object({
|
|
161
|
+
orderId: z.string().check(z.regex(/^0x[0-9a-fA-F]{64}$/)),
|
|
162
|
+
sourceDepository: z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/)),
|
|
163
|
+
sourceObservedAt: z.iso.datetime(),
|
|
164
|
+
sourceSweepTransactionHash: z.string().check(z.regex(/^0x[0-9a-fA-F]{64}$/)),
|
|
165
|
+
}),
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
export const RequestCorrelation = z.object({
|
|
169
|
+
relay: z.object({
|
|
170
|
+
depositor: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
|
|
171
|
+
depository: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
|
|
172
|
+
orderId: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{64}$/))),
|
|
173
|
+
solver: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
|
|
174
|
+
}),
|
|
175
|
+
})
|
|
176
|
+
|
|
101
177
|
const RequestTransaction = z.object({
|
|
102
|
-
|
|
178
|
+
txHash: z.string().check(z.minLength(1)),
|
|
103
179
|
})
|
|
104
180
|
|
|
105
181
|
export const Requests = z.object({
|
|
@@ -113,11 +189,35 @@ namespace schema {
|
|
|
113
189
|
}),
|
|
114
190
|
),
|
|
115
191
|
depositAddress: z.optional(
|
|
116
|
-
z.
|
|
117
|
-
|
|
118
|
-
|
|
192
|
+
z.nullable(
|
|
193
|
+
z.object({
|
|
194
|
+
depositTxHash: z.optional(z.nullable(z.string().check(z.minLength(1)))),
|
|
195
|
+
}),
|
|
196
|
+
),
|
|
119
197
|
),
|
|
120
198
|
id: z.string().check(z.minLength(1)),
|
|
199
|
+
protocol: z.optional(
|
|
200
|
+
z.nullable(
|
|
201
|
+
z.object({
|
|
202
|
+
deposit: z.optional(
|
|
203
|
+
z.object({
|
|
204
|
+
origin: z.optional(
|
|
205
|
+
z.object({
|
|
206
|
+
depositor: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
|
|
207
|
+
depository: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
|
|
208
|
+
}),
|
|
209
|
+
),
|
|
210
|
+
}),
|
|
211
|
+
),
|
|
212
|
+
orderId: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{64}$/))),
|
|
213
|
+
solver: z.optional(
|
|
214
|
+
z.object({
|
|
215
|
+
address: z.optional(z.string().check(z.regex(/^0x[0-9a-fA-F]{40}$/))),
|
|
216
|
+
}),
|
|
217
|
+
),
|
|
218
|
+
}),
|
|
219
|
+
),
|
|
220
|
+
),
|
|
121
221
|
status: z.string().check(z.minLength(1)),
|
|
122
222
|
updatedAt: z.iso.datetime(),
|
|
123
223
|
}),
|
|
@@ -128,11 +228,20 @@ namespace schema {
|
|
|
128
228
|
data: z.object({
|
|
129
229
|
depositAddress: z.nullable(z.object({ address: z.string().check(z.minLength(1)) })),
|
|
130
230
|
requestId: z.optional(z.string().check(z.minLength(1))),
|
|
231
|
+
status: z.string().check(z.minLength(1)),
|
|
232
|
+
updatedAt: z.number().check(z.int(), z.nonnegative()),
|
|
131
233
|
}),
|
|
132
234
|
event: z.literal('request.status.updated'),
|
|
133
235
|
})
|
|
134
236
|
}
|
|
135
237
|
|
|
238
|
+
/** Validates a Relay request-list response and reports payload drift with field-level details. */
|
|
239
|
+
export function parseDepositAddressRequests(value: unknown) {
|
|
240
|
+
const parsed = schema.Requests.safeParse(value)
|
|
241
|
+
if (!parsed.success) throw new FundingProvider.ProviderPayloadError(z.prettifyError(parsed.error))
|
|
242
|
+
return parsed.data
|
|
243
|
+
}
|
|
244
|
+
|
|
136
245
|
/** Creates a Relay funding provider backed by `/quote/v2`. */
|
|
137
246
|
export function relay(options: relay.Options = {}): FundingProvider.Provider<'relay'> {
|
|
138
247
|
if (
|
|
@@ -146,6 +255,15 @@ export function relay(options: relay.Options = {}): FundingProvider.Provider<'re
|
|
|
146
255
|
const endpoint = quoteEndpoint(options)
|
|
147
256
|
const fetch = options.fetch ?? defaultFetch
|
|
148
257
|
const now = options.now ?? (() => new Date())
|
|
258
|
+
const observeDepositAddressRequest = options.observation
|
|
259
|
+
? createDepositAddressObserver({
|
|
260
|
+
...options.observation,
|
|
261
|
+
...(options.apiKey ? { apiKey: options.apiKey } : {}),
|
|
262
|
+
...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),
|
|
263
|
+
fetch,
|
|
264
|
+
now,
|
|
265
|
+
})
|
|
266
|
+
: undefined
|
|
149
267
|
const webhook = (() => {
|
|
150
268
|
if (!options.apiKey || !options.webhook) return undefined
|
|
151
269
|
const { apiKey, webhook } = options
|
|
@@ -153,6 +271,7 @@ export function relay(options: relay.Options = {}): FundingProvider.Provider<'re
|
|
|
153
271
|
verifyWebhook(webhookOptions(input, apiKey))
|
|
154
272
|
return {
|
|
155
273
|
async receive(input: FundingProvider.Webhook.receive.Parameters) {
|
|
274
|
+
const receivedAt = now().toISOString()
|
|
156
275
|
const hint = (() => {
|
|
157
276
|
try {
|
|
158
277
|
return verify(input)
|
|
@@ -169,7 +288,8 @@ export function relay(options: relay.Options = {}): FundingProvider.Provider<'re
|
|
|
169
288
|
webhook.onResult?.('ignored')
|
|
170
289
|
return { type: 'ignored' as const }
|
|
171
290
|
}
|
|
172
|
-
const
|
|
291
|
+
const database = Db.get(webhook.db)
|
|
292
|
+
const address = await FundingDepositAddresses.getByProviderAddress(database, {
|
|
173
293
|
address: hint.address,
|
|
174
294
|
providerId: 'relay',
|
|
175
295
|
})
|
|
@@ -177,11 +297,33 @@ export function relay(options: relay.Options = {}): FundingProvider.Provider<'re
|
|
|
177
297
|
webhook.onResult?.('unknown_address')
|
|
178
298
|
return { type: 'unknown_address' as const }
|
|
179
299
|
}
|
|
300
|
+
if (hint.requestId)
|
|
301
|
+
await FundingDepositRequestObservations.observeWebhook(database, {
|
|
302
|
+
depositAddressId: address.id,
|
|
303
|
+
providerRequestId: hint.requestId,
|
|
304
|
+
receivedAt,
|
|
305
|
+
}).catch((cause) => {
|
|
306
|
+
webhook.onResult?.('observation_failed')
|
|
307
|
+
throw cause
|
|
308
|
+
})
|
|
180
309
|
try {
|
|
310
|
+
const webhookTiming = (() => {
|
|
311
|
+
if (!hint.providerStatus || !hint.providerUpdatedAt || !hint.requestId || !hint.sentAt)
|
|
312
|
+
return undefined
|
|
313
|
+
return {
|
|
314
|
+
enqueuedAt: now().toISOString(),
|
|
315
|
+
providerStatus: hint.providerStatus,
|
|
316
|
+
providerUpdatedAt: hint.providerUpdatedAt,
|
|
317
|
+
receivedAt,
|
|
318
|
+
sentAt: hint.sentAt,
|
|
319
|
+
}
|
|
320
|
+
})()
|
|
181
321
|
await webhook.dispatch({
|
|
182
322
|
addressId: address.id,
|
|
183
323
|
...(hint.requestId ? { requestId: hint.requestId } : {}),
|
|
324
|
+
trigger: 'webhook',
|
|
184
325
|
type: 'funding:deposit-address:reconcile',
|
|
326
|
+
...(webhookTiming ? { webhookTiming } : {}),
|
|
185
327
|
})
|
|
186
328
|
} catch (cause) {
|
|
187
329
|
webhook.onResult?.('enqueue_failed')
|
|
@@ -323,10 +465,8 @@ export function relay(options: relay.Options = {}): FundingProvider.Provider<'re
|
|
|
323
465
|
async listDepositAddressRequests({ address, continuation, requestId }, signal) {
|
|
324
466
|
const requestUrl = requestsEndpoint(options)
|
|
325
467
|
if (requestId) requestUrl.searchParams.set('id', requestId)
|
|
326
|
-
else
|
|
327
|
-
|
|
328
|
-
requestUrl.searchParams.set('includeChildRequests', 'true')
|
|
329
|
-
}
|
|
468
|
+
else requestUrl.searchParams.set('depositAddress', address)
|
|
469
|
+
requestUrl.searchParams.set('includeChildRequests', 'true')
|
|
330
470
|
requestUrl.searchParams.set('limit', '50')
|
|
331
471
|
requestUrl.searchParams.set('sortBy', 'updatedAt')
|
|
332
472
|
requestUrl.searchParams.set('sortDirection', 'desc')
|
|
@@ -337,22 +477,41 @@ export function relay(options: relay.Options = {}): FundingProvider.Provider<'re
|
|
|
337
477
|
method: 'GET',
|
|
338
478
|
signal,
|
|
339
479
|
})
|
|
340
|
-
const parsed =
|
|
341
|
-
if (!parsed.success) throw new FundingProvider.ProviderPayloadError()
|
|
480
|
+
const parsed = parseDepositAddressRequests(response)
|
|
342
481
|
return {
|
|
343
|
-
...(parsed.
|
|
344
|
-
requests: parsed.
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
482
|
+
...(parsed.continuation ? { continuation: parsed.continuation } : {}),
|
|
483
|
+
requests: parsed.requests.map((request) => {
|
|
484
|
+
const correlation = (() => {
|
|
485
|
+
const { protocol } = request
|
|
486
|
+
if (!protocol) return undefined
|
|
487
|
+
const relay = {
|
|
488
|
+
...(protocol.deposit?.origin?.depositor
|
|
489
|
+
? { depositor: protocol.deposit.origin.depositor }
|
|
490
|
+
: {}),
|
|
491
|
+
...(protocol.deposit?.origin?.depository
|
|
492
|
+
? { depository: protocol.deposit.origin.depository }
|
|
493
|
+
: {}),
|
|
494
|
+
...(protocol.orderId ? { orderId: protocol.orderId } : {}),
|
|
495
|
+
...(protocol.solver?.address ? { solver: protocol.solver.address } : {}),
|
|
496
|
+
}
|
|
497
|
+
if (Object.keys(relay).length === 0) return undefined
|
|
498
|
+
return schema.RequestCorrelation.parse({ relay })
|
|
499
|
+
})()
|
|
500
|
+
return {
|
|
501
|
+
createdAt: new Date(request.createdAt).toISOString(),
|
|
502
|
+
destinationTransactionHashes: request.data?.outTxs?.map((tx) => tx.txHash) ?? [],
|
|
503
|
+
id: request.id,
|
|
504
|
+
...(correlation ? { providerState: correlation } : {}),
|
|
505
|
+
sourceTransactionHashes: request.depositAddress?.depositTxHash
|
|
506
|
+
? [request.depositAddress.depositTxHash]
|
|
507
|
+
: [],
|
|
508
|
+
status: request.status,
|
|
509
|
+
updatedAt: new Date(request.updatedAt).toISOString(),
|
|
510
|
+
}
|
|
511
|
+
}),
|
|
354
512
|
}
|
|
355
513
|
},
|
|
514
|
+
...(observeDepositAddressRequest ? { observeDepositAddressRequest } : {}),
|
|
356
515
|
async prepareTransfer({ candidate, method, mode, recipient, sender, slippageBps }, signal) {
|
|
357
516
|
// Catalog capabilities gate deposit addresses and exact destination
|
|
358
517
|
// off this adapter; refuse them defensively if one slips through.
|
|
@@ -445,6 +604,8 @@ export declare namespace relay {
|
|
|
445
604
|
fetch?: FundingProvider.Fetch | undefined
|
|
446
605
|
/** Clock override for deterministic tests. */
|
|
447
606
|
now?: (() => Date) | undefined
|
|
607
|
+
/** EVM chain evidence used to accelerate authenticated provider delivery. */
|
|
608
|
+
observation?: Observation | undefined
|
|
448
609
|
/** Optional recipient override; defaults to `userAddress`. */
|
|
449
610
|
recipientAddress?: string | undefined
|
|
450
611
|
/** Deposit subsidy policy enabled for this integration. */
|
|
@@ -455,6 +616,16 @@ export declare namespace relay {
|
|
|
455
616
|
webhook?: Webhook | undefined
|
|
456
617
|
}
|
|
457
618
|
|
|
619
|
+
/** Chain evidence configuration for Relay delivery observation. */
|
|
620
|
+
type Observation = {
|
|
621
|
+
/** Authoritative funding database or per-request database factory. */
|
|
622
|
+
db: Db.Source
|
|
623
|
+
/** Tempo chain that receives a shared RPC URL override. */
|
|
624
|
+
defaultChainId?: Viem.ChainId | undefined
|
|
625
|
+
/** Shared or per-EVM-chain RPC URL environment value. */
|
|
626
|
+
rpcUrlEnv?: Viem.UrlResolver | undefined
|
|
627
|
+
}
|
|
628
|
+
|
|
458
629
|
/** Authenticated Relay webhook integration attached to a provider instance. */
|
|
459
630
|
type Webhook = {
|
|
460
631
|
/** Authoritative funding database or per-request database factory. */
|
|
@@ -470,11 +641,327 @@ export declare namespace relay {
|
|
|
470
641
|
| 'authentication_failed'
|
|
471
642
|
| 'enqueue_failed'
|
|
472
643
|
| 'ignored'
|
|
644
|
+
| 'observation_failed'
|
|
473
645
|
| 'payload_invalid'
|
|
474
646
|
| 'queued'
|
|
475
647
|
| 'unknown_address'
|
|
476
648
|
}
|
|
477
649
|
|
|
650
|
+
type DepositAddressObserverOptions = relay.Observation & {
|
|
651
|
+
apiKey?: string | undefined
|
|
652
|
+
baseUrl?: string | undefined
|
|
653
|
+
fetch: FundingProvider.Fetch
|
|
654
|
+
now: () => Date
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
type RelayChain = z.output<typeof schema.Chains>['chains'][number]
|
|
658
|
+
type RelayDeliveryObservation = z.output<typeof schema.DeliveryObservation>['relay']
|
|
659
|
+
|
|
660
|
+
function createDepositAddressObserver(
|
|
661
|
+
options: DepositAddressObserverOptions,
|
|
662
|
+
): NonNullable<FundingProvider.Provider['observeDepositAddressRequest']> {
|
|
663
|
+
let chainsExpiresAt = 0
|
|
664
|
+
let chainsPromise: Promise<readonly RelayChain[]> | undefined
|
|
665
|
+
let urlsExpiresAt = 0
|
|
666
|
+
let urlsPromise: Promise<ReadonlyMap<string, readonly string[]>> | undefined
|
|
667
|
+
const getChains = (signal: AbortSignal) => {
|
|
668
|
+
if (chainsPromise && options.now().getTime() < chainsExpiresAt) return chainsPromise
|
|
669
|
+
chainsExpiresAt = options.now().getTime() + relayChainsCacheTtlMs
|
|
670
|
+
chainsPromise = FundingProvider.requestJson(chainsEndpoint(options), {
|
|
671
|
+
fetch: options.fetch,
|
|
672
|
+
headers: options.apiKey ? { 'x-api-key': options.apiKey } : undefined,
|
|
673
|
+
signal,
|
|
674
|
+
})
|
|
675
|
+
.then((value) => schema.Chains.parse(value).chains)
|
|
676
|
+
.catch((cause) => {
|
|
677
|
+
chainsExpiresAt = 0
|
|
678
|
+
chainsPromise = undefined
|
|
679
|
+
throw cause
|
|
680
|
+
})
|
|
681
|
+
return chainsPromise
|
|
682
|
+
}
|
|
683
|
+
const getUrls = () => {
|
|
684
|
+
if (urlsPromise && options.now().getTime() < urlsExpiresAt) return urlsPromise
|
|
685
|
+
urlsExpiresAt = options.now().getTime() + relayChainsCacheTtlMs
|
|
686
|
+
urlsPromise = FundingCatalog.read(Db.get(options.db))
|
|
687
|
+
.then(
|
|
688
|
+
(catalog) =>
|
|
689
|
+
new Map<string, readonly string[]>(
|
|
690
|
+
[...catalog.chainsByKey.values()].map(
|
|
691
|
+
(chain) =>
|
|
692
|
+
[
|
|
693
|
+
chain.id,
|
|
694
|
+
Evidence.resolveRpcUrls({
|
|
695
|
+
chain,
|
|
696
|
+
defaultChainId: options.defaultChainId ?? Viem.defaultChainId,
|
|
697
|
+
rpcUrlEnv: options.rpcUrlEnv,
|
|
698
|
+
}),
|
|
699
|
+
] as const,
|
|
700
|
+
),
|
|
701
|
+
),
|
|
702
|
+
)
|
|
703
|
+
.catch((cause) => {
|
|
704
|
+
urlsExpiresAt = 0
|
|
705
|
+
urlsPromise = undefined
|
|
706
|
+
throw cause
|
|
707
|
+
})
|
|
708
|
+
return urlsPromise
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return async (parameters, signal) => {
|
|
712
|
+
if (!(deliveryObservationStatuses as readonly string[]).includes(parameters.request.status))
|
|
713
|
+
return { type: 'unsupported' }
|
|
714
|
+
if (parameters.sourceChain.kind !== 'evm' || parameters.destinationChain.kind !== 'evm')
|
|
715
|
+
return { type: 'unsupported' }
|
|
716
|
+
if (
|
|
717
|
+
!Address.validate(parameters.address, { strict: false }) ||
|
|
718
|
+
!Address.validate(parameters.destinationToken.address, { strict: false }) ||
|
|
719
|
+
!Address.validate(parameters.recipient, { strict: false }) ||
|
|
720
|
+
!Address.validate(parameters.refundAddress, { strict: false }) ||
|
|
721
|
+
!Address.validate(parameters.sourceToken.address, { strict: false }) ||
|
|
722
|
+
!Hash.validate(parameters.source.transactionHash)
|
|
723
|
+
)
|
|
724
|
+
return { type: 'unsupported' }
|
|
725
|
+
|
|
726
|
+
const [chains, urls] = await Promise.all([getChains(signal), getUrls()])
|
|
727
|
+
const sourceChain = relayEvmChain(chains, parameters.sourceChain)
|
|
728
|
+
const destinationChain = relayEvmChain(chains, parameters.destinationChain)
|
|
729
|
+
const sourceUrls = urls.get(parameters.sourceChain.id)
|
|
730
|
+
const destinationUrls = urls.get(parameters.destinationChain.id)
|
|
731
|
+
const depository = sourceChain?.protocol?.v2?.depository
|
|
732
|
+
const solvers = destinationChain?.solverAddresses
|
|
733
|
+
if (
|
|
734
|
+
!depository ||
|
|
735
|
+
!solvers ||
|
|
736
|
+
solvers.length === 0 ||
|
|
737
|
+
!sourceUrls?.length ||
|
|
738
|
+
!destinationUrls?.length
|
|
739
|
+
)
|
|
740
|
+
return { type: 'unsupported' }
|
|
741
|
+
|
|
742
|
+
const parsedCorrelation = schema.RequestCorrelation.safeParse(parameters.request.providerState)
|
|
743
|
+
const correlation = parsedCorrelation.success ? parsedCorrelation.data.relay : undefined
|
|
744
|
+
if (
|
|
745
|
+
(correlation?.depository &&
|
|
746
|
+
!Address.isEqual(
|
|
747
|
+
correlation.depository as Address.Address,
|
|
748
|
+
depository as Address.Address,
|
|
749
|
+
)) ||
|
|
750
|
+
(correlation?.solver &&
|
|
751
|
+
!solvers.some((solver) =>
|
|
752
|
+
Address.isEqual(solver as Address.Address, correlation.solver as Address.Address),
|
|
753
|
+
))
|
|
754
|
+
)
|
|
755
|
+
return { type: 'unsupported' }
|
|
756
|
+
const depositor = (correlation?.depositor ?? parameters.refundAddress) as Address.Address
|
|
757
|
+
|
|
758
|
+
const stored = schema.DeliveryObservation.safeParse(parameters.providerState)
|
|
759
|
+
const sourceObservation = await (async () => {
|
|
760
|
+
if (stored.success) return stored.data.relay
|
|
761
|
+
const sourceClient = jsonRpcClient({ fetch: options.fetch, urls: sourceUrls })
|
|
762
|
+
const sourceReceipt = await getTransactionReceipt(sourceClient, {
|
|
763
|
+
hash: parameters.source.transactionHash as Hex.Hex,
|
|
764
|
+
})
|
|
765
|
+
if (sourceReceipt.status !== 'success' || sourceReceipt.blockNumber === null) return undefined
|
|
766
|
+
const sourceHead = await getBlockNumber(sourceClient, { cacheTime: 0 })
|
|
767
|
+
const sweepLogs = await getLogs(sourceClient, {
|
|
768
|
+
address: depository as Address.Address,
|
|
769
|
+
event: relayErc20DepositEvent,
|
|
770
|
+
fromBlock: sourceReceipt.blockNumber,
|
|
771
|
+
toBlock: minimum(sourceHead, sourceReceipt.blockNumber + sourceSweepForwardBlocks),
|
|
772
|
+
})
|
|
773
|
+
const observations: RelayDeliveryObservation[] = []
|
|
774
|
+
for (const log of sweepLogs) {
|
|
775
|
+
if (!log.transactionHash) continue
|
|
776
|
+
const event = decodeEvent(relayErc20DepositEvent, log)
|
|
777
|
+
if (
|
|
778
|
+
!event ||
|
|
779
|
+
!Address.isEqual(event.from, depositor) ||
|
|
780
|
+
!Address.isEqual(event.token, parameters.sourceToken.address as Address.Address) ||
|
|
781
|
+
event.amount !== BigInt(parameters.source.amount) ||
|
|
782
|
+
(correlation?.orderId && !Hex.isEqual(event.id, correlation.orderId as Hex.Hex))
|
|
783
|
+
)
|
|
784
|
+
continue
|
|
785
|
+
const transaction = await getTransaction(sourceClient, { hash: log.transactionHash })
|
|
786
|
+
const call = decodeRelayErc20Deposit(transaction.input)
|
|
787
|
+
if (
|
|
788
|
+
!call ||
|
|
789
|
+
!Address.isEqual(transaction.from, parameters.address as Address.Address) ||
|
|
790
|
+
!transaction.to ||
|
|
791
|
+
!Address.isEqual(transaction.to, depository as Address.Address) ||
|
|
792
|
+
!Address.isEqual(call.depositor, event.from) ||
|
|
793
|
+
!Address.isEqual(call.token, event.token) ||
|
|
794
|
+
(call.amount !== undefined && call.amount !== event.amount) ||
|
|
795
|
+
!Hex.isEqual(call.id, event.id)
|
|
796
|
+
)
|
|
797
|
+
continue
|
|
798
|
+
observations.push({
|
|
799
|
+
orderId: event.id,
|
|
800
|
+
sourceDepository: depository,
|
|
801
|
+
sourceObservedAt: options.now().toISOString(),
|
|
802
|
+
sourceSweepTransactionHash: log.transactionHash,
|
|
803
|
+
})
|
|
804
|
+
}
|
|
805
|
+
return observations.length === 1 ? observations[0] : undefined
|
|
806
|
+
})()
|
|
807
|
+
if (!sourceObservation) return { type: 'pending' }
|
|
808
|
+
|
|
809
|
+
const destinationClient = jsonRpcClient({ fetch: options.fetch, urls: destinationUrls })
|
|
810
|
+
const destinationHead = await getBlockNumber(destinationClient, { cacheTime: 0 })
|
|
811
|
+
const destinationLogs = await getLogs(destinationClient, {
|
|
812
|
+
address: parameters.destinationToken.address as Address.Address,
|
|
813
|
+
args: { to: parameters.recipient as Address.Address },
|
|
814
|
+
event: transferEvent,
|
|
815
|
+
fromBlock:
|
|
816
|
+
destinationHead > deliveryBackfillBlocks ? destinationHead - deliveryBackfillBlocks : 0n,
|
|
817
|
+
toBlock: destinationHead,
|
|
818
|
+
})
|
|
819
|
+
const solverAddresses = (correlation?.solver ? [correlation.solver] : solvers).map(
|
|
820
|
+
(address) => address as Address.Address,
|
|
821
|
+
)
|
|
822
|
+
const correlatedTransactionHashes: Hex.Hex[] = []
|
|
823
|
+
const uncorrelatedTransactionHashes: Hex.Hex[] = []
|
|
824
|
+
for (const log of destinationLogs) {
|
|
825
|
+
if (
|
|
826
|
+
!log.transactionHash ||
|
|
827
|
+
correlatedTransactionHashes.includes(log.transactionHash) ||
|
|
828
|
+
uncorrelatedTransactionHashes.includes(log.transactionHash)
|
|
829
|
+
)
|
|
830
|
+
continue
|
|
831
|
+
const event = decodeEvent(transferEvent, log)
|
|
832
|
+
if (
|
|
833
|
+
!event ||
|
|
834
|
+
event.value === 0n ||
|
|
835
|
+
!Address.isEqual(event.to, parameters.recipient as Address.Address) ||
|
|
836
|
+
!solverAddresses.some((address) => Address.isEqual(address, event.from))
|
|
837
|
+
)
|
|
838
|
+
continue
|
|
839
|
+
const transaction = await getTransaction(destinationClient, { hash: log.transactionHash })
|
|
840
|
+
const call = decodeRelayTransfer(transaction.input)
|
|
841
|
+
if (
|
|
842
|
+
!call ||
|
|
843
|
+
!solverAddresses.some((address) => Address.isEqual(address, transaction.from)) ||
|
|
844
|
+
!Address.isEqual(event.from, transaction.from) ||
|
|
845
|
+
!transaction.to ||
|
|
846
|
+
!Address.isEqual(transaction.to, parameters.destinationToken.address as Address.Address) ||
|
|
847
|
+
!Address.isEqual(call.recipient, event.to) ||
|
|
848
|
+
call.amount !== event.value ||
|
|
849
|
+
(call.orderId && !Hex.isEqual(call.orderId, sourceObservation.orderId as Hex.Hex))
|
|
850
|
+
)
|
|
851
|
+
continue
|
|
852
|
+
if (call.orderId) correlatedTransactionHashes.push(log.transactionHash)
|
|
853
|
+
else uncorrelatedTransactionHashes.push(log.transactionHash)
|
|
854
|
+
}
|
|
855
|
+
const destinationTransactionHashes = (() => {
|
|
856
|
+
if (correlatedTransactionHashes.length > 0) return correlatedTransactionHashes
|
|
857
|
+
if (uncorrelatedTransactionHashes.length === 1) return uncorrelatedTransactionHashes
|
|
858
|
+
return []
|
|
859
|
+
})()
|
|
860
|
+
const providerState = { relay: sourceObservation }
|
|
861
|
+
if (destinationTransactionHashes.length === 0)
|
|
862
|
+
return {
|
|
863
|
+
providerState,
|
|
864
|
+
providerTransactionHashes: [sourceObservation.sourceSweepTransactionHash],
|
|
865
|
+
type: 'pending',
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return {
|
|
869
|
+
destinationTransactionHashes,
|
|
870
|
+
providerState: {
|
|
871
|
+
relay: {
|
|
872
|
+
...sourceObservation,
|
|
873
|
+
destinationObservedAt: options.now().toISOString(),
|
|
874
|
+
},
|
|
875
|
+
},
|
|
876
|
+
providerTransactionHashes: [
|
|
877
|
+
sourceObservation.sourceSweepTransactionHash,
|
|
878
|
+
...destinationTransactionHashes,
|
|
879
|
+
],
|
|
880
|
+
type: 'verified',
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function chainsEndpoint(options: Pick<DepositAddressObserverOptions, 'baseUrl'>): URL {
|
|
886
|
+
const endpoint = new URL(options.baseUrl ?? defaultBaseUrl)
|
|
887
|
+
endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/chains`
|
|
888
|
+
return endpoint
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function decodeRelayErc20Deposit(data: Hex.Hex) {
|
|
892
|
+
const fullAllowanceLength = 2 + 2 * (4 + 32 * 3)
|
|
893
|
+
const withAmountLength = 2 + 2 * (4 + 32 * 4)
|
|
894
|
+
try {
|
|
895
|
+
if (data.length === withAmountLength) {
|
|
896
|
+
const [depositor, token, amount, id] = AbiFunction.decodeData(depositErc20WithAmount, data)
|
|
897
|
+
return { amount, depositor, id, token }
|
|
898
|
+
}
|
|
899
|
+
if (data.length === fullAllowanceLength) {
|
|
900
|
+
const [depositor, token, id] = AbiFunction.decodeData(depositErc20FullAllowance, data)
|
|
901
|
+
return { depositor, id, token }
|
|
902
|
+
}
|
|
903
|
+
return undefined
|
|
904
|
+
} catch {
|
|
905
|
+
return undefined
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
function decodeRelayTransfer(data: Hex.Hex) {
|
|
910
|
+
const encodedLength = 2 + 2 * (4 + 32 * 2)
|
|
911
|
+
if (data.length !== encodedLength && data.length !== encodedLength + 64) return undefined
|
|
912
|
+
try {
|
|
913
|
+
const [recipient, amount] = AbiFunction.decodeData(
|
|
914
|
+
relayTransfer,
|
|
915
|
+
data.slice(0, encodedLength) as Hex.Hex,
|
|
916
|
+
)
|
|
917
|
+
return {
|
|
918
|
+
amount,
|
|
919
|
+
...(data.length > encodedLength
|
|
920
|
+
? { orderId: `0x${data.slice(encodedLength)}` as Hex.Hex }
|
|
921
|
+
: {}),
|
|
922
|
+
recipient,
|
|
923
|
+
}
|
|
924
|
+
} catch {
|
|
925
|
+
return undefined
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function decodeEvent<const event extends AbiEvent.AbiEvent>(
|
|
930
|
+
abiEvent: event,
|
|
931
|
+
log: { data: Hex.Hex; topics: readonly Hex.Hex[] },
|
|
932
|
+
): AbiEvent.decode.ReturnType<event> | undefined {
|
|
933
|
+
try {
|
|
934
|
+
return AbiEvent.decode(abiEvent, log)
|
|
935
|
+
} catch {
|
|
936
|
+
return undefined
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function jsonRpcClient(options: { fetch: FundingProvider.Fetch; urls: readonly string[] }) {
|
|
941
|
+
return createClient({
|
|
942
|
+
transport: fallback(
|
|
943
|
+
options.urls.map((url) => {
|
|
944
|
+
const transport = RpcTransport.fromHttp(url, { fetchFn: options.fetch })
|
|
945
|
+
return custom(transport, { retryCount: 0 })
|
|
946
|
+
}),
|
|
947
|
+
),
|
|
948
|
+
})
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function minimum(left: bigint, right: bigint): bigint {
|
|
952
|
+
return left < right ? left : right
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
function relayEvmChain(
|
|
956
|
+
chains: readonly RelayChain[],
|
|
957
|
+
chain: FundingProvider.ChainRef,
|
|
958
|
+
): RelayChain | undefined {
|
|
959
|
+
if (chain.kind !== 'evm') return undefined
|
|
960
|
+
const match = /^eip155:(\d+)$/.exec(chain.id)
|
|
961
|
+
if (!match?.[1]) return undefined
|
|
962
|
+
return chains.find((candidate) => candidate.id === Number(match[1]))
|
|
963
|
+
}
|
|
964
|
+
|
|
478
965
|
type VerifyWebhookOptions = {
|
|
479
966
|
body: string
|
|
480
967
|
secret: string
|
|
@@ -485,8 +972,7 @@ type VerifyWebhookOptions = {
|
|
|
485
972
|
function verifyWebhook(options: VerifyWebhookOptions): FundingProvider.Webhook.verify.ReturnType {
|
|
486
973
|
const timestamp = Number(options.timestamp)
|
|
487
974
|
if (!Number.isSafeInteger(timestamp)) throw new WebhookSignatureError()
|
|
488
|
-
|
|
489
|
-
if (Math.abs(now - timestamp) > webhookSignatureToleranceSeconds)
|
|
975
|
+
if (Math.abs(Date.now() - timestamp) > webhookSignatureToleranceMs)
|
|
490
976
|
throw new WebhookSignatureError()
|
|
491
977
|
if (!/^[0-9a-fA-F]{64}$/.test(options.signature)) throw new WebhookSignatureError()
|
|
492
978
|
const expected = Hash.hmac256(
|
|
@@ -506,7 +992,10 @@ function verifyWebhook(options: VerifyWebhookOptions): FundingProvider.Webhook.v
|
|
|
506
992
|
if (!parsed.success) throw new WebhookPayloadError()
|
|
507
993
|
return {
|
|
508
994
|
address: parsed.data.data.depositAddress?.address,
|
|
995
|
+
providerStatus: webhookStatus(parsed.data.data.status),
|
|
996
|
+
providerUpdatedAt: webhookTimestamp(parsed.data.data.updatedAt),
|
|
509
997
|
requestId: parsed.data.data.requestId,
|
|
998
|
+
sentAt: webhookTimestamp(timestamp),
|
|
510
999
|
}
|
|
511
1000
|
}
|
|
512
1001
|
|
|
@@ -528,7 +1017,7 @@ function quoteEndpoint(options: relay.Options): URL {
|
|
|
528
1017
|
|
|
529
1018
|
function requestsEndpoint(options: relay.Options): URL {
|
|
530
1019
|
const endpoint = new URL(options.baseUrl ?? defaultBaseUrl)
|
|
531
|
-
endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/requests/
|
|
1020
|
+
endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/requests/v3`
|
|
532
1021
|
return endpoint
|
|
533
1022
|
}
|
|
534
1023
|
|
|
@@ -540,6 +1029,18 @@ function timingSafeEqual(left: string, right: string): boolean {
|
|
|
540
1029
|
return mismatch === 0
|
|
541
1030
|
}
|
|
542
1031
|
|
|
1032
|
+
function webhookStatus(status: string): string {
|
|
1033
|
+
return (webhookStatuses as readonly string[]).includes(status) ? status : 'unknown'
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function webhookTimestamp(value: number): string {
|
|
1037
|
+
try {
|
|
1038
|
+
return new Date(value).toISOString()
|
|
1039
|
+
} catch {
|
|
1040
|
+
throw new WebhookPayloadError()
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
543
1044
|
function relayChainId(chain: FundingProvider.Chain): number {
|
|
544
1045
|
if (chain.id.startsWith('eip155:')) return Number(FundingChain.eip155Id(chain))
|
|
545
1046
|
// Relay uses a numeric chain ID for Solana instead of its CAIP-2 reference.
|