@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/src/config.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { getAddress, keccak256, stringToHex, zeroAddress, type Hex } from "viem";
2
- import _ from 'lodash'
3
- import type { BridgeKey, Token, TokenOut } from "./types.js";
1
+ import _ from 'lodash';
2
+ import { getAddress, type Hex, keccak256, stringToHex, zeroAddress } from 'viem';
3
+ import type { BridgeKey, Token, TokenOut } from './types.js';
4
4
 
5
5
  /**
6
6
  * Add here your chain id as hex, be sure to add your chain in the chainsMetadata object too
@@ -16,26 +16,26 @@ export enum Chains {
16
16
  }
17
17
 
18
18
  /** The key of a chain - human readable for query params */
19
- export type ChainKey = keyof typeof Chains
19
+ export type ChainKey = keyof typeof Chains;
20
20
 
21
21
  /** A map of chain ids to chain keys */
22
22
  export const chainIdToKey = new Map<Chains, ChainKey>(
23
23
  Object.entries(Chains).map(([key, chain]) => [chain, key] as [Chains, ChainKey]),
24
- )
24
+ );
25
25
 
26
26
  /**
27
27
  * Converts a chain id to a chain key
28
28
  * @param chainId - the chain id
29
29
  * @returns the chain key
30
30
  */
31
- export const toChain = (chainId: number | string) => `0x${Number(chainId).toString(16)}` as Chains
31
+ export const toChain = (chainId: number | string) => `0x${Number(chainId).toString(16)}` as Chains;
32
32
 
33
33
  /**
34
34
  * Converts a chain id to a chain key
35
35
  * @param chainId - the chain id
36
36
  * @returns the chain key
37
37
  */
38
- export const toChainKey = (chainId: number | string) => chainIdToKey.get(toChain(chainId))
38
+ export const toChainKey = (chainId: number | string) => chainIdToKey.get(toChain(chainId));
39
39
 
40
40
  // /** The provider of an omnibridge */
41
41
  // export enum Provider {
@@ -43,13 +43,12 @@ export const toChainKey = (chainId: number | string) => chainIdToKey.get(toChain
43
43
  // TOKENSEX = 'tokensex',
44
44
  // }
45
45
 
46
-
47
46
  export const Providers = {
48
47
  PULSECHAIN: 'pulsechain',
49
48
  TOKENSEX: 'tokensex',
50
- } as const
49
+ } as const;
51
50
 
52
- export type Provider = typeof Providers[keyof typeof Providers]
51
+ export type Provider = (typeof Providers)[keyof typeof Providers];
53
52
 
54
53
  /** A map of chains to native asset out addresses */
55
54
  export const nativeAssetOut = {
@@ -58,12 +57,12 @@ export const nativeAssetOut = {
58
57
  [Chains.PLS]: '0xA1077a294dDE1B09bB078844df40758a5D0f9a27',
59
58
  [Chains.SEP]: '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9',
60
59
  [Chains.V4PLS]: '0x70499adEBB11Efd915E3b69E700c331778628707',
61
- } as Record<Chains, Hex>
60
+ } as Record<Chains, Hex>;
62
61
 
63
62
  /** A map of chains to deprecated native asset out addresses */
64
63
  export const deprecatedNativeAssetOut = {
65
64
  [Chains.PLS]: new Set(['0x97Ac4a2439A47c07ad535bb1188c989dae755341']),
66
- } as Partial<Record<Chains, Set<Hex>>>
65
+ } as Partial<Record<Chains, Set<Hex>>>;
67
66
 
68
67
  /** A map of chains to uni v2 router addresses */
69
68
  export const uniV2Routers = {
@@ -78,7 +77,7 @@ export const uniV2Routers = {
78
77
  '0xDaE9dd3d1A52CfCe9d5F2fAC7fDe164D500E50f7',
79
78
  '0x636f6407B90661b73b1C0F7e24F4C79f624d0738',
80
79
  ],
81
- } as Record<Chains, Hex[]>
80
+ } as Record<Chains, Hex[]>;
82
81
 
83
82
  /** A map of chains to native token symbols */
84
83
  export const nativeTokenSymbol = {
@@ -87,7 +86,7 @@ export const nativeTokenSymbol = {
87
86
  [Chains.ETH]: 'ETH',
88
87
  [Chains.SEP]: 'sepETH',
89
88
  [Chains.BNB]: 'BNB',
90
- } as Record<Chains, string>
89
+ } as Record<Chains, string>;
91
90
 
