@gibs/bridge-sdk 1.10.1 → 1.12.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/package.json +12 -5
- package/src/abis.ts +3 -70
- package/src/chain-info.test.ts +968 -0
- package/src/chain-info.ts +161 -127
- package/src/chains.test.ts +123 -0
- package/src/chains.ts +5 -5
- package/src/config.test.ts +679 -0
- package/src/config.ts +138 -118
- package/src/fee-type.test.ts +45 -0
- package/src/fee-type.ts +2 -2
- package/src/image-links.test.ts +160 -0
- package/src/image-links.ts +20 -16
- package/src/index.ts +8 -8
- package/src/settings.test.ts +2754 -0
- package/src/settings.ts +325 -348
- package/src/types.ts +25 -25
- package/tsconfig.json +2 -1
- package/vitest.config.ts +13 -0
- package/dist/src/abis.d.ts +0 -384
- package/dist/src/abis.js +0 -68
- package/dist/src/chain-info.d.ts +0 -130
- package/dist/src/chain-info.js +0 -350
- package/dist/src/chains.d.ts +0 -6
- package/dist/src/chains.js +0 -86
- package/dist/src/config.d.ts +0 -136
- package/dist/src/config.js +0 -386
- package/dist/src/fee-type.d.ts +0 -10
- package/dist/src/fee-type.js +0 -9
- package/dist/src/image-links.d.ts +0 -41
- package/dist/src/image-links.js +0 -45
- package/dist/src/index.d.ts +0 -8
- package/dist/src/index.js +0 -8
- package/dist/src/settings.d.ts +0 -357
- package/dist/src/settings.js +0 -642
- package/dist/src/types.d.ts +0 -44
- package/dist/src/types.js +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,2754 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Mock viem functions
|
|
4
|
+
vi.mock('viem', async () => {
|
|
5
|
+
const actual = await vi.importActual<typeof import('viem')>('viem');
|
|
6
|
+
return {
|
|
7
|
+
...actual,
|
|
8
|
+
encodeFunctionData: vi.fn(() => '0xMOCKED_FUNCTION_DATA' as any),
|
|
9
|
+
encodeAbiParameters: vi.fn(() => '0xMOCKED_ABI_PARAMS' as any),
|
|
10
|
+
concatHex: vi.fn((parts: any[]) => parts.join('') as any),
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
import { getAddress, type Hex, zeroAddress } from 'viem';
|
|
15
|
+
import { Chains, nativeAssetOut, type Pathway } from './config';
|
|
16
|
+
import { FeeType } from './fee-type';
|
|
17
|
+
import {
|
|
18
|
+
amountAfterBridgeFee,
|
|
19
|
+
amountToBridge,
|
|
20
|
+
assetOut,
|
|
21
|
+
assetOutKey,
|
|
22
|
+
availableCompensationMaximum,
|
|
23
|
+
basisPoints,
|
|
24
|
+
bridgeCost,
|
|
25
|
+
bridgePathway,
|
|
26
|
+
desiredCompensationRatio,
|
|
27
|
+
desiredExcessCompensationBasisPoints,
|
|
28
|
+
destinationDataParam,
|
|
29
|
+
estimatedAmountOut,
|
|
30
|
+
estimatedCost,
|
|
31
|
+
estimatedFee,
|
|
32
|
+
estimatedNativeNetworkCost,
|
|
33
|
+
estimatedTokenNetworkCost,
|
|
34
|
+
feeDirectorStructEncoded,
|
|
35
|
+
interactingWithBridgeToken,
|
|
36
|
+
isUndercompensated,
|
|
37
|
+
limit,
|
|
38
|
+
networkSwitchAssetOutAddress,
|
|
39
|
+
oneEther,
|
|
40
|
+
oneTokenInt,
|
|
41
|
+
reasonableFixedFee,
|
|
42
|
+
reasonablePercentFee,
|
|
43
|
+
reasonablePercentOnGasLimit,
|
|
44
|
+
reasonablePercentOnTopOfGasFee,
|
|
45
|
+
transactionInputs,
|
|
46
|
+
} from './settings';
|
|
47
|
+
import type { BridgeKey, Token } from './types';
|
|
48
|
+
|
|
49
|
+
describe('settings', () => {
|
|
50
|
+
describe('constants', () => {
|
|
51
|
+
it('should have basisPoints as 10000', () => {
|
|
52
|
+
expect(basisPoints).toBe(10_000n);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should have oneEther as 10^18', () => {
|
|
56
|
+
expect(oneEther).toBe(1000000000000000000n);
|
|
57
|
+
expect(oneEther).toBe(10n ** 18n);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('limit', () => {
|
|
62
|
+
it('should return 0 for null', () => {
|
|
63
|
+
expect(limit(null)).toBe(0n);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should return 0 for falsy bigint', () => {
|
|
67
|
+
expect(limit(0n)).toBe(0n);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return the value for positive bigint', () => {
|
|
71
|
+
expect(limit(1000n)).toBe(1000n);
|
|
72
|
+
expect(limit(12345678901234567890n)).toBe(12345678901234567890n);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('oneTokenInt', () => {
|
|
77
|
+
it('should return 1n when assetIn is null', () => {
|
|
78
|
+
expect(oneTokenInt({ assetIn: null })).toBe(1n);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should calculate 10^decimals for token', () => {
|
|
82
|
+
const usdt = { decimals: 6, address: '0x123', chainId: 1, name: 'USDT', symbol: 'USDT' };
|
|
83
|
+
expect(oneTokenInt({ assetIn: usdt })).toBe(1000000n); // 10^6
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should handle 18 decimal tokens (standard ERC20)', () => {
|
|
87
|
+
const dai = { decimals: 18, address: '0x456', chainId: 1, name: 'DAI', symbol: 'DAI' };
|
|
88
|
+
expect(oneTokenInt({ assetIn: dai })).toBe(10n ** 18n);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should handle tokens with 0 decimals', () => {
|
|
92
|
+
const token = { decimals: 0, address: '0x789', chainId: 1, name: 'TOKEN', symbol: 'TKN' };
|
|
93
|
+
expect(oneTokenInt({ assetIn: token })).toBe(1n);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should handle tokens with large decimal values', () => {
|
|
97
|
+
const token = { decimals: 24, address: '0xabc', chainId: 1, name: 'TOKEN', symbol: 'TKN' };
|
|
98
|
+
expect(oneTokenInt({ assetIn: token })).toBe(10n ** 24n);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('interactingWithBridgeToken', () => {
|
|
103
|
+
const foreignAddress = '0xdAC17F958D2ee523a2206206994597C13D831ec7';
|
|
104
|
+
const homeAddress = '0xA1077a294dDE1B09bB078844df40758a5D0f9a27';
|
|
105
|
+
|
|
106
|
+
it('should return false when assetInAddress is null', () => {
|
|
107
|
+
const result = interactingWithBridgeToken({
|
|
108
|
+
assetLink: null,
|
|
109
|
+
assetInAddress: null,
|
|
110
|
+
});
|
|
111
|
+
expect(result).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should return false when assetLink is null', () => {
|
|
115
|
+
const result = interactingWithBridgeToken({
|
|
116
|
+
assetLink: null,
|
|
117
|
+
assetInAddress: foreignAddress,
|
|
118
|
+
});
|
|
119
|
+
expect(result).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should return true when assetInAddress matches foreign address', () => {
|
|
123
|
+
const assetLink = {
|
|
124
|
+
toForeign: { foreign: foreignAddress },
|
|
125
|
+
toHome: { home: homeAddress },
|
|
126
|
+
};
|
|
127
|
+
const result = interactingWithBridgeToken({
|
|
128
|
+
assetLink: assetLink as any,
|
|
129
|
+
assetInAddress: foreignAddress,
|
|
130
|
+
});
|
|
131
|
+
expect(result).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should return true when assetInAddress matches home address', () => {
|
|
135
|
+
const assetLink = {
|
|
136
|
+
toForeign: { foreign: foreignAddress },
|
|
137
|
+
toHome: { home: homeAddress },
|
|
138
|
+
};
|
|
139
|
+
const result = interactingWithBridgeToken({
|
|
140
|
+
assetLink: assetLink as any,
|
|
141
|
+
assetInAddress: homeAddress,
|
|
142
|
+
});
|
|
143
|
+
expect(result).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should handle checksummed addresses correctly', () => {
|
|
147
|
+
const assetLink = {
|
|
148
|
+
toForeign: { foreign: foreignAddress.toLowerCase() },
|
|
149
|
+
toHome: { home: homeAddress.toLowerCase() },
|
|
150
|
+
};
|
|
151
|
+
// Should match even with different casing due to getAddress checksumming
|
|
152
|
+
const result = interactingWithBridgeToken({
|
|
153
|
+
assetLink: assetLink as any,
|
|
154
|
+
assetInAddress: foreignAddress, // checksummed
|
|
155
|
+
});
|
|
156
|
+
expect(result).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should return false when assetInAddress does not match either address', () => {
|
|
160
|
+
const assetLink = {
|
|
161
|
+
toForeign: { foreign: foreignAddress },
|
|
162
|
+
toHome: { home: homeAddress },
|
|
163
|
+
};
|
|
164
|
+
const result = interactingWithBridgeToken({
|
|
165
|
+
assetLink: assetLink as any,
|
|
166
|
+
assetInAddress: '0x0000000000000000000000000000000000000000',
|
|
167
|
+
});
|
|
168
|
+
expect(result).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should return false when toForeign is missing', () => {
|
|
172
|
+
const assetLink = {
|
|
173
|
+
toHome: { home: homeAddress },
|
|
174
|
+
};
|
|
175
|
+
const result = interactingWithBridgeToken({
|
|
176
|
+
assetLink: assetLink as any,
|
|
177
|
+
assetInAddress: foreignAddress,
|
|
178
|
+
});
|
|
179
|
+
expect(result).toBe(false);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should return false when toHome is missing', () => {
|
|
183
|
+
const assetLink = {
|
|
184
|
+
toForeign: { foreign: foreignAddress },
|
|
185
|
+
};
|
|
186
|
+
const result = interactingWithBridgeToken({
|
|
187
|
+
assetLink: assetLink as any,
|
|
188
|
+
assetInAddress: homeAddress,
|
|
189
|
+
});
|
|
190
|
+
expect(result).toBe(false);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('amountAfterBridgeFee', () => {
|
|
195
|
+
it('should return null when bridgeCost is null', () => {
|
|
196
|
+
const result = amountAfterBridgeFee({
|
|
197
|
+
amountToBridge: 1000n,
|
|
198
|
+
bridgeCost: null,
|
|
199
|
+
});
|
|
200
|
+
expect(result).toBeNull();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should calculate amount after fee correctly', () => {
|
|
204
|
+
const result = amountAfterBridgeFee({
|
|
205
|
+
amountToBridge: 1000n,
|
|
206
|
+
bridgeCost: 100n,
|
|
207
|
+
});
|
|
208
|
+
expect(result).toBe(900n);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should return 0 when fee exceeds amount', () => {
|
|
212
|
+
const result = amountAfterBridgeFee({
|
|
213
|
+
amountToBridge: 100n,
|
|
214
|
+
bridgeCost: 200n,
|
|
215
|
+
});
|
|
216
|
+
expect(result).toBe(0n);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should handle zero fee', () => {
|
|
220
|
+
const result = amountAfterBridgeFee({
|
|
221
|
+
amountToBridge: 1000n,
|
|
222
|
+
bridgeCost: 0n,
|
|
223
|
+
});
|
|
224
|
+
expect(result).toBe(1000n);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should handle exact fee amount', () => {
|
|
228
|
+
const result = amountAfterBridgeFee({
|
|
229
|
+
amountToBridge: 1000n,
|
|
230
|
+
bridgeCost: 1000n,
|
|
231
|
+
});
|
|
232
|
+
expect(result).toBe(0n);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should handle large amounts', () => {
|
|
236
|
+
const largeAmount = 1000000000000000000n; // 1 ETH in wei
|
|
237
|
+
const largeFee = 10000000000000000n; // 0.01 ETH
|
|
238
|
+
const result = amountAfterBridgeFee({
|
|
239
|
+
amountToBridge: largeAmount,
|
|
240
|
+
bridgeCost: largeFee,
|
|
241
|
+
});
|
|
242
|
+
expect(result).toBe(largeAmount - largeFee);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should maintain precision with bigint', () => {
|
|
246
|
+
const amount = 123456789012345678n;
|
|
247
|
+
const fee = 1n;
|
|
248
|
+
const result = amountAfterBridgeFee({
|
|
249
|
+
amountToBridge: amount,
|
|
250
|
+
bridgeCost: fee,
|
|
251
|
+
});
|
|
252
|
+
expect(result).toBe(123456789012345677n);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe('assetOutKey', () => {
|
|
257
|
+
it('should return null when bridgeKeyPath is empty', () => {
|
|
258
|
+
expect(
|
|
259
|
+
assetOutKey({
|
|
260
|
+
bridgeKeyPath: '',
|
|
261
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
262
|
+
unwrap: false,
|
|
263
|
+
}),
|
|
264
|
+
).toBeNull();
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should return null when assetInAddress is null', () => {
|
|
268
|
+
expect(
|
|
269
|
+
assetOutKey({
|
|
270
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
271
|
+
assetInAddress: null,
|
|
272
|
+
unwrap: false,
|
|
273
|
+
}),
|
|
274
|
+
).toBeNull();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should return null when assetInAddress is undefined', () => {
|
|
278
|
+
expect(
|
|
279
|
+
assetOutKey({
|
|
280
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
281
|
+
assetInAddress: undefined,
|
|
282
|
+
unwrap: false,
|
|
283
|
+
}),
|
|
284
|
+
).toBeNull();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should create key for ERC20 asset', () => {
|
|
288
|
+
const result = assetOutKey({
|
|
289
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
290
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
291
|
+
unwrap: false,
|
|
292
|
+
});
|
|
293
|
+
expect(result).toBe('pulsechain/0x1/0x171/erc20/0xdac17f958d2ee523a2206206994597c13d831ec7');
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('should create key for native (unwrapped) asset', () => {
|
|
297
|
+
const result = assetOutKey({
|
|
298
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
299
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
300
|
+
unwrap: true,
|
|
301
|
+
});
|
|
302
|
+
expect(result).toBe('pulsechain/0x1/0x171/native/0xdac17f958d2ee523a2206206994597c13d831ec7');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should lowercase the address', () => {
|
|
306
|
+
const result = assetOutKey({
|
|
307
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
308
|
+
assetInAddress: '0xDAC17F958D2EE523A2206206994597C13D831EC7', // uppercase
|
|
309
|
+
unwrap: false,
|
|
310
|
+
});
|
|
311
|
+
expect(result).toContain('0xdac17f958d2ee523a2206206994597c13d831ec7');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should handle different bridge paths', () => {
|
|
315
|
+
const key1 = assetOutKey({
|
|
316
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
317
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
318
|
+
unwrap: false,
|
|
319
|
+
});
|
|
320
|
+
const key2 = assetOutKey({
|
|
321
|
+
bridgeKeyPath: 'tokensex/0x38/0x171',
|
|
322
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
323
|
+
unwrap: false,
|
|
324
|
+
});
|
|
325
|
+
expect(key1).not.toBe(key2);
|
|
326
|
+
expect(key1).toContain('pulsechain');
|
|
327
|
+
expect(key2).toContain('tokensex');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should differentiate between native and erc20 for same asset', () => {
|
|
331
|
+
const erc20Key = assetOutKey({
|
|
332
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
333
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
334
|
+
unwrap: false,
|
|
335
|
+
});
|
|
336
|
+
const nativeKey = assetOutKey({
|
|
337
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
338
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
339
|
+
unwrap: true,
|
|
340
|
+
});
|
|
341
|
+
expect(erc20Key).not.toBe(nativeKey);
|
|
342
|
+
expect(erc20Key).toContain('/erc20/');
|
|
343
|
+
expect(nativeKey).toContain('/native/');
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe('desiredCompensationRatio', () => {
|
|
348
|
+
it('should return oneEther when excess is 0', () => {
|
|
349
|
+
const result = desiredCompensationRatio({
|
|
350
|
+
desiredExcessCompensationBasisPoints: 0n,
|
|
351
|
+
});
|
|
352
|
+
expect(result).toBe(oneEther);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should calculate ratio for 10% excess (1000 basis points)', () => {
|
|
356
|
+
const result = desiredCompensationRatio({
|
|
357
|
+
desiredExcessCompensationBasisPoints: 1_000n,
|
|
358
|
+
});
|
|
359
|
+
// 1 ETH + (1000 * 1 ETH / 10000) = 1 ETH + 0.1 ETH = 1.1 ETH
|
|
360
|
+
expect(result).toBe(oneEther + (1_000n * oneEther) / basisPoints);
|
|
361
|
+
expect(result).toBe(1100000000000000000n); // 1.1 ETH
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should calculate ratio for 50% excess (5000 basis points)', () => {
|
|
365
|
+
const result = desiredCompensationRatio({
|
|
366
|
+
desiredExcessCompensationBasisPoints: 5_000n,
|
|
367
|
+
});
|
|
368
|
+
// 1 ETH + (5000 * 1 ETH / 10000) = 1 ETH + 0.5 ETH = 1.5 ETH
|
|
369
|
+
expect(result).toBe(1500000000000000000n); // 1.5 ETH
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('should calculate ratio for 100% excess (10000 basis points)', () => {
|
|
373
|
+
const result = desiredCompensationRatio({
|
|
374
|
+
desiredExcessCompensationBasisPoints: 10_000n,
|
|
375
|
+
});
|
|
376
|
+
// 1 ETH + (10000 * 1 ETH / 10000) = 1 ETH + 1 ETH = 2 ETH
|
|
377
|
+
expect(result).toBe(2000000000000000000n); // 2 ETH
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should handle small excess amounts (1 basis point)', () => {
|
|
381
|
+
const result = desiredCompensationRatio({
|
|
382
|
+
desiredExcessCompensationBasisPoints: 1n,
|
|
383
|
+
});
|
|
384
|
+
// 1 ETH + (1 * 1 ETH / 10000) = 1 ETH + 0.0001 ETH
|
|
385
|
+
expect(result).toBe(1000100000000000000n);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should use correct formula with oneEther and basisPoints', () => {
|
|
389
|
+
const excess = 2_500n; // 25%
|
|
390
|
+
const result = desiredCompensationRatio({
|
|
391
|
+
desiredExcessCompensationBasisPoints: excess,
|
|
392
|
+
});
|
|
393
|
+
const expected = oneEther + (excess * oneEther) / basisPoints;
|
|
394
|
+
expect(result).toBe(expected);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should maintain precision with bigint arithmetic', () => {
|
|
398
|
+
const excess = 3_333n; // 33.33%
|
|
399
|
+
const result = desiredCompensationRatio({
|
|
400
|
+
desiredExcessCompensationBasisPoints: excess,
|
|
401
|
+
});
|
|
402
|
+
// Should not lose precision
|
|
403
|
+
expect(result).toBe(oneEther + (3_333n * oneEther) / basisPoints);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
describe('amountToBridge', () => {
|
|
408
|
+
const mockToken = {
|
|
409
|
+
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
410
|
+
chainId: 1,
|
|
411
|
+
name: 'USDT',
|
|
412
|
+
symbol: 'USDT',
|
|
413
|
+
decimals: 6,
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
it('should return amountIn when valid', () => {
|
|
417
|
+
const result = amountToBridge({
|
|
418
|
+
amountIn: 1000n,
|
|
419
|
+
assetIn: mockToken,
|
|
420
|
+
});
|
|
421
|
+
expect(result).toBe(1000n);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should return 0n when amountIn is null', () => {
|
|
425
|
+
const result = amountToBridge({
|
|
426
|
+
amountIn: null,
|
|
427
|
+
assetIn: mockToken,
|
|
428
|
+
});
|
|
429
|
+
expect(result).toBe(0n);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('should return 0n when amountIn is 0n', () => {
|
|
433
|
+
const result = amountToBridge({
|
|
434
|
+
amountIn: 0n,
|
|
435
|
+
assetIn: mockToken,
|
|
436
|
+
});
|
|
437
|
+
expect(result).toBe(0n);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('should return 0n when assetIn is null', () => {
|
|
441
|
+
const result = amountToBridge({
|
|
442
|
+
amountIn: 1000n,
|
|
443
|
+
assetIn: null,
|
|
444
|
+
});
|
|
445
|
+
expect(result).toBe(0n);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('should return 0n when both are null', () => {
|
|
449
|
+
const result = amountToBridge({
|
|
450
|
+
amountIn: null,
|
|
451
|
+
assetIn: null,
|
|
452
|
+
});
|
|
453
|
+
expect(result).toBe(0n);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should handle large amounts', () => {
|
|
457
|
+
const largeAmount = 1000000000000000000n; // 1 ETH in wei
|
|
458
|
+
const result = amountToBridge({
|
|
459
|
+
amountIn: largeAmount,
|
|
460
|
+
assetIn: mockToken,
|
|
461
|
+
});
|
|
462
|
+
expect(result).toBe(largeAmount);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('should validate both amountIn and assetIn together', () => {
|
|
466
|
+
// Only returns the amount if BOTH are valid
|
|
467
|
+
expect(amountToBridge({ amountIn: 100n, assetIn: mockToken })).toBe(100n);
|
|
468
|
+
expect(amountToBridge({ amountIn: null, assetIn: mockToken })).toBe(0n);
|
|
469
|
+
expect(amountToBridge({ amountIn: 100n, assetIn: null })).toBe(0n);
|
|
470
|
+
expect(amountToBridge({ amountIn: 0n, assetIn: mockToken })).toBe(0n);
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
describe('bridgeCost', () => {
|
|
475
|
+
it('should return null when bridgeFee is null', () => {
|
|
476
|
+
const result = bridgeCost({
|
|
477
|
+
amountToBridge: 1000n,
|
|
478
|
+
bridgeFee: null,
|
|
479
|
+
});
|
|
480
|
+
expect(result).toBeNull();
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should calculate cost correctly with percentage fee', () => {
|
|
484
|
+
const amount = oneEther; // 1 ETH
|
|
485
|
+
const fee = oneEther / 100n; // 1% = 0.01 ETH
|
|
486
|
+
const result = bridgeCost({
|
|
487
|
+
amountToBridge: amount,
|
|
488
|
+
bridgeFee: fee,
|
|
489
|
+
});
|
|
490
|
+
// (1 ETH * 0.01 ETH) / 1 ETH = 0.01 ETH
|
|
491
|
+
expect(result).toBe((amount * fee) / oneEther);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('should handle zero fee', () => {
|
|
495
|
+
const result = bridgeCost({
|
|
496
|
+
amountToBridge: 1000n,
|
|
497
|
+
bridgeFee: 0n,
|
|
498
|
+
});
|
|
499
|
+
expect(result).toBe(0n);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('should calculate with small fee', () => {
|
|
503
|
+
const amount = 1000000n; // 1 USDT (6 decimals)
|
|
504
|
+
const fee = oneEther / 1000n; // 0.1% fee
|
|
505
|
+
const result = bridgeCost({
|
|
506
|
+
amountToBridge: amount,
|
|
507
|
+
bridgeFee: fee,
|
|
508
|
+
});
|
|
509
|
+
expect(result).toBe((amount * fee) / oneEther);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('should maintain precision with large numbers', () => {
|
|
513
|
+
const amount = 123456789012345678n;
|
|
514
|
+
const fee = oneEther / 50n; // 2% fee
|
|
515
|
+
const result = bridgeCost({
|
|
516
|
+
amountToBridge: amount,
|
|
517
|
+
bridgeFee: fee,
|
|
518
|
+
});
|
|
519
|
+
const expected = (amount * fee) / oneEther;
|
|
520
|
+
expect(result).toBe(expected);
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('should use oneEther as divisor for percentage calculation', () => {
|
|
524
|
+
const amount = 500n * oneEther; // 500 ETH
|
|
525
|
+
const fee = oneEther / 200n; // 0.5% = 0.005 ETH per 1 ETH
|
|
526
|
+
const result = bridgeCost({
|
|
527
|
+
amountToBridge: amount,
|
|
528
|
+
bridgeFee: fee,
|
|
529
|
+
});
|
|
530
|
+
// (500 ETH * 0.005) / 1 = 2.5 ETH
|
|
531
|
+
expect(result).toBe((500n * oneEther * fee) / oneEther);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it('should handle fee equal to oneEther (100%)', () => {
|
|
535
|
+
const amount = 1000n * oneEther;
|
|
536
|
+
const fee = oneEther; // 100% fee
|
|
537
|
+
const result = bridgeCost({
|
|
538
|
+
amountToBridge: amount,
|
|
539
|
+
bridgeFee: fee,
|
|
540
|
+
});
|
|
541
|
+
// (1000 ETH * 1 ETH) / 1 ETH = 1000 ETH (entire amount)
|
|
542
|
+
expect(result).toBe(amount);
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
describe('networkSwitchAssetOutAddress', () => {
|
|
547
|
+
const mockToken = {
|
|
548
|
+
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
549
|
+
chainId: 1,
|
|
550
|
+
name: 'USDT',
|
|
551
|
+
symbol: 'USDT',
|
|
552
|
+
decimals: 6,
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
const wrappedPLS = {
|
|
556
|
+
address: nativeAssetOut[Chains.PLS],
|
|
557
|
+
chainId: 369,
|
|
558
|
+
name: 'WPLS',
|
|
559
|
+
symbol: 'WPLS',
|
|
560
|
+
decimals: 18,
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
it('should return null when assetOut is null', () => {
|
|
564
|
+
const result = networkSwitchAssetOutAddress({
|
|
565
|
+
assetOut: null,
|
|
566
|
+
unwrap: false,
|
|
567
|
+
toChain: Chains.ETH,
|
|
568
|
+
});
|
|
569
|
+
expect(result).toBeNull();
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it('should return assetOut address when not unwrapping', () => {
|
|
573
|
+
const result = networkSwitchAssetOutAddress({
|
|
574
|
+
assetOut: mockToken,
|
|
575
|
+
unwrap: false,
|
|
576
|
+
toChain: Chains.ETH,
|
|
577
|
+
});
|
|
578
|
+
expect(result).toBe(mockToken.address);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it('should return zeroAddress when unwrapping native asset', () => {
|
|
582
|
+
const result = networkSwitchAssetOutAddress({
|
|
583
|
+
assetOut: wrappedPLS,
|
|
584
|
+
unwrap: true,
|
|
585
|
+
toChain: Chains.PLS,
|
|
586
|
+
});
|
|
587
|
+
expect(result).toBe(zeroAddress);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('should return assetOut address when unwrapping non-native asset', () => {
|
|
591
|
+
const result = networkSwitchAssetOutAddress({
|
|
592
|
+
assetOut: mockToken,
|
|
593
|
+
unwrap: true,
|
|
594
|
+
toChain: Chains.ETH,
|
|
595
|
+
});
|
|
596
|
+
expect(result).toBe(mockToken.address);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('should check native asset out per chain', () => {
|
|
600
|
+
const wrappedETH = {
|
|
601
|
+
address: nativeAssetOut[Chains.ETH],
|
|
602
|
+
chainId: 1,
|
|
603
|
+
name: 'WETH',
|
|
604
|
+
symbol: 'WETH',
|
|
605
|
+
decimals: 18,
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const result = networkSwitchAssetOutAddress({
|
|
609
|
+
assetOut: wrappedETH,
|
|
610
|
+
unwrap: true,
|
|
611
|
+
toChain: Chains.ETH,
|
|
612
|
+
});
|
|
613
|
+
expect(result).toBe(zeroAddress);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it('should not unwrap if chain does not match', () => {
|
|
617
|
+
// WPLS address but different chain
|
|
618
|
+
const result = networkSwitchAssetOutAddress({
|
|
619
|
+
assetOut: wrappedPLS,
|
|
620
|
+
unwrap: true,
|
|
621
|
+
toChain: Chains.ETH, // Different chain
|
|
622
|
+
});
|
|
623
|
+
// Should NOT unwrap because nativeAssetOut[ETH] !== WPLS address
|
|
624
|
+
expect(result).toBe(wrappedPLS.address);
|
|
625
|
+
expect(result).not.toBe(zeroAddress);
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
it('should handle all supported chains', () => {
|
|
629
|
+
Object.entries(nativeAssetOut).forEach(([chain, wrappedAddress]) => {
|
|
630
|
+
const token = {
|
|
631
|
+
address: wrappedAddress,
|
|
632
|
+
chainId: Number(chain),
|
|
633
|
+
name: 'Wrapped',
|
|
634
|
+
symbol: 'W',
|
|
635
|
+
decimals: 18,
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
const result = networkSwitchAssetOutAddress({
|
|
639
|
+
assetOut: token,
|
|
640
|
+
unwrap: true,
|
|
641
|
+
toChain: chain as Chains,
|
|
642
|
+
});
|
|
643
|
+
expect(result).toBe(zeroAddress);
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
describe('estimatedAmountOut', () => {
|
|
649
|
+
it('should return null when fee is not a bigint', () => {
|
|
650
|
+
expect(
|
|
651
|
+
estimatedAmountOut({
|
|
652
|
+
amountAfterBridgeFee: 1000n,
|
|
653
|
+
fee: null,
|
|
654
|
+
}),
|
|
655
|
+
).toBeNull();
|
|
656
|
+
|
|
657
|
+
expect(
|
|
658
|
+
estimatedAmountOut({
|
|
659
|
+
amountAfterBridgeFee: 1000n,
|
|
660
|
+
fee: undefined as any,
|
|
661
|
+
}),
|
|
662
|
+
).toBeNull();
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('should return null when amountAfterBridgeFee is null', () => {
|
|
666
|
+
expect(
|
|
667
|
+
estimatedAmountOut({
|
|
668
|
+
amountAfterBridgeFee: null,
|
|
669
|
+
fee: 100n,
|
|
670
|
+
}),
|
|
671
|
+
).toBeNull();
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it('should return 0n when fee exceeds amount', () => {
|
|
675
|
+
expect(
|
|
676
|
+
estimatedAmountOut({
|
|
677
|
+
amountAfterBridgeFee: 100n,
|
|
678
|
+
fee: 200n,
|
|
679
|
+
}),
|
|
680
|
+
).toBe(0n);
|
|
681
|
+
|
|
682
|
+
expect(
|
|
683
|
+
estimatedAmountOut({
|
|
684
|
+
amountAfterBridgeFee: 50n,
|
|
685
|
+
fee: 51n,
|
|
686
|
+
}),
|
|
687
|
+
).toBe(0n);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('should calculate amount out correctly', () => {
|
|
691
|
+
expect(
|
|
692
|
+
estimatedAmountOut({
|
|
693
|
+
amountAfterBridgeFee: 1000n,
|
|
694
|
+
fee: 100n,
|
|
695
|
+
}),
|
|
696
|
+
).toBe(900n);
|
|
697
|
+
|
|
698
|
+
expect(
|
|
699
|
+
estimatedAmountOut({
|
|
700
|
+
amountAfterBridgeFee: 500000n,
|
|
701
|
+
fee: 2500n,
|
|
702
|
+
}),
|
|
703
|
+
).toBe(497500n);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
it('should handle zero fee', () => {
|
|
707
|
+
expect(
|
|
708
|
+
estimatedAmountOut({
|
|
709
|
+
amountAfterBridgeFee: 1000n,
|
|
710
|
+
fee: 0n,
|
|
711
|
+
}),
|
|
712
|
+
).toBe(1000n);
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it('should handle exact fee amount', () => {
|
|
716
|
+
expect(
|
|
717
|
+
estimatedAmountOut({
|
|
718
|
+
amountAfterBridgeFee: 1000n,
|
|
719
|
+
fee: 1000n,
|
|
720
|
+
}),
|
|
721
|
+
).toBe(0n);
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
it('should handle large amounts', () => {
|
|
725
|
+
const largeAmount = oneEther * 1000n; // 1000 ETH
|
|
726
|
+
const largeFee = oneEther; // 1 ETH
|
|
727
|
+
expect(
|
|
728
|
+
estimatedAmountOut({
|
|
729
|
+
amountAfterBridgeFee: largeAmount,
|
|
730
|
+
fee: largeFee,
|
|
731
|
+
}),
|
|
732
|
+
).toBe(largeAmount - largeFee);
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it('should maintain precision with bigint', () => {
|
|
736
|
+
const amount = 123456789012345678n;
|
|
737
|
+
const fee = 1n;
|
|
738
|
+
expect(
|
|
739
|
+
estimatedAmountOut({
|
|
740
|
+
amountAfterBridgeFee: amount,
|
|
741
|
+
fee: fee,
|
|
742
|
+
}),
|
|
743
|
+
).toBe(123456789012345677n);
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
describe('estimatedFee', () => {
|
|
748
|
+
const baseParams = {
|
|
749
|
+
amountAfterBridgeFee: 1000n * oneEther,
|
|
750
|
+
fee: 50n * oneEther,
|
|
751
|
+
percentFee: oneEther / 100n, // 1%
|
|
752
|
+
limit: 21000n,
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
it('should return 0n when not delivering', () => {
|
|
756
|
+
expect(
|
|
757
|
+
estimatedFee({
|
|
758
|
+
...baseParams,
|
|
759
|
+
feeType: FeeType.FIXED,
|
|
760
|
+
costsToDeliver: false,
|
|
761
|
+
}),
|
|
762
|
+
).toBe(0n);
|
|
763
|
+
|
|
764
|
+
expect(
|
|
765
|
+
estimatedFee({
|
|
766
|
+
...baseParams,
|
|
767
|
+
feeType: FeeType.PERCENT,
|
|
768
|
+
costsToDeliver: false,
|
|
769
|
+
}),
|
|
770
|
+
).toBe(0n);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('should return fixed fee for FIXED type', () => {
|
|
774
|
+
expect(
|
|
775
|
+
estimatedFee({
|
|
776
|
+
...baseParams,
|
|
777
|
+
feeType: FeeType.FIXED,
|
|
778
|
+
costsToDeliver: true,
|
|
779
|
+
}),
|
|
780
|
+
).toBe(50n * oneEther);
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
it('should calculate percent fee for PERCENT type', () => {
|
|
784
|
+
const result = estimatedFee({
|
|
785
|
+
...baseParams,
|
|
786
|
+
feeType: FeeType.PERCENT,
|
|
787
|
+
costsToDeliver: true,
|
|
788
|
+
});
|
|
789
|
+
// 1000 ETH * 1% = 10 ETH
|
|
790
|
+
const expected = (1000n * oneEther * (oneEther / 100n)) / oneEther;
|
|
791
|
+
expect(result).toBe(expected);
|
|
792
|
+
expect(result).toBe(10n * oneEther);
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
it('should return limit for GAS_TIP type', () => {
|
|
796
|
+
expect(
|
|
797
|
+
estimatedFee({
|
|
798
|
+
...baseParams,
|
|
799
|
+
feeType: FeeType.GAS_TIP,
|
|
800
|
+
costsToDeliver: true,
|
|
801
|
+
}),
|
|
802
|
+
).toBe(21000n);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it('should return null for PERCENT when amountAfterBridgeFee is null', () => {
|
|
806
|
+
expect(
|
|
807
|
+
estimatedFee({
|
|
808
|
+
...baseParams,
|
|
809
|
+
amountAfterBridgeFee: null,
|
|
810
|
+
feeType: FeeType.PERCENT,
|
|
811
|
+
costsToDeliver: true,
|
|
812
|
+
}),
|
|
813
|
+
).toBeNull();
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
it('should handle zero percent fee', () => {
|
|
817
|
+
const result = estimatedFee({
|
|
818
|
+
...baseParams,
|
|
819
|
+
percentFee: 0n,
|
|
820
|
+
feeType: FeeType.PERCENT,
|
|
821
|
+
costsToDeliver: true,
|
|
822
|
+
});
|
|
823
|
+
expect(result).toBe(0n);
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
it('should handle high percent fee', () => {
|
|
827
|
+
const result = estimatedFee({
|
|
828
|
+
...baseParams,
|
|
829
|
+
percentFee: oneEther / 10n, // 10%
|
|
830
|
+
feeType: FeeType.PERCENT,
|
|
831
|
+
costsToDeliver: true,
|
|
832
|
+
});
|
|
833
|
+
// 1000 ETH * 10% = 100 ETH
|
|
834
|
+
expect(result).toBe(100n * oneEther);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it('should return null when amountAfterBridgeFee is null with fixedFee calculation', () => {
|
|
838
|
+
const result = estimatedFee({
|
|
839
|
+
...baseParams,
|
|
840
|
+
amountAfterBridgeFee: null,
|
|
841
|
+
feeType: FeeType.PERCENT,
|
|
842
|
+
costsToDeliver: true,
|
|
843
|
+
});
|
|
844
|
+
expect(result).toBeNull();
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
it('should handle all FeeType enum values', () => {
|
|
848
|
+
// FIXED
|
|
849
|
+
const fixed = estimatedFee({
|
|
850
|
+
...baseParams,
|
|
851
|
+
feeType: FeeType.FIXED,
|
|
852
|
+
costsToDeliver: true,
|
|
853
|
+
});
|
|
854
|
+
expect(fixed).toBe(baseParams.fee);
|
|
855
|
+
|
|
856
|
+
// PERCENT
|
|
857
|
+
const percent = estimatedFee({
|
|
858
|
+
...baseParams,
|
|
859
|
+
feeType: FeeType.PERCENT,
|
|
860
|
+
costsToDeliver: true,
|
|
861
|
+
});
|
|
862
|
+
expect(percent).toBeDefined();
|
|
863
|
+
expect(typeof percent).toBe('bigint');
|
|
864
|
+
|
|
865
|
+
// GAS_TIP
|
|
866
|
+
const gasTip = estimatedFee({
|
|
867
|
+
...baseParams,
|
|
868
|
+
feeType: FeeType.GAS_TIP,
|
|
869
|
+
costsToDeliver: true,
|
|
870
|
+
});
|
|
871
|
+
expect(gasTip).toBe(baseParams.limit);
|
|
872
|
+
});
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
describe('assetOut', () => {
|
|
876
|
+
const mockToken1 = {
|
|
877
|
+
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
878
|
+
chainId: 1,
|
|
879
|
+
name: 'USDT',
|
|
880
|
+
symbol: 'USDT',
|
|
881
|
+
decimals: 6,
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
const mockToken2 = {
|
|
885
|
+
address: '0xA1077a294dDE1B09bB078844df40758a5D0f9a27',
|
|
886
|
+
chainId: 369,
|
|
887
|
+
name: 'WPLS',
|
|
888
|
+
symbol: 'WPLS',
|
|
889
|
+
decimals: 18,
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
it('should return null when assetInAddress is null', () => {
|
|
893
|
+
const assetsOut = new Map();
|
|
894
|
+
expect(
|
|
895
|
+
assetOut({
|
|
896
|
+
assetInAddress: null,
|
|
897
|
+
assetsOut,
|
|
898
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
899
|
+
unwrap: false,
|
|
900
|
+
}),
|
|
901
|
+
).toBeNull();
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
it('should return null when assetOutKey returns null', () => {
|
|
905
|
+
const assetsOut = new Map();
|
|
906
|
+
expect(
|
|
907
|
+
assetOut({
|
|
908
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
909
|
+
assetsOut,
|
|
910
|
+
bridgeKeyPath: '', // Empty bridge path causes assetOutKey to return null
|
|
911
|
+
unwrap: false,
|
|
912
|
+
}),
|
|
913
|
+
).toBeNull();
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
it('should return token from map when key exists', () => {
|
|
917
|
+
const assetsOut = new Map();
|
|
918
|
+
const key = 'pulsechain/0x1/0x171/erc20/0xdac17f958d2ee523a2206206994597c13d831ec7';
|
|
919
|
+
assetsOut.set(key, mockToken1);
|
|
920
|
+
|
|
921
|
+
const result = assetOut({
|
|
922
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
923
|
+
assetsOut,
|
|
924
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
925
|
+
unwrap: false,
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
expect(result).toEqual(mockToken1);
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
it('should return null when key does not exist in map', () => {
|
|
932
|
+
const assetsOut = new Map();
|
|
933
|
+
|
|
934
|
+
const result = assetOut({
|
|
935
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
936
|
+
assetsOut,
|
|
937
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
938
|
+
unwrap: false,
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
expect(result).toBeNull();
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
it('should handle unwrap flag correctly', () => {
|
|
945
|
+
const assetsOut = new Map();
|
|
946
|
+
const erc20Key = 'pulsechain/0x1/0x171/erc20/0xdac17f958d2ee523a2206206994597c13d831ec7';
|
|
947
|
+
const nativeKey = 'pulsechain/0x1/0x171/native/0xdac17f958d2ee523a2206206994597c13d831ec7';
|
|
948
|
+
|
|
949
|
+
assetsOut.set(erc20Key, mockToken1);
|
|
950
|
+
assetsOut.set(nativeKey, mockToken2);
|
|
951
|
+
|
|
952
|
+
// Without unwrap - should look for erc20 key
|
|
953
|
+
const erc20Result = assetOut({
|
|
954
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
955
|
+
assetsOut,
|
|
956
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
957
|
+
unwrap: false,
|
|
958
|
+
});
|
|
959
|
+
expect(erc20Result).toEqual(mockToken1);
|
|
960
|
+
|
|
961
|
+
// With unwrap - should look for native key
|
|
962
|
+
const nativeResult = assetOut({
|
|
963
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
964
|
+
assetsOut,
|
|
965
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
966
|
+
unwrap: true,
|
|
967
|
+
});
|
|
968
|
+
expect(nativeResult).toEqual(mockToken2);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it('should handle different bridge paths', () => {
|
|
972
|
+
const assetsOut = new Map();
|
|
973
|
+
const key1 = 'pulsechain/0x1/0x171/erc20/0xdac17f958d2ee523a2206206994597c13d831ec7';
|
|
974
|
+
const key2 = 'tokensex/0x38/0x171/erc20/0xdac17f958d2ee523a2206206994597c13d831ec7';
|
|
975
|
+
|
|
976
|
+
assetsOut.set(key1, mockToken1);
|
|
977
|
+
assetsOut.set(key2, mockToken2);
|
|
978
|
+
|
|
979
|
+
const result1 = assetOut({
|
|
980
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
981
|
+
assetsOut,
|
|
982
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
983
|
+
unwrap: false,
|
|
984
|
+
});
|
|
985
|
+
expect(result1).toEqual(mockToken1);
|
|
986
|
+
|
|
987
|
+
const result2 = assetOut({
|
|
988
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
989
|
+
assetsOut,
|
|
990
|
+
bridgeKeyPath: 'tokensex/0x38/0x171',
|
|
991
|
+
unwrap: false,
|
|
992
|
+
});
|
|
993
|
+
expect(result2).toEqual(mockToken2);
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
it('should handle case-insensitive address matching via assetOutKey', () => {
|
|
997
|
+
const assetsOut = new Map();
|
|
998
|
+
// Key is always lowercase due to assetOutKey
|
|
999
|
+
const key = 'pulsechain/0x1/0x171/erc20/0xdac17f958d2ee523a2206206994597c13d831ec7';
|
|
1000
|
+
assetsOut.set(key, mockToken1);
|
|
1001
|
+
|
|
1002
|
+
// Should work with uppercase address
|
|
1003
|
+
const result = assetOut({
|
|
1004
|
+
assetInAddress: '0xDAC17F958D2EE523A2206206994597C13D831EC7',
|
|
1005
|
+
assetsOut,
|
|
1006
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
1007
|
+
unwrap: false,
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
expect(result).toEqual(mockToken1);
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
it('should return null when map has other keys but not the requested one', () => {
|
|
1014
|
+
const assetsOut = new Map();
|
|
1015
|
+
assetsOut.set('some/other/key', mockToken1);
|
|
1016
|
+
assetsOut.set('another/different/key', mockToken2);
|
|
1017
|
+
|
|
1018
|
+
const result = assetOut({
|
|
1019
|
+
assetInAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
1020
|
+
assetsOut,
|
|
1021
|
+
bridgeKeyPath: 'pulsechain/0x1/0x171',
|
|
1022
|
+
unwrap: false,
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
expect(result).toBeNull();
|
|
1026
|
+
});
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
describe('estimatedNativeNetworkCost', () => {
|
|
1030
|
+
it('should return null when estimatedGas is null', () => {
|
|
1031
|
+
expect(
|
|
1032
|
+
estimatedNativeNetworkCost({
|
|
1033
|
+
estimatedGas: null,
|
|
1034
|
+
latestBaseFeePerGas: 30n * 10n ** 9n, // 30 gwei
|
|
1035
|
+
requiresDelivery: true,
|
|
1036
|
+
}),
|
|
1037
|
+
).toBeNull();
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
it('should return null when latestBaseFeePerGas is null', () => {
|
|
1041
|
+
expect(
|
|
1042
|
+
estimatedNativeNetworkCost({
|
|
1043
|
+
estimatedGas: 21000n,
|
|
1044
|
+
latestBaseFeePerGas: null,
|
|
1045
|
+
requiresDelivery: true,
|
|
1046
|
+
}),
|
|
1047
|
+
).toBeNull();
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
it('should return null when requiresDelivery is false', () => {
|
|
1051
|
+
expect(
|
|
1052
|
+
estimatedNativeNetworkCost({
|
|
1053
|
+
estimatedGas: 21000n,
|
|
1054
|
+
latestBaseFeePerGas: 30n * 10n ** 9n,
|
|
1055
|
+
requiresDelivery: false,
|
|
1056
|
+
}),
|
|
1057
|
+
).toBeNull();
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
it('should calculate cost correctly', () => {
|
|
1061
|
+
const gas = 21000n;
|
|
1062
|
+
const baseFee = 30n * 10n ** 9n; // 30 gwei
|
|
1063
|
+
const result = estimatedNativeNetworkCost({
|
|
1064
|
+
estimatedGas: gas,
|
|
1065
|
+
latestBaseFeePerGas: baseFee,
|
|
1066
|
+
requiresDelivery: true,
|
|
1067
|
+
});
|
|
1068
|
+
expect(result).toBe(gas * baseFee);
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
it('should handle high gas costs', () => {
|
|
1072
|
+
const gas = 500000n; // Complex transaction
|
|
1073
|
+
const baseFee = 100n * 10n ** 9n; // 100 gwei
|
|
1074
|
+
const result = estimatedNativeNetworkCost({
|
|
1075
|
+
estimatedGas: gas,
|
|
1076
|
+
latestBaseFeePerGas: baseFee,
|
|
1077
|
+
requiresDelivery: true,
|
|
1078
|
+
});
|
|
1079
|
+
expect(result).toBe(gas * baseFee);
|
|
1080
|
+
expect(result).toBe(50000000000000000n); // 0.05 ETH
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
it('should handle low gas costs', () => {
|
|
1084
|
+
const gas = 21000n;
|
|
1085
|
+
const baseFee = 1n * 10n ** 9n; // 1 gwei
|
|
1086
|
+
const result = estimatedNativeNetworkCost({
|
|
1087
|
+
estimatedGas: gas,
|
|
1088
|
+
latestBaseFeePerGas: baseFee,
|
|
1089
|
+
requiresDelivery: true,
|
|
1090
|
+
});
|
|
1091
|
+
expect(result).toBe(21000000000000n); // 0.000021 ETH
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
it('should return BigInt type', () => {
|
|
1095
|
+
const result = estimatedNativeNetworkCost({
|
|
1096
|
+
estimatedGas: 21000n,
|
|
1097
|
+
latestBaseFeePerGas: 30n * 10n ** 9n,
|
|
1098
|
+
requiresDelivery: true,
|
|
1099
|
+
});
|
|
1100
|
+
expect(typeof result).toBe('bigint');
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
it('should handle zero values gracefully', () => {
|
|
1104
|
+
// Zero gas should return null (falsy check)
|
|
1105
|
+
expect(
|
|
1106
|
+
estimatedNativeNetworkCost({
|
|
1107
|
+
estimatedGas: 0n,
|
|
1108
|
+
latestBaseFeePerGas: 30n * 10n ** 9n,
|
|
1109
|
+
requiresDelivery: true,
|
|
1110
|
+
}),
|
|
1111
|
+
).toBeNull();
|
|
1112
|
+
|
|
1113
|
+
// Zero base fee should return null (falsy check)
|
|
1114
|
+
expect(
|
|
1115
|
+
estimatedNativeNetworkCost({
|
|
1116
|
+
estimatedGas: 21000n,
|
|
1117
|
+
latestBaseFeePerGas: 0n,
|
|
1118
|
+
requiresDelivery: true,
|
|
1119
|
+
}),
|
|
1120
|
+
).toBeNull();
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
it('should require all three parameters to be valid', () => {
|
|
1124
|
+
// All valid - should calculate
|
|
1125
|
+
expect(
|
|
1126
|
+
estimatedNativeNetworkCost({
|
|
1127
|
+
estimatedGas: 21000n,
|
|
1128
|
+
latestBaseFeePerGas: 30n * 10n ** 9n,
|
|
1129
|
+
requiresDelivery: true,
|
|
1130
|
+
}),
|
|
1131
|
+
).not.toBeNull();
|
|
1132
|
+
|
|
1133
|
+
// Any invalid - should return null
|
|
1134
|
+
expect(
|
|
1135
|
+
estimatedNativeNetworkCost({
|
|
1136
|
+
estimatedGas: null,
|
|
1137
|
+
latestBaseFeePerGas: 30n * 10n ** 9n,
|
|
1138
|
+
requiresDelivery: true,
|
|
1139
|
+
}),
|
|
1140
|
+
).toBeNull();
|
|
1141
|
+
|
|
1142
|
+
expect(
|
|
1143
|
+
estimatedNativeNetworkCost({
|
|
1144
|
+
estimatedGas: 21000n,
|
|
1145
|
+
latestBaseFeePerGas: null,
|
|
1146
|
+
requiresDelivery: true,
|
|
1147
|
+
}),
|
|
1148
|
+
).toBeNull();
|
|
1149
|
+
|
|
1150
|
+
expect(
|
|
1151
|
+
estimatedNativeNetworkCost({
|
|
1152
|
+
estimatedGas: 21000n,
|
|
1153
|
+
latestBaseFeePerGas: 30n * 10n ** 9n,
|
|
1154
|
+
requiresDelivery: false,
|
|
1155
|
+
}),
|
|
1156
|
+
).toBeNull();
|
|
1157
|
+
});
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
describe('reasonablePercentOnGasLimit', () => {
|
|
1161
|
+
it('should calculate with both parameters provided', () => {
|
|
1162
|
+
const result = reasonablePercentOnGasLimit({
|
|
1163
|
+
gasTipFee: oneEther / 100n, // 1% tip
|
|
1164
|
+
estimatedTokenNetworkCost: 10n * oneEther, // 10 tokens
|
|
1165
|
+
});
|
|
1166
|
+
// Formula: (25000 * ((1 ETH + 0.01 ETH) * 10 ETH)) / (1 ETH * 10000)
|
|
1167
|
+
const expected =
|
|
1168
|
+
(25_000n * ((oneEther + oneEther / 100n) * 10n * oneEther)) / (oneEther * basisPoints);
|
|
1169
|
+
expect(result).toBe(expected);
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
it('should handle null gasTipFee (treats as 0)', () => {
|
|
1173
|
+
const result = reasonablePercentOnGasLimit({
|
|
1174
|
+
gasTipFee: null,
|
|
1175
|
+
estimatedTokenNetworkCost: 10n * oneEther,
|
|
1176
|
+
});
|
|
1177
|
+
// Formula with gasTipFee as 0: (25000 * ((1 ETH + 0) * 10 ETH)) / (1 ETH * 10000)
|
|
1178
|
+
const expected = (25_000n * ((oneEther + 0n) * 10n * oneEther)) / (oneEther * basisPoints);
|
|
1179
|
+
expect(result).toBe(expected);
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
it('should handle null estimatedTokenNetworkCost (treats as 0)', () => {
|
|
1183
|
+
const result = reasonablePercentOnGasLimit({
|
|
1184
|
+
gasTipFee: oneEther / 100n,
|
|
1185
|
+
estimatedTokenNetworkCost: null,
|
|
1186
|
+
});
|
|
1187
|
+
// Formula with cost as 0: (25000 * ((1.01 ETH) * 0)) / (1 ETH * 10000) = 0
|
|
1188
|
+
expect(result).toBe(0n);
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
it('should handle both parameters as null', () => {
|
|
1192
|
+
const result = reasonablePercentOnGasLimit({
|
|
1193
|
+
gasTipFee: null,
|
|
1194
|
+
estimatedTokenNetworkCost: null,
|
|
1195
|
+
});
|
|
1196
|
+
expect(result).toBe(0n);
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
it('should handle zero gasTipFee', () => {
|
|
1200
|
+
const result = reasonablePercentOnGasLimit({
|
|
1201
|
+
gasTipFee: 0n,
|
|
1202
|
+
estimatedTokenNetworkCost: 10n * oneEther,
|
|
1203
|
+
});
|
|
1204
|
+
const expected = (25_000n * oneEther * 10n * oneEther) / (oneEther * basisPoints);
|
|
1205
|
+
expect(result).toBe(expected);
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
it('should handle zero estimatedTokenNetworkCost', () => {
|
|
1209
|
+
const result = reasonablePercentOnGasLimit({
|
|
1210
|
+
gasTipFee: oneEther / 100n,
|
|
1211
|
+
estimatedTokenNetworkCost: 0n,
|
|
1212
|
+
});
|
|
1213
|
+
expect(result).toBe(0n);
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
it('should handle large values', () => {
|
|
1217
|
+
const largeTip = oneEther; // 100% tip
|
|
1218
|
+
const largeCost = 1000n * oneEther; // 1000 tokens
|
|
1219
|
+
const result = reasonablePercentOnGasLimit({
|
|
1220
|
+
gasTipFee: largeTip,
|
|
1221
|
+
estimatedTokenNetworkCost: largeCost,
|
|
1222
|
+
});
|
|
1223
|
+
const expected = (25_000n * ((oneEther + largeTip) * largeCost)) / (oneEther * basisPoints);
|
|
1224
|
+
expect(result).toBe(expected);
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
it('should use correct constants (25000 and basisPoints)', () => {
|
|
1228
|
+
const result = reasonablePercentOnGasLimit({
|
|
1229
|
+
gasTipFee: oneEther / 10n, // 10%
|
|
1230
|
+
estimatedTokenNetworkCost: 100n * oneEther,
|
|
1231
|
+
});
|
|
1232
|
+
// Verify formula uses 25000 and basisPoints
|
|
1233
|
+
const expected =
|
|
1234
|
+
(25_000n * ((oneEther + oneEther / 10n) * 100n * oneEther)) / (oneEther * basisPoints);
|
|
1235
|
+
expect(result).toBe(expected);
|
|
1236
|
+
expect(basisPoints).toBe(10_000n); // Verify constant
|
|
1237
|
+
});
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
describe('reasonablePercentOnTopOfGasFee', () => {
|
|
1241
|
+
it('should calculate percent from basis points', () => {
|
|
1242
|
+
const result = reasonablePercentOnTopOfGasFee({
|
|
1243
|
+
desiredExcessCompensationBasisPoints: 1000n, // 10%
|
|
1244
|
+
});
|
|
1245
|
+
// Formula: 1000 * (1 ETH / 10000) = 0.1 ETH
|
|
1246
|
+
expect(result).toBe(1000n * (oneEther / basisPoints));
|
|
1247
|
+
expect(result).toBe(oneEther / 10n);
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
it('should handle zero basis points', () => {
|
|
1251
|
+
const result = reasonablePercentOnTopOfGasFee({
|
|
1252
|
+
desiredExcessCompensationBasisPoints: 0n,
|
|
1253
|
+
});
|
|
1254
|
+
expect(result).toBe(0n);
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
it('should handle 100% (10000 basis points)', () => {
|
|
1258
|
+
const result = reasonablePercentOnTopOfGasFee({
|
|
1259
|
+
desiredExcessCompensationBasisPoints: 10_000n,
|
|
1260
|
+
});
|
|
1261
|
+
// 10000 * (1 ETH / 10000) = 1 ETH
|
|
1262
|
+
expect(result).toBe(oneEther);
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
it('should handle 50% (5000 basis points)', () => {
|
|
1266
|
+
const result = reasonablePercentOnTopOfGasFee({
|
|
1267
|
+
desiredExcessCompensationBasisPoints: 5_000n,
|
|
1268
|
+
});
|
|
1269
|
+
// 5000 * (1 ETH / 10000) = 0.5 ETH
|
|
1270
|
+
expect(result).toBe(oneEther / 2n);
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
it('should handle 1% (100 basis points)', () => {
|
|
1274
|
+
const result = reasonablePercentOnTopOfGasFee({
|
|
1275
|
+
desiredExcessCompensationBasisPoints: 100n,
|
|
1276
|
+
});
|
|
1277
|
+
// 100 * (1 ETH / 10000) = 0.01 ETH
|
|
1278
|
+
expect(result).toBe(oneEther / 100n);
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
it('should handle 0.01% (1 basis point)', () => {
|
|
1282
|
+
const result = reasonablePercentOnTopOfGasFee({
|
|
1283
|
+
desiredExcessCompensationBasisPoints: 1n,
|
|
1284
|
+
});
|
|
1285
|
+
// 1 * (1 ETH / 10000) = 0.0001 ETH
|
|
1286
|
+
expect(result).toBe(oneEther / 10_000n);
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
it('should maintain precision with bigint', () => {
|
|
1290
|
+
const result = reasonablePercentOnTopOfGasFee({
|
|
1291
|
+
desiredExcessCompensationBasisPoints: 3_333n, // 33.33%
|
|
1292
|
+
});
|
|
1293
|
+
const expected = 3_333n * (oneEther / basisPoints);
|
|
1294
|
+
expect(result).toBe(expected);
|
|
1295
|
+
// Should not lose precision
|
|
1296
|
+
expect(result).not.toBe(oneEther / 3n); // Not exactly 1/3 due to rounding
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
it('should use oneEther and basisPoints constants', () => {
|
|
1300
|
+
const testBps = 2_500n; // 25%
|
|
1301
|
+
const result = reasonablePercentOnTopOfGasFee({
|
|
1302
|
+
desiredExcessCompensationBasisPoints: testBps,
|
|
1303
|
+
});
|
|
1304
|
+
expect(result).toBe(testBps * (oneEther / basisPoints));
|
|
1305
|
+
expect(oneEther).toBe(10n ** 18n);
|
|
1306
|
+
expect(basisPoints).toBe(10_000n);
|
|
1307
|
+
});
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
describe('estimatedTokenNetworkCost', () => {
|
|
1311
|
+
const baseParams = {
|
|
1312
|
+
estimatedNativeNetworkCost: 630000000000000n, // 0.00063 ETH
|
|
1313
|
+
priceCorrective: oneEther / 1000n, // Token worth 0.001 ETH
|
|
1314
|
+
decimals: 18,
|
|
1315
|
+
oneTokenInt: oneEther,
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
it('should return null when priceCorrective is null', () => {
|
|
1319
|
+
expect(
|
|
1320
|
+
estimatedTokenNetworkCost({
|
|
1321
|
+
...baseParams,
|
|
1322
|
+
priceCorrective: null,
|
|
1323
|
+
}),
|
|
1324
|
+
).toBeNull();
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
it('should return null when oneTokenInt is null', () => {
|
|
1328
|
+
expect(
|
|
1329
|
+
estimatedTokenNetworkCost({
|
|
1330
|
+
...baseParams,
|
|
1331
|
+
oneTokenInt: null,
|
|
1332
|
+
}),
|
|
1333
|
+
).toBeNull();
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
it('should return null when estimatedNativeNetworkCost is null', () => {
|
|
1337
|
+
expect(
|
|
1338
|
+
estimatedTokenNetworkCost({
|
|
1339
|
+
...baseParams,
|
|
1340
|
+
estimatedNativeNetworkCost: null,
|
|
1341
|
+
}),
|
|
1342
|
+
).toBeNull();
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
it('should return null when decimals is null', () => {
|
|
1346
|
+
expect(
|
|
1347
|
+
estimatedTokenNetworkCost({
|
|
1348
|
+
...baseParams,
|
|
1349
|
+
decimals: null,
|
|
1350
|
+
}),
|
|
1351
|
+
).toBeNull();
|
|
1352
|
+
});
|
|
1353
|
+
|
|
1354
|
+
it('should calculate token cost correctly', () => {
|
|
1355
|
+
const result = estimatedTokenNetworkCost(baseParams);
|
|
1356
|
+
// (0.00063 ETH * 1 ETH) / 0.001 ETH = 0.63 tokens
|
|
1357
|
+
const expected =
|
|
1358
|
+
(baseParams.estimatedNativeNetworkCost * baseParams.oneTokenInt) /
|
|
1359
|
+
baseParams.priceCorrective;
|
|
1360
|
+
expect(result).toBe(expected);
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
it('should handle expensive tokens (high price corrective)', () => {
|
|
1364
|
+
const result = estimatedTokenNetworkCost({
|
|
1365
|
+
estimatedNativeNetworkCost: oneEther / 100n, // 0.01 ETH
|
|
1366
|
+
priceCorrective: 100n * oneEther, // Token worth 100 ETH
|
|
1367
|
+
decimals: 18,
|
|
1368
|
+
oneTokenInt: oneEther,
|
|
1369
|
+
});
|
|
1370
|
+
// (0.01 ETH * 1 ETH) / 100 ETH = 0.0001 tokens
|
|
1371
|
+
const expected = ((oneEther / 100n) * oneEther) / (100n * oneEther);
|
|
1372
|
+
expect(result).toBe(expected);
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
it('should handle cheap tokens (low price corrective)', () => {
|
|
1376
|
+
const result = estimatedTokenNetworkCost({
|
|
1377
|
+
estimatedNativeNetworkCost: oneEther / 1000n, // 0.001 ETH
|
|
1378
|
+
priceCorrective: oneEther / 1000000n, // Token worth 0.000001 ETH
|
|
1379
|
+
decimals: 18,
|
|
1380
|
+
oneTokenInt: oneEther,
|
|
1381
|
+
});
|
|
1382
|
+
// (0.001 ETH * 1 ETH) / 0.000001 ETH = 1000 tokens
|
|
1383
|
+
const expected = ((oneEther / 1000n) * oneEther) / (oneEther / 1000000n);
|
|
1384
|
+
expect(result).toBe(expected);
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
it('should handle different token decimals', () => {
|
|
1388
|
+
// USDT with 6 decimals
|
|
1389
|
+
const usdtResult = estimatedTokenNetworkCost({
|
|
1390
|
+
estimatedNativeNetworkCost: oneEther / 100n,
|
|
1391
|
+
priceCorrective: oneEther / 3000n, // $3000 per ETH
|
|
1392
|
+
decimals: 6,
|
|
1393
|
+
oneTokenInt: 10n ** 6n, // 1 USDT
|
|
1394
|
+
});
|
|
1395
|
+
expect(usdtResult).toBeDefined();
|
|
1396
|
+
const expected = ((oneEther / 100n) * 10n ** 6n) / (oneEther / 3000n);
|
|
1397
|
+
expect(usdtResult).toBe(expected);
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
it('should return null when priceCorrective is 0', () => {
|
|
1401
|
+
expect(
|
|
1402
|
+
estimatedTokenNetworkCost({
|
|
1403
|
+
...baseParams,
|
|
1404
|
+
priceCorrective: 0n,
|
|
1405
|
+
}),
|
|
1406
|
+
).toBeNull();
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
it('should return null when oneTokenInt is 0', () => {
|
|
1410
|
+
expect(
|
|
1411
|
+
estimatedTokenNetworkCost({
|
|
1412
|
+
...baseParams,
|
|
1413
|
+
oneTokenInt: 0n,
|
|
1414
|
+
}),
|
|
1415
|
+
).toBeNull();
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
it('should return null when estimatedNativeNetworkCost is 0', () => {
|
|
1419
|
+
expect(
|
|
1420
|
+
estimatedTokenNetworkCost({
|
|
1421
|
+
...baseParams,
|
|
1422
|
+
estimatedNativeNetworkCost: 0n,
|
|
1423
|
+
}),
|
|
1424
|
+
).toBeNull();
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
it('should require all four parameters to be valid', () => {
|
|
1428
|
+
// All valid - should calculate
|
|
1429
|
+
expect(estimatedTokenNetworkCost(baseParams)).not.toBeNull();
|
|
1430
|
+
|
|
1431
|
+
// Any null - should return null
|
|
1432
|
+
expect(
|
|
1433
|
+
estimatedTokenNetworkCost({
|
|
1434
|
+
...baseParams,
|
|
1435
|
+
priceCorrective: null,
|
|
1436
|
+
}),
|
|
1437
|
+
).toBeNull();
|
|
1438
|
+
|
|
1439
|
+
expect(
|
|
1440
|
+
estimatedTokenNetworkCost({
|
|
1441
|
+
...baseParams,
|
|
1442
|
+
oneTokenInt: null,
|
|
1443
|
+
}),
|
|
1444
|
+
).toBeNull();
|
|
1445
|
+
|
|
1446
|
+
expect(
|
|
1447
|
+
estimatedTokenNetworkCost({
|
|
1448
|
+
...baseParams,
|
|
1449
|
+
estimatedNativeNetworkCost: null,
|
|
1450
|
+
}),
|
|
1451
|
+
).toBeNull();
|
|
1452
|
+
|
|
1453
|
+
expect(
|
|
1454
|
+
estimatedTokenNetworkCost({
|
|
1455
|
+
...baseParams,
|
|
1456
|
+
decimals: null,
|
|
1457
|
+
}),
|
|
1458
|
+
).toBeNull();
|
|
1459
|
+
});
|
|
1460
|
+
|
|
1461
|
+
it('should maintain bigint precision', () => {
|
|
1462
|
+
const result = estimatedTokenNetworkCost({
|
|
1463
|
+
estimatedNativeNetworkCost: 123456789012345n,
|
|
1464
|
+
priceCorrective: 987654321098765n,
|
|
1465
|
+
decimals: 18,
|
|
1466
|
+
oneTokenInt: oneEther,
|
|
1467
|
+
});
|
|
1468
|
+
const expected = (123456789012345n * oneEther) / 987654321098765n;
|
|
1469
|
+
expect(result).toBe(expected);
|
|
1470
|
+
});
|
|
1471
|
+
|
|
1472
|
+
it('should use formula: (nativeCost * oneToken) / priceCorrective', () => {
|
|
1473
|
+
const nativeCost = (5n * oneEther) / 10n; // 0.5 ETH
|
|
1474
|
+
const price = 2n * oneEther; // 2 ETH per token
|
|
1475
|
+
const tokenInt = oneEther;
|
|
1476
|
+
|
|
1477
|
+
const result = estimatedTokenNetworkCost({
|
|
1478
|
+
estimatedNativeNetworkCost: nativeCost,
|
|
1479
|
+
priceCorrective: price,
|
|
1480
|
+
decimals: 18,
|
|
1481
|
+
oneTokenInt: tokenInt,
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
// Verify formula
|
|
1485
|
+
expect(result).toBe((nativeCost * tokenInt) / price);
|
|
1486
|
+
// 0.5 ETH / (2 ETH per token) = 0.25 tokens
|
|
1487
|
+
expect(result).toBe(oneEther / 4n);
|
|
1488
|
+
});
|
|
1489
|
+
});
|
|
1490
|
+
});
|
|
1491
|
+
|
|
1492
|
+
describe('desiredExcessCompensationBasisPoints', () => {
|
|
1493
|
+
const mockBridgeKey: BridgeKey = ['pulsechain', Chains.PLS, Chains.ETH];
|
|
1494
|
+
const mockToken: Token = {
|
|
1495
|
+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
1496
|
+
decimals: 6,
|
|
1497
|
+
name: 'USD Coin',
|
|
1498
|
+
symbol: 'USDC',
|
|
1499
|
+
chainId: parseInt(Chains.PLS, 16),
|
|
1500
|
+
};
|
|
1501
|
+
const mockPathway: Pathway = {
|
|
1502
|
+
from: '0xFrom' as Hex,
|
|
1503
|
+
requiresDelivery: true,
|
|
1504
|
+
feeManager: 'from',
|
|
1505
|
+
nativeRouter: '0xNative' as Hex,
|
|
1506
|
+
usesExtraParam: false,
|
|
1507
|
+
};
|
|
1508
|
+
|
|
1509
|
+
it('should return 0n when path does not require delivery', () => {
|
|
1510
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1511
|
+
path: { ...mockPathway, requiresDelivery: false },
|
|
1512
|
+
assetIn: mockToken,
|
|
1513
|
+
assetOut: mockToken,
|
|
1514
|
+
bridgeKey: mockBridgeKey,
|
|
1515
|
+
feeType: FeeType.FIXED,
|
|
1516
|
+
list: new Set(),
|
|
1517
|
+
assetOutAddress: mockToken.address,
|
|
1518
|
+
});
|
|
1519
|
+
expect(result).toBe(0n);
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
it('should return 0n when assetIn is null', () => {
|
|
1523
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1524
|
+
path: mockPathway,
|
|
1525
|
+
assetIn: null,
|
|
1526
|
+
assetOut: mockToken,
|
|
1527
|
+
bridgeKey: mockBridgeKey,
|
|
1528
|
+
feeType: FeeType.FIXED,
|
|
1529
|
+
list: new Set(),
|
|
1530
|
+
assetOutAddress: mockToken.address,
|
|
1531
|
+
});
|
|
1532
|
+
expect(result).toBe(0n);
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
it('should return 1_000n for PERCENT fee type', () => {
|
|
1536
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1537
|
+
path: mockPathway,
|
|
1538
|
+
assetIn: mockToken,
|
|
1539
|
+
assetOut: mockToken,
|
|
1540
|
+
bridgeKey: mockBridgeKey,
|
|
1541
|
+
feeType: FeeType.PERCENT,
|
|
1542
|
+
list: new Set(),
|
|
1543
|
+
assetOutAddress: mockToken.address,
|
|
1544
|
+
});
|
|
1545
|
+
expect(result).toBe(1_000n);
|
|
1546
|
+
});
|
|
1547
|
+
|
|
1548
|
+
it('should return 1_000n when assetIn is native', () => {
|
|
1549
|
+
const nativeToken = { ...mockToken, address: zeroAddress };
|
|
1550
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1551
|
+
path: mockPathway,
|
|
1552
|
+
assetIn: nativeToken,
|
|
1553
|
+
assetOut: mockToken,
|
|
1554
|
+
bridgeKey: mockBridgeKey,
|
|
1555
|
+
feeType: FeeType.FIXED,
|
|
1556
|
+
list: new Set(),
|
|
1557
|
+
assetOutAddress: mockToken.address,
|
|
1558
|
+
});
|
|
1559
|
+
expect(result).toBe(1_000n);
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
it('should return 1_000n when assetOut is native', () => {
|
|
1563
|
+
const nativeToken = { ...mockToken, address: zeroAddress };
|
|
1564
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1565
|
+
path: mockPathway,
|
|
1566
|
+
assetIn: mockToken,
|
|
1567
|
+
assetOut: nativeToken,
|
|
1568
|
+
bridgeKey: mockBridgeKey,
|
|
1569
|
+
feeType: FeeType.FIXED,
|
|
1570
|
+
list: new Set(),
|
|
1571
|
+
assetOutAddress: nativeToken.address,
|
|
1572
|
+
});
|
|
1573
|
+
expect(result).toBe(1_000n);
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
it('should return 5_000n when assetIn is in whitelist', () => {
|
|
1577
|
+
const list = new Set([getAddress(mockToken.address)]);
|
|
1578
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1579
|
+
path: mockPathway,
|
|
1580
|
+
assetIn: mockToken,
|
|
1581
|
+
assetOut: mockToken,
|
|
1582
|
+
bridgeKey: mockBridgeKey,
|
|
1583
|
+
feeType: FeeType.FIXED,
|
|
1584
|
+
list,
|
|
1585
|
+
assetOutAddress: '0x0000000000000000000000000000000000000001',
|
|
1586
|
+
});
|
|
1587
|
+
expect(result).toBe(5_000n);
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
it('should return 5_000n when assetOutAddress is in whitelist', () => {
|
|
1591
|
+
const outAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
1592
|
+
const list = new Set([getAddress(outAddress)]);
|
|
1593
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1594
|
+
path: mockPathway,
|
|
1595
|
+
assetIn: mockToken,
|
|
1596
|
+
assetOut: mockToken,
|
|
1597
|
+
bridgeKey: mockBridgeKey,
|
|
1598
|
+
feeType: FeeType.FIXED,
|
|
1599
|
+
list,
|
|
1600
|
+
assetOutAddress: outAddress,
|
|
1601
|
+
});
|
|
1602
|
+
expect(result).toBe(5_000n);
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
it('should return 10_000n for non-whitelisted tokens', () => {
|
|
1606
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1607
|
+
path: mockPathway,
|
|
1608
|
+
assetIn: mockToken,
|
|
1609
|
+
assetOut: mockToken,
|
|
1610
|
+
bridgeKey: mockBridgeKey,
|
|
1611
|
+
feeType: FeeType.FIXED,
|
|
1612
|
+
list: new Set(),
|
|
1613
|
+
assetOutAddress: '0x0000000000000000000000000000000000000001',
|
|
1614
|
+
});
|
|
1615
|
+
expect(result).toBe(10_000n);
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
it('should handle null path gracefully', () => {
|
|
1619
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1620
|
+
path: null,
|
|
1621
|
+
assetIn: mockToken,
|
|
1622
|
+
assetOut: mockToken,
|
|
1623
|
+
bridgeKey: mockBridgeKey,
|
|
1624
|
+
feeType: FeeType.FIXED,
|
|
1625
|
+
list: new Set(),
|
|
1626
|
+
assetOutAddress: mockToken.address,
|
|
1627
|
+
});
|
|
1628
|
+
expect(result).toBe(0n);
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
it('should handle null assetOutAddress', () => {
|
|
1632
|
+
const result = desiredExcessCompensationBasisPoints({
|
|
1633
|
+
path: mockPathway,
|
|
1634
|
+
assetIn: mockToken,
|
|
1635
|
+
assetOut: mockToken,
|
|
1636
|
+
bridgeKey: mockBridgeKey,
|
|
1637
|
+
feeType: FeeType.FIXED,
|
|
1638
|
+
list: new Set(),
|
|
1639
|
+
assetOutAddress: null,
|
|
1640
|
+
});
|
|
1641
|
+
expect(result).toBe(10_000n);
|
|
1642
|
+
});
|
|
1643
|
+
});
|
|
1644
|
+
|
|
1645
|
+
describe('estimatedCost', () => {
|
|
1646
|
+
const baseParams = {
|
|
1647
|
+
shouldDeliver: true,
|
|
1648
|
+
amountAfterBridgeFee: oneEther,
|
|
1649
|
+
percentFee: oneEther / 100n, // 1%
|
|
1650
|
+
estimatedTokenNetworkCost: oneEther / 10n, // 0.1 tokens
|
|
1651
|
+
reasonablePercentOnTopOfGasFee: oneEther / 10n, // 10%
|
|
1652
|
+
feeType: FeeType.PERCENT,
|
|
1653
|
+
};
|
|
1654
|
+
|
|
1655
|
+
it('should return null when shouldDeliver is false', () => {
|
|
1656
|
+
const result = estimatedCost({
|
|
1657
|
+
...baseParams,
|
|
1658
|
+
shouldDeliver: false,
|
|
1659
|
+
});
|
|
1660
|
+
expect(result).toBeNull();
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
it('should calculate cost correctly for PERCENT fee type', () => {
|
|
1664
|
+
const result = estimatedCost(baseParams);
|
|
1665
|
+
const expected = (baseParams.amountAfterBridgeFee * baseParams.percentFee) / oneEther;
|
|
1666
|
+
expect(result).toBe(expected);
|
|
1667
|
+
});
|
|
1668
|
+
|
|
1669
|
+
it('should return null for PERCENT when amountAfterBridgeFee is null', () => {
|
|
1670
|
+
const result = estimatedCost({
|
|
1671
|
+
...baseParams,
|
|
1672
|
+
amountAfterBridgeFee: null,
|
|
1673
|
+
});
|
|
1674
|
+
expect(result).toBeNull();
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
it('should calculate cost correctly for GAS_TIP fee type', () => {
|
|
1678
|
+
const result = estimatedCost({
|
|
1679
|
+
...baseParams,
|
|
1680
|
+
feeType: FeeType.GAS_TIP,
|
|
1681
|
+
});
|
|
1682
|
+
const expected =
|
|
1683
|
+
baseParams.estimatedTokenNetworkCost * (baseParams.reasonablePercentOnTopOfGasFee + oneEther);
|
|
1684
|
+
expect(result).toBe(expected);
|
|
1685
|
+
});
|
|
1686
|
+
|
|
1687
|
+
it('should return null for GAS_TIP when estimatedTokenNetworkCost is null', () => {
|
|
1688
|
+
const result = estimatedCost({
|
|
1689
|
+
...baseParams,
|
|
1690
|
+
feeType: FeeType.GAS_TIP,
|
|
1691
|
+
estimatedTokenNetworkCost: null,
|
|
1692
|
+
});
|
|
1693
|
+
expect(result).toBeNull();
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
it('should return null for GAS_TIP when reasonablePercentOnTopOfGasFee is null', () => {
|
|
1697
|
+
const result = estimatedCost({
|
|
1698
|
+
...baseParams,
|
|
1699
|
+
feeType: FeeType.GAS_TIP,
|
|
1700
|
+
reasonablePercentOnTopOfGasFee: null,
|
|
1701
|
+
});
|
|
1702
|
+
expect(result).toBeNull();
|
|
1703
|
+
});
|
|
1704
|
+
|
|
1705
|
+
it('should handle zero percentFee for PERCENT type', () => {
|
|
1706
|
+
const result = estimatedCost({
|
|
1707
|
+
...baseParams,
|
|
1708
|
+
percentFee: 0n,
|
|
1709
|
+
});
|
|
1710
|
+
expect(result).toBe(0n);
|
|
1711
|
+
});
|
|
1712
|
+
|
|
1713
|
+
it('should handle FIXED fee type (defaults to GAS_TIP calculation)', () => {
|
|
1714
|
+
const result = estimatedCost({
|
|
1715
|
+
...baseParams,
|
|
1716
|
+
feeType: FeeType.FIXED,
|
|
1717
|
+
});
|
|
1718
|
+
// FIXED falls through to GAS_TIP calculation
|
|
1719
|
+
const expected =
|
|
1720
|
+
baseParams.estimatedTokenNetworkCost * (baseParams.reasonablePercentOnTopOfGasFee + oneEther);
|
|
1721
|
+
expect(result).toBe(expected);
|
|
1722
|
+
});
|
|
1723
|
+
|
|
1724
|
+
it('should handle null percentFee for PERCENT type', () => {
|
|
1725
|
+
const result = estimatedCost({
|
|
1726
|
+
...baseParams,
|
|
1727
|
+
percentFee: null,
|
|
1728
|
+
});
|
|
1729
|
+
expect(result).toBe(0n); // Uses 0n as default
|
|
1730
|
+
});
|
|
1731
|
+
});
|
|
1732
|
+
|
|
1733
|
+
describe('reasonableFixedFee', () => {
|
|
1734
|
+
it('should calculate fee correctly', () => {
|
|
1735
|
+
const estimatedTokenNetworkCost = oneEther;
|
|
1736
|
+
const desiredExcessCompensationBasisPoints = 1_000n;
|
|
1737
|
+
const result = reasonableFixedFee({
|
|
1738
|
+
estimatedTokenNetworkCost,
|
|
1739
|
+
desiredExcessCompensationBasisPoints,
|
|
1740
|
+
});
|
|
1741
|
+
const expected =
|
|
1742
|
+
(estimatedTokenNetworkCost * (25_000n + desiredExcessCompensationBasisPoints)) / basisPoints;
|
|
1743
|
+
expect(result).toBe(expected);
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
it('should return null when estimatedTokenNetworkCost is null', () => {
|
|
1747
|
+
const result = reasonableFixedFee({
|
|
1748
|
+
estimatedTokenNetworkCost: null,
|
|
1749
|
+
desiredExcessCompensationBasisPoints: 1_000n,
|
|
1750
|
+
});
|
|
1751
|
+
expect(result).toBeNull();
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1754
|
+
it('should handle zero estimatedTokenNetworkCost', () => {
|
|
1755
|
+
const result = reasonableFixedFee({
|
|
1756
|
+
estimatedTokenNetworkCost: 0n,
|
|
1757
|
+
desiredExcessCompensationBasisPoints: 1_000n,
|
|
1758
|
+
});
|
|
1759
|
+
expect(result).toBeNull();
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
it('should handle zero desiredExcessCompensationBasisPoints', () => {
|
|
1763
|
+
const estimatedTokenNetworkCost = oneEther;
|
|
1764
|
+
const result = reasonableFixedFee({
|
|
1765
|
+
estimatedTokenNetworkCost,
|
|
1766
|
+
desiredExcessCompensationBasisPoints: 0n,
|
|
1767
|
+
});
|
|
1768
|
+
const expected = (estimatedTokenNetworkCost * 25_000n) / basisPoints;
|
|
1769
|
+
expect(result).toBe(expected);
|
|
1770
|
+
});
|
|
1771
|
+
|
|
1772
|
+
it('should handle large desiredExcessCompensationBasisPoints', () => {
|
|
1773
|
+
const estimatedTokenNetworkCost = oneEther;
|
|
1774
|
+
const desiredExcessCompensationBasisPoints = 10_000n;
|
|
1775
|
+
const result = reasonableFixedFee({
|
|
1776
|
+
estimatedTokenNetworkCost,
|
|
1777
|
+
desiredExcessCompensationBasisPoints,
|
|
1778
|
+
});
|
|
1779
|
+
const expected = (estimatedTokenNetworkCost * 35_000n) / basisPoints;
|
|
1780
|
+
expect(result).toBe(expected);
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1783
|
+
it('should maintain precision with small values', () => {
|
|
1784
|
+
const estimatedTokenNetworkCost = 100n;
|
|
1785
|
+
const desiredExcessCompensationBasisPoints = 500n;
|
|
1786
|
+
const result = reasonableFixedFee({
|
|
1787
|
+
estimatedTokenNetworkCost,
|
|
1788
|
+
desiredExcessCompensationBasisPoints,
|
|
1789
|
+
});
|
|
1790
|
+
expect(result).toBe((100n * 25_500n) / basisPoints);
|
|
1791
|
+
});
|
|
1792
|
+
});
|
|
1793
|
+
|
|
1794
|
+
describe('reasonablePercentFee', () => {
|
|
1795
|
+
const basisFeeTruncator = 10n ** 14n;
|
|
1796
|
+
const max = 1_000n * basisFeeTruncator;
|
|
1797
|
+
const min = 5n * basisFeeTruncator;
|
|
1798
|
+
|
|
1799
|
+
it('should return null when reasonableFixedFee is null', () => {
|
|
1800
|
+
const result = reasonablePercentFee({
|
|
1801
|
+
reasonableFixedFee: null,
|
|
1802
|
+
amountAfterBridgeFee: oneEther,
|
|
1803
|
+
});
|
|
1804
|
+
expect(result).toBeNull();
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
it('should return null when amountAfterBridgeFee is null', () => {
|
|
1808
|
+
const result = reasonablePercentFee({
|
|
1809
|
+
reasonableFixedFee: oneEther,
|
|
1810
|
+
amountAfterBridgeFee: null,
|
|
1811
|
+
});
|
|
1812
|
+
expect(result).toBeNull();
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
it('should return max (10%) when highFee < reasonableFixedFee', () => {
|
|
1816
|
+
const amountAfterBridgeFee = oneEther;
|
|
1817
|
+
const reasonableFixedFee = amountAfterBridgeFee / 10n + 1n;
|
|
1818
|
+
const result = reasonablePercentFee({
|
|
1819
|
+
reasonableFixedFee,
|
|
1820
|
+
amountAfterBridgeFee,
|
|
1821
|
+
});
|
|
1822
|
+
expect(result).toBe(max);
|
|
1823
|
+
});
|
|
1824
|
+
|
|
1825
|
+
it('should return min when calculated percent is below minimum', () => {
|
|
1826
|
+
const amountAfterBridgeFee = oneEther * 1000n;
|
|
1827
|
+
const reasonableFixedFee = oneEther / 100n; // Very small fee
|
|
1828
|
+
const result = reasonablePercentFee({
|
|
1829
|
+
reasonableFixedFee,
|
|
1830
|
+
amountAfterBridgeFee,
|
|
1831
|
+
});
|
|
1832
|
+
expect(result).toBe(min);
|
|
1833
|
+
});
|
|
1834
|
+
|
|
1835
|
+
it('should return max when calculated percent exceeds maximum', () => {
|
|
1836
|
+
const amountAfterBridgeFee = oneEther;
|
|
1837
|
+
const reasonableFixedFee = oneEther / 5n; // 20%
|
|
1838
|
+
const result = reasonablePercentFee({
|
|
1839
|
+
reasonableFixedFee,
|
|
1840
|
+
amountAfterBridgeFee,
|
|
1841
|
+
});
|
|
1842
|
+
expect(result).toBe(max);
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
it('should return truncated percent when in valid range', () => {
|
|
1846
|
+
const amountAfterBridgeFee = oneEther;
|
|
1847
|
+
const reasonableFixedFee = oneEther / 100n; // 1%
|
|
1848
|
+
const result = reasonablePercentFee({
|
|
1849
|
+
reasonableFixedFee,
|
|
1850
|
+
amountAfterBridgeFee,
|
|
1851
|
+
});
|
|
1852
|
+
// 1% = 0.01 * 10^18 = 10^16
|
|
1853
|
+
// highResPercent = (10^16 * 10^18) / 10^18 = 10^16
|
|
1854
|
+
// basisPoint = 10^16 / 10^14 = 100
|
|
1855
|
+
// result = 100 * 10^14 = 10^16
|
|
1856
|
+
expect(result).toBe(100n * basisFeeTruncator);
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
it('should handle truncation correctly', () => {
|
|
1860
|
+
const amountAfterBridgeFee = oneEther;
|
|
1861
|
+
const reasonableFixedFee = oneEther / 50n; // 2%
|
|
1862
|
+
const result = reasonablePercentFee({
|
|
1863
|
+
reasonableFixedFee,
|
|
1864
|
+
amountAfterBridgeFee,
|
|
1865
|
+
});
|
|
1866
|
+
// 2% = 0.02 * 10^18 = 2 * 10^16
|
|
1867
|
+
// highResPercent = (2 * 10^16 * 10^18) / 10^18 = 2 * 10^16
|
|
1868
|
+
// basisPoint = 2 * 10^16 / 10^14 = 200
|
|
1869
|
+
// result = 200 * 10^14 = 2 * 10^16
|
|
1870
|
+
expect(result).toBe(200n * basisFeeTruncator);
|
|
1871
|
+
});
|
|
1872
|
+
|
|
1873
|
+
it('should handle zero reasonableFixedFee', () => {
|
|
1874
|
+
const result = reasonablePercentFee({
|
|
1875
|
+
reasonableFixedFee: 0n,
|
|
1876
|
+
amountAfterBridgeFee: oneEther,
|
|
1877
|
+
});
|
|
1878
|
+
expect(result).toBeNull();
|
|
1879
|
+
});
|
|
1880
|
+
|
|
1881
|
+
it('should handle edge case near minimum boundary', () => {
|
|
1882
|
+
const amountAfterBridgeFee = oneEther * 200n;
|
|
1883
|
+
const reasonableFixedFee = oneEther; // 0.5%
|
|
1884
|
+
const result = reasonablePercentFee({
|
|
1885
|
+
reasonableFixedFee,
|
|
1886
|
+
amountAfterBridgeFee,
|
|
1887
|
+
});
|
|
1888
|
+
// highResPercent = (10^18 * 10^18) / (200 * 10^18) = 5 * 10^15
|
|
1889
|
+
// basisPoint = 5 * 10^15 / 10^14 = 50
|
|
1890
|
+
// result = 50 * 10^14 = 5 * 10^15
|
|
1891
|
+
expect(result).toBe(50n * basisFeeTruncator);
|
|
1892
|
+
});
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1895
|
+
describe('availableCompensationMaximum', () => {
|
|
1896
|
+
const baseParams = {
|
|
1897
|
+
amountAfterBridgeFee: oneEther,
|
|
1898
|
+
percentFee: oneEther / 100n, // 1%
|
|
1899
|
+
feeType: FeeType.PERCENT,
|
|
1900
|
+
fixedFee: oneEther / 10n,
|
|
1901
|
+
limit: oneEther / 5n,
|
|
1902
|
+
};
|
|
1903
|
+
|
|
1904
|
+
it('should calculate correctly for PERCENT fee type', () => {
|
|
1905
|
+
const result = availableCompensationMaximum(baseParams);
|
|
1906
|
+
const expected = (baseParams.amountAfterBridgeFee * baseParams.percentFee) / oneEther;
|
|
1907
|
+
expect(result).toBe(expected);
|
|
1908
|
+
});
|
|
1909
|
+
|
|
1910
|
+
it('should return null for PERCENT when amountAfterBridgeFee is null', () => {
|
|
1911
|
+
const result = availableCompensationMaximum({
|
|
1912
|
+
...baseParams,
|
|
1913
|
+
amountAfterBridgeFee: null,
|
|
1914
|
+
});
|
|
1915
|
+
expect(result).toBeNull();
|
|
1916
|
+
});
|
|
1917
|
+
|
|
1918
|
+
it('should return limit for GAS_TIP fee type', () => {
|
|
1919
|
+
const result = availableCompensationMaximum({
|
|
1920
|
+
...baseParams,
|
|
1921
|
+
feeType: FeeType.GAS_TIP,
|
|
1922
|
+
});
|
|
1923
|
+
expect(result).toBe(baseParams.limit);
|
|
1924
|
+
});
|
|
1925
|
+
|
|
1926
|
+
it('should return fixedFee for FIXED fee type', () => {
|
|
1927
|
+
const result = availableCompensationMaximum({
|
|
1928
|
+
...baseParams,
|
|
1929
|
+
feeType: FeeType.FIXED,
|
|
1930
|
+
});
|
|
1931
|
+
expect(result).toBe(baseParams.fixedFee);
|
|
1932
|
+
});
|
|
1933
|
+
|
|
1934
|
+
it('should return 0n for FIXED when fixedFee is null', () => {
|
|
1935
|
+
const result = availableCompensationMaximum({
|
|
1936
|
+
...baseParams,
|
|
1937
|
+
feeType: FeeType.FIXED,
|
|
1938
|
+
fixedFee: null,
|
|
1939
|
+
});
|
|
1940
|
+
expect(result).toBe(0n);
|
|
1941
|
+
});
|
|
1942
|
+
|
|
1943
|
+
it('should handle zero percentFee for PERCENT type', () => {
|
|
1944
|
+
const result = availableCompensationMaximum({
|
|
1945
|
+
...baseParams,
|
|
1946
|
+
percentFee: 0n,
|
|
1947
|
+
});
|
|
1948
|
+
expect(result).toBe(0n);
|
|
1949
|
+
});
|
|
1950
|
+
|
|
1951
|
+
it('should handle null percentFee for PERCENT type', () => {
|
|
1952
|
+
const result = availableCompensationMaximum({
|
|
1953
|
+
...baseParams,
|
|
1954
|
+
percentFee: null,
|
|
1955
|
+
});
|
|
1956
|
+
expect(result).toBe(0n);
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
it('should handle zero limit for GAS_TIP type', () => {
|
|
1960
|
+
const result = availableCompensationMaximum({
|
|
1961
|
+
...baseParams,
|
|
1962
|
+
feeType: FeeType.GAS_TIP,
|
|
1963
|
+
limit: 0n,
|
|
1964
|
+
});
|
|
1965
|
+
expect(result).toBe(0n);
|
|
1966
|
+
});
|
|
1967
|
+
});
|
|
1968
|
+
|
|
1969
|
+
describe('isUndercompensated', () => {
|
|
1970
|
+
const baseParams = {
|
|
1971
|
+
estimatedTokenNetworkCost: oneEther / 10n, // 0.1 tokens
|
|
1972
|
+
feeType: FeeType.FIXED,
|
|
1973
|
+
limit: oneEther / 5n, // 0.2 tokens
|
|
1974
|
+
amountAfterBridgeFee: oneEther,
|
|
1975
|
+
desiredExcessCompensationBasisPoints: 1_000n, // 10%
|
|
1976
|
+
};
|
|
1977
|
+
|
|
1978
|
+
it('should return false when amountAfterBridgeFee is null', () => {
|
|
1979
|
+
const result = isUndercompensated({
|
|
1980
|
+
...baseParams,
|
|
1981
|
+
amountAfterBridgeFee: null,
|
|
1982
|
+
});
|
|
1983
|
+
expect(result).toBe(false);
|
|
1984
|
+
});
|
|
1985
|
+
|
|
1986
|
+
it('should return false when amountAfterBridgeFee is 0', () => {
|
|
1987
|
+
const result = isUndercompensated({
|
|
1988
|
+
...baseParams,
|
|
1989
|
+
amountAfterBridgeFee: 0n,
|
|
1990
|
+
});
|
|
1991
|
+
expect(result).toBe(false);
|
|
1992
|
+
});
|
|
1993
|
+
|
|
1994
|
+
it('should return false for GAS_TIP fee type', () => {
|
|
1995
|
+
const result = isUndercompensated({
|
|
1996
|
+
...baseParams,
|
|
1997
|
+
feeType: FeeType.GAS_TIP,
|
|
1998
|
+
});
|
|
1999
|
+
expect(result).toBe(false);
|
|
2000
|
+
});
|
|
2001
|
+
|
|
2002
|
+
it('should return false when estimatedTokenNetworkCost is null', () => {
|
|
2003
|
+
const result = isUndercompensated({
|
|
2004
|
+
...baseParams,
|
|
2005
|
+
estimatedTokenNetworkCost: null,
|
|
2006
|
+
});
|
|
2007
|
+
expect(result).toBe(false);
|
|
2008
|
+
});
|
|
2009
|
+
|
|
2010
|
+
it('should return true when desired compensation exceeds limit', () => {
|
|
2011
|
+
// estimatedTokenNetworkCost = 0.1 ETH
|
|
2012
|
+
// desiredCompensationRatio = 1.0 + (1000 / 10000) = 1.1
|
|
2013
|
+
// desiredCompensationForNetworkCost = 0.1 * 1.1 = 0.11 ETH
|
|
2014
|
+
// limit = 0.2 ETH, so 0.11 < 0.2 = false
|
|
2015
|
+
const result = isUndercompensated({
|
|
2016
|
+
...baseParams,
|
|
2017
|
+
limit: oneEther / 100n, // 0.01 tokens (very low limit)
|
|
2018
|
+
});
|
|
2019
|
+
// Now 0.11 > 0.01 = true (undercompensated)
|
|
2020
|
+
expect(result).toBe(true);
|
|
2021
|
+
});
|
|
2022
|
+
|
|
2023
|
+
it('should return false when compensation is adequate', () => {
|
|
2024
|
+
const result = isUndercompensated(baseParams);
|
|
2025
|
+
// desiredCompensation = 0.1 * 1.1 = 0.11 ETH
|
|
2026
|
+
// limit = 0.2 ETH, so 0.11 < 0.2 = false (adequate)
|
|
2027
|
+
expect(result).toBe(false);
|
|
2028
|
+
});
|
|
2029
|
+
|
|
2030
|
+
it('should handle zero desiredExcessCompensationBasisPoints', () => {
|
|
2031
|
+
const result = isUndercompensated({
|
|
2032
|
+
...baseParams,
|
|
2033
|
+
desiredExcessCompensationBasisPoints: 0n,
|
|
2034
|
+
});
|
|
2035
|
+
// desiredCompensationRatio = 1.0
|
|
2036
|
+
// desiredCompensation = 0.1 * 1.0 = 0.1 ETH
|
|
2037
|
+
// limit = 0.2 ETH, so false (adequate)
|
|
2038
|
+
expect(result).toBe(false);
|
|
2039
|
+
});
|
|
2040
|
+
|
|
2041
|
+
it('should handle large desiredExcessCompensationBasisPoints', () => {
|
|
2042
|
+
const result = isUndercompensated({
|
|
2043
|
+
...baseParams,
|
|
2044
|
+
desiredExcessCompensationBasisPoints: 10_000n, // 100%
|
|
2045
|
+
limit: oneEther / 10n, // 0.1 tokens
|
|
2046
|
+
});
|
|
2047
|
+
// desiredCompensationRatio = 1.0 + 1.0 = 2.0
|
|
2048
|
+
// desiredCompensation = 0.1 * 2.0 = 0.2 ETH
|
|
2049
|
+
// limit = 0.1 ETH, so 0.2 > 0.1 = true (undercompensated)
|
|
2050
|
+
expect(result).toBe(true);
|
|
2051
|
+
});
|
|
2052
|
+
|
|
2053
|
+
it('should handle edge case at exact limit', () => {
|
|
2054
|
+
// Set limit to exactly match desired compensation
|
|
2055
|
+
const estimatedTokenNetworkCost = oneEther / 10n;
|
|
2056
|
+
const desiredExcessCompensationBasisPoints = 1_000n;
|
|
2057
|
+
const desiredCompensationRatio =
|
|
2058
|
+
oneEther + (desiredExcessCompensationBasisPoints * oneEther) / basisPoints;
|
|
2059
|
+
const limit = (estimatedTokenNetworkCost * desiredCompensationRatio) / oneEther;
|
|
2060
|
+
|
|
2061
|
+
const result = isUndercompensated({
|
|
2062
|
+
estimatedTokenNetworkCost,
|
|
2063
|
+
feeType: FeeType.FIXED,
|
|
2064
|
+
limit,
|
|
2065
|
+
amountAfterBridgeFee: oneEther,
|
|
2066
|
+
desiredExcessCompensationBasisPoints,
|
|
2067
|
+
});
|
|
2068
|
+
expect(result).toBe(false); // Exactly at limit, not under
|
|
2069
|
+
});
|
|
2070
|
+
|
|
2071
|
+
it('should handle PERCENT fee type', () => {
|
|
2072
|
+
const result = isUndercompensated({
|
|
2073
|
+
...baseParams,
|
|
2074
|
+
feeType: FeeType.PERCENT,
|
|
2075
|
+
limit: oneEther / 100n,
|
|
2076
|
+
});
|
|
2077
|
+
// PERCENT fee type still does the calculation
|
|
2078
|
+
expect(result).toBe(true);
|
|
2079
|
+
});
|
|
2080
|
+
});
|
|
2081
|
+
|
|
2082
|
+
describe('bridgePathway', () => {
|
|
2083
|
+
it('should return pathway for valid bridge', () => {
|
|
2084
|
+
const result = bridgePathway({
|
|
2085
|
+
bridgeKey: ['pulsechain', Chains.PLS, Chains.ETH],
|
|
2086
|
+
isProd: true,
|
|
2087
|
+
assetIn: null,
|
|
2088
|
+
});
|
|
2089
|
+
// Should return a pathway (exact structure depends on config)
|
|
2090
|
+
expect(result).not.toBeNull();
|
|
2091
|
+
if (result) {
|
|
2092
|
+
expect(result).toHaveProperty('from');
|
|
2093
|
+
expect(result).toHaveProperty('to');
|
|
2094
|
+
expect(result).toHaveProperty('requiresDelivery');
|
|
2095
|
+
}
|
|
2096
|
+
});
|
|
2097
|
+
|
|
2098
|
+
it('should return pathway for valid bridge with assetIn', () => {
|
|
2099
|
+
const token: Token = {
|
|
2100
|
+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
2101
|
+
decimals: 6,
|
|
2102
|
+
name: 'USD Coin',
|
|
2103
|
+
symbol: 'USDC',
|
|
2104
|
+
chainId: parseInt(Chains.PLS, 16),
|
|
2105
|
+
};
|
|
2106
|
+
const result = bridgePathway({
|
|
2107
|
+
bridgeKey: ['pulsechain', Chains.PLS, Chains.ETH],
|
|
2108
|
+
isProd: true,
|
|
2109
|
+
assetIn: token,
|
|
2110
|
+
});
|
|
2111
|
+
expect(result).not.toBeNull();
|
|
2112
|
+
});
|
|
2113
|
+
|
|
2114
|
+
it('should respect isProd flag', () => {
|
|
2115
|
+
const resultProd = bridgePathway({
|
|
2116
|
+
bridgeKey: ['pulsechain', Chains.PLS, Chains.ETH],
|
|
2117
|
+
isProd: true,
|
|
2118
|
+
assetIn: null,
|
|
2119
|
+
});
|
|
2120
|
+
const resultDev = bridgePathway({
|
|
2121
|
+
bridgeKey: ['pulsechain', Chains.PLS, Chains.ETH],
|
|
2122
|
+
isProd: false,
|
|
2123
|
+
assetIn: null,
|
|
2124
|
+
});
|
|
2125
|
+
// Both should return pathways but they might differ
|
|
2126
|
+
expect(resultProd).not.toBeNull();
|
|
2127
|
+
expect(resultDev).not.toBeNull();
|
|
2128
|
+
});
|
|
2129
|
+
|
|
2130
|
+
it('should handle tokensex provider', () => {
|
|
2131
|
+
const result = bridgePathway({
|
|
2132
|
+
bridgeKey: ['tokensex', Chains.PLS, Chains.BNB],
|
|
2133
|
+
isProd: true,
|
|
2134
|
+
assetIn: null,
|
|
2135
|
+
});
|
|
2136
|
+
expect(result).not.toBeNull();
|
|
2137
|
+
if (result) {
|
|
2138
|
+
expect(result).toHaveProperty('from');
|
|
2139
|
+
expect(result).toHaveProperty('feeManager');
|
|
2140
|
+
}
|
|
2141
|
+
});
|
|
2142
|
+
|
|
2143
|
+
it('should pass assetIn address to pathway function', () => {
|
|
2144
|
+
const token: Token = {
|
|
2145
|
+
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
2146
|
+
decimals: 6,
|
|
2147
|
+
name: 'Tether',
|
|
2148
|
+
symbol: 'USDT',
|
|
2149
|
+
chainId: parseInt(Chains.ETH, 16),
|
|
2150
|
+
};
|
|
2151
|
+
const result = bridgePathway({
|
|
2152
|
+
bridgeKey: ['pulsechain', Chains.PLS, Chains.ETH],
|
|
2153
|
+
isProd: true,
|
|
2154
|
+
assetIn: token,
|
|
2155
|
+
});
|
|
2156
|
+
expect(result).not.toBeNull();
|
|
2157
|
+
});
|
|
2158
|
+
});
|
|
2159
|
+
|
|
2160
|
+
describe('destinationDataParam', () => {
|
|
2161
|
+
const baseParams = {
|
|
2162
|
+
recipient: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
|
|
2163
|
+
bridgePathway: {
|
|
2164
|
+
from: '0x1' as Hex,
|
|
2165
|
+
to: '0x2' as Hex,
|
|
2166
|
+
requiresDelivery: false,
|
|
2167
|
+
feeManager: 'from' as const,
|
|
2168
|
+
nativeRouter: '0x3' as Hex,
|
|
2169
|
+
usesExtraParam: false,
|
|
2170
|
+
} as any,
|
|
2171
|
+
assetIn: {
|
|
2172
|
+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
2173
|
+
decimals: 6,
|
|
2174
|
+
name: 'USDC',
|
|
2175
|
+
symbol: 'USDC',
|
|
2176
|
+
chainId: 1,
|
|
2177
|
+
} as Token,
|
|
2178
|
+
destinationRouter: '0x4' as Hex,
|
|
2179
|
+
feeDirectorStructEncoded: null,
|
|
2180
|
+
assetLink: { toHome: '0x5', toForeign: '0x6' } as any,
|
|
2181
|
+
shouldDeliver: false,
|
|
2182
|
+
unwrap: false,
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
it('should return null when recipient is null', () => {
|
|
2186
|
+
const result = destinationDataParam({
|
|
2187
|
+
...baseParams,
|
|
2188
|
+
recipient: null,
|
|
2189
|
+
});
|
|
2190
|
+
expect(result).toBeNull();
|
|
2191
|
+
});
|
|
2192
|
+
|
|
2193
|
+
it('should return null when bridgePathway is null', () => {
|
|
2194
|
+
const result = destinationDataParam({
|
|
2195
|
+
...baseParams,
|
|
2196
|
+
bridgePathway: null,
|
|
2197
|
+
});
|
|
2198
|
+
expect(result).toBeNull();
|
|
2199
|
+
});
|
|
2200
|
+
|
|
2201
|
+
it('should return null when assetIn is null', () => {
|
|
2202
|
+
const result = destinationDataParam({
|
|
2203
|
+
...baseParams,
|
|
2204
|
+
assetIn: null,
|
|
2205
|
+
});
|
|
2206
|
+
expect(result).toBeNull();
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2209
|
+
it('should return null when destinationRouter is null', () => {
|
|
2210
|
+
const result = destinationDataParam({
|
|
2211
|
+
...baseParams,
|
|
2212
|
+
destinationRouter: null,
|
|
2213
|
+
});
|
|
2214
|
+
expect(result).toBeNull();
|
|
2215
|
+
});
|
|
2216
|
+
|
|
2217
|
+
it('should return null when requiresDelivery but no feeDirectorStructEncoded', () => {
|
|
2218
|
+
const result = destinationDataParam({
|
|
2219
|
+
...baseParams,
|
|
2220
|
+
bridgePathway: {
|
|
2221
|
+
...baseParams.bridgePathway,
|
|
2222
|
+
requiresDelivery: true,
|
|
2223
|
+
},
|
|
2224
|
+
feeDirectorStructEncoded: null,
|
|
2225
|
+
});
|
|
2226
|
+
expect(result).toBeNull();
|
|
2227
|
+
});
|
|
2228
|
+
|
|
2229
|
+
it('should return null when assetLink is null', () => {
|
|
2230
|
+
const result = destinationDataParam({
|
|
2231
|
+
...baseParams,
|
|
2232
|
+
assetLink: null,
|
|
2233
|
+
});
|
|
2234
|
+
expect(result).toBeNull();
|
|
2235
|
+
});
|
|
2236
|
+
|
|
2237
|
+
it('should return recipient when not requiring delivery', () => {
|
|
2238
|
+
const result = destinationDataParam(baseParams);
|
|
2239
|
+
expect(result).toBe(baseParams.recipient);
|
|
2240
|
+
});
|
|
2241
|
+
|
|
2242
|
+
it('should return concatHex of destinationRouter and recipient when unwrap is true', () => {
|
|
2243
|
+
const result = destinationDataParam({
|
|
2244
|
+
...baseParams,
|
|
2245
|
+
unwrap: true,
|
|
2246
|
+
});
|
|
2247
|
+
// concatHex mock joins the parts
|
|
2248
|
+
expect(result).toBe(`${baseParams.destinationRouter}${baseParams.recipient}`);
|
|
2249
|
+
});
|
|
2250
|
+
|
|
2251
|
+
it('should return recipient when requiresDelivery but shouldDeliver is false', () => {
|
|
2252
|
+
const result = destinationDataParam({
|
|
2253
|
+
...baseParams,
|
|
2254
|
+
bridgePathway: {
|
|
2255
|
+
...baseParams.bridgePathway,
|
|
2256
|
+
requiresDelivery: true,
|
|
2257
|
+
} as any,
|
|
2258
|
+
feeDirectorStructEncoded: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' as Hex,
|
|
2259
|
+
shouldDeliver: false,
|
|
2260
|
+
});
|
|
2261
|
+
expect(result).toBe(baseParams.recipient);
|
|
2262
|
+
});
|
|
2263
|
+
|
|
2264
|
+
it('should return concatHex when requiresDelivery and shouldDeliver with feeDirectorStructEncoded', () => {
|
|
2265
|
+
const feeStruct = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' as Hex;
|
|
2266
|
+
const result = destinationDataParam({
|
|
2267
|
+
...baseParams,
|
|
2268
|
+
bridgePathway: {
|
|
2269
|
+
...baseParams.bridgePathway,
|
|
2270
|
+
requiresDelivery: true,
|
|
2271
|
+
} as any,
|
|
2272
|
+
feeDirectorStructEncoded: feeStruct,
|
|
2273
|
+
shouldDeliver: true,
|
|
2274
|
+
});
|
|
2275
|
+
// concatHex mock joins the parts
|
|
2276
|
+
expect(result).toBe(`${baseParams.destinationRouter}${feeStruct}`);
|
|
2277
|
+
});
|
|
2278
|
+
|
|
2279
|
+
});
|
|
2280
|
+
|
|
2281
|
+
describe('feeDirectorStructEncoded', () => {
|
|
2282
|
+
const baseParams = {
|
|
2283
|
+
recipient: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
|
|
2284
|
+
feeType: FeeType.FIXED,
|
|
2285
|
+
priceCorrective: oneEther,
|
|
2286
|
+
gasTipFee: oneEther / 100n,
|
|
2287
|
+
percentFee: oneEther / 50n,
|
|
2288
|
+
feeTypeSettings: 1n,
|
|
2289
|
+
limit: oneEther,
|
|
2290
|
+
bridgePathway: {
|
|
2291
|
+
requiresDelivery: true,
|
|
2292
|
+
from: '0x1' as Hex,
|
|
2293
|
+
} as any,
|
|
2294
|
+
assetOut: {
|
|
2295
|
+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
2296
|
+
decimals: 6,
|
|
2297
|
+
name: 'USDC',
|
|
2298
|
+
symbol: 'USDC',
|
|
2299
|
+
chainId: 1,
|
|
2300
|
+
} as Token,
|
|
2301
|
+
};
|
|
2302
|
+
|
|
2303
|
+
it('should return null when assetOut is null', () => {
|
|
2304
|
+
const result = feeDirectorStructEncoded({
|
|
2305
|
+
...baseParams,
|
|
2306
|
+
assetOut: null,
|
|
2307
|
+
});
|
|
2308
|
+
expect(result).toBeNull();
|
|
2309
|
+
});
|
|
2310
|
+
|
|
2311
|
+
it('should return null when bridgePathway is null', () => {
|
|
2312
|
+
const result = feeDirectorStructEncoded({
|
|
2313
|
+
...baseParams,
|
|
2314
|
+
bridgePathway: null,
|
|
2315
|
+
});
|
|
2316
|
+
expect(result).toBeNull();
|
|
2317
|
+
});
|
|
2318
|
+
|
|
2319
|
+
it('should return null when pathway does not require delivery', () => {
|
|
2320
|
+
const result = feeDirectorStructEncoded({
|
|
2321
|
+
...baseParams,
|
|
2322
|
+
bridgePathway: {
|
|
2323
|
+
requiresDelivery: false,
|
|
2324
|
+
} as any,
|
|
2325
|
+
});
|
|
2326
|
+
expect(result).toBeNull();
|
|
2327
|
+
});
|
|
2328
|
+
|
|
2329
|
+
it('should return null when priceCorrective is null', () => {
|
|
2330
|
+
const result = feeDirectorStructEncoded({
|
|
2331
|
+
...baseParams,
|
|
2332
|
+
priceCorrective: null,
|
|
2333
|
+
});
|
|
2334
|
+
expect(result).toBeNull();
|
|
2335
|
+
});
|
|
2336
|
+
|
|
2337
|
+
it('should return null when priceCorrective is zero for GAS_TIP', () => {
|
|
2338
|
+
const result = feeDirectorStructEncoded({
|
|
2339
|
+
...baseParams,
|
|
2340
|
+
feeType: FeeType.GAS_TIP,
|
|
2341
|
+
priceCorrective: 0n,
|
|
2342
|
+
});
|
|
2343
|
+
expect(result).toBeNull();
|
|
2344
|
+
});
|
|
2345
|
+
|
|
2346
|
+
it('should return null when recipient is null', () => {
|
|
2347
|
+
const result = feeDirectorStructEncoded({
|
|
2348
|
+
...baseParams,
|
|
2349
|
+
recipient: null,
|
|
2350
|
+
});
|
|
2351
|
+
expect(result).toBeNull();
|
|
2352
|
+
});
|
|
2353
|
+
|
|
2354
|
+
it('should return null when recipient is invalid address', () => {
|
|
2355
|
+
const result = feeDirectorStructEncoded({
|
|
2356
|
+
...baseParams,
|
|
2357
|
+
recipient: '0xinvalid' as Hex,
|
|
2358
|
+
});
|
|
2359
|
+
expect(result).toBeNull();
|
|
2360
|
+
});
|
|
2361
|
+
});
|
|
2362
|
+
|
|
2363
|
+
describe('transactionInputs', () => {
|
|
2364
|
+
const baseParams = {
|
|
2365
|
+
bridgePathway: {
|
|
2366
|
+
from: '0x1111111111111111111111111111111111111111' as Hex,
|
|
2367
|
+
to: '0x2222222222222222222222222222222222222222' as Hex,
|
|
2368
|
+
requiresDelivery: false,
|
|
2369
|
+
feeManager: 'from' as const,
|
|
2370
|
+
nativeRouter: '0x3333333333333333333333333333333333333333' as Hex,
|
|
2371
|
+
usesExtraParam: false,
|
|
2372
|
+
} as any,
|
|
2373
|
+
assetIn: {
|
|
2374
|
+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
2375
|
+
decimals: 6,
|
|
2376
|
+
name: 'USDC',
|
|
2377
|
+
symbol: 'USDC',
|
|
2378
|
+
chainId: 1,
|
|
2379
|
+
} as Token,
|
|
2380
|
+
recipient: '0x4444444444444444444444444444444444444444' as Hex,
|
|
2381
|
+
account: '0x5555555555555555555555555555555555555555' as Hex,
|
|
2382
|
+
assetLink: {
|
|
2383
|
+
toHome: '0x6666666666666666666666666666666666666666',
|
|
2384
|
+
toForeign: '0x7777777777777777777777777777777777777777',
|
|
2385
|
+
} as any,
|
|
2386
|
+
destinationDataParam: '0xDa7a' as Hex,
|
|
2387
|
+
amountToBridge: oneEther,
|
|
2388
|
+
feeDirectorStructEncoded: null,
|
|
2389
|
+
shouldDeliver: false,
|
|
2390
|
+
};
|
|
2391
|
+
|
|
2392
|
+
it('should return null when bridgePathway is null', () => {
|
|
2393
|
+
const result = transactionInputs({
|
|
2394
|
+
...baseParams,
|
|
2395
|
+
bridgePathway: null,
|
|
2396
|
+
});
|
|
2397
|
+
expect(result).toBeNull();
|
|
2398
|
+
});
|
|
2399
|
+
|
|
2400
|
+
it('should return null when recipient is null', () => {
|
|
2401
|
+
const result = transactionInputs({
|
|
2402
|
+
...baseParams,
|
|
2403
|
+
recipient: null,
|
|
2404
|
+
});
|
|
2405
|
+
expect(result).toBeNull();
|
|
2406
|
+
});
|
|
2407
|
+
|
|
2408
|
+
it('should return null when recipient is not a valid address', () => {
|
|
2409
|
+
const result = transactionInputs({
|
|
2410
|
+
...baseParams,
|
|
2411
|
+
recipient: '0xinvalid' as Hex,
|
|
2412
|
+
});
|
|
2413
|
+
expect(result).toBeNull();
|
|
2414
|
+
});
|
|
2415
|
+
|
|
2416
|
+
it('should return null when recipient is zero address', () => {
|
|
2417
|
+
const result = transactionInputs({
|
|
2418
|
+
...baseParams,
|
|
2419
|
+
recipient: zeroAddress,
|
|
2420
|
+
});
|
|
2421
|
+
expect(result).toBeNull();
|
|
2422
|
+
});
|
|
2423
|
+
|
|
2424
|
+
it('should return null when account is null', () => {
|
|
2425
|
+
const result = transactionInputs({
|
|
2426
|
+
...baseParams,
|
|
2427
|
+
account: null,
|
|
2428
|
+
});
|
|
2429
|
+
expect(result).toBeNull();
|
|
2430
|
+
});
|
|
2431
|
+
|
|
2432
|
+
it('should return null when account is not a valid address', () => {
|
|
2433
|
+
const result = transactionInputs({
|
|
2434
|
+
...baseParams,
|
|
2435
|
+
account: '0xinvalid' as Hex,
|
|
2436
|
+
});
|
|
2437
|
+
expect(result).toBeNull();
|
|
2438
|
+
});
|
|
2439
|
+
|
|
2440
|
+
it('should return null when account is zero address', () => {
|
|
2441
|
+
const result = transactionInputs({
|
|
2442
|
+
...baseParams,
|
|
2443
|
+
account: zeroAddress,
|
|
2444
|
+
});
|
|
2445
|
+
expect(result).toBeNull();
|
|
2446
|
+
});
|
|
2447
|
+
|
|
2448
|
+
it('should return null when assetIn is null', () => {
|
|
2449
|
+
const result = transactionInputs({
|
|
2450
|
+
...baseParams,
|
|
2451
|
+
assetIn: null,
|
|
2452
|
+
});
|
|
2453
|
+
expect(result).toBeNull();
|
|
2454
|
+
});
|
|
2455
|
+
|
|
2456
|
+
it('should return null when assetLink is null', () => {
|
|
2457
|
+
const result = transactionInputs({
|
|
2458
|
+
...baseParams,
|
|
2459
|
+
assetLink: null,
|
|
2460
|
+
});
|
|
2461
|
+
expect(result).toBeNull();
|
|
2462
|
+
});
|
|
2463
|
+
|
|
2464
|
+
describe('bridged token (interactingWithBridgeToken = true)', () => {
|
|
2465
|
+
const bridgedTokenParams = {
|
|
2466
|
+
...baseParams,
|
|
2467
|
+
assetIn: {
|
|
2468
|
+
address: '0x6666666666666666666666666666666666666666', // matches toHome from assetLink
|
|
2469
|
+
decimals: 18,
|
|
2470
|
+
name: 'Bridged Token',
|
|
2471
|
+
symbol: 'BTK',
|
|
2472
|
+
chainId: 1,
|
|
2473
|
+
} as Token,
|
|
2474
|
+
assetLink: {
|
|
2475
|
+
toHome: {
|
|
2476
|
+
home: '0x6666666666666666666666666666666666666666' as Hex,
|
|
2477
|
+
foreign: '0x9999999999999999999999999999999999999999' as Hex,
|
|
2478
|
+
},
|
|
2479
|
+
} as any,
|
|
2480
|
+
};
|
|
2481
|
+
|
|
2482
|
+
it('should return null when destinationDataParam is missing', () => {
|
|
2483
|
+
const result = transactionInputs({
|
|
2484
|
+
...bridgedTokenParams,
|
|
2485
|
+
destinationDataParam: null,
|
|
2486
|
+
});
|
|
2487
|
+
expect(result).toBeNull();
|
|
2488
|
+
});
|
|
2489
|
+
|
|
2490
|
+
it('should encode transferAndCall without extra param', () => {
|
|
2491
|
+
const result = transactionInputs({
|
|
2492
|
+
...bridgedTokenParams,
|
|
2493
|
+
bridgePathway: {
|
|
2494
|
+
...baseParams.bridgePathway,
|
|
2495
|
+
usesExtraParam: false,
|
|
2496
|
+
} as any,
|
|
2497
|
+
});
|
|
2498
|
+
expect(result).toBeDefined();
|
|
2499
|
+
expect(result?.to).toBe(bridgedTokenParams.assetIn.address);
|
|
2500
|
+
expect(result?.value).toBe(0n);
|
|
2501
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2502
|
+
});
|
|
2503
|
+
|
|
2504
|
+
it('should encode transferAndCall with extra param', () => {
|
|
2505
|
+
const result = transactionInputs({
|
|
2506
|
+
...bridgedTokenParams,
|
|
2507
|
+
bridgePathway: {
|
|
2508
|
+
...baseParams.bridgePathway,
|
|
2509
|
+
usesExtraParam: true,
|
|
2510
|
+
} as any,
|
|
2511
|
+
});
|
|
2512
|
+
expect(result).toBeDefined();
|
|
2513
|
+
expect(result?.to).toBe(bridgedTokenParams.assetIn.address);
|
|
2514
|
+
expect(result?.value).toBe(0n);
|
|
2515
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2516
|
+
});
|
|
2517
|
+
});
|
|
2518
|
+
|
|
2519
|
+
describe('native token input (assetIn.address === zeroAddress)', () => {
|
|
2520
|
+
const nativeTokenParams = {
|
|
2521
|
+
...baseParams,
|
|
2522
|
+
assetIn: {
|
|
2523
|
+
address: zeroAddress,
|
|
2524
|
+
decimals: 18,
|
|
2525
|
+
name: 'Ether',
|
|
2526
|
+
symbol: 'ETH',
|
|
2527
|
+
chainId: 1,
|
|
2528
|
+
} as Token,
|
|
2529
|
+
};
|
|
2530
|
+
|
|
2531
|
+
describe('with delivery (feeManager = from, shouldDeliver = true)', () => {
|
|
2532
|
+
const deliveryParams = {
|
|
2533
|
+
...nativeTokenParams,
|
|
2534
|
+
shouldDeliver: true,
|
|
2535
|
+
feeDirectorStructEncoded: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' as Hex,
|
|
2536
|
+
bridgePathway: {
|
|
2537
|
+
...baseParams.bridgePathway,
|
|
2538
|
+
feeManager: 'from' as const,
|
|
2539
|
+
destinationRouter: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' as Hex,
|
|
2540
|
+
} as any,
|
|
2541
|
+
};
|
|
2542
|
+
|
|
2543
|
+
it('should return null when feeDirectorStructEncoded is missing', () => {
|
|
2544
|
+
const result = transactionInputs({
|
|
2545
|
+
...deliveryParams,
|
|
2546
|
+
feeDirectorStructEncoded: null,
|
|
2547
|
+
});
|
|
2548
|
+
expect(result).toBeNull();
|
|
2549
|
+
});
|
|
2550
|
+
|
|
2551
|
+
it('should return null when destinationRouter is missing', () => {
|
|
2552
|
+
const result = transactionInputs({
|
|
2553
|
+
...deliveryParams,
|
|
2554
|
+
bridgePathway: {
|
|
2555
|
+
...deliveryParams.bridgePathway,
|
|
2556
|
+
destinationRouter: null,
|
|
2557
|
+
} as any,
|
|
2558
|
+
});
|
|
2559
|
+
expect(result).toBeNull();
|
|
2560
|
+
});
|
|
2561
|
+
|
|
2562
|
+
it('should encode relayTokensAndCall without extra param', () => {
|
|
2563
|
+
const result = transactionInputs({
|
|
2564
|
+
...deliveryParams,
|
|
2565
|
+
bridgePathway: {
|
|
2566
|
+
...deliveryParams.bridgePathway,
|
|
2567
|
+
usesExtraParam: false,
|
|
2568
|
+
} as any,
|
|
2569
|
+
});
|
|
2570
|
+
expect(result).toBeDefined();
|
|
2571
|
+
expect(result?.to).toBe(baseParams.bridgePathway.nativeRouter);
|
|
2572
|
+
expect(result?.value).toBe(oneEther);
|
|
2573
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2574
|
+
});
|
|
2575
|
+
|
|
2576
|
+
it('should encode relayTokensAndCall with extra param', () => {
|
|
2577
|
+
const result = transactionInputs({
|
|
2578
|
+
...deliveryParams,
|
|
2579
|
+
bridgePathway: {
|
|
2580
|
+
...deliveryParams.bridgePathway,
|
|
2581
|
+
usesExtraParam: true,
|
|
2582
|
+
} as any,
|
|
2583
|
+
});
|
|
2584
|
+
expect(result).toBeDefined();
|
|
2585
|
+
expect(result?.to).toBe(baseParams.bridgePathway.nativeRouter);
|
|
2586
|
+
expect(result?.value).toBe(oneEther);
|
|
2587
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2588
|
+
});
|
|
2589
|
+
});
|
|
2590
|
+
|
|
2591
|
+
describe('without delivery (foreign to home or no delivery)', () => {
|
|
2592
|
+
const noDeliveryParams = {
|
|
2593
|
+
...nativeTokenParams,
|
|
2594
|
+
shouldDeliver: false,
|
|
2595
|
+
};
|
|
2596
|
+
|
|
2597
|
+
it('should encode wrapAndRelayTokens without extra param', () => {
|
|
2598
|
+
const result = transactionInputs({
|
|
2599
|
+
...noDeliveryParams,
|
|
2600
|
+
bridgePathway: {
|
|
2601
|
+
...baseParams.bridgePathway,
|
|
2602
|
+
usesExtraParam: false,
|
|
2603
|
+
} as any,
|
|
2604
|
+
});
|
|
2605
|
+
expect(result).toBeDefined();
|
|
2606
|
+
expect(result?.to).toBe(baseParams.bridgePathway.nativeRouter);
|
|
2607
|
+
expect(result?.value).toBe(oneEther);
|
|
2608
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2609
|
+
});
|
|
2610
|
+
|
|
2611
|
+
it('should encode wrapAndRelayTokens with extra param', () => {
|
|
2612
|
+
const result = transactionInputs({
|
|
2613
|
+
...noDeliveryParams,
|
|
2614
|
+
bridgePathway: {
|
|
2615
|
+
...baseParams.bridgePathway,
|
|
2616
|
+
usesExtraParam: true,
|
|
2617
|
+
} as any,
|
|
2618
|
+
});
|
|
2619
|
+
expect(result).toBeDefined();
|
|
2620
|
+
expect(result?.to).toBe(baseParams.bridgePathway.nativeRouter);
|
|
2621
|
+
expect(result?.value).toBe(oneEther);
|
|
2622
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2623
|
+
});
|
|
2624
|
+
});
|
|
2625
|
+
});
|
|
2626
|
+
|
|
2627
|
+
describe('ERC20 token input (tokens native to this side)', () => {
|
|
2628
|
+
const erc20Params = {
|
|
2629
|
+
...baseParams,
|
|
2630
|
+
assetIn: {
|
|
2631
|
+
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
2632
|
+
decimals: 6,
|
|
2633
|
+
name: 'USDC',
|
|
2634
|
+
symbol: 'USDC',
|
|
2635
|
+
chainId: 1,
|
|
2636
|
+
} as Token,
|
|
2637
|
+
// assetLink with addresses that don't match assetIn.address so interactingWithBridgeToken returns false
|
|
2638
|
+
assetLink: {
|
|
2639
|
+
toForeign: { home: '0x7777777777777777777777777777777777777777' as Hex, foreign: null },
|
|
2640
|
+
toHome: { home: null, foreign: '0x8888888888888888888888888888888888888888' as Hex },
|
|
2641
|
+
} as any,
|
|
2642
|
+
};
|
|
2643
|
+
|
|
2644
|
+
describe('with delivery (shouldDeliver = true, requiresDelivery = true)', () => {
|
|
2645
|
+
const deliveryParams = {
|
|
2646
|
+
...erc20Params,
|
|
2647
|
+
shouldDeliver: true,
|
|
2648
|
+
feeDirectorStructEncoded: '0xcccccccccccccccccccccccccccccccccccccccc' as Hex,
|
|
2649
|
+
bridgePathway: {
|
|
2650
|
+
...baseParams.bridgePathway,
|
|
2651
|
+
requiresDelivery: true,
|
|
2652
|
+
destinationRouter: '0xdddddddddddddddddddddddddddddddddddddddd' as Hex,
|
|
2653
|
+
} as any,
|
|
2654
|
+
};
|
|
2655
|
+
|
|
2656
|
+
it('should return null when feeDirectorStructEncoded is missing', () => {
|
|
2657
|
+
const result = transactionInputs({
|
|
2658
|
+
...deliveryParams,
|
|
2659
|
+
feeDirectorStructEncoded: null,
|
|
2660
|
+
});
|
|
2661
|
+
expect(result).toBeNull();
|
|
2662
|
+
});
|
|
2663
|
+
|
|
2664
|
+
it('should return null when destinationRouter is missing', () => {
|
|
2665
|
+
const result = transactionInputs({
|
|
2666
|
+
...deliveryParams,
|
|
2667
|
+
bridgePathway: {
|
|
2668
|
+
...deliveryParams.bridgePathway,
|
|
2669
|
+
destinationRouter: null,
|
|
2670
|
+
} as any,
|
|
2671
|
+
});
|
|
2672
|
+
expect(result).toBeNull();
|
|
2673
|
+
});
|
|
2674
|
+
|
|
2675
|
+
it('should encode relayTokensAndCall without extra param', () => {
|
|
2676
|
+
const result = transactionInputs({
|
|
2677
|
+
...deliveryParams,
|
|
2678
|
+
bridgePathway: {
|
|
2679
|
+
...deliveryParams.bridgePathway,
|
|
2680
|
+
usesExtraParam: false,
|
|
2681
|
+
} as any,
|
|
2682
|
+
});
|
|
2683
|
+
expect(result).toBeDefined();
|
|
2684
|
+
expect(result?.to).toBe(baseParams.bridgePathway.from);
|
|
2685
|
+
expect(result?.value).toBe(0n);
|
|
2686
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2687
|
+
});
|
|
2688
|
+
|
|
2689
|
+
it('should encode relayTokensAndCall with extra param', () => {
|
|
2690
|
+
const result = transactionInputs({
|
|
2691
|
+
...deliveryParams,
|
|
2692
|
+
bridgePathway: {
|
|
2693
|
+
...deliveryParams.bridgePathway,
|
|
2694
|
+
usesExtraParam: true,
|
|
2695
|
+
} as any,
|
|
2696
|
+
});
|
|
2697
|
+
expect(result).toBeDefined();
|
|
2698
|
+
expect(result?.to).toBe(baseParams.bridgePathway.from);
|
|
2699
|
+
expect(result?.value).toBe(0n);
|
|
2700
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2701
|
+
});
|
|
2702
|
+
});
|
|
2703
|
+
|
|
2704
|
+
describe('without delivery (shouldDeliver = false or requiresDelivery = false)', () => {
|
|
2705
|
+
const noDeliveryParams = {
|
|
2706
|
+
...erc20Params,
|
|
2707
|
+
shouldDeliver: false,
|
|
2708
|
+
bridgePathway: {
|
|
2709
|
+
...baseParams.bridgePathway,
|
|
2710
|
+
requiresDelivery: false,
|
|
2711
|
+
} as any,
|
|
2712
|
+
};
|
|
2713
|
+
|
|
2714
|
+
it('should encode relayTokens without extra param', () => {
|
|
2715
|
+
const result = transactionInputs({
|
|
2716
|
+
...noDeliveryParams,
|
|
2717
|
+
bridgePathway: {
|
|
2718
|
+
...noDeliveryParams.bridgePathway,
|
|
2719
|
+
usesExtraParam: false,
|
|
2720
|
+
} as any,
|
|
2721
|
+
});
|
|
2722
|
+
expect(result).toBeDefined();
|
|
2723
|
+
expect(result?.to).toBe(baseParams.bridgePathway.from);
|
|
2724
|
+
expect(result?.value).toBe(0n);
|
|
2725
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2726
|
+
});
|
|
2727
|
+
|
|
2728
|
+
it('should encode relayTokens with extra param', () => {
|
|
2729
|
+
const result = transactionInputs({
|
|
2730
|
+
...noDeliveryParams,
|
|
2731
|
+
bridgePathway: {
|
|
2732
|
+
...noDeliveryParams.bridgePathway,
|
|
2733
|
+
usesExtraParam: true,
|
|
2734
|
+
} as any,
|
|
2735
|
+
});
|
|
2736
|
+
expect(result).toBeDefined();
|
|
2737
|
+
expect(result?.to).toBe(baseParams.bridgePathway.from);
|
|
2738
|
+
expect(result?.value).toBe(0n);
|
|
2739
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2740
|
+
});
|
|
2741
|
+
|
|
2742
|
+
it('should encode relayTokens when shouldDeliver is true but requiresDelivery is false', () => {
|
|
2743
|
+
const result = transactionInputs({
|
|
2744
|
+
...noDeliveryParams,
|
|
2745
|
+
shouldDeliver: true, // Even with shouldDeliver true, requiresDelivery false takes precedence
|
|
2746
|
+
});
|
|
2747
|
+
expect(result).toBeDefined();
|
|
2748
|
+
expect(result?.to).toBe(baseParams.bridgePathway.from);
|
|
2749
|
+
expect(result?.value).toBe(0n);
|
|
2750
|
+
expect(result?.data).toBe('0xMOCKED_FUNCTION_DATA');
|
|
2751
|
+
});
|
|
2752
|
+
});
|
|
2753
|
+
});
|
|
2754
|
+
});
|