@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.
@@ -0,0 +1,679 @@
1
+ import { zeroAddress } from 'viem';
2
+ import { describe, expect, it } from 'vitest';
3
+ import {
4
+ Chains,
5
+ canChangeUnwrap,
6
+ defaultAssetIn,
7
+ inferBridgeKey,
8
+ isNative,
9
+ isUnwrappable,
10
+ nativeAssetOut,
11
+ nativeTokenName,
12
+ nativeTokenSymbol,
13
+ Providers,
14
+ pathway,
15
+ toChain,
16
+ toChainKey,
17
+ validBridgeKeys,
18
+ } from './config';
19
+ import type { BridgeKey, Token } from './types';
20
+
21
+ describe('config', () => {
22
+ describe('toChain', () => {
23
+ it('should convert chain ID to hex string', () => {
24
+ expect(toChain(1)).toBe('0x1');
25
+ expect(toChain(369)).toBe('0x171');
26
+ expect(toChain(56)).toBe('0x38');
27
+ });
28
+
29
+ it('should handle string input', () => {
30
+ expect(toChain('1')).toBe('0x1');
31
+ expect(toChain('369')).toBe('0x171');
32
+ });
33
+
34
+ it('should handle zero', () => {
35
+ expect(toChain(0)).toBe('0x0');
36
+ });
37
+
38
+ it('should handle large chain IDs', () => {
39
+ expect(toChain(11155111)).toBe('0xaa36a7');
40
+ });
41
+ });
42
+
43
+ describe('toChainKey', () => {
44
+ it('should convert Chains enum to ChainKey string', () => {
45
+ expect(toChainKey(Chains.ETH)).toBe('ETH');
46
+ expect(toChainKey(Chains.PLS)).toBe('PLS');
47
+ expect(toChainKey(Chains.BNB)).toBe('BNB');
48
+ });
49
+
50
+ it('should handle numeric chain IDs', () => {
51
+ expect(toChainKey(1)).toBe('ETH');
52
+ expect(toChainKey(369)).toBe('PLS');
53
+ expect(toChainKey(56)).toBe('BNB');
54
+ });
55
+
56
+ it('should handle string hex values', () => {
57
+ expect(toChainKey('0x1' as Chains)).toBe('ETH');
58
+ expect(toChainKey('0x171' as Chains)).toBe('PLS');
59
+ expect(toChainKey('0x38' as Chains)).toBe('BNB');
60
+ });
61
+ });
62
+
63
+ describe('pathway', () => {
64
+ it('should return null for null bridgeKey', () => {
65
+ expect(pathway(null, true)).toBeUndefined();
66
+ });
67
+
68
+ it('should return pathway for valid mainnet bridge key', () => {
69
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
70
+ const result = pathway(bridgeKey, true);
71
+
72
+ expect(result).toBeDefined();
73
+ expect(result).toHaveProperty('from');
74
+ expect(result).toHaveProperty('to');
75
+ expect(result).toHaveProperty('validator');
76
+ expect(result).toHaveProperty('toHome');
77
+ });
78
+
79
+ it('should use testnet pathways when isProd is false', () => {
80
+ const testnetBridgeKey: BridgeKey = [
81
+ Providers.PULSECHAIN,
82
+ '0xaa36a7' as Chains, // Sepolia
83
+ '0x3af' as Chains, // Pulsechain testnet
84
+ ];
85
+ const result = pathway(testnetBridgeKey, false);
86
+
87
+ // Should find pathway in testnet config
88
+ expect(result).toBeDefined();
89
+ });
90
+
91
+ it('should not use testnet pathways when isProd is true', () => {
92
+ const testnetBridgeKey: BridgeKey = [
93
+ Providers.PULSECHAIN,
94
+ '0xaa36a7' as Chains,
95
+ '0x3af' as Chains,
96
+ ];
97
+ const result = pathway(testnetBridgeKey, true);
98
+
99
+ // Should not find pathway in prod config
100
+ expect(result).toBeUndefined();
101
+ });
102
+
103
+ it('should apply setting overrides for specific asset addresses', () => {
104
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
105
+ const assetAddress = '0x0000000000000000000000000000000000000000';
106
+
107
+ const baseResult = pathway(bridgeKey, true);
108
+ const overriddenResult = pathway(bridgeKey, true, assetAddress);
109
+
110
+ expect(baseResult).toBeDefined();
111
+ expect(overriddenResult).toBeDefined();
112
+ // Result should exist (specific overrides depend on config data)
113
+ });
114
+
115
+ it('should handle checksummed addresses', () => {
116
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
117
+ // Lowercase and checksummed versions should both work
118
+ const lowercase = '0xdac17f958d2ee523a2206206994597c13d831ec7';
119
+ const checksummed = '0xdAC17F958D2ee523a2206206994597C13D831ec7';
120
+
121
+ const result1 = pathway(bridgeKey, true, lowercase);
122
+ const result2 = pathway(bridgeKey, true, checksummed);
123
+
124
+ // Both should return the same pathway configuration
125
+ expect(result1).toBeDefined();
126
+ expect(result2).toBeDefined();
127
+ });
128
+
129
+ it('should apply settingOverrides for specific assets', () => {
130
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.PLS, Chains.ETH];
131
+
132
+ // Get pathway without asset address
133
+ const basePathway = pathway(bridgeKey, true);
134
+
135
+ // Get pathway with zeroAddress (has override)
136
+ const overriddenPathway = pathway(bridgeKey, true, zeroAddress);
137
+
138
+ expect(basePathway).toBeDefined();
139
+ expect(overriddenPathway).toBeDefined();
140
+
141
+ // The overridden pathway should have different properties
142
+ if (basePathway && overriddenPathway) {
143
+ // With override, some properties should change
144
+ expect(overriddenPathway.from).toBeDefined();
145
+ expect(overriddenPathway.to).toBeDefined();
146
+ }
147
+ });
148
+
149
+ it('should apply settingOverrides for wrapped native asset', () => {
150
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.PLS, Chains.ETH];
151
+
152
+ // WPLS address on Pulsechain
153
+ const wplsAddress = nativeAssetOut[Chains.PLS];
154
+
155
+ const basePathway = pathway(bridgeKey, true);
156
+ const overriddenPathway = pathway(bridgeKey, true, wplsAddress);
157
+
158
+ expect(basePathway).toBeDefined();
159
+ expect(overriddenPathway).toBeDefined();
160
+
161
+ // Both should exist but may have different settings
162
+ if (basePathway && overriddenPathway) {
163
+ expect(overriddenPathway.from).toBeDefined();
164
+ expect(overriddenPathway.to).toBeDefined();
165
+ }
166
+ });
167
+ });
168
+
169
+ describe('pathway configuration completeness', () => {
170
+ it('should have valid pathway configs for all main bridge keys', () => {
171
+ const mainBridgeKeys: BridgeKey[] = [
172
+ [Providers.PULSECHAIN, Chains.ETH, Chains.PLS],
173
+ [Providers.PULSECHAIN, Chains.PLS, Chains.ETH],
174
+ [Providers.TOKENSEX, Chains.BNB, Chains.PLS],
175
+ [Providers.TOKENSEX, Chains.PLS, Chains.BNB],
176
+ ];
177
+
178
+ mainBridgeKeys.forEach((bridgeKey) => {
179
+ const result = pathway(bridgeKey, true);
180
+ expect(result).toBeDefined();
181
+ expect(result).toHaveProperty('from');
182
+ expect(result).toHaveProperty('to');
183
+ expect(result).toHaveProperty('validator');
184
+ expect(result).toHaveProperty('feeManager');
185
+ expect(result).toHaveProperty('toHome');
186
+ expect(result).toHaveProperty('requiresDelivery');
187
+ });
188
+ });
189
+
190
+ it('should have toHome property set correctly', () => {
191
+ // ETH -> PLS (foreign to home) should have toHome = true
192
+ const foreignToHome: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
193
+ const foreignToHomePathway = pathway(foreignToHome, true);
194
+ expect(foreignToHomePathway?.toHome).toBe(true);
195
+
196
+ // PLS -> ETH (home to foreign) should have toHome = false
197
+ const homeToForeign: BridgeKey = [Providers.PULSECHAIN, Chains.PLS, Chains.ETH];
198
+ const homeToForeignPathway = pathway(homeToForeign, true);
199
+ expect(homeToForeignPathway?.toHome).toBe(false);
200
+ });
201
+
202
+ it('should have correct feeManager for each direction', () => {
203
+ // ETH -> PLS should have feeManager = 'to' (destination)
204
+ const foreignToHome: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
205
+ const foreignToHomePathway = pathway(foreignToHome, true);
206
+ expect(foreignToHomePathway?.feeManager).toBe('to');
207
+
208
+ // PLS -> ETH should have feeManager = 'from' (source)
209
+ const homeToForeign: BridgeKey = [Providers.PULSECHAIN, Chains.PLS, Chains.ETH];
210
+ const homeToForeignPathway = pathway(homeToForeign, true);
211
+ expect(homeToForeignPathway?.feeManager).toBe('from');
212
+ });
213
+
214
+ it('should have correct requiresDelivery for each direction', () => {
215
+ // ETH -> PLS (foreign to home) should NOT require delivery
216
+ const foreignToHome: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
217
+ const foreignToHomePathway = pathway(foreignToHome, true);
218
+ expect(foreignToHomePathway?.requiresDelivery).toBe(false);
219
+
220
+ // PLS -> ETH (home to foreign) should require delivery
221
+ const homeToForeign: BridgeKey = [Providers.PULSECHAIN, Chains.PLS, Chains.ETH];
222
+ const homeToForeignPathway = pathway(homeToForeign, true);
223
+ expect(homeToForeignPathway?.requiresDelivery).toBe(true);
224
+ });
225
+ });
226
+
227
+ describe('inferBridgeKey', () => {
228
+ it('should keep valid bridge key unchanged', () => {
229
+ const currentKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
230
+ const result = inferBridgeKey({
231
+ currentKey,
232
+ provider: Providers.PULSECHAIN,
233
+ });
234
+
235
+ expect(result).toEqual([Providers.PULSECHAIN, Chains.ETH, Chains.PLS]);
236
+ });
237
+
238
+ it('should override fromChain when provided', () => {
239
+ const currentKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
240
+ const result = inferBridgeKey({
241
+ currentKey,
242
+ provider: Providers.PULSECHAIN,
243
+ fromChain: Chains.PLS,
244
+ });
245
+
246
+ expect(result[1]).toBe(Chains.PLS);
247
+ });
248
+
249
+ it('should override toChain when provided and valid', () => {
250
+ const currentKey: BridgeKey = [Providers.PULSECHAIN, Chains.PLS, Chains.ETH];
251
+ const result = inferBridgeKey({
252
+ currentKey,
253
+ provider: Providers.PULSECHAIN,
254
+ fromChain: Chains.ETH,
255
+ toChain: Chains.PLS,
256
+ });
257
+
258
+ expect(result[1]).toBe(Chains.ETH);
259
+ expect(result[2]).toBe(Chains.PLS);
260
+ });
261
+
262
+ it('should fallback to valid toChain if provided toChain is invalid', () => {
263
+ const currentKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
264
+ const result = inferBridgeKey({
265
+ currentKey,
266
+ provider: Providers.PULSECHAIN,
267
+ toChain: Chains.ETH, // ETH -> ETH is invalid
268
+ });
269
+
270
+ // Should fallback to first valid toChain (PLS)
271
+ expect(result[1]).toBe(Chains.ETH);
272
+ expect(result[2]).toBe(Chains.PLS);
273
+ });
274
+
275
+ it('should use first available chain if fromChain is invalid', () => {
276
+ const currentKey: BridgeKey = [Providers.PULSECHAIN, '0x999' as Chains, Chains.PLS];
277
+ const result = inferBridgeKey({
278
+ currentKey,
279
+ provider: Providers.PULSECHAIN,
280
+ });
281
+
282
+ // Should fallback to a valid fromChain
283
+ expect(result[0]).toBe(Providers.PULSECHAIN);
284
+ expect(result[1]).toBeDefined();
285
+ expect(result[2]).toBeDefined();
286
+ });
287
+
288
+ it('should use first available chain if toChain is invalid', () => {
289
+ const currentKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, '0x999' as Chains];
290
+ const result = inferBridgeKey({
291
+ currentKey,
292
+ provider: Providers.PULSECHAIN,
293
+ });
294
+
295
+ // Should fallback to a valid toChain
296
+ expect(result[0]).toBe(Providers.PULSECHAIN);
297
+ expect(result[1]).toBe(Chains.ETH);
298
+ expect(result[2]).toBeDefined();
299
+ });
300
+
301
+ it('should handle provider changes', () => {
302
+ const currentKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
303
+ const result = inferBridgeKey({
304
+ currentKey,
305
+ provider: Providers.TOKENSEX,
306
+ });
307
+
308
+ expect(result[0]).toBe(Providers.TOKENSEX);
309
+ // Should find valid chains for TOKENSEX provider
310
+ expect(result[1]).toBeDefined();
311
+ expect(result[2]).toBeDefined();
312
+ });
313
+ });
314
+
315
+ describe('nativeAssetOut', () => {
316
+ it('should have wrapped native asset for each chain', () => {
317
+ expect(nativeAssetOut[Chains.PLS]).toBeDefined();
318
+ expect(nativeAssetOut[Chains.ETH]).toBeDefined();
319
+ expect(nativeAssetOut[Chains.BNB]).toBeDefined();
320
+ });
321
+
322
+ it('should return valid ethereum addresses', () => {
323
+ Object.values(nativeAssetOut).forEach((address) => {
324
+ expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/);
325
+ });
326
+ });
327
+ });
328
+
329
+ describe('nativeTokenName and nativeTokenSymbol', () => {
330
+ it('should have names for all main chains', () => {
331
+ expect(nativeTokenName[Chains.PLS]).toBe('Pulse');
332
+ expect(nativeTokenName[Chains.ETH]).toBe('Ether');
333
+ expect(nativeTokenName[Chains.BNB]).toBe('BNB');
334
+ });
335
+
336
+ it('should have symbols for all main chains', () => {
337
+ expect(nativeTokenSymbol[Chains.PLS]).toBe('PLS');
338
+ expect(nativeTokenSymbol[Chains.ETH]).toBe('ETH');
339
+ expect(nativeTokenSymbol[Chains.BNB]).toBe('BNB');
340
+ });
341
+ });
342
+
343
+ describe('isNative', () => {
344
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
345
+
346
+ it('should identify zero address as native', () => {
347
+ const nativeToken = {
348
+ address: zeroAddress,
349
+ chainId: 1,
350
+ name: 'Ether',
351
+ symbol: 'ETH',
352
+ decimals: 18,
353
+ };
354
+ expect(isNative(nativeToken, bridgeKey)).toBe(true);
355
+ });
356
+
357
+ it('should identify wrapped native addresses as native', () => {
358
+ const wrappedETH = {
359
+ address: nativeAssetOut[Chains.ETH],
360
+ chainId: 1,
361
+ name: 'Wrapped Ether',
362
+ symbol: 'WETH',
363
+ decimals: 18,
364
+ };
365
+ expect(isNative(wrappedETH, bridgeKey)).toBe(true);
366
+ });
367
+
368
+ it('should identify bridgedNativeAssetOut as native', () => {
369
+ const path = pathway(bridgeKey, true);
370
+ const bridgedNative = {
371
+ address: path!.bridgedNativeAssetOut!,
372
+ chainId: 369,
373
+ name: 'WETH from Ethereum',
374
+ symbol: 'WETH',
375
+ decimals: 18,
376
+ };
377
+ expect(isNative(bridgedNative, bridgeKey)).toBe(true);
378
+ });
379
+
380
+ it('should NOT identify tokens with "from Pulsechain" in name as native', () => {
381
+ const fromPulsechain = {
382
+ address: zeroAddress,
383
+ chainId: 1,
384
+ name: 'Token from Pulsechain',
385
+ symbol: 'TKN',
386
+ decimals: 18,
387
+ };
388
+ expect(isNative(fromPulsechain, bridgeKey)).toBe(false);
389
+ });
390
+
391
+ it('should identify non-native ERC20 tokens', () => {
392
+ const usdt = {
393
+ address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
394
+ chainId: 1,
395
+ name: 'Tether USD',
396
+ symbol: 'USDT',
397
+ decimals: 6,
398
+ };
399
+ expect(isNative(usdt, bridgeKey)).toBe(false);
400
+ });
401
+
402
+ it('should return false for null inputs', () => {
403
+ expect(isNative(null, bridgeKey)).toBe(false);
404
+ expect(isNative({ address: zeroAddress, chainId: 1 } as any, null)).toBe(false);
405
+ });
406
+ });
407
+
408
+ describe('isUnwrappable', () => {
409
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
410
+
411
+ it('should return false when bridgeKey is null', () => {
412
+ const token: Token = {
413
+ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
414
+ decimals: 6,
415
+ name: 'USD Coin',
416
+ symbol: 'USDC',
417
+ chainId: 1,
418
+ };
419
+ expect(isUnwrappable(null, token)).toBe(false);
420
+ });
421
+
422
+ it('should return false when assetIn is null', () => {
423
+ expect(isUnwrappable(bridgeKey, null)).toBe(false);
424
+ });
425
+
426
+ it('should return false when pathway does not exist', () => {
427
+ // Use invalid bridge key
428
+ const invalidKey: BridgeKey = ['pulsechain', '0x999' as Chains, '0x888' as Chains];
429
+ const token: Token = {
430
+ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
431
+ decimals: 6,
432
+ name: 'USD Coin',
433
+ symbol: 'USDC',
434
+ chainId: 1,
435
+ };
436
+ expect(isUnwrappable(invalidKey, token)).toBe(false);
437
+ });
438
+
439
+ it('should return true when asset is bridgedNativeAssetOut', () => {
440
+ const path = pathway(bridgeKey, false);
441
+ const bridgedNative: Token = {
442
+ address: path!.bridgedNativeAssetOut,
443
+ decimals: 18,
444
+ name: 'Wrapped Ether from Ethereum',
445
+ symbol: 'WETH',
446
+ chainId: parseInt(Chains.PLS, 16),
447
+ };
448
+ expect(isUnwrappable(bridgeKey, bridgedNative)).toBe(true);
449
+ });
450
+
451
+ it('should return false for regular ERC20 tokens', () => {
452
+ const usdt: Token = {
453
+ address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
454
+ decimals: 6,
455
+ name: 'Tether USD',
456
+ symbol: 'USDT',
457
+ chainId: 1,
458
+ };
459
+ expect(isUnwrappable(bridgeKey, usdt)).toBe(false);
460
+ });
461
+
462
+ it('should return false for zero address', () => {
463
+ const native: Token = {
464
+ address: zeroAddress,
465
+ decimals: 18,
466
+ name: 'Ether',
467
+ symbol: 'ETH',
468
+ chainId: 1,
469
+ };
470
+ expect(isUnwrappable(bridgeKey, native)).toBe(false);
471
+ });
472
+
473
+ it('should handle checksummed addresses correctly', () => {
474
+ const path = pathway(bridgeKey, false);
475
+ const lowercaseAddr = path!.bridgedNativeAssetOut.toLowerCase();
476
+ const tokenLowercase: Token = {
477
+ address: lowercaseAddr as `0x${string}`,
478
+ decimals: 18,
479
+ name: 'Wrapped Ether',
480
+ symbol: 'WETH',
481
+ chainId: parseInt(Chains.PLS, 16),
482
+ };
483
+ // Should still identify as unwrappable due to getAddress normalization
484
+ expect(isUnwrappable(bridgeKey, tokenLowercase)).toBe(true);
485
+ });
486
+ });
487
+
488
+ describe('canChangeUnwrap', () => {
489
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
490
+
491
+ it('should return false when assetIn is null', () => {
492
+ expect(canChangeUnwrap(bridgeKey, null)).toBe(false);
493
+ });
494
+
495
+ it('should return false when assetIn is not unwrappable', () => {
496
+ const usdt: Token = {
497
+ address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
498
+ decimals: 6,
499
+ name: 'Tether USD',
500
+ symbol: 'USDT',
501
+ chainId: 1,
502
+ };
503
+ expect(canChangeUnwrap(bridgeKey, usdt)).toBe(false);
504
+ });
505
+
506
+ it('should return true when assetIn is unwrappable', () => {
507
+ const path = pathway(bridgeKey, false);
508
+ const bridgedNative: Token = {
509
+ address: path!.bridgedNativeAssetOut,
510
+ decimals: 18,
511
+ name: 'Wrapped Ether from Ethereum',
512
+ symbol: 'WETH',
513
+ chainId: parseInt(Chains.PLS, 16),
514
+ };
515
+ expect(canChangeUnwrap(bridgeKey, bridgedNative)).toBe(true);
516
+ });
517
+
518
+ it('should return false for zero address', () => {
519
+ const native: Token = {
520
+ address: zeroAddress,
521
+ decimals: 18,
522
+ name: 'Ether',
523
+ symbol: 'ETH',
524
+ chainId: 1,
525
+ };
526
+ expect(canChangeUnwrap(bridgeKey, native)).toBe(false);
527
+ });
528
+
529
+ it('should handle different bridge configurations', () => {
530
+ const tokenSexKey: BridgeKey = [Providers.TOKENSEX, Chains.BNB, Chains.PLS];
531
+ const path = pathway(tokenSexKey, false);
532
+
533
+ if (path?.bridgedNativeAssetOut) {
534
+ const bridgedBNB: Token = {
535
+ address: path.bridgedNativeAssetOut,
536
+ decimals: 18,
537
+ name: 'Wrapped BNB from BSC',
538
+ symbol: 'WBNB',
539
+ chainId: parseInt(Chains.PLS, 16),
540
+ };
541
+ expect(canChangeUnwrap(tokenSexKey, bridgedBNB)).toBe(true);
542
+ }
543
+ });
544
+ });
545
+
546
+ describe('defaultAssetIn', () => {
547
+ it('should return null when bridgeKey is null', () => {
548
+ const result = defaultAssetIn(null, true);
549
+ expect(result).toBeNull();
550
+ });
551
+
552
+ it('should return bridgedNativeAssetOut for valid bridge', () => {
553
+ const bridgeKey: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
554
+ const result = defaultAssetIn(bridgeKey, true);
555
+
556
+ // Should return the bridged native asset address
557
+ expect(result).toBeDefined();
558
+ expect(typeof result).toBe('string');
559
+ expect(result).toMatch(/^0x[a-fA-F0-9]{40}$/);
560
+ });
561
+
562
+ it('should return different values for different bridges', () => {
563
+ const plsToEth: BridgeKey = [Providers.PULSECHAIN, Chains.PLS, Chains.ETH];
564
+ const ethToPls: BridgeKey = [Providers.PULSECHAIN, Chains.ETH, Chains.PLS];
565
+
566
+ const result1 = defaultAssetIn(plsToEth, true);
567
+ const result2 = defaultAssetIn(ethToPls, true);
568
+
569
+ // Different bridge directions should have different default assets
570
+ expect(result1).toBeDefined();
571
+ expect(result2).toBeDefined();
572
+ });
573
+
574
+ it('should handle testnet bridges', () => {
575
+ const testnetKey: BridgeKey = [
576
+ Providers.PULSECHAIN,
577
+ '0xaa36a7' as Chains, // Sepolia
578
+ '0x3af' as Chains, // Pulsechain testnet
579
+ ];
580
+ const result = defaultAssetIn(testnetKey, false);
581
+
582
+ expect(result).toBeDefined();
583
+ });
584
+
585
+ it('should return null for invalid bridge in prod mode', () => {
586
+ const testnetKey: BridgeKey = [Providers.PULSECHAIN, '0xaa36a7' as Chains, '0x3af' as Chains];
587
+ const result = defaultAssetIn(testnetKey, true);
588
+
589
+ // Testnet bridge should not work in prod mode
590
+ expect(result).toBeUndefined();
591
+ });
592
+
593
+ it('should work with TOKENSEX provider', () => {
594
+ const tokenSexKey: BridgeKey = [Providers.TOKENSEX, Chains.PLS, Chains.BNB];
595
+ const result = defaultAssetIn(tokenSexKey, true);
596
+
597
+ expect(result).toBeDefined();
598
+ });
599
+ });
600
+
601
+ describe('validBridgeKeys', () => {
602
+ it('should return only mainnet keys when isProd is true', () => {
603
+ const result = validBridgeKeys(true);
604
+
605
+ expect(result).toBeDefined();
606
+ expect(Array.isArray(result)).toBe(true);
607
+ expect(result.length).toBeGreaterThan(0);
608
+
609
+ // All keys should be mainnet bridges
610
+ result.forEach((key) => {
611
+ expect(Array.isArray(key)).toBe(true);
612
+ expect(key.length).toBe(3);
613
+ });
614
+ });
615
+
616
+ it('should return mainnet + testnet keys when isProd is false', () => {
617
+ const prodKeys = validBridgeKeys(true);
618
+ const allKeys = validBridgeKeys(false);
619
+
620
+ expect(allKeys.length).toBeGreaterThanOrEqual(prodKeys.length);
621
+
622
+ // Should include all mainnet keys
623
+ prodKeys.forEach((prodKey) => {
624
+ expect(allKeys).toContainEqual(prodKey);
625
+ });
626
+ });
627
+
628
+ it('should include pulsechain mainnet bridges in prod', () => {
629
+ const keys = validBridgeKeys(true);
630
+
631
+ const hasPulsechainETH = keys.some(
632
+ (key) => key[0] === Providers.PULSECHAIN && key[1] === Chains.PLS && key[2] === Chains.ETH,
633
+ );
634
+ const hasETHPulsechain = keys.some(
635
+ (key) => key[0] === Providers.PULSECHAIN && key[1] === Chains.ETH && key[2] === Chains.PLS,
636
+ );
637
+
638
+ expect(hasPulsechainETH || hasETHPulsechain).toBe(true);
639
+ });
640
+
641
+ it('should include testnet bridges only in dev mode', () => {
642
+ const prodKeys = validBridgeKeys(true);
643
+ const devKeys = validBridgeKeys(false);
644
+
645
+ // Check if any key contains testnet chains (Sepolia or V4PLS)
646
+ const prodHasTestnet = prodKeys.some(
647
+ (key) =>
648
+ key[1] === Chains.SEP ||
649
+ key[2] === Chains.SEP ||
650
+ key[1] === Chains.V4PLS ||
651
+ key[2] === Chains.V4PLS,
652
+ );
653
+ const devHasTestnet = devKeys.some(
654
+ (key) =>
655
+ key[1] === Chains.SEP ||
656
+ key[2] === Chains.SEP ||
657
+ key[1] === Chains.V4PLS ||
658
+ key[2] === Chains.V4PLS,
659
+ );
660
+
661
+ // Prod should not have testnet, dev might have testnet
662
+ expect(prodHasTestnet).toBe(false);
663
+ // Dev keys should be at least as many as prod keys
664
+ expect(devKeys.length).toBeGreaterThanOrEqual(prodKeys.length);
665
+ });
666
+
667
+ it('should return array of tuples with [Provider, Chains, Chains] structure', () => {
668
+ const keys = validBridgeKeys(true);
669
+
670
+ keys.forEach((key) => {
671
+ expect(Array.isArray(key)).toBe(true);
672
+ expect(key.length).toBe(3);
673
+ expect(typeof key[0]).toBe('string'); // Provider
674
+ expect(typeof key[1]).toBe('string'); // Chains (hex)
675
+ expect(typeof key[2]).toBe('string'); // Chains (hex)
676
+ });
677
+ });
678
+ });
679
+ });