92
91
  /** A map of chains to native token names */
93
92
  export const nativeTokenName = {
@@ -96,29 +95,29 @@ export const nativeTokenName = {
96
95
  [Chains.ETH]: 'Ether',
97
96
  [Chains.SEP]: 'Sepolia Ether',
98
97
  [Chains.BNB]: 'BNB',
99
- } as Record<Chains, string>
98
+ } as Record<Chains, string>;
100
99
 
101
100
  /** The pathway of a bridge */
102
101
  export type Pathway = {
103
- from: Hex
104
- to: Hex
105
- destinationRouter: Hex | null
106
- nativeRouter: Hex
107
- validator: Hex
102
+ from: Hex;
103
+ to: Hex;
104
+ destinationRouter: Hex | null;
105
+ nativeRouter: Hex;
106
+ validator: Hex;
108
107
  // defaultAssetIn: Token
109
- usesExtraParam: boolean
110
- feeManager: 'from' | 'to'
111
- toHome: boolean
112
- requiresDelivery: boolean
113
- bridgedNativeAssetOut: Hex
114
- settingOverrides?: Record<Hex, Partial<Pathway>>
115
- }
108
+ usesExtraParam: boolean;
109
+ feeManager: 'from' | 'to';
110
+ toHome: boolean;
111
+ requiresDelivery: boolean;
112
+ bridgedNativeAssetOut: Hex;
113
+ settingOverrides?: Record<Hex, Partial<Pathway>>;
114
+ };
116
115
 
117
116
  /** A map of providers to pathways */
118
117
  export type DeepPathwayConfig = Record<
119
118
  Provider,
120
119
  Partial<Record<Chains, Partial<Record<Chains, Pathway>>>>
121
- >
120
+ >;
122
121
 
123
122
  /** The settings for the pulsechain pls eth pathway */
124
123
  const pulsechainPLSETHSettings = {
@@ -126,7 +125,7 @@ const pulsechainPLSETHSettings = {
126
125
  to: '0xe20E337DB2a00b1C37139c873B92a0AAd3F468bF',
127
126
  destinationRouter: '0x1F0144Ce3BDaf11944Fe0beF6444599a0500695B',
128
127
  nativeRouter: '0xE66877Cfe4CEc10ffBbBB3092B7001321AB5809D',
129
- }
128
+ };
130
129
 
131
130
  export const pathways = {
132
131
  [Providers.PULSECHAIN]: {
@@ -205,7 +204,7 @@ export const pathways = {
205
204
  },
206
205
  },
207
206
  },
208
- } as DeepPathwayConfig
207
+ } as DeepPathwayConfig;
209
208
 
210
209
  export const testnetPathways = {
211
210
  [Providers.TOKENSEX]: {
@@ -261,7 +260,7 @@ export const testnetPathways = {
261
260
  },
262
261
  },
263
262
  },
264
- } as DeepPathwayConfig
263
+ } as DeepPathwayConfig;
265
264
 
266
265
  /** The mainnet bridge keys */
267
266
  const mainnetBridgeKeys = [
@@ -269,19 +268,19 @@ const mainnetBridgeKeys = [
269
268
  [Providers.PULSECHAIN, Chains.ETH, Chains.PLS],
270
269
  [Providers.TOKENSEX, Chains.PLS, Chains.BNB],
271
270
  [Providers.TOKENSEX, Chains.BNB, Chains.PLS],
272
- ] as BridgeKey[]
271
+ ] as BridgeKey[];
273
272
 
274
273
  /** The testnet bridge keys */
275
274
  const testnetBridgeKeys = [
276
275
  [Providers.PULSECHAIN, Chains.SEP, Chains.V4PLS],
277
276
  [Providers.PULSECHAIN, Chains.V4PLS, Chains.SEP],
278
- ] as BridgeKey[]
277
+ ] as BridgeKey[];
279
278
 
280
279
  /** Returns the valid bridge keys for the given is prod */
281
280
  export const validBridgeKeys = (isProd: boolean) => [
282
281
  ...mainnetBridgeKeys,
283
282
  ...(isProd ? [] : testnetBridgeKeys),
284
- ]
283
+ ];
285
284
 
