@avalabs/bridge-unified 1.0.1 → 2.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 (76) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +25 -0
  4. package/CHANGELOG.md +12 -0
  5. package/README.md +137 -71
  6. package/dist/index.cjs +11 -3
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +207 -34
  9. package/dist/index.d.ts +207 -34
  10. package/dist/index.js +4 -3
  11. package/dist/index.js.map +1 -1
  12. package/jest.config.js +9 -0
  13. package/package.json +15 -9
  14. package/src/bridges/cctp/__mocks__/asset.mock.ts +15 -0
  15. package/src/bridges/cctp/__mocks__/bridge-transfer.mock.ts +48 -0
  16. package/src/bridges/cctp/__mocks__/chain.mocks.ts +31 -0
  17. package/src/bridges/cctp/__mocks__/config.mock.ts +42 -0
  18. package/src/bridges/cctp/abis/erc20.ts +117 -0
  19. package/src/bridges/cctp/abis/message-transmitter.ts +318 -0
  20. package/src/bridges/cctp/abis/token-router.ts +843 -0
  21. package/src/bridges/cctp/factory.test.ts +73 -0
  22. package/src/bridges/cctp/factory.ts +32 -0
  23. package/src/bridges/cctp/handlers/get-assets.test.ts +47 -0
  24. package/src/bridges/cctp/handlers/get-assets.ts +27 -0
  25. package/src/bridges/cctp/handlers/get-fees.test.ts +61 -0
  26. package/src/bridges/cctp/handlers/get-fees.ts +26 -0
  27. package/src/bridges/cctp/handlers/track-transfer.test.ts +775 -0
  28. package/src/bridges/cctp/handlers/track-transfer.ts +365 -0
  29. package/src/bridges/cctp/handlers/transfer-asset.test.ts +429 -0
  30. package/src/bridges/cctp/handlers/transfer-asset.ts +179 -0
  31. package/src/bridges/cctp/index.ts +1 -0
  32. package/src/bridges/cctp/types/chain.ts +4 -0
  33. package/src/bridges/cctp/types/config.ts +19 -0
  34. package/src/bridges/cctp/utils/config.test.ts +49 -0
  35. package/src/bridges/cctp/utils/config.ts +36 -0
  36. package/src/bridges/cctp/utils/transfer-data.test.ts +83 -0
  37. package/src/bridges/cctp/utils/transfer-data.ts +48 -0
  38. package/src/errors/bridge-error.ts +11 -0
  39. package/src/errors/bridge-initialization-error.ts +9 -0
  40. package/src/errors/bridge-unavailable-error.ts +9 -0
  41. package/src/errors/index.ts +4 -20
  42. package/src/errors/invalid-params-error.ts +9 -0
  43. package/src/index.ts +3 -1
  44. package/src/types/asset.ts +26 -0
  45. package/src/types/bridge.ts +63 -0
  46. package/src/types/chain.ts +10 -0
  47. package/src/types/config.ts +10 -0
  48. package/src/types/environment.ts +4 -0
  49. package/src/types/error.ts +19 -0
  50. package/src/types/index.ts +9 -0
  51. package/src/types/provider.ts +12 -0
  52. package/src/types/signer.ts +18 -0
  53. package/src/types/transfer.ts +35 -0
  54. package/src/unified-bridge-service.test.ts +208 -0
  55. package/src/unified-bridge-service.ts +90 -0
  56. package/src/utils/bridge-types.test.ts +103 -0
  57. package/src/utils/bridge-types.ts +32 -0
  58. package/src/utils/caip2.test.ts +44 -0
  59. package/src/utils/caip2.ts +41 -0
  60. package/src/utils/client.test.ts +97 -0
  61. package/src/utils/client.ts +44 -0
  62. package/src/utils/ensure-config.test.ts +43 -0
  63. package/src/utils/ensure-config.ts +12 -0
  64. package/src/utils/index.ts +2 -0
  65. package/src/utils/network-fee.test.ts +24 -0
  66. package/src/utils/network-fee.ts +6 -0
  67. package/src/utils/retry-promise.test.ts +115 -0
  68. package/src/utils/retry-promise.ts +72 -0
  69. package/src/utils/wait.test.ts +33 -0
  70. package/src/utils/wait.ts +4 -0
  71. package/tsconfig.jest.json +7 -0
  72. package/tsconfig.json +2 -1
  73. package/src/bridge-service.ts +0 -18
  74. package/src/handlers/get-bridge-router.ts +0 -25
  75. package/src/handlers/submit-and-watch-bridge-transaction.ts +0 -1
  76. package/src/handlers/submit-bridge-transaction-step.ts +0 -22
