forked from koshikraj/ottopus
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchains.test.ts
More file actions
113 lines (97 loc) · 4.46 KB
/
Copy pathchains.test.ts
File metadata and controls
113 lines (97 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { describe, expect, it } from 'vitest'
import { CaipError, NATIVE_COIN_TYPE_CHAINS, nativeAssetOf } from './caip.js'
import {
ALCHEMY_NETWORK,
chainName,
explorerAddressUrl,
explorerTxUrl,
findChain,
providerServes,
isSupportedChain,
listChains,
requireChain,
rpcUrlFor,
} from './chains.js'
describe('the registry', () => {
it('knows Base by its CAIP-2 id', () => {
const base = findChain('eip155:8453')
expect(base).toMatchObject({ id: 'eip155:8453', evmId: 8453, name: 'Base', testnet: false })
expect(base!.nativeCurrency.symbol).toBe('ETH')
expect(base!.publicRpcUrls.length).toBeGreaterThan(0)
})
it('covers most EVM chains, not two', () => {
expect(listChains().filter((c) => !c.testnet).length).toBeGreaterThan(100)
})
/**
* The coin-type table in caip.ts and viem's list must not drift: a chain we
* can name the currency of but cannot read is a portfolio row nobody can
* act on.
*/
it('knows every chain the coin-type table names', () => {
for (const ref of NATIVE_COIN_TYPE_CHAINS) {
const id = `eip155:${ref}`
expect(isSupportedChain(id), id).toBe(true)
expect(() => nativeAssetOf(id)).not.toThrow()
}
})
it('agrees with the coin-type table about which chains spend ETH', () => {
for (const ref of NATIVE_COIN_TYPE_CHAINS) {
const spendsEth = nativeAssetOf(`eip155:${ref}`).endsWith('/slip44:60')
expect(findChain(`eip155:${ref}`)!.nativeCurrency.symbol === 'ETH', ref).toBe(spendsEth)
}
})
it('refuses an unknown chain by name, never falling back to Ethereum', () => {
expect(findChain('eip155:99999999999')).toBeNull()
expect(() => requireChain('eip155:99999999999')).toThrow(CaipError)
expect(() => requireChain('eip155:99999999999')).toThrow(/eip155:99999999999/)
})
/** viem holds HyperEVM and two retired testnets under 999. The mainnet wins. */
it('resolves a shared id to the mainnet, whatever order viem exports them in', () => {
expect(findChain('eip155:999')).toMatchObject({ name: 'HyperEVM', testnet: false })
expect(findChain('eip155:999')!.nativeCurrency.symbol).toBe('HYPE')
})
it('has no opinion about non-EVM namespaces', () => {
expect(findChain({ namespace: 'solana', reference: 'mainnet' })).toBeNull()
})
it('names a chain, and falls back to the id for one it does not know', () => {
expect(chainName('eip155:1')).toBe('Ethereum')
expect(chainName('eip155:99999999999')).toBe('eip155:99999999999')
})
})
describe('rpc resolution', () => {
it('substitutes the chain id into a provider template', () => {
expect(rpcUrlFor('eip155:8453', 'https://rpc.example/v1/{chainId}/KEY')).toBe('https://rpc.example/v1/8453/KEY')
})
it('substitutes Alchemy’s network name for a chain it serves', () => {
expect(rpcUrlFor('eip155:8453', 'https://{network}.g.alchemy.com/v2/KEY')).toBe('https://base-mainnet.g.alchemy.com/v2/KEY')
expect(rpcUrlFor('eip155:56', 'https://{network}.g.alchemy.com/v2/KEY')).toBe('https://bnb-mainnet.g.alchemy.com/v2/KEY')
expect(providerServes('eip155:8453', 'https://{network}.g.alchemy.com/v2/KEY')).toBe(true)
})
/** Fantom is in the coin-type table and not on Alchemy; it must still read. */
it('falls back to the public endpoint for a chain the provider does not name', () => {
const template = 'https://{network}.g.alchemy.com/v2/KEY'
expect(providerServes('eip155:250', template)).toBe(false)
expect(rpcUrlFor('eip155:250', template)).not.toContain('alchemy')
expect(rpcUrlFor('eip155:250', template)).toMatch(/^https:\/\//)
})
it('names only chains viem knows in the Alchemy table', () => {
for (const evmId of Object.keys(ALCHEMY_NETWORK)) {
expect(isSupportedChain(`eip155:${evmId}`), evmId).toBe(true)
}
})
it('falls back to the public endpoint without a template', () => {
expect(rpcUrlFor('eip155:8453')).toMatch(/^https:\/\//)
})
it('refuses a chain it does not know even with a template', () => {
expect(() => rpcUrlFor('eip155:99999999999', 'https://rpc.example/{chainId}')).toThrow(CaipError)
})
})
describe('explorer links', () => {
it('links a transaction and an address', () => {
expect(explorerTxUrl('eip155:8453', '0xabc')).toBe('https://basescan.org/tx/0xabc')
expect(explorerAddressUrl('eip155:1', '0xdef')).toBe('https://etherscan.io/address/0xdef')
})
it('is null rather than a broken link for a chain with no explorer', () => {
expect(explorerTxUrl('eip155:99999999999', '0xabc')).toBeNull()
})
})