286
285
  /**
287
286
  * Infers the bridge key from the current key, provider, from chain, and to chain
@@ -297,24 +296,24 @@ export const inferBridgeKey = ({
297
296
  fromChain,
298
297
  toChain,
299
298
  }: {
300
- currentKey: BridgeKey
301
- provider: Provider
302
- fromChain?: Chains
303
- toChain?: Chains
299
+ currentKey: BridgeKey;
300
+ provider: Provider;
301
+ fromChain?: Chains;
302
+ toChain?: Chains;
304
303
  }) => {
305
- const [, fromChainKey, toChainKey] = currentKey
306
- const from = fromChain ?? fromChainKey
307
- const to = toChain ?? toChainKey
308
- const currentFromChainIsValid = !!_.get(pathways, [provider, from])
304
+ const [, fromChainKey, toChainKey] = currentKey;
305
+ const from = fromChain ?? fromChainKey;
306
+ const to = toChain ?? toChainKey;
307
+ const currentFromChainIsValid = !!_.get(pathways, [provider, from]);
309
308
  const nextFromChain = currentFromChainIsValid
310
309
  ? from
311
- : (Object.keys(pathways[provider])[0] as Chains)
312
- const currentToChainIsValid = !!_.get(pathways, [provider, nextFromChain, to])
310
+ : (Object.keys(pathways[provider])[0] as Chains);
311
+ const currentToChainIsValid = !!_.get(pathways, [provider, nextFromChain, to]);
313
312
  const nextToChain = currentToChainIsValid
314
313
  ? to
315
- : (Object.keys(pathways[provider]![nextFromChain]!)[0] as Chains)
316
- return [provider, nextFromChain, nextToChain] as BridgeKey
317
- }
314
+ : (Object.keys(pathways[provider]![nextFromChain]!)[0] as Chains);
315
+ return [provider, nextFromChain, nextToChain] as BridgeKey;
316
+ };
318
317
 
319
318
  /**
320
319
  * Returns the pathway for the given bridge key, is prod, and asset in address
@@ -323,20 +322,25 @@ export const inferBridgeKey = ({
323
322
  * @param assetInAddress - the asset in address
324
323
  * @returns the pathway
325
324
  */
326
- export const pathway = (bridgeKey: BridgeKey | null, isProd: boolean, assetInAddress?: string | Hex | null | undefined) => {
327
- if (!bridgeKey) return
328
- let pathway = _.get(pathways, bridgeKey) || (!isProd ? _.get(testnetPathways, bridgeKey) : undefined)
325
+ export const pathway = (
326
+ bridgeKey: BridgeKey | null,
327
+ isProd: boolean,
328
+ assetInAddress?: string | Hex | null | undefined,
329
+ ) => {
330
+ if (!bridgeKey) return;
331
+ let pathway =
332
+ _.get(pathways, bridgeKey) || (!isProd ? _.get(testnetPathways, bridgeKey) : undefined);
329
333
  if (assetInAddress) {
330
- const addr = getAddress(assetInAddress)
334
+ const addr = getAddress(assetInAddress);
331
335
  if (pathway?.settingOverrides?.[addr]) {
332
336
  pathway = {
333
337
  ...pathway,
334
338
  ...pathway.settingOverrides[addr],
335
- }
339
+ };
336
340
  }
337
341
  }
338
- return pathway
339
- }
342
+ return pathway;
343
+ };
340
344
 
341
345
  /**
342
346
  * Returns the default asset in for the given bridge key and is prod
@@ -345,11 +349,11 @@ export const pathway = (bridgeKey: BridgeKey | null, isProd: boolean, assetInAdd
345
349
  * @returns the default asset in
346
350
  */
347
351
  export const defaultAssetIn = ($bridgeKey: BridgeKey | null, isProd: boolean) => {
348
- if (!$bridgeKey) return null
349
- const conf = pathway($bridgeKey, isProd)
350
- const defaultAssetIn = _.get(conf, ['bridgedNativeAssetOut']) as Hex | undefined
351
- return defaultAssetIn
352
- }
352
+ if (!$bridgeKey) return null;
353
+ const conf = pathway($bridgeKey, isProd);
354
+ const defaultAssetIn = _.get(conf, ['bridgedNativeAssetOut']) as Hex | undefined;
355
+ return defaultAssetIn;
356
+ };
353
357
 
354
358
  /**
355
359
  * Returns whether the given asset is the native asset for the given bridge key
@@ -359,16 +363,16 @@ export const defaultAssetIn = ($bridgeKey: BridgeKey | null, isProd: boolean) =>
359
363
  */
