@avalabs/bridge-unified 2.1.1 → 3.0.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 (77) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +115 -31
  3. package/dist/index.cjs +36 -10
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +252 -84
  6. package/dist/index.d.ts +252 -84
  7. package/dist/index.js +9 -4
  8. package/dist/index.js.map +1 -1
  9. package/package.json +14 -4
  10. package/.turbo/turbo-build.log +0 -22
  11. package/.turbo/turbo-lint.log +0 -4
  12. package/.turbo/turbo-test.log +0 -26
  13. package/CHANGELOG.md +0 -37
  14. package/jest.config.js +0 -9
  15. package/src/bridges/cctp/__mocks__/asset.mock.ts +0 -15
  16. package/src/bridges/cctp/__mocks__/bridge-transfer.mock.ts +0 -48
  17. package/src/bridges/cctp/__mocks__/chain.mocks.ts +0 -33
  18. package/src/bridges/cctp/__mocks__/config.mock.ts +0 -45
  19. package/src/bridges/cctp/abis/erc20.ts +0 -117
  20. package/src/bridges/cctp/abis/message-transmitter.ts +0 -318
  21. package/src/bridges/cctp/abis/token-router.ts +0 -843
  22. package/src/bridges/cctp/factory.test.ts +0 -73
  23. package/src/bridges/cctp/factory.ts +0 -36
  24. package/src/bridges/cctp/handlers/estimate-gas.test.ts +0 -110
  25. package/src/bridges/cctp/handlers/estimate-gas.ts +0 -58
  26. package/src/bridges/cctp/handlers/get-assets.test.ts +0 -47
  27. package/src/bridges/cctp/handlers/get-assets.ts +0 -27
  28. package/src/bridges/cctp/handlers/get-fees.test.ts +0 -61
  29. package/src/bridges/cctp/handlers/get-fees.ts +0 -26
  30. package/src/bridges/cctp/handlers/track-transfer.test.ts +0 -779
  31. package/src/bridges/cctp/handlers/track-transfer.ts +0 -365
  32. package/src/bridges/cctp/handlers/transfer-asset.test.ts +0 -429
  33. package/src/bridges/cctp/handlers/transfer-asset.ts +0 -179
  34. package/src/bridges/cctp/index.ts +0 -1
  35. package/src/bridges/cctp/types/chain.ts +0 -9
  36. package/src/bridges/cctp/types/config.ts +0 -20
  37. package/src/bridges/cctp/utils/build-tx.ts +0 -30
  38. package/src/bridges/cctp/utils/config.test.ts +0 -49
  39. package/src/bridges/cctp/utils/config.ts +0 -36
  40. package/src/bridges/cctp/utils/transfer-data.test.ts +0 -83
  41. package/src/bridges/cctp/utils/transfer-data.ts +0 -48
  42. package/src/errors/bridge-error.ts +0 -11
  43. package/src/errors/bridge-initialization-error.ts +0 -9
  44. package/src/errors/bridge-unavailable-error.ts +0 -9
  45. package/src/errors/index.ts +0 -4
  46. package/src/errors/invalid-params-error.ts +0 -9
  47. package/src/index.ts +0 -3
  48. package/src/types/asset.ts +0 -26
  49. package/src/types/bridge.ts +0 -64
  50. package/src/types/chain.ts +0 -10
  51. package/src/types/config.ts +0 -10
  52. package/src/types/environment.ts +0 -4
  53. package/src/types/error.ts +0 -19
  54. package/src/types/index.ts +0 -9
  55. package/src/types/provider.ts +0 -12
  56. package/src/types/signer.ts +0 -18
  57. package/src/types/transfer.ts +0 -35
  58. package/src/unified-bridge-service.test.ts +0 -209
  59. package/src/unified-bridge-service.ts +0 -97
  60. package/src/utils/bridge-types.test.ts +0 -103
  61. package/src/utils/bridge-types.ts +0 -32
  62. package/src/utils/caip2.test.ts +0 -44
  63. package/src/utils/caip2.ts +0 -41
  64. package/src/utils/client.test.ts +0 -97
  65. package/src/utils/client.ts +0 -44
  66. package/src/utils/ensure-config.test.ts +0 -43
  67. package/src/utils/ensure-config.ts +0 -12
  68. package/src/utils/index.ts +0 -2
  69. package/src/utils/network-fee.test.ts +0 -24
  70. package/src/utils/network-fee.ts +0 -6
  71. package/src/utils/retry-promise.test.ts +0 -115
  72. package/src/utils/retry-promise.ts +0 -72
  73. package/src/utils/wait.test.ts +0 -33
  74. package/src/utils/wait.ts +0 -4
  75. package/tsconfig.jest.json +0 -7
  76. package/tsconfig.json +0 -9
  77. package/tsup.config.ts +0 -4
