@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,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
|
+
});
|