360
364
  export const isNative = (asset: Token | TokenOut | null, bridgeKey: BridgeKey | null) => {
361
365
  if (!bridgeKey || !asset) {
362
- return false
366
+ return false;
363
367
  }
364
- const path = pathway(bridgeKey, false)
368
+ const path = pathway(bridgeKey, false);
365
369
  return (
366
370
  getAddress(asset.address!) === getAddress(path!.bridgedNativeAssetOut!) ||
367
- (asset.address === zeroAddress ||
371
+ ((asset.address === zeroAddress ||
368
372
  nativeAssetOut[toChain(asset.chainId)]?.toLowerCase() === asset.address?.toLowerCase()) &&
369
- !asset.name.includes(' from Pulsechain')
370
- )
371
- }
373
+ !asset.name.includes(' from Pulsechain'))
374
+ );
375
+ };
372
376
 
373
377
  /**
374
378
  * Returns whether the given asset is unwrappable for the given bridge key
@@ -381,13 +385,13 @@ export const isUnwrappable = (
381
385
  assetIn: Pick<Token, 'address'> | null,
382
386
  ) => {
383
387
  if (!bridgeKey || !assetIn) {
384
- return false
388
+ return false;
385
389
  }
386
- const addr = getAddress(assetIn.address)
387
- const path = pathway(bridgeKey, false)
388
- if (!path) return false
389
- return path.bridgedNativeAssetOut === getAddress(addr)
390
- }
390
+ const addr = getAddress(assetIn.address);
391
+ const path = pathway(bridgeKey, false);
392
+ if (!path) return false;
393
+ return path.bridgedNativeAssetOut === getAddress(addr);
394
+ };
391
395
 
392
396
  /**
393
397
  * Returns whether the given asset can be unwrapped for the given bridge key
@@ -396,8 +400,8 @@ export const isUnwrappable = (
396
400
  * @returns boolean
397
401
  */
398
402
  export const canChangeUnwrap = (bridgeKey: BridgeKey, assetIn: Token | null) => {
399
- return !!assetIn && isUnwrappable(bridgeKey, assetIn)
400
- }
403
+ return !!assetIn && isUnwrappable(bridgeKey, assetIn);
404
+ };
401
405
 
402
406
  export const chains = {
403
407
  ethereum: 1,
@@ -405,15 +409,24 @@ export const chains = {
405
409
  pulsechain: 369,
406
410
  pulsechainV4: 943,
407
411
  sepolia: 11155111,
408
- } as const
412
+ } as const;
413
+
414
+ export const chainIds = Object.values(chains);
409
415
 
410
- export const chainIds = Object.values(chains)
416
+ /** Start blocks aligned with when bridge validators began operating — canonical reference for all ponder indexers in this monorepo. */
417
+ export const startBlocks: Record<keyof typeof chains, number> = {
418
+ ethereum: 17_264_119,
419
+ bsc: 28_987_313,
420
+ pulsechain: 17_268_300,
421
+ pulsechainV4: 16_564_233,
422
+ sepolia: 3_331_901,
423
+ };
411
424
 
412
- export type ChainId = (typeof chainIds)[number]
425
+ export type ChainId = (typeof chainIds)[number];
413
426
 
414
- export type Side = 'home' | 'foreign'
427
+ export type Side = 'home' | 'foreign';
415
428
 
416
- export type MinimalKey = `${ChainId}-${Hex}`
429
+ export type MinimalKey = `${ChainId}-${Hex}`;
417
430
 
418
431
  // export const Providers = {
419
432
  // PULSECHAIN: 'pulsechain',
@@ -421,60 +434,67 @@ export type MinimalKey = `${ChainId}-${Hex}`
421
434
  // } as const
422
435
 
423
436
  export type PathwayInfo = {
424
- pair: string
425
- key: string
426
- home: Hex
427
- foreign: Hex
428
- }
437
+ pair: string;
438
+ key: string;
439
+ home: Hex;
440
+ foreign: Hex;
441
+ };
429
442
 
430
- export type ProviderHomeChainEntry = Partial<Record<ChainId, PathwayInfo[]>>
443
+ export type ProviderHomeChainEntry = Partial<Record<ChainId, PathwayInfo[]>>;
431
444
 
432
- export type ProviderEntries = Partial<Record<ChainId, ProviderHomeChainEntry>>
445
+ export type ProviderEntries = Partial<Record<ChainId, ProviderHomeChainEntry>>;
433
446
 