@@ -1,365 +0,0 @@
1
- import { decodeEventLog, type Address } from 'viem';
2
- import {
3
- ErrorReason,
4
- type BridgeConfig,
5
- type BridgeService,
6
- type BridgeTransfer,
7
- type TrackingParams,
8
- ErrorCode,
9
- } from '../../../types';
10
- import { getClientForChain } from '../../../utils/client';
11
- import { TOKEN_ROUTER_ABI } from '../abis/token-router';
12
- import { getNetworkFeeEVM } from '../../../utils/network-fee';
13
- import { retryPromise, type Done } from '../../../utils/retry-promise';
14
- import { getTrackingDelayByChainId } from '../utils/config';
15
- import { InvalidParamsError } from '../../../errors';
16
-
17
- // Maximum time (in ms) before the transaction is considered "timed out"
18
- export const TRACKING_LIMIT_MS = 1000 * 60 * 60 * 3;
19
- // The max blocks that can be queried before receiving an error response
20
- export const MAX_BLOCKS = 1024n;
21
- // The delay time before tracking starts
22
- export const INITIAL_DELAY = 5000;
23
-
24
- /**
25
- * Mutates the `initial` transfer by merging it with the `updated` properties if they are not undefined
26
- * Invokes the `updateListener` with a copy of the updated `initial` transfer
27
- */
28
- const updateTransfer = (
29
- initial: BridgeTransfer,
30
- updated: Partial<BridgeTransfer>,
31
- updateListener: TrackingParams['updateListener'],
32
- ) => {
33
- Object.assign(initial, Object.fromEntries(Object.entries(updated).filter(([, value]) => value !== undefined)));
34
- updateListener({ ...initial });
35
- };
36
-
37
- /**
38
- * Polls the source network until it's able to get the CCTP message's `nonce` from the source transaction's logs
39
- * Updates the provided `BridgeTransfer` and broadcasts the changes via `updateListener`
40
- */
41
- export const trackSourceTx = async (config: BridgeConfig, params: TrackingParams) => {
42
- const { sourceProvider, targetProvider, updateListener, bridgeTransfer } = params;
43
- const sourceClient = getClientForChain({ chain: bridgeTransfer.sourceChain, provider: sourceProvider });
44
- const sourceChainData = config.find((chainData) => chainData.chainId === bridgeTransfer.sourceChain.chainId);
45
- const targetClient = getClientForChain({ chain: bridgeTransfer.targetChain, provider: targetProvider });
46
- const targetChainData = config.find((chainData) => chainData.chainId === bridgeTransfer.targetChain.chainId);
47
- const updateableTransfer = { ...bridgeTransfer };
48
-
49
- if (!sourceChainData || !targetChainData) {
50
- throw new InvalidParamsError(ErrorReason.CHAIN_NOT_SUPPORTED);
51
- }
52
-
53
- const tracker = async (done: Done<BridgeTransfer>) => {
54
- /**
55
- * Return early if:
56
- * - transfer has already completed successfully or due to some error
57
- * - the transfer state already has the message's nonce
58
- */
59
- if (updateableTransfer.completedAt || updateableTransfer.metadata?.nonce) {
60
- return done(updateableTransfer);
61
- }
62
-
63
- /**
64
- * Check if the transaction has timed out
65
- */
66
- if (updateableTransfer.sourceStartedAt + TRACKING_LIMIT_MS <= Date.now()) {
67
- updateTransfer(updateableTransfer, { completedAt: Date.now(), errorCode: ErrorCode.TIMEOUT }, updateListener);
68
- return done(updateableTransfer);
69
- }
70
-
71
- /**
72
- * Get the transaction's receipt.
73
- * Throws if the transaction has't been processed by the network yet.
74
- */
75
- const txReceipt = await sourceClient.getTransactionReceipt({
76
- hash: updateableTransfer.sourceTxHash as Address,
77
- });
78
-
79
- /**
80
- * Calculate the network fee if needed.
81
- */
82
- if (!updateableTransfer.sourceNetworkFee) {
83
- const tx = await sourceClient.getTransaction({ hash: updateableTransfer.sourceTxHash as Address });
84
- const networkFee = getNetworkFeeEVM(tx, txReceipt);
85
-
86
- if (networkFee) {
87
- updateTransfer(updateableTransfer, { sourceNetworkFee: networkFee }, updateListener);
88
- }
89
- }
90
-
91
- /**
92
- * Update the state and terminate if the transaction was reverted
93
- */
94
- if (txReceipt.status === 'reverted') {
95
- updateTransfer(
96
- updateableTransfer,
97
- { completedAt: Date.now(), errorCode: ErrorCode.TRANSACTION_REVERTED },
98
- updateListener,
99
- );
100
- return done(updateableTransfer);
101
- }
102
-
103
- /**
104
- * Check the confirmation count.
105
- * - update the sourceConfirmationCount if it increased
106
- * - update the startBlockNumber if confirmation count increased but is not enough
107
- * - keeps polling until it's greater than requiredSourceConfirmationCount
108
- */
109
- const confirmationCount = await sourceClient.getTransactionConfirmations({
110
- hash: updateableTransfer.sourceTxHash as Address,
111
- });
112
- const hasMoreConfirmations = confirmationCount > updateableTransfer.sourceConfirmationCount;
113
- const hasRequiredConfirmations = confirmationCount >= updateableTransfer.requiredSourceConfirmationCount;
114
-
115
- if (hasMoreConfirmations) {
116
- const changes = {} as Partial<BridgeTransfer>;
117
- changes.sourceConfirmationCount = Number(confirmationCount);
118
-
119
- if (!hasRequiredConfirmations) {
120
- changes.startBlockNumber = await targetClient.getBlockNumber();
121
- }
122
-
123
- updateTransfer(updateableTransfer, changes, updateListener);
124
- }
125
-
126
- if (!hasRequiredConfirmations) {
127
- return;
128
- }
129
-
130
- if (!updateableTransfer.startBlockNumber) {
131
- updateTransfer(updateableTransfer, { startBlockNumber: await targetClient.getBlockNumber() }, updateListener);
132
- }
133
-
134
- /**
135
- * Get the `TransferTokens` event's log entry from the receipt
136
- */
137
- const transferEventLog = txReceipt.logs.find((log) => {
138
- if (log.address.toLowerCase() === sourceChainData.tokenRouterAddress.toLowerCase()) {
139
- const event = decodeEventLog({
140
- abi: TOKEN_ROUTER_ABI,
141
- ...log,
142
- });
143
-
144
- return event.eventName === 'TransferTokens';
145
- }
146
-
147
- return false;
148
- });
149
-
150
- if (!transferEventLog) {
151
- throw new InvalidParamsError(
152
- ErrorReason.INVALID_PARAMS,
153
- `unable to find a TransferTokens event in source transaction "${updateableTransfer.sourceTxHash}"`,
154
- );
155
- }
156
-
157
- /**
158
- * Get the nonce used by the message transmitter from the event's log
159
- * https://developers.circle.com/stablecoins/docs/evm-smart-contracts#receivemessage
160
- */
161
- const transferEvent = decodeEventLog({
162
- abi: TOKEN_ROUTER_ABI,
163
- eventName: 'TransferTokens',
164
- ...transferEventLog,
165
- });
166
-
167
- // save the nonce and broadcast
168
- const nonce = transferEvent.args.nonce;
169
- updateTransfer(updateableTransfer, { targetStartedAt: Date.now(), metadata: { nonce } }, updateListener);
170
- return done(updateableTransfer);
171
- };
172
-
173
- return retryPromise<BridgeTransfer>({
174
- promise: tracker,
175
- delay: getTrackingDelayByChainId(sourceChainData.chainId),
176
- startAfter: INITIAL_DELAY,
177
- });
178
- };
179
-
180
- /**
181
- * Polls the target network until it finds the transaction that matches the message's nonce
182
- * Updates the provided `BridgeTransfer` and broadcasts the changes via `updateListener`
183
- */
184
- export const trackTargetTx = async (config: BridgeConfig, params: TrackingParams) => {
185
- const { targetProvider, updateListener, bridgeTransfer } = params;
186
- const updateableTransfer = { ...bridgeTransfer };
187
-
188
- if (!bridgeTransfer.completedAt && !bridgeTransfer.metadata?.nonce) {
189
- throw new InvalidParamsError(ErrorReason.INVALID_PARAMS, `nonce is missing`);
190
- }
191
-
192
- if (!bridgeTransfer.startBlockNumber) {
193
- throw new InvalidParamsError(ErrorReason.INVALID_PARAMS, `startBlockNumber is missing`);
194
- }
195
-
196
- const targetClient = getClientForChain({ chain: bridgeTransfer.targetChain, provider: targetProvider });
197
- const targetChainData = config.find((chainData) => chainData.chainId === bridgeTransfer.targetChain.chainId);
198
-
199
- if (!targetChainData) {
200
- throw new InvalidParamsError(
201
- ErrorReason.INVALID_PARAMS,
202
- `unknown target chain "${bridgeTransfer.targetChain.chainId}"`,
203
- );
204
- }
205
-
206
- const tracker = async (done: Done<BridgeTransfer>) => {
207
- /**
208
- * Return early if:
209
- * - transfer has already completed successfully or due to some error
210
- */
211
- if (updateableTransfer.completedAt) {
212
- return done(updateableTransfer);
213
- }
214
-
215
- /**
216
- * Check if `startBlockNumber` became falsy for whatever reason
217
- */
218
- if (!updateableTransfer.startBlockNumber) {
219
- updateTransfer(
220
- updateableTransfer,
221
- { completedAt: Date.now(), errorCode: ErrorCode.INVALID_PARAMS },
222
- updateListener,
223
- );
224
- return done(updateableTransfer);
225
- }
226
-
227
- /**
228
- * Check if the transaction has timed out
229
- */
230
- if (updateableTransfer.sourceStartedAt + TRACKING_LIMIT_MS <= Date.now()) {
231
- updateTransfer(updateableTransfer, { completedAt: Date.now(), errorCode: ErrorCode.TIMEOUT }, updateListener);
232
- return done(updateableTransfer);
233
- }
234
-
235
- if (!updateableTransfer.targetTxHash) {
236
- const lastBlockNumber = await targetClient.getBlockNumber();
237
- const lowestBlockNumber = updateableTransfer.startBlockNumber - MAX_BLOCKS;
238
- const fromBlock = lowestBlockNumber >= 0n ? lowestBlockNumber : 'earliest';
239
- const highestBlockNumber = updateableTransfer.startBlockNumber + MAX_BLOCKS;
240
- const toBlock = highestBlockNumber < lastBlockNumber ? highestBlockNumber : 'latest';
241
-
242
- const targetLogs = await targetClient.getLogs({
243
- address: targetChainData.messageTransmitterAddress,
244
- event: {
245
- name: 'MessageReceived',
246
- type: 'event',
247
- inputs: [
248
- { indexed: true, internalType: 'address', name: 'caller', type: 'address' },
249
- { indexed: false, internalType: 'uint32', name: 'sourceDomain', type: 'uint32' },
250
- { indexed: true, internalType: 'uint64', name: 'nonce', type: 'uint64' },
251
- { indexed: false, internalType: 'bytes32', name: 'sender', type: 'bytes32' },
252
- { indexed: false, internalType: 'bytes', name: 'messageBody', type: 'bytes' },
253
- ],
254
- },
255
- args: { nonce: updateableTransfer.metadata!.nonce as bigint },
256
- fromBlock,
257
- toBlock,
258
- });
259
-
260
- if (targetLogs[0]?.transactionHash) {
261
- updateTransfer(updateableTransfer, { targetTxHash: targetLogs[0].transactionHash }, updateListener);
262
- } else {
263
- updateTransfer(updateableTransfer, { startBlockNumber: lastBlockNumber }, updateListener);
264
- return;
265
- }
266
- }
267
-
268
- /**
269
- * Get the transaction's receipt.
270
- * Throws if the transaction has't been processed by the network yet.
271
- */
272
- const txReceipt = await targetClient.getTransactionReceipt({
273
- hash: updateableTransfer.targetTxHash as Address,
274
- });
275
-
276
- /**
277
- * Calculate the network fee if needed.
278
- */
279
- if (!updateableTransfer.targetNetworkFee) {
280
- const tx = await targetClient.getTransaction({ hash: updateableTransfer.targetTxHash as Address });
281
- const networkFee = getNetworkFeeEVM(tx, txReceipt);
282
-
283
- if (networkFee) {
284
- updateTransfer(updateableTransfer, { targetNetworkFee: networkFee }, updateListener);
285
- }
286
- }
287
-
288
- /**
289
- * Update the state and terminate if the transaction was reverted
290
- */
291
- if (txReceipt.status === 'reverted') {
292
- updateTransfer(
293
- updateableTransfer,
294
- { completedAt: Date.now(), errorCode: ErrorCode.TRANSACTION_REVERTED },
295
- updateListener,
296
- );
297
- return done(updateableTransfer);
298
- }
299
-
300
- /**
301
- * Check the confirmation count.
302
- * - update the targetConfirmationCount if it increased
303
- * - keeps polling until it's greater than requiredTargetConfirmationCount
304
- */
305
- const confirmationCount = await targetClient.getTransactionConfirmations({
306
- hash: updateableTransfer.targetTxHash as Address,
307
- });
308
- const hasMoreConfirmations = confirmationCount > updateableTransfer.targetConfirmationCount;
309
- const hasRequiredConfirmations = confirmationCount >= updateableTransfer.requiredTargetConfirmationCount;
310
-
311
- if (hasMoreConfirmations) {
312
- updateTransfer(updateableTransfer, { targetConfirmationCount: Number(confirmationCount) }, updateListener);
313
- }
314
-
315
- if (!hasRequiredConfirmations) {
316
- return;
317
- }
318
-
319
- updateTransfer(updateableTransfer, { completedAt: Date.now() }, updateListener);
320
- return done(updateableTransfer);
321
- };
322
-
323
- return retryPromise<BridgeTransfer>({
324
- promise: tracker,
325
- delay: getTrackingDelayByChainId(targetChainData.chainId),
326
- startAfter: INITIAL_DELAY,
327
- });
328
- };
329
-
330
- export function trackTransfer(bridge: BridgeService, params: TrackingParams) {
331
- let abortFn: (() => void) | undefined;
332
-
333
- const cancel = () => {
334
- abortFn?.();
335
- };
336
-
337
- const executeTracking = async () => {
338
- await bridge.ensureHasConfig();
339
-
340
- const { sourceProvider, targetProvider, updateListener, bridgeTransfer } = params;
341
-
342
- const { result: sourceTracker, cancel: cancelSourceTracking } = await trackSourceTx(bridge.config!, {
343
- sourceProvider,
344
- targetProvider,
345
- updateListener,
346
- bridgeTransfer,
347
- });
348
- abortFn = cancelSourceTracking;
349
- const transferAfterSourceFinished = await sourceTracker;
350
-
351
- const { result: targetTracker, cancel: cancelTargetTracking } = await trackTargetTx(bridge.config!, {
352
- sourceProvider,
353
- targetProvider,
354
- updateListener,
355
- bridgeTransfer: transferAfterSourceFinished,
356
- });
357
- abortFn = cancelTargetTracking;
358
- return targetTracker;
359
- };
360
-
361
- return {
362
- result: executeTracking(),
363
- cancel,
364
- };
365
- }