tapimo 0.13.0 → 0.14.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/dist/App.d.ts +6064 -4829
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +1 -0
- package/dist/App.js.map +1 -1
- package/dist/Webhooks.js +2 -2
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +1008 -917
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +48 -0
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +2 -1
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +92 -3
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +69 -7
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +74 -74
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +69 -69
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/Zones.d.ts +12 -12
- package/dist/apps/Zones.js +3 -3
- package/dist/apps/data/App.d.ts +2909 -2523
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +11 -6
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +22 -22
- package/dist/apps/data/routes/earn.d.ts +183 -7
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +434 -75
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +15 -0
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +180 -114
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +18 -19
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +93 -93
- package/dist/apps/data/routes/transfers.d.ts +12 -12
- package/dist/apps/data/routes/valuation.d.ts +30 -30
- package/dist/apps/data/routes/verified-tokens.d.ts +5 -5
- package/dist/apps/data/routes/webhooks.d.ts +805 -102
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +47 -5
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/funding/App.d.ts +456 -45
- package/dist/apps/funding/App.d.ts.map +1 -1
- package/dist/apps/funding/App.js +1 -1
- package/dist/apps/funding/App.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +17 -17
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/transfers.d.ts +654 -73
- package/dist/apps/funding/routes/transfers.d.ts.map +1 -1
- package/dist/apps/funding/routes/transfers.js +211 -5
- package/dist/apps/funding/routes/transfers.js.map +1 -1
- package/dist/apps/management/App.d.ts +831 -387
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/Billing.d.ts +1 -1
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +17 -1
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +76 -76
- package/dist/apps/management/routes/billing.d.ts +76 -76
- package/dist/apps/management/routes/faucet.d.ts +12 -12
- package/dist/apps/management/routes/invitations.d.ts +24 -24
- package/dist/apps/management/routes/invite-links.d.ts +46 -46
- package/dist/apps/management/routes/members.d.ts +46 -46
- package/dist/apps/management/routes/orgs.d.ts +20 -20
- package/dist/apps/management/routes/projects.d.ts +44 -44
- package/dist/apps/management/routes/usage.d.ts +32 -32
- package/dist/apps/management/routes/verified-token-requests.d.ts +16 -16
- package/dist/apps/management/routes/webhooks.d.ts +707 -57
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +21 -21
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +115 -23
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
- package/dist/apps/relay/Sponsorships.js +27 -1
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +22 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +8 -3
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +16 -1
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/fundingTransferTransactions.d.ts +38 -0
- package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -1
- package/dist/db/tables/fundingTransferTransactions.js +44 -3
- package/dist/db/tables/fundingTransferTransactions.js.map +1 -1
- package/dist/db/tables/fundingTransfers.d.ts +17 -0
- package/dist/db/tables/fundingTransfers.d.ts.map +1 -1
- package/dist/db/tables/fundingTransfers.js +21 -0
- package/dist/db/tables/fundingTransfers.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +24 -0
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +27 -0
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/projects.d.ts +25 -1
- package/dist/db/tables/projects.d.ts.map +1 -1
- package/dist/db/tables/projects.js +26 -0
- package/dist/db/tables/projects.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +44 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +146 -13
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +10 -6
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +9 -6
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/EarnVaults.js +5 -5
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/Log.d.ts +1 -1
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/Viem.d.ts +16 -1
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +12 -2
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/funding/DepositWebhook.d.ts.map +1 -1
- package/dist/internal/funding/DepositWebhook.js +2 -1
- package/dist/internal/funding/DepositWebhook.js.map +1 -1
- package/dist/internal/funding/Evidence.d.ts.map +1 -1
- package/dist/internal/funding/Evidence.js +2 -10
- package/dist/internal/funding/Evidence.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +40 -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/SourceObservation.d.ts.map +1 -1
- package/dist/internal/funding/SourceObservation.js +5 -7
- package/dist/internal/funding/SourceObservation.js.map +1 -1
- package/dist/internal/funding/Transfer.d.ts +101 -6
- package/dist/internal/funding/Transfer.d.ts.map +1 -1
- package/dist/internal/funding/Transfer.js +162 -34
- package/dist/internal/funding/Transfer.js.map +1 -1
- package/dist/internal/funding/TransferReconciliation.d.ts +52 -0
- package/dist/internal/funding/TransferReconciliation.d.ts.map +1 -0
- package/dist/internal/funding/TransferReconciliation.js +192 -0
- package/dist/internal/funding/TransferReconciliation.js.map +1 -0
- package/dist/internal/funding/TransferWebhook.d.ts +21 -0
- package/dist/internal/funding/TransferWebhook.d.ts.map +1 -0
- package/dist/internal/funding/TransferWebhook.js +43 -0
- package/dist/internal/funding/TransferWebhook.js.map +1 -0
- 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.map +1 -1
- package/dist/internal/funding/providers/relay.js +3 -12
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/dist/internal/funding/providers/stargate.d.ts +99 -0
- package/dist/internal/funding/providers/stargate.d.ts.map +1 -1
- package/dist/internal/funding/providers/stargate.js +337 -35
- package/dist/internal/funding/providers/stargate.js.map +1 -1
- package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
- package/dist/internal/webhookDestinations/slack.js +16 -0
- package/dist/internal/webhookDestinations/slack.js.map +1 -1
- package/package.json +1 -1
- package/src/App.ts +1 -0
- package/src/Client.test-d.ts +83 -2
- package/src/Webhooks.ts +2 -2
- package/src/admin/apps/earn-vaults.ts +2 -1
- package/src/admin/apps/organizations.test.ts +53 -1
- package/src/admin/apps/organizations.ts +90 -5
- package/src/admin/ui/src/lib/organizations.test.ts +112 -1
- package/src/admin/ui/src/lib/organizations.ts +55 -0
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +116 -0
- package/src/admin/ui.generated.ts +69 -69
- package/src/apps/Zones.test.ts +3 -3
- package/src/apps/Zones.ts +3 -3
- package/src/apps/composition.test.ts +1 -0
- package/src/apps/data/App.ts +15 -6
- package/src/apps/data/routes/earn.test.ts +369 -14
- package/src/apps/data/routes/earn.ts +548 -79
- package/src/apps/data/routes/exchanges.test.ts +740 -15
- package/src/apps/data/routes/exchanges.ts +954 -753
- package/src/apps/data/routes/fee-amm.ts +17 -18
- package/src/apps/data/routes/webhooks.test.ts +21 -5
- package/src/apps/data/routes/webhooks.ts +63 -8
- package/src/apps/funding/App.ts +12 -2
- package/src/apps/funding/routes/deposit-addresses.test.ts +4 -4
- package/src/apps/funding/routes/deposit-addresses.ts +2 -2
- package/src/apps/funding/routes/transfers.test.ts +510 -19
- package/src/apps/funding/routes/transfers.ts +271 -5
- package/src/apps/management/Billing.ts +18 -5
- package/src/apps/management/routes/webhooks.test.ts +3 -3
- package/src/apps/management/routes/webhooks.ts +24 -25
- package/src/apps/relay/App.test.ts +212 -1
- package/src/apps/relay/App.ts +158 -29
- package/src/apps/relay/Sponsorships.ts +24 -1
- package/src/db/Db.ts +35 -0
- package/src/db/Schema.ts +17 -2
- package/src/db/tables/fundingTransferTransactions.test.ts +43 -0
- package/src/db/tables/fundingTransferTransactions.ts +79 -3
- package/src/db/tables/fundingTransfers.test.ts +26 -0
- package/src/db/tables/fundingTransfers.ts +36 -0
- package/src/db/tables/organizations.test.ts +41 -0
- package/src/db/tables/organizations.ts +49 -0
- package/src/db/tables/projects.test.ts +55 -0
- package/src/db/tables/projects.ts +46 -1
- package/src/db/tables/sponsoredTransactions.test.ts +338 -0
- package/src/db/tables/sponsoredTransactions.ts +197 -13
- package/src/db/tables/webhookSubscriptions.ts +18 -11
- package/src/internal/EarnVaults.ts +5 -5
- package/src/internal/Log.test-d.ts +2 -0
- package/src/internal/Log.ts +2 -0
- package/src/internal/Viem.ts +39 -3
- package/src/internal/Webhooks.test.ts +45 -0
- package/src/internal/funding/DepositWebhook.ts +2 -1
- package/src/internal/funding/Evidence.ts +2 -13
- package/src/internal/funding/Provider.ts +43 -0
- package/src/internal/funding/SourceObservation.ts +5 -9
- package/src/internal/funding/Transfer.test.ts +155 -0
- package/src/internal/funding/Transfer.ts +254 -37
- package/src/internal/funding/TransferReconciliation.test.ts +147 -0
- package/src/internal/funding/TransferReconciliation.ts +264 -0
- package/src/internal/funding/TransferWebhook.test.ts +128 -0
- package/src/internal/funding/TransferWebhook.ts +67 -0
- package/src/internal/funding/index.test.ts +1 -0
- package/src/internal/funding/index.ts +1 -0
- package/src/internal/funding/providers/relay.ts +3 -15
- package/src/internal/funding/providers/stargate.test.ts +169 -4
- package/src/internal/funding/providers/stargate.ts +498 -51
- package/src/internal/webhookDestinations/slack.ts +15 -0
|
@@ -1,16 +1,34 @@
|
|
|
1
|
-
import { AbiFunction, Address,
|
|
2
|
-
import {
|
|
1
|
+
import { AbiEvent, AbiFunction, Address, Hash, Hex } from 'ox'
|
|
2
|
+
import {
|
|
3
|
+
BlockNotFoundError,
|
|
4
|
+
parseEventLogs,
|
|
5
|
+
publicActions,
|
|
6
|
+
TransactionReceiptNotFoundError,
|
|
7
|
+
} from 'viem'
|
|
3
8
|
import * as z from 'zod/mini'
|
|
4
9
|
|
|
10
|
+
import * as Viem from '../../Viem.js'
|
|
5
11
|
import * as Action from '../Action.js'
|
|
6
12
|
import type * as Catalog from '../Catalog.js'
|
|
13
|
+
import * as Chain from '../Chain.js'
|
|
7
14
|
import * as FundingProvider from '../Provider.js'
|
|
8
15
|
|
|
9
16
|
const approve = AbiFunction.from('function approve(address spender, uint256 amount)')
|
|
10
17
|
const defaultFetch: FundingProvider.Fetch = (input, init) => globalThis.fetch(input, init)
|
|
11
18
|
const destinationChainId = 'eip155:4217'
|
|
12
19
|
const destinationEid = 30_410
|
|
20
|
+
const destinationPool = '0x8c76e2f6c5ceda9aa7772e7eff30280226c44392'
|
|
13
21
|
const destinationToken = '0x20C000000000000000000000b9537d11c60E8b50'
|
|
22
|
+
const layerZeroEndpoint = '0x1a44076050125825900e736c501f859c50fE728c'
|
|
23
|
+
const oftReceived = AbiEvent.from(
|
|
24
|
+
'event OFTReceived(bytes32 indexed guid, uint32 srcEid, address indexed toAddress, uint256 amountReceivedLD)',
|
|
25
|
+
)
|
|
26
|
+
const oftSent = AbiEvent.from(
|
|
27
|
+
'event OFTSent(bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD)',
|
|
28
|
+
)
|
|
29
|
+
const packetSent = AbiEvent.from(
|
|
30
|
+
'event PacketSent(bytes encodedPayload, bytes options, address sendLibrary)',
|
|
31
|
+
)
|
|
14
32
|
const quoteOft = AbiFunction.from(
|
|
15
33
|
'function quoteOFT((uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam) view returns ((uint256 minAmountLD, uint256 maxAmountLD) limit, (int256 feeAmountLD, string description)[] oftFeeDetails, (uint256 amountSentLD, uint256 amountReceivedLD) receipt)',
|
|
16
34
|
)
|
|
@@ -21,21 +39,48 @@ const sendToken = AbiFunction.from(
|
|
|
21
39
|
'function sendToken((uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam, (uint256 nativeFee, uint256 lzTokenFee) fee, address refundAddress) payable returns ((bytes32 guid, uint64 nonce, (uint256 nativeFee, uint256 lzTokenFee) fee) msgReceipt, (uint256 amountSentLD, uint256 amountReceivedLD) oftReceipt, (uint72 ticketId, bytes passengerBytes) ticket)',
|
|
22
40
|
)
|
|
23
41
|
const quoteTtlMs = 60_000
|
|
42
|
+
const maximumLogBlockRange = 100_000n
|
|
43
|
+
const sourceEids = {
|
|
44
|
+
'eip155:1': 30_101,
|
|
45
|
+
'eip155:8453': 30_184,
|
|
46
|
+
} as const
|
|
47
|
+
const transferEvent = AbiEvent.from(
|
|
48
|
+
'event Transfer(address indexed from, address indexed to, uint256 value)',
|
|
49
|
+
)
|
|
24
50
|
const zeroAddress = '0x0000000000000000000000000000000000000000'
|
|
25
51
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
z
|
|
52
|
+
/** Stargate provider schemas. */
|
|
53
|
+
export namespace schema {
|
|
54
|
+
/** Configuration captured from one selected Stargate route. */
|
|
55
|
+
export const Configuration = z
|
|
56
|
+
.strictObject({
|
|
57
|
+
destinationPoolAddress: z.optional(
|
|
58
|
+
z
|
|
59
|
+
.string()
|
|
60
|
+
.check(
|
|
61
|
+
z.regex(/^0x[0-9a-fA-F]{40}$/),
|
|
62
|
+
z.describe('Stargate pool contract on the Tempo destination chain.'),
|
|
63
|
+
),
|
|
33
64
|
),
|
|
34
|
-
|
|
35
|
-
|
|
65
|
+
poolAddress: z
|
|
66
|
+
.string()
|
|
67
|
+
.check(
|
|
68
|
+
z.regex(/^0x[0-9a-fA-F]{40}$/),
|
|
69
|
+
z.describe('Stargate pool contract on the source chain.'),
|
|
70
|
+
),
|
|
71
|
+
sourceEid: z.optional(
|
|
72
|
+
z
|
|
73
|
+
.number()
|
|
74
|
+
.check(z.int(), z.positive(), z.describe('LayerZero endpoint ID for the source chain.')),
|
|
75
|
+
),
|
|
76
|
+
})
|
|
77
|
+
.check(z.describe('Stargate configuration for one funding route.'))
|
|
78
|
+
}
|
|
36
79
|
|
|
37
80
|
type Deployment = {
|
|
81
|
+
destinationPool: Address.Address
|
|
38
82
|
pool: Address.Address
|
|
83
|
+
sourceEid: number
|
|
39
84
|
sourceToken: Address.Address
|
|
40
85
|
}
|
|
41
86
|
|
|
@@ -72,7 +117,11 @@ export function stargate(options: stargate.Options = {}) {
|
|
|
72
117
|
amount: candidate.sourceAmount.amount,
|
|
73
118
|
recipient: zeroAddress,
|
|
74
119
|
})
|
|
75
|
-
const [limit, feeDetails, receipt] = await
|
|
120
|
+
const [limit, feeDetails, receipt] = await rpcClient({
|
|
121
|
+
fetch,
|
|
122
|
+
signal,
|
|
123
|
+
urls: route.source.chain.rpcUrls,
|
|
124
|
+
}).readContract({
|
|
76
125
|
abi: [quoteOft],
|
|
77
126
|
address: deployment.pool,
|
|
78
127
|
args: [sendParam],
|
|
@@ -107,8 +156,8 @@ export function stargate(options: stargate.Options = {}) {
|
|
|
107
156
|
})
|
|
108
157
|
if (!deployment) throw new FundingProvider.ProviderUnavailableError()
|
|
109
158
|
const sendParam = createSendParam({ amount: candidate.sourceAmount.amount, recipient })
|
|
110
|
-
const rpc =
|
|
111
|
-
const [[limit, feeDetails, receipt], messagingFee] = await Promise.all([
|
|
159
|
+
const rpc = rpcClient({ fetch, signal, urls: candidate.route.source.chain.rpcUrls })
|
|
160
|
+
const [[limit, feeDetails, receipt], messagingFee, destinationBlock] = await Promise.all([
|
|
112
161
|
rpc.readContract({
|
|
113
162
|
abi: [quoteOft],
|
|
114
163
|
address: deployment.pool,
|
|
@@ -122,6 +171,11 @@ export function stargate(options: stargate.Options = {}) {
|
|
|
122
171
|
args: [sendParam, false],
|
|
123
172
|
functionName: 'quoteSend',
|
|
124
173
|
}),
|
|
174
|
+
rpcClient({
|
|
175
|
+
fetch,
|
|
176
|
+
signal,
|
|
177
|
+
urls: candidate.route.destination.chain.rpcUrls,
|
|
178
|
+
}).getBlock({ blockTag: 'finalized', includeTransactions: false }),
|
|
125
179
|
])
|
|
126
180
|
if (!isExactQuote({ amount: candidate.sourceAmount.amount, feeDetails, limit, receipt }))
|
|
127
181
|
throw new FundingProvider.ProviderUnavailableError()
|
|
@@ -172,6 +226,10 @@ export function stargate(options: stargate.Options = {}) {
|
|
|
172
226
|
token: nativeToken(candidate.sourceChain.id),
|
|
173
227
|
},
|
|
174
228
|
],
|
|
229
|
+
providerState: {
|
|
230
|
+
destinationBlockNumber: destinationBlock.number.toString(),
|
|
231
|
+
routeConfiguration: candidate.configuration,
|
|
232
|
+
},
|
|
175
233
|
quality: quality(),
|
|
176
234
|
sampledAt: sampledAt.toISOString(),
|
|
177
235
|
}
|
|
@@ -190,6 +248,62 @@ export function stargate(options: stargate.Options = {}) {
|
|
|
190
248
|
}),
|
|
191
249
|
}
|
|
192
250
|
},
|
|
251
|
+
async verifySourceTransaction(
|
|
252
|
+
{
|
|
253
|
+
destinationChain,
|
|
254
|
+
providerState,
|
|
255
|
+
routeConfiguration,
|
|
256
|
+
sourceChain,
|
|
257
|
+
transactionHash,
|
|
258
|
+
transfer,
|
|
259
|
+
},
|
|
260
|
+
signal,
|
|
261
|
+
) {
|
|
262
|
+
if (transfer.method !== 'transaction' || transfer.mode !== 'exactSource' || !transfer.sender)
|
|
263
|
+
return { type: 'invalid' }
|
|
264
|
+
const configuration = providerState?.['routeConfiguration'] ?? routeConfiguration
|
|
265
|
+
const evidence = await verifySourceTransaction({
|
|
266
|
+
configuration,
|
|
267
|
+
destinationTokenAddress: transfer.destinationToken.address,
|
|
268
|
+
fetch,
|
|
269
|
+
recipient: transfer.recipient,
|
|
270
|
+
sender: transfer.sender,
|
|
271
|
+
signal,
|
|
272
|
+
sourceAmount: transfer.sourceAmount.baseUnits,
|
|
273
|
+
sourceChain,
|
|
274
|
+
sourceTokenAddress: transfer.sourceToken.address,
|
|
275
|
+
transactionHash,
|
|
276
|
+
validAfter: transfer.quote.sampledAt,
|
|
277
|
+
})
|
|
278
|
+
if (evidence.type === 'invalid') return evidence
|
|
279
|
+
if (evidence.type !== 'verified') return { type: 'pending' }
|
|
280
|
+
const destinationBlockNumber =
|
|
281
|
+
typeof providerState?.['destinationBlockNumber'] === 'string' &&
|
|
282
|
+
/^\d+$/.test(providerState['destinationBlockNumber'])
|
|
283
|
+
? providerState['destinationBlockNumber']
|
|
284
|
+
: await blockNumberBefore({
|
|
285
|
+
chain: destinationChain,
|
|
286
|
+
fetch,
|
|
287
|
+
signal,
|
|
288
|
+
timestamp: transfer.quote.sampledAt,
|
|
289
|
+
})
|
|
290
|
+
return {
|
|
291
|
+
providerState: {
|
|
292
|
+
...providerState,
|
|
293
|
+
destinationBlockNumber,
|
|
294
|
+
routeConfiguration: configuration,
|
|
295
|
+
stargate: {
|
|
296
|
+
guid: evidence.guid,
|
|
297
|
+
sourceBlock: {
|
|
298
|
+
hash: evidence.sourceBlock.hash,
|
|
299
|
+
number: evidence.sourceBlock.number.toString(),
|
|
300
|
+
},
|
|
301
|
+
sourceEid: evidence.sourceEid,
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
type: 'verified',
|
|
305
|
+
}
|
|
306
|
+
},
|
|
193
307
|
})
|
|
194
308
|
}
|
|
195
309
|
|
|
@@ -218,32 +332,25 @@ export function validateAction(options: validateAction.Options): readonly Action
|
|
|
218
332
|
const [approval, execution] = action.calls
|
|
219
333
|
if (
|
|
220
334
|
!Address.isEqual(approval!.to as Address.Address, deployment.sourceToken) ||
|
|
221
|
-
BigInt(approval!.value) !== 0n
|
|
222
|
-
!Address.isEqual(execution!.to as Address.Address, deployment.pool)
|
|
335
|
+
BigInt(approval!.value) !== 0n
|
|
223
336
|
)
|
|
224
337
|
throw new FundingProvider.ProviderPayloadError()
|
|
225
338
|
|
|
226
339
|
const [spender, approvalAmount] = AbiFunction.decodeData(approve, approval!.data as Hex.Hex)
|
|
227
|
-
const [sendParam, messagingFee, refundAddress] = AbiFunction.decodeData(
|
|
228
|
-
sendToken,
|
|
229
|
-
execution!.data as Hex.Hex,
|
|
230
|
-
)
|
|
231
|
-
const nativeValue = BigInt(execution!.value)
|
|
232
340
|
if (
|
|
233
341
|
!Address.isEqual(spender, deployment.pool) ||
|
|
234
|
-
approvalAmount !== BigInt(options.sourceAmount)
|
|
235
|
-
sendParam.amountLD !== BigInt(options.sourceAmount) ||
|
|
236
|
-
sendParam.composeMsg !== '0x' ||
|
|
237
|
-
sendParam.dstEid !== destinationEid ||
|
|
238
|
-
sendParam.extraOptions !== '0x' ||
|
|
239
|
-
sendParam.minAmountLD !== BigInt(options.sourceAmount) ||
|
|
240
|
-
sendParam.oftCmd !== '0x' ||
|
|
241
|
-
!Hex.isEqual(sendParam.to, recipientBytes32(options.recipient)) ||
|
|
242
|
-
messagingFee.lzTokenFee !== 0n ||
|
|
243
|
-
messagingFee.nativeFee !== nativeValue ||
|
|
244
|
-
!Address.isEqual(refundAddress, options.sender as Address.Address)
|
|
342
|
+
approvalAmount !== BigInt(options.sourceAmount)
|
|
245
343
|
)
|
|
246
344
|
throw new FundingProvider.ProviderPayloadError()
|
|
345
|
+
validateSend({
|
|
346
|
+
data: execution!.data as Hex.Hex,
|
|
347
|
+
nativeValue: BigInt(execution!.value),
|
|
348
|
+
pool: deployment.pool,
|
|
349
|
+
recipient: options.recipient,
|
|
350
|
+
sender: options.sender,
|
|
351
|
+
sourceAmount: options.sourceAmount,
|
|
352
|
+
to: execution!.to as Address.Address,
|
|
353
|
+
})
|
|
247
354
|
return [{ to: execution!.to, value: execution!.value }]
|
|
248
355
|
} catch (cause) {
|
|
249
356
|
if (cause instanceof FundingProvider.ProviderPayloadError) throw cause
|
|
@@ -273,28 +380,331 @@ export declare namespace validateAction {
|
|
|
273
380
|
}
|
|
274
381
|
}
|
|
275
382
|
|
|
276
|
-
|
|
277
|
-
|
|
383
|
+
/** Verifies a submitted Stargate source transaction against stored transfer terms. */
|
|
384
|
+
export async function verifySourceTransaction(
|
|
385
|
+
options: verifySourceTransaction.Options,
|
|
386
|
+
): Promise<verifySourceTransaction.Result> {
|
|
387
|
+
const deployment = resolveDeployment({
|
|
388
|
+
configuration: options.configuration,
|
|
389
|
+
destinationChainId,
|
|
390
|
+
destinationTokenAddress: options.destinationTokenAddress,
|
|
391
|
+
sourceChainId: options.sourceChain.id,
|
|
392
|
+
sourceTokenAddress: options.sourceTokenAddress,
|
|
393
|
+
})
|
|
394
|
+
if (
|
|
395
|
+
!deployment ||
|
|
396
|
+
options.sourceChain.kind !== 'evm' ||
|
|
397
|
+
!Hash.validate(options.transactionHash) ||
|
|
398
|
+
!Address.validate(options.recipient, { strict: false }) ||
|
|
399
|
+
!Address.validate(options.sender, { strict: false })
|
|
400
|
+
)
|
|
401
|
+
return { type: 'invalid' }
|
|
402
|
+
|
|
403
|
+
const rpc = rpcClient({
|
|
404
|
+
fetch: options.fetch ?? defaultFetch,
|
|
405
|
+
signal: options.signal,
|
|
406
|
+
urls: options.sourceChain.rpcUrls,
|
|
407
|
+
})
|
|
408
|
+
const hash = options.transactionHash as Hex.Hex
|
|
409
|
+
const [chainId, receipt] = await Promise.all([
|
|
410
|
+
rpc.getChainId(),
|
|
411
|
+
rpc.getTransactionReceipt({ hash }).catch((cause) => {
|
|
412
|
+
if (cause instanceof TransactionReceiptNotFoundError) return undefined
|
|
413
|
+
throw cause
|
|
414
|
+
}),
|
|
415
|
+
])
|
|
416
|
+
if (chainId !== Number(Chain.eip155Id(options.sourceChain))) return { type: 'invalid' }
|
|
417
|
+
const block =
|
|
418
|
+
!receipt || receipt.blockNumber === null
|
|
419
|
+
? undefined
|
|
420
|
+
: await rpc
|
|
421
|
+
.getBlock({ blockNumber: receipt.blockNumber, includeTransactions: false })
|
|
422
|
+
.catch((cause) => {
|
|
423
|
+
if (cause instanceof BlockNotFoundError) return undefined
|
|
424
|
+
throw cause
|
|
425
|
+
})
|
|
426
|
+
if (
|
|
427
|
+
!receipt ||
|
|
428
|
+
receipt.blockNumber === null ||
|
|
429
|
+
!block ||
|
|
430
|
+
!Hex.isEqual(block.hash, receipt.blockHash)
|
|
431
|
+
) {
|
|
432
|
+
if (!options.sourceBlock) return { type: 'pending' }
|
|
433
|
+
const finalized = await rpc.getBlock({ blockTag: 'finalized', includeTransactions: false })
|
|
434
|
+
if (finalized.number < options.sourceBlock.number) return { type: 'pending' }
|
|
435
|
+
const canonical = await rpc
|
|
436
|
+
.getBlock({ blockNumber: options.sourceBlock.number, includeTransactions: false })
|
|
437
|
+
.catch((cause) => {
|
|
438
|
+
if (cause instanceof BlockNotFoundError) return undefined
|
|
439
|
+
throw cause
|
|
440
|
+
})
|
|
441
|
+
if (!canonical || Hex.isEqual(canonical.hash, options.sourceBlock.hash))
|
|
442
|
+
return { type: 'pending' }
|
|
443
|
+
return { type: 'reorged' }
|
|
444
|
+
}
|
|
445
|
+
if (receipt.status !== 'success') return { type: 'invalid' }
|
|
446
|
+
const validAfter = Math.floor(Date.parse(options.validAfter) / 1_000)
|
|
447
|
+
if (!Number.isSafeInteger(validAfter) || block.timestamp < BigInt(validAfter))
|
|
448
|
+
return { type: 'invalid' }
|
|
449
|
+
|
|
450
|
+
const amount = BigInt(options.sourceAmount)
|
|
451
|
+
const sourceEvents = parseEventLogs({ abi: [oftSent], logs: receipt.logs, strict: true }).filter(
|
|
452
|
+
({ address, args }) =>
|
|
453
|
+
Address.isEqual(address, deployment.pool) &&
|
|
454
|
+
args.dstEid === destinationEid &&
|
|
455
|
+
Address.isEqual(args.fromAddress, options.sender as Address.Address) &&
|
|
456
|
+
args.amountSentLD === amount &&
|
|
457
|
+
args.amountReceivedLD === amount,
|
|
458
|
+
)
|
|
459
|
+
const transfers = parseEventLogs({
|
|
460
|
+
abi: [transferEvent],
|
|
461
|
+
logs: receipt.logs,
|
|
462
|
+
strict: true,
|
|
463
|
+
}).filter(
|
|
464
|
+
({ address, args }) =>
|
|
465
|
+
Address.isEqual(address, deployment.sourceToken) &&
|
|
466
|
+
Address.isEqual(args.from, options.sender as Address.Address) &&
|
|
467
|
+
Address.isEqual(args.to, deployment.pool) &&
|
|
468
|
+
args.value === amount,
|
|
469
|
+
)
|
|
470
|
+
if (sourceEvents.length !== 1 || transfers.length !== 1) return { type: 'invalid' }
|
|
471
|
+
const guid = sourceEvents[0]!.args.guid
|
|
472
|
+
// PacketV1's 113-byte header is followed by Stargate's type, asset id, recipient, and amount.
|
|
473
|
+
const packets = parseEventLogs({ abi: [packetSent], logs: receipt.logs, strict: true }).filter(
|
|
474
|
+
({ address, args }) => {
|
|
475
|
+
const packet = args.encodedPayload
|
|
476
|
+
return (
|
|
477
|
+
Address.isEqual(address, layerZeroEndpoint) &&
|
|
478
|
+
Hex.size(packet) === 156 &&
|
|
479
|
+
Hex.toNumber(Hex.slice(packet, 0, 1)) === 1 &&
|
|
480
|
+
Hex.toNumber(Hex.slice(packet, 9, 13)) === deployment.sourceEid &&
|
|
481
|
+
Hex.toNumber(Hex.slice(packet, 45, 49)) === destinationEid &&
|
|
482
|
+
Hex.isEqual(Hex.slice(packet, 81, 113), guid) &&
|
|
483
|
+
Hex.toNumber(Hex.slice(packet, 113, 114)) === 1 &&
|
|
484
|
+
Hex.isEqual(Hex.slice(packet, 116, 148), recipientBytes32(options.recipient)) &&
|
|
485
|
+
Hex.toBigInt(Hex.slice(packet, 148, 156)) === amount
|
|
486
|
+
)
|
|
487
|
+
},
|
|
488
|
+
)
|
|
489
|
+
if (packets.length !== 1) return { type: 'invalid' }
|
|
490
|
+
return {
|
|
491
|
+
guid,
|
|
492
|
+
sourceBlock: { hash: block.hash, number: block.number },
|
|
493
|
+
sourceEid: deployment.sourceEid,
|
|
494
|
+
type: 'verified',
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export declare namespace verifySourceTransaction {
|
|
499
|
+
/** Canonical source block that originally contained the transaction. */
|
|
500
|
+
type Block = {
|
|
501
|
+
/** Source block hash. */
|
|
502
|
+
hash: Hex.Hex
|
|
503
|
+
/** Source block number. */
|
|
504
|
+
number: bigint
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** Stored terms and chain access needed to verify one source transaction. */
|
|
508
|
+
type Options = {
|
|
509
|
+
/** Stargate configuration for the selected route. */
|
|
510
|
+
configuration: unknown
|
|
511
|
+
/** Curated Tempo destination token. */
|
|
512
|
+
destinationTokenAddress: string
|
|
513
|
+
/** Fetch implementation override for chain RPC requests. */
|
|
514
|
+
fetch?: FundingProvider.Fetch | undefined
|
|
515
|
+
/** Tempo beneficiary encoded in the Stargate send. */
|
|
516
|
+
recipient: string
|
|
517
|
+
/** Source account and LayerZero refund recipient. */
|
|
518
|
+
sender: string
|
|
519
|
+
/** Optional request cancellation signal. */
|
|
520
|
+
signal?: AbortSignal | undefined
|
|
521
|
+
/** Exact source amount in base units. */
|
|
522
|
+
sourceAmount: string
|
|
523
|
+
/** Canonical block captured during initial verification. */
|
|
524
|
+
sourceBlock?: Block | undefined
|
|
525
|
+
/** Source chain and RPC endpoints. */
|
|
526
|
+
sourceChain: Chain.Chain
|
|
527
|
+
/** Curated source token. */
|
|
528
|
+
sourceTokenAddress: string
|
|
529
|
+
/** Submitted EVM transaction hash. */
|
|
530
|
+
transactionHash: string
|
|
531
|
+
/** Earliest accepted source block timestamp. */
|
|
532
|
+
validAfter: string
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** Source transaction verification outcome. */
|
|
536
|
+
type Result =
|
|
537
|
+
| { type: 'invalid' }
|
|
538
|
+
| { type: 'pending' }
|
|
539
|
+
| { type: 'reorged' }
|
|
540
|
+
| { guid: Hex.Hex; sourceBlock: Block; sourceEid: number; type: 'verified' }
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** Finds and verifies the finalized Stargate delivery directly on Tempo. */
|
|
544
|
+
export async function verifyDestinationDelivery(
|
|
545
|
+
options: verifyDestinationDelivery.Options,
|
|
546
|
+
): Promise<verifyDestinationDelivery.Result> {
|
|
547
|
+
const deployment = resolveDeployment({
|
|
548
|
+
configuration: options.configuration,
|
|
549
|
+
destinationChainId: options.destinationChain.id,
|
|
550
|
+
destinationTokenAddress: options.destinationTokenAddress,
|
|
551
|
+
sourceChainId: options.sourceChainId,
|
|
552
|
+
sourceTokenAddress: options.sourceTokenAddress,
|
|
553
|
+
})
|
|
554
|
+
if (
|
|
555
|
+
!deployment ||
|
|
556
|
+
options.destinationChain.kind !== 'evm' ||
|
|
557
|
+
!/^\d+$/.test(options.destinationBlockNumber) ||
|
|
558
|
+
!Hash.validate(options.guid) ||
|
|
559
|
+
!Address.validate(options.recipient, { strict: false })
|
|
560
|
+
)
|
|
561
|
+
return { type: 'invalid' }
|
|
562
|
+
|
|
563
|
+
const rpc = rpcClient({
|
|
564
|
+
fetch: options.fetch ?? defaultFetch,
|
|
565
|
+
urls: options.destinationChain.rpcUrls,
|
|
566
|
+
})
|
|
567
|
+
const [chainId, finalized] = await Promise.all([
|
|
568
|
+
rpc.getChainId(),
|
|
569
|
+
rpc.getBlock({ blockTag: 'finalized', includeTransactions: false }),
|
|
570
|
+
])
|
|
571
|
+
if (chainId !== Number(Chain.eip155Id(options.destinationChain))) return { type: 'invalid' }
|
|
572
|
+
const amount = BigInt(options.destinationAmount)
|
|
573
|
+
const discovered = await (async () => {
|
|
574
|
+
for (
|
|
575
|
+
let fromBlock = BigInt(options.destinationBlockNumber);
|
|
576
|
+
fromBlock <= finalized.number;
|
|
577
|
+
fromBlock += maximumLogBlockRange
|
|
578
|
+
) {
|
|
579
|
+
const toBlock = fromBlock + maximumLogBlockRange - 1n
|
|
580
|
+
const logs = await rpc.getLogs({
|
|
581
|
+
address: deployment.destinationPool,
|
|
582
|
+
args: { guid: options.guid, toAddress: options.recipient as Address.Address },
|
|
583
|
+
event: oftReceived,
|
|
584
|
+
fromBlock,
|
|
585
|
+
strict: true,
|
|
586
|
+
toBlock: toBlock > finalized.number ? finalized.number : toBlock,
|
|
587
|
+
})
|
|
588
|
+
const matches = logs.filter(
|
|
589
|
+
({ args, transactionHash }) =>
|
|
590
|
+
transactionHash &&
|
|
591
|
+
args.srcEid === deployment.sourceEid &&
|
|
592
|
+
args.amountReceivedLD === amount,
|
|
593
|
+
)
|
|
594
|
+
if (logs.length !== matches.length || matches.length > 1) return { type: 'invalid' as const }
|
|
595
|
+
if (matches[0]?.transactionHash)
|
|
596
|
+
return { transactionHash: matches[0].transactionHash, type: 'found' as const }
|
|
597
|
+
}
|
|
598
|
+
return { type: 'pending' as const }
|
|
599
|
+
})()
|
|
600
|
+
if (discovered.type !== 'found') return discovered
|
|
601
|
+
const { transactionHash } = discovered
|
|
602
|
+
const receipt = await rpc.getTransactionReceipt({ hash: transactionHash }).catch((cause) => {
|
|
603
|
+
if (cause instanceof TransactionReceiptNotFoundError) return undefined
|
|
604
|
+
throw cause
|
|
605
|
+
})
|
|
606
|
+
if (!receipt || receipt.blockNumber === null) return { type: 'pending' }
|
|
607
|
+
if (receipt.status !== 'success' || receipt.blockNumber > finalized.number)
|
|
608
|
+
return { type: 'invalid' }
|
|
609
|
+
|
|
610
|
+
const transfers = parseEventLogs({
|
|
611
|
+
abi: [transferEvent],
|
|
612
|
+
logs: receipt.logs,
|
|
613
|
+
strict: true,
|
|
614
|
+
}).filter(
|
|
615
|
+
({ address, args }) =>
|
|
616
|
+
Address.isEqual(address, destinationToken as Address.Address) &&
|
|
617
|
+
Address.isEqual(args.from, zeroAddress) &&
|
|
618
|
+
Address.isEqual(args.to, options.recipient as Address.Address) &&
|
|
619
|
+
args.value === amount,
|
|
620
|
+
)
|
|
621
|
+
if (transfers.length !== 1) return { type: 'invalid' }
|
|
622
|
+
return { transactionHash, type: 'verified' }
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export declare namespace verifyDestinationDelivery {
|
|
626
|
+
/** Stored terms and chain access needed to discover and verify one delivery. */
|
|
627
|
+
type Options = {
|
|
628
|
+
/** Stargate configuration for the selected route. */
|
|
629
|
+
configuration: unknown
|
|
630
|
+
/** Exact destination amount in base units. */
|
|
631
|
+
destinationAmount: string
|
|
632
|
+
/** Tempo block captured before the source transaction was broadcast. */
|
|
633
|
+
destinationBlockNumber: string
|
|
634
|
+
/** Tempo chain and RPC endpoints. */
|
|
635
|
+
destinationChain: Chain.Chain
|
|
636
|
+
/** Curated Tempo destination token. */
|
|
637
|
+
destinationTokenAddress: string
|
|
638
|
+
/** Fetch implementation override for Tempo RPC requests. */
|
|
639
|
+
fetch?: FundingProvider.Fetch | undefined
|
|
640
|
+
/** GUID emitted by the verified source Stargate pool. */
|
|
641
|
+
guid: Hex.Hex
|
|
642
|
+
/** Tempo beneficiary. */
|
|
643
|
+
recipient: string
|
|
644
|
+
/** Source chain CAIP-2 id. */
|
|
645
|
+
sourceChainId: string
|
|
646
|
+
/** Curated source token. */
|
|
647
|
+
sourceTokenAddress: string
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/** Destination delivery verification outcome. */
|
|
651
|
+
type Result =
|
|
652
|
+
| { type: 'invalid' }
|
|
653
|
+
| { type: 'pending' }
|
|
654
|
+
| { transactionHash: Hex.Hex; type: 'verified' }
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function rpcClient(options: rpcClient.Options) {
|
|
658
|
+
if (options.urls.length === 0) throw new FundingProvider.ProviderConfigurationError()
|
|
659
|
+
return Viem.createEvmClient(options).extend(publicActions)
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
declare namespace rpcClient {
|
|
663
|
+
type Options = {
|
|
664
|
+
fetch: FundingProvider.Fetch
|
|
665
|
+
signal?: AbortSignal | undefined
|
|
666
|
+
urls: readonly string[]
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
async function blockNumberBefore(options: blockNumberBefore.Options) {
|
|
671
|
+
if (options.chain.kind !== 'evm') throw new FundingProvider.ProviderConfigurationError()
|
|
672
|
+
const timestamp = Date.parse(options.timestamp)
|
|
673
|
+
if (!Number.isFinite(timestamp)) throw new FundingProvider.ProviderPayloadError()
|
|
674
|
+
const rpc = rpcClient({
|
|
675
|
+
fetch: options.fetch,
|
|
676
|
+
signal: options.signal,
|
|
677
|
+
urls: options.chain.rpcUrls,
|
|
678
|
+
})
|
|
679
|
+
const [chainId, finalized] = await Promise.all([
|
|
680
|
+
rpc.getChainId(),
|
|
681
|
+
rpc.getBlock({ blockTag: 'finalized', includeTransactions: false }),
|
|
682
|
+
])
|
|
683
|
+
if (chainId !== Number(Chain.eip155Id(options.chain)))
|
|
278
684
|
throw new FundingProvider.ProviderConfigurationError()
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}).extend(publicActions)
|
|
685
|
+
|
|
686
|
+
const target = BigInt(Math.floor(timestamp / 1_000) - 1)
|
|
687
|
+
let lower = 0n
|
|
688
|
+
let upper = finalized.number
|
|
689
|
+
while (lower < upper) {
|
|
690
|
+
const middle = (lower + upper + 1n) / 2n
|
|
691
|
+
const block = await rpc.getBlock({ blockNumber: middle, includeTransactions: false })
|
|
692
|
+
if (block.timestamp <= target) lower = middle
|
|
693
|
+
else upper = middle - 1n
|
|
694
|
+
}
|
|
695
|
+
return lower.toString()
|
|
291
696
|
}
|
|
292
697
|
|
|
293
|
-
declare namespace
|
|
698
|
+
declare namespace blockNumberBefore {
|
|
294
699
|
type Options = {
|
|
700
|
+
/** Destination chain whose block history is searched. */
|
|
701
|
+
chain: Chain.Chain
|
|
702
|
+
/** Fetch implementation used for RPC requests. */
|
|
295
703
|
fetch: FundingProvider.Fetch
|
|
296
|
-
|
|
297
|
-
signal
|
|
704
|
+
/** Optional request cancellation signal. */
|
|
705
|
+
signal?: AbortSignal | undefined
|
|
706
|
+
/** Exclusive ISO 8601 timestamp upper bound. */
|
|
707
|
+
timestamp: string
|
|
298
708
|
}
|
|
299
709
|
}
|
|
300
710
|
|
|
@@ -365,10 +775,45 @@ function recipientBytes32(recipient: string) {
|
|
|
365
775
|
return Hex.padLeft(recipient as Hex.Hex, 32)
|
|
366
776
|
}
|
|
367
777
|
|
|
778
|
+
function validateSend(options: validateSend.Options) {
|
|
779
|
+
const [sendParam, messagingFee, refundAddress] = AbiFunction.decodeData(sendToken, options.data)
|
|
780
|
+
const amount = BigInt(options.sourceAmount)
|
|
781
|
+
if (
|
|
782
|
+
!Address.isEqual(options.to, options.pool) ||
|
|
783
|
+
sendParam.amountLD !== amount ||
|
|
784
|
+
sendParam.composeMsg !== '0x' ||
|
|
785
|
+
sendParam.dstEid !== destinationEid ||
|
|
786
|
+
sendParam.extraOptions !== '0x' ||
|
|
787
|
+
sendParam.minAmountLD !== amount ||
|
|
788
|
+
sendParam.oftCmd !== '0x' ||
|
|
789
|
+
!Hex.isEqual(sendParam.to, recipientBytes32(options.recipient)) ||
|
|
790
|
+
messagingFee.lzTokenFee !== 0n ||
|
|
791
|
+
messagingFee.nativeFee !== options.nativeValue ||
|
|
792
|
+
!Address.isEqual(refundAddress, options.sender as Address.Address)
|
|
793
|
+
)
|
|
794
|
+
throw new FundingProvider.ProviderPayloadError()
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
declare namespace validateSend {
|
|
798
|
+
type Options = {
|
|
799
|
+
data: Hex.Hex
|
|
800
|
+
nativeValue: bigint
|
|
801
|
+
pool: Address.Address
|
|
802
|
+
recipient: string
|
|
803
|
+
sender: string
|
|
804
|
+
sourceAmount: string
|
|
805
|
+
to: Address.Address
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
368
809
|
function resolveDeployment(options: resolveDeployment.Options): Deployment | undefined {
|
|
369
|
-
const parsed =
|
|
810
|
+
const parsed = schema.Configuration.safeParse(options.configuration)
|
|
811
|
+
const sourceEid =
|
|
812
|
+
parsed.success &&
|
|
813
|
+
(parsed.data.sourceEid ?? sourceEids[options.sourceChainId as keyof typeof sourceEids])
|
|
370
814
|
if (
|
|
371
815
|
!parsed.success ||
|
|
816
|
+
!sourceEid ||
|
|
372
817
|
options.destinationChainId !== destinationChainId ||
|
|
373
818
|
!options.sourceChainId.startsWith('eip155:') ||
|
|
374
819
|
!Address.validate(options.destinationTokenAddress, { strict: false }) ||
|
|
@@ -377,14 +822,16 @@ function resolveDeployment(options: resolveDeployment.Options): Deployment | und
|
|
|
377
822
|
)
|
|
378
823
|
return undefined
|
|
379
824
|
return {
|
|
825
|
+
destinationPool: Address.checksum(parsed.data.destinationPoolAddress ?? destinationPool),
|
|
380
826
|
pool: Address.checksum(parsed.data.poolAddress),
|
|
827
|
+
sourceEid,
|
|
381
828
|
sourceToken: Address.checksum(options.sourceTokenAddress),
|
|
382
829
|
}
|
|
383
830
|
}
|
|
384
831
|
|
|
385
832
|
declare namespace resolveDeployment {
|
|
386
833
|
type Options = {
|
|
387
|
-
configuration
|
|
834
|
+
configuration: unknown
|
|
388
835
|
destinationChainId: string
|
|
389
836
|
destinationTokenAddress: string
|
|
390
837
|
sourceChainId: string
|
|
@@ -42,6 +42,7 @@ type Block =
|
|
|
42
42
|
const friendlyName: Record<Webhooks.Envelope['type'], string> = {
|
|
43
43
|
'block:created': ':link: Block created',
|
|
44
44
|
'funding:deposit.updated': ':inbox_tray: Funding deposit updated',
|
|
45
|
+
'funding:transfer.updated': ':arrows_counterclockwise: Funding transfer updated',
|
|
45
46
|
'log:emitted': ':label: Log emitted',
|
|
46
47
|
ping: ':bell: Webhook test ping',
|
|
47
48
|
'token:transfer': ':money_with_wings: Token transfer',
|
|
@@ -152,6 +153,7 @@ function renderEvent(
|
|
|
152
153
|
if (type === 'log:emitted') return logLayout(data, base)
|
|
153
154
|
if (type === 'block:created') return blockLayout(data, base)
|
|
154
155
|
if (type === 'funding:deposit.updated') return fundingDepositLayout(data)
|
|
156
|
+
if (type === 'funding:transfer.updated') return fundingTransferLayout(data)
|
|
155
157
|
return pingLayout()
|
|
156
158
|
}
|
|
157
159
|
|
|
@@ -168,6 +170,19 @@ function fundingDepositLayout(data: Record<string, unknown>): Layout {
|
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
172
|
|
|
173
|
+
function fundingTransferLayout(data: Record<string, unknown>): Layout {
|
|
174
|
+
const fields: Field[] = []
|
|
175
|
+
field(fields, 'Transfer', escape(str(data['id']) ?? 'unknown'))
|
|
176
|
+
field(fields, 'Status', escape(str(data['status']) ?? 'unknown'))
|
|
177
|
+
const recipient = str(data['recipient'])
|
|
178
|
+
if (recipient !== undefined) field(fields, 'Recipient', escape(recipient))
|
|
179
|
+
return {
|
|
180
|
+
fields,
|
|
181
|
+
header: friendlyName['funding:transfer.updated'],
|
|
182
|
+
text: `Funding transfer updated · ${str(data['status']) ?? 'unknown'}`,
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
171
186
|
function transferLayout(data: Record<string, unknown>, base: string | undefined): Layout {
|
|
172
187
|
const amount = str(data['amount'])
|
|
173
188
|
const token =
|