434
- export type BridgeConfig = Record<Provider, ProviderEntries>
447
+ export type BridgeConfig = Record<Provider, ProviderEntries>;
435
448
 
436
449
  // provider -> home -> foreign
437
450
  export const bridgeConfigs = {
438
451
  [Providers.PULSECHAIN]: {
439
452
  [chains.pulsechain]: {
440
- [chains.ethereum]: [{
441
- pair: 'mainnet',
442
- key: 'erc20',
443
- home: '0x4fD0aaa7506f3d9cB8274bdB946Ec42A1b8751Ef',
444
- foreign: '0x1715a3e4a142d8b698131108995174f37aeba10d',
445
- }, {
446
- pair: 'mainnet',
447
- key: 'native',
448
- home: '0x0e18d0d556b652794EF12Bf68B2dC857EF5f3996',
449
- foreign: '0xe20E337DB2a00b1C37139c873B92a0AAd3F468bF',
450
- }],
453
+ [chains.ethereum]: [
454
+ {
455
+ pair: 'mainnet',
456
+ key: 'erc20',
457
+ home: '0x4fD0aaa7506f3d9cB8274bdB946Ec42A1b8751Ef',
458
+ foreign: '0x1715a3e4a142d8b698131108995174f37aeba10d',
459
+ },
460
+ {
461
+ pair: 'mainnet',
462
+ key: 'native',
463
+ home: '0x0e18d0d556b652794EF12Bf68B2dC857EF5f3996',
464
+ foreign: '0xe20E337DB2a00b1C37139c873B92a0AAd3F468bF',
465
+ },
466
+ ],
451
467
  },
452
468
  [chains.pulsechainV4]: {
453
- [chains.sepolia]: [{
454
- pair: 'v4',
455
- key: 'erc20',
456
- home: '0x6B08a50865aDeCe6e3869D9AfbB316d0a0436B6c',
457
- foreign: '0x546e37DAA15cdb82fd1a717E5dEEa4AF08D4349A',
458
- }, {
459
- pair: 'v4',
460
- key: 'native',
461
- home: '0x81c10846F40fA2B173FC47958D0a892529983F50',
462
- foreign: '0x5e238ef968467cf443ef5ecb683b76c5a04a0421',
463
- }],
469
+ [chains.sepolia]: [
470
+ {
471
+ pair: 'v4',
472
+ key: 'erc20',
473
+ home: '0x6B08a50865aDeCe6e3869D9AfbB316d0a0436B6c',
474
+ foreign: '0x546e37DAA15cdb82fd1a717E5dEEa4AF08D4349A',
475
+ },
476
+ {
477
+ pair: 'v4',
478
+ key: 'native',
479
+ home: '0x81c10846F40fA2B173FC47958D0a892529983F50',
480
+ foreign: '0x5e238ef968467cf443ef5ecb683b76c5a04a0421',
481
+ },
482
+ ],
464
483
  },
465
484
  },
466
485
  [Providers.TOKENSEX]: {
467
486
  [chains.pulsechain]: {
468
- [chains.bsc]: [{
469
- pair: 'bsctokensex',
470
- key: 'erc20',
471
- home: '0xf1DFc63e10fF01b8c3d307529b47AefaD2154C0e',
472
- foreign: '0xb4005881e81a6ecd2c1f75d58e8e41f28d59c6b1',
473
- }],
487
+ [chains.bsc]: [
488
+ {
489
+ pair: 'bsctokensex',
490
+ key: 'erc20',
491
+ home: '0xf1DFc63e10fF01b8c3d307529b47AefaD2154C0e',
492
+ foreign: '0xb4005881e81a6ecd2c1f75d58e8e41f28d59c6b1',
493
+ },
494
+ ],
474
495
  },
475
496
  },
476
- } as BridgeConfig
477
-
497
+ } as BridgeConfig;
478
498
 