@@ -0,0 +1,73 @@
1
+ import {
2
+ BridgeType,
3
+ type BridgeService,
4
+ type FeeParams,
5
+ type TransferParams,
6
+ type TrackingParams,
7
+ } from '../../types/bridge';
8
+ import { Environment } from '../../types/environment';
9
+ import { ensureHasConfig } from '../../utils/ensure-config';
10
+ import { CCTP_CONFIG } from './__mocks__/config.mock';
11
+ import { cctpBridgeFactory } from './factory';
12
+ import { getAssets } from './handlers/get-assets';
13
+ import { getFees } from './handlers/get-fees';
14
+ import { trackTransfer } from './handlers/track-transfer';
15
+ import { transferAsset } from './handlers/transfer-asset';
16
+ import { getConfig } from './utils/config';
17
+
18
+ jest.mock('./utils/config');
19
+ jest.mock('../../utils/ensure-config');
20
+ jest.mock('./handlers/get-assets');
21
+ jest.mock('./handlers/get-fees');
22
+ jest.mock('./handlers/transfer-asset');
23
+ jest.mock('./handlers/track-transfer');
24
+
25
+ describe('CCTP factory', () => {
26
+ let service: BridgeService;
27
+ const environment = Environment.TEST;
28
+
29
+ beforeEach(() => {
30
+ jest.resetAllMocks();
31
+ service = cctpBridgeFactory(environment);
32
+ (getConfig as jest.Mock).mockResolvedValue(CCTP_CONFIG);
33
+ });
34
+
35
+ it('contains the correct properties', async () => {
36
+ expect(service.type).toEqual(BridgeType.CCTP);
37
+ expect(service.config).toBeNull();
38
+
39
+ await service.updateConfig();
40
+ expect(getConfig).toHaveBeenCalledWith(environment);
41
+ expect(service.config).toStrictEqual(CCTP_CONFIG);
42
+ });
43
+
44
+ describe('handlers', () => {
45
+ it('calls ensureHasConfig correctly', async () => {
46
+ await service.ensureHasConfig();
47
+ expect(ensureHasConfig).toHaveBeenCalledWith(service);
48
+ });
49
+
50
+ it('calls ensureHasConfig correctly', async () => {
51
+ await service.getAssets();
52
+ expect(getAssets).toHaveBeenCalledWith(service);
53
+ });
54
+
55
+ it('calls getFees correctly', async () => {
56
+ const feeParams = { foo: 'bar' } as unknown as FeeParams;
57
+ await service.getFees(feeParams);
58
+ expect(getFees).toHaveBeenCalledWith(service, feeParams);
59
+ });
60
+
61
+ it('calls transferAsset correctly', async () => {
62
+ const transferAssetParams = { foo: 'bar' } as unknown as TransferParams;
63
+ await service.transferAsset(transferAssetParams);
64
+ expect(transferAsset).toHaveBeenCalledWith(service, transferAssetParams, environment);
65
+ });
66
+
67
+ it('calls trackTransfer correctly', () => {
68
+ const trackTransferParams = { foo: 'bar' } as unknown as TrackingParams;
69
+ service.trackTransfer(trackTransferParams);
70
+ expect(trackTransfer).toHaveBeenCalledWith(service, trackTransferParams);
71
+ });
72
+ });
73
+ });
@@ -0,0 +1,32 @@
1
+ import { type BridgeServiceFactory, BridgeType } from '../../types/bridge';
2
+ import { getConfig } from './utils/config';
3
+ import { getAssets } from './handlers/get-assets';
4
+ import { getFees } from './handlers/get-fees';
5
+ import { transferAsset } from './handlers/transfer-asset';
6
+ import { ensureHasConfig } from '../../utils/ensure-config';
7
+ import { trackTransfer } from './handlers/track-transfer';
8
+
9
+ export const cctpBridgeFactory: BridgeServiceFactory = (environment) => {
10
+ return {
11
+ type: BridgeType.CCTP,
12
+ config: null,
13
+ updateConfig: async function () {
14
+ this.config = await getConfig(environment);
15
+ },
16
+ ensureHasConfig: async function () {
17
+ return ensureHasConfig(this);
18
+ },
19
+ getAssets: async function () {
20
+ return getAssets(this);
21
+ },
22
+ getFees: async function (params) {
23
+ return getFees(this, params);
24
+ },
25
+ transferAsset: async function (params) {
26
+ return transferAsset(this, params, environment);
27
+ },
28
+ trackTransfer: function (params) {
29
+ return trackTransfer(this, params);
30
+ },
31
+ };
32
+ };
@@ -0,0 +1,47 @@
1
+ import { TokenType, type BridgeService, BridgeType } from '../../../types';
2
+ import { SOURCE_CHAIN, TARGET_CHAIN } from '../__mocks__/chain.mocks';
3
+ import { CCTP_CONFIG, SOURCE_TOKEN_ADDRESS, TARGET_TOKEN_ADDRESS } from '../__mocks__/config.mock';
4
+ import { getAssets } from './get-assets';
5
+
6
+ describe('CCTP getAssets', () => {
7
+ const bridgeMock = {
8
+ ensureHasConfig: jest.fn(),
9
+ config: CCTP_CONFIG,
10
+ } as unknown as BridgeService;
11
+
12
+ it('calls ensureHasConfig', async () => {
13
+ await getAssets(bridgeMock);
14
+ expect(bridgeMock.ensureHasConfig).toHaveBeenCalled();
15
+ });
16
+
17
+ it('returns the correct assets', async () => {
18
+ const assets = await getAssets(bridgeMock);
19
+
20
+ expect(assets).toStrictEqual({
21
+ [SOURCE_CHAIN.chainId]: [
22
+ {
23
+ address: SOURCE_TOKEN_ADDRESS,
24
+ name: 'USD Coin',
25
+ symbol: 'USDC',
26
+ decimals: 6,
27
+ type: TokenType.ERC20,
28
+ destinations: {
29
+ [TARGET_CHAIN.chainId]: [BridgeType.CCTP],
30
+ },
31
+ },
32
+ ],
33
+ [TARGET_CHAIN.chainId]: [
34
+ {
35
+ address: TARGET_TOKEN_ADDRESS,
36
+ name: 'USD Coin',
37
+ symbol: 'USDC',
38
+ decimals: 6,
39
+ type: TokenType.ERC20,
40
+ destinations: {
41
+ [SOURCE_CHAIN.chainId]: [BridgeType.CCTP],
42
+ },
43
+ },
44
+ ],
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,27 @@
1
+ import { TokenType, type BridgeService, type ChainAssetMap, type DestinationInfo, BridgeType } from '../../../types';
2
+
3
+ export async function getAssets(bridge: BridgeService) {
4
+ await bridge.ensureHasConfig();
5
+
6
+ const chainIds = bridge.config!.map((chainData) => chainData.chainId);
7
+
8
+ return bridge.config!.reduce<ChainAssetMap>((assets, chainData) => {
9
+ assets[chainData.chainId] = chainData.tokens.map((asset) => ({
10
+ ...asset,
11
+ type: TokenType.ERC20,
12
+ destinations: chainIds.reduce<DestinationInfo>((destinations, chainId) => {
13
+ if (chainData.chainId !== chainId) {
14
+ if (!destinations[chainId]) {
15
+ destinations[chainId] = [];
16
+ }
17
+
18
+ destinations[chainId]?.push(BridgeType.CCTP);
19
+ }
20
+
21
+ return destinations;
22
+ }, {}),
23
+ }));
24
+
25
+ return assets;
26
+ }, {});
27
+ }
@@ -0,0 +1,61 @@
1
+ import type { BridgeService, FeeParams } from '../../../types';
2
+ import { getClientForChain } from '../../../utils/client';
3
+ import { BRIDGE_ASSET } from '../__mocks__/asset.mock';
4
+ import { SOURCE_CHAIN, TARGET_CHAIN } from '../__mocks__/chain.mocks';
5
+ import { CCTP_CONFIG, SOURCE_ROUTER_ADDRESS } from '../__mocks__/config.mock';
6
+ import { TOKEN_ROUTER_ABI } from '../abis/token-router';
7
+ import { getFees } from './get-fees';
8
+
9
+ jest.mock('../../../utils/client');
10
+
11
+ describe('CCTP getFees', () => {
12
+ const feeParams = {
13
+ sourceChain: SOURCE_CHAIN,
14
+ targetChain: TARGET_CHAIN,
15
+ asset: BRIDGE_ASSET,
16
+ amount: 1_000_000,
17
+ } as unknown as FeeParams;
18
+
19
+ const bridgeMock = {
20
+ ensureHasConfig: jest.fn(),
21
+ config: CCTP_CONFIG,
22
+ } as unknown as BridgeService;
23
+
24
+ const clientMock = {
25
+ readContract: jest.fn(),
26
+ };
27
+
28
+ beforeEach(() => {
29
+ jest.resetAllMocks();
30
+ (getClientForChain as jest.Mock).mockReturnValue(clientMock);
31
+ });
32
+
33
+ it('calls ensureHasConfig', async () => {
34
+ const error = new Error('error');
35
+ (bridgeMock.ensureHasConfig as jest.Mock).mockRejectedValueOnce(error);
36
+ await expect(getFees(bridgeMock, feeParams)).rejects.toThrow(error);
37
+
38
+ expect(bridgeMock.ensureHasConfig).toHaveBeenCalled();
39
+ expect(getClientForChain).not.toHaveBeenCalled();
40
+ expect(clientMock.readContract).not.toHaveBeenCalled();
41
+ });
42
+
43
+ it('returns the fee correctly', async () => {
44
+ const feeAmount = 2_000_000;
45
+ clientMock.readContract.mockResolvedValueOnce(feeAmount);
46
+
47
+ const fees = await getFees(bridgeMock, feeParams);
48
+
49
+ expect(fees).toStrictEqual({
50
+ [BRIDGE_ASSET.address!]: feeAmount,
51
+ });
52
+ expect(bridgeMock.ensureHasConfig).toHaveBeenCalled();
53
+ expect(getClientForChain).toHaveBeenCalledWith({ chain: feeParams.sourceChain, provider: undefined });
54
+ expect(clientMock.readContract).toHaveBeenCalledWith({
55
+ address: SOURCE_ROUTER_ADDRESS,
56
+ abi: TOKEN_ROUTER_ABI,
57
+ functionName: 'calculateFee',
58
+ args: [feeParams.amount, 1],
59
+ });
60
+ });
61
+ });
@@ -0,0 +1,26 @@
1
+ import type { AssetFeeMap, BridgeService, FeeParams } from '../../../types';
2
+ import { getClientForChain } from '../../../utils/client';
3
+ import { TOKEN_ROUTER_ABI } from '../abis/token-router';
4
+ import { getTransferData } from '../utils/transfer-data';
5
+
6
+ export async function getFees(bridge: BridgeService, params: FeeParams) {
7
+ await bridge.ensureHasConfig();
8
+
9
+ const { sourceChain, targetChain, asset, amount, provider } = params;
10
+ const { sourceChainData, targetChainData, burnToken } = getTransferData(
11
+ { sourceChain, targetChain, asset, amount },
12
+ bridge.config!,
13
+ );
14
+
15
+ const client = getClientForChain({ chain: sourceChain, provider });
16
+ const feeAmount = await client.readContract({
17
+ address: sourceChainData.tokenRouterAddress,
18
+ abi: TOKEN_ROUTER_ABI,
19
+ functionName: 'calculateFee',
20
+ args: [amount, targetChainData.domain],
21
+ });
22
+
23
+ return {
24
+ [burnToken.address]: feeAmount,
25
+ } as AssetFeeMap;
26
+ }