479
- export const HOME_TO_FOREIGN_FEE = keccak256(stringToHex("homeToForeignFee"))
480
- export const FOREIGN_TO_HOME_FEE = keccak256(stringToHex("foreignToHomeFee"))
499
+ export const HOME_TO_FOREIGN_FEE = keccak256(stringToHex('homeToForeignFee'));
500
+ export const FOREIGN_TO_HOME_FEE = keccak256(stringToHex('foreignToHomeFee'));
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { FeeType, feeTypeValToKeyMap } from './fee-type';
3
+
4
+ describe('fee-type', () => {
5
+ describe('FeeType enum', () => {
6
+ it('should have PERCENT fee type', () => {
7
+ expect(FeeType.PERCENT).toBe('%');
8
+ });
9
+
10
+ it('should have GAS_TIP fee type', () => {
11
+ expect(FeeType.GAS_TIP).toBe('gas+%');
12
+ });
13
+
14
+ it('should have FIXED fee type', () => {
15
+ expect(FeeType.FIXED).toBe('fixed');
16
+ });
17
+
18
+ it('should have exactly 3 fee types', () => {
19
+ const keys = Object.keys(FeeType);
20
+ expect(keys).toHaveLength(3);
21
+ });
22
+ });
23
+
24
+ describe('feeTypeValToKeyMap', () => {
25
+ it('should map PERCENT value to key', () => {
26
+ expect(feeTypeValToKeyMap.get(FeeType.PERCENT)).toBe('PERCENT');
27
+ });
28
+
29
+ it('should map GAS_TIP value to key', () => {
30
+ expect(feeTypeValToKeyMap.get(FeeType.GAS_TIP)).toBe('GAS_TIP');
31
+ });
32
+
33
+ it('should map FIXED value to key', () => {
34
+ expect(feeTypeValToKeyMap.get(FeeType.FIXED)).toBe('FIXED');
35
+ });
36
+
37
+ it('should map all fee type values to keys', () => {
38
+ expect(feeTypeValToKeyMap.size).toBe(3);
39
+ });
40
+
41
+ it('should return undefined for invalid fee type', () => {
42
+ expect(feeTypeValToKeyMap.get('invalid' as FeeType)).toBeUndefined();
43
+ });
44
+ });
45
+ });
package/src/fee-type.ts CHANGED
@@ -6,9 +6,9 @@ export enum FeeType {
6
6
  }
7
7
 
8
8
  /** The keys of the fee type */
9
- export type FeeTypeKeys = keyof typeof FeeType
9
+ export type FeeTypeKeys = keyof typeof FeeType;
10
10
 
11
11
  /** A map of fee type values to keys */
12
12
  export const feeTypeValToKeyMap = new Map<FeeType, FeeTypeKeys>(
13
13
  (Object.keys(FeeType) as FeeTypeKeys[]).map((key) => [FeeType[key], key]),
14
- )
14
+ );
@@ -0,0 +1,160 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { getImageRoot, image, images, list, network, setImageRoot } from './image-links';
3
+
4
+ describe('image-links', () => {
5
+ beforeEach(() => {
6
+ // Reset to default before each test
7
+ setImageRoot('https://gib.show');
8
+ });
9
+
10
+ describe('setImageRoot and getImageRoot', () => {
11
+ it('should have default image root', () => {
12
+ expect(getImageRoot()).toBe('https://gib.show');
13
+ });
14
+
15
+ it('should update image root', () => {
16
+ setImageRoot('https://custom.domain');
17
+ expect(getImageRoot()).toBe('https://custom.domain');
18
+ });
19
+
20
+ it('should affect all subsequent URL generations', () => {
21
+ setImageRoot('https://test.com');
22
+ expect(list('/tokens')).toBe('https://test.com/list/tokens');
23
+ expect(network(1)).toBe('https://test.com/image/1');
24
+ });
25
+ });
26
+
27
+ describe('list', () => {
28
+ it('should generate list URL with path', () => {
29
+ expect(list('/pulsex')).toBe('https://gib.show/list/pulsex');
30
+ });
31
+
32
+ it('should handle paths without leading slash', () => {
33
+ expect(list('tokens')).toBe('https://gib.show/listtokens');
34
+ });
35
+
36
+ it('should handle paths with query params', () => {
37
+ expect(list('/pulsechain-bridge/foreign?extensions=bridgeInfo')).toBe(
38
+ 'https://gib.show/list/pulsechain-bridge/foreign?extensions=bridgeInfo',
39
+ );
40
+ });
41
+
42
+ it('should handle empty path', () => {
43
+ expect(list('')).toBe('https://gib.show/list');
44
+ });
45
+ });
46
+
47
+ describe('network', () => {
48
+ it('should generate network image URL for Ethereum', () => {
49
+ expect(network(1)).toBe('https://gib.show/image/1');
50
+ });
51
+
52
+ it('should generate network image URL for PulseChain', () => {
53
+ expect(network(369)).toBe('https://gib.show/image/369');
54
+ });
55
+
56
+ it('should generate network image URL for BSC', () => {
57
+ expect(network(56)).toBe('https://gib.show/image/56');
58
+ });
59
+
60
+ it('should convert string numbers to number', () => {
61
+ expect(network('369' as any)).toBe('https://gib.show/image/369');
62
+ });
63
+
64
+ it('should handle zero chainId', () => {
65
+ expect(network(0)).toBe('https://gib.show/image/0');
66
+ });
67
+
68
+ it('should handle falsy chainId', () => {
69
+ expect(network(null as any)).toBe('https://gib.show/image/0');
70
+ expect(network(undefined as any)).toBe('https://gib.show/image/NaN');
71
+ });
72
+ });
73
+
74
+ describe('image', () => {
75
+ it('should use logoURI if provided', () => {
76
+ const token = {
77
+ chainId: 1,
78
+ address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
79
+ logoURI: 'https://custom.com/usdt.png',
80
+ };
81
+ expect(image(token)).toBe('https://custom.com/usdt.png');
82
+ });
83
+
84
+ it('should generate URL from chainId and address if no logoURI', () => {
85
+ const token = {
86
+ chainId: 1,
87
+ address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
88
+ };
89
+ expect(image(token)).toBe(
90
+ 'https://gib.show/image/1/0xdAC17F958D2ee523a2206206994597C13D831ec7',
91
+ );
92
+ });
93
+
94
+ it('should checksum the address', () => {
95
+ const token = {
96
+ chainId: 1,
97
+ address: '0xdac17f958d2ee523a2206206994597c13d831ec7', // lowercase
98
+ };
99
+ const result = image(token);
100
+ // Address should be checksummed in the URL
101
+ expect(result).toContain('0xdAC17F958D2ee523a2206206994597C13D831ec7');
102
+ });
103
+
104
+ it('should handle null logoURI', () => {
105
+ const token = {
106
+ chainId: 369,
107
+ address: '0xa1077a294dde1b09bb078844df40758a5d0f9a27',
108
+ logoURI: null,
109
+ };
110
+ expect(image(token)).toBe(
111
+ 'https://gib.show/image/369/0xA1077a294dDE1B09bB078844df40758a5D0f9a27',
112
+ );
113
+ });
114
+
115
+ it('should handle different chain IDs', () => {
116
+ const bscToken = {
117
+ chainId: 56,
118
+ address: '0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82',
119
+ };
120
+ expect(image(bscToken)).toBe(
121
+ 'https://gib.show/image/56/0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82',
122
+ );
123
+ });
124
+ });
125
+
126
+ describe('images', () => {
127
+ it('should generate multi-source image URL', () => {
128
+ const sources = ['source1', 'source2', 'source3'];
129
+ expect(images(sources)).toBe('https://gib.show/image/?i=source1&i=source2&i=source3');
130
+ });
131
+
132
+ it('should handle single source', () => {
133
+ expect(images(['single'])).toBe('https://gib.show/image/?i=single');
134
+ });
135
+
136
+ it('should handle empty array', () => {
137
+ expect(images([])).toBe('https://gib.show/image/?');
138
+ });
139
+
140
+ it('should handle URL-encoded sources', () => {
141
+ const sources = ['https://example.com/image1.png', 'https://example.com/image2.png'];
142
+ expect(images(sources)).toBe(
143
+ 'https://gib.show/image/?i=https://example.com/image1.png&i=https://example.com/image2.png',
144
+ );
145
+ });
146
+ });
147
+
148
+ describe('integration', () => {
149
+ it('should work with custom root across all functions', () => {
150
+ setImageRoot('https://cdn.example.com');
151
+
152
+ expect(list('/tokens')).toBe('https://cdn.example.com/list/tokens');
153
+ expect(network(1)).toBe('https://cdn.example.com/image/1');
154
+ expect(image({ chainId: 1, address: '0xdac17f958d2ee523a2206206994597c13d831ec7' })).toBe(
155
+ 'https://cdn.example.com/image/1/0xdAC17F958D2ee523a2206206994597C13D831ec7',
156
+ );
157
+ expect(images(['a', 'b'])).toBe('https://cdn.example.com/image/?i=a&i=b');
158
+ });
159
+ });
160
+ });