Skip to content

Commit 33c4c24

Browse files
izayljxom
andauthored
chore: add chain url avaliable check (#1309)
* feat: add chain url avaliable check * ci: add check action * chore: rename check file * fix: getSocket withTimeout * refactor: use bun test * polish * lint --------- Co-authored-by: moxey.eth <jakemoxey@gmail.com>
1 parent 9f92eaf commit 33c4c24

File tree

6 files changed

+131
-5
lines changed

6 files changed

+131
-5
lines changed

.github/workflows/chaincheck.yml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Check chains
2+
on:
3+
workflow_dispatch:
4+
workflow_call:
5+
pull_request:
6+
paths:
7+
- src/chains/**
8+
9+
jobs:
10+
chaincheck:
11+
name: Check chains
12+
needs: build
13+
runs-on: ubuntu-latest
14+
continue-on-error: true
15+
timeout-minutes: 10
16+
17+
steps:
18+
- name: Clone repository
19+
uses: actions/checkout@v4
20+
21+
- name: Install dependencies
22+
uses: ./.github/actions/install-dependencies
23+
24+
- name: Check chain
25+
run: bun run chaincheck

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --outDir ./src/_cjs --removeComments --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./src/_cjs/package.json",
1010
"build:esm": "tsc --project ./tsconfig.build.json --module es2015 --outDir ./src/_esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./src/_esm/package.json",
1111
"build:types": "tsc --project ./tsconfig.build.json --module esnext --declarationDir ./src/_types --emitDeclarationOnly --declaration --declarationMap",
12+
"chaincheck": "bun test test/chains/check.test.ts",
1213
"changeset": "changeset",
1314
"changeset:release": "bun run build && bun run prepublishOnly && changeset publish",
1415
"changeset:version": "changeset version && bun scripts/updateVersion.ts && bun install --lockfile-only",

src/utils/promise/withTimeout.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ export type WithTimeoutErrorType = ErrorType
55
export function withTimeout<TData>(
66
fn: ({ signal }: { signal?: AbortController['signal'] }) => Promise<TData>,
77
{
8-
errorInstance,
8+
errorInstance = new Error('timed out'),
99
timeout,
1010
signal,
1111
}: {
1212
// The error instance to throw when the timeout is reached.
13-
errorInstance: Error
13+
errorInstance?: Error
1414
// The timeout (in ms).
1515
timeout: number
1616
// Whether or not the timeout should use an abort signal.

test/chains/check.test.ts

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { expect, test } from 'bun:test'
2+
3+
import * as chains from '~viem/chains/index.js'
4+
import type { Chain } from '~viem/types/chain.js'
5+
import { withTimeout } from '~viem/utils/promise/withTimeout.js'
6+
import { getSocket, rpc } from '~viem/utils/rpc.js'
7+
8+
const defaultTimeout = 10_000
9+
10+
const chains_ = Object.values(chains) as readonly Chain[]
11+
chains_.forEach((chain) => {
12+
const httpRpcUrls = chain.rpcUrls.default.http
13+
if (httpRpcUrls)
14+
test(
15+
`${chain.name}: check http urls`,
16+
async () => {
17+
await assertHttpRpcUrls(chain.id, httpRpcUrls)
18+
},
19+
{ timeout: defaultTimeout },
20+
)
21+
22+
const webSocketRpcUrls = chain.rpcUrls.default.webSocket
23+
if (webSocketRpcUrls)
24+
test(
25+
`${chain.name}: check web socket urls`,
26+
async () => {
27+
await assertWebSocketRpcUrls(chain.id, webSocketRpcUrls)
28+
},
29+
{ timeout: defaultTimeout },
30+
)
31+
32+
const explorerUrl = chain.blockExplorers?.default.url
33+
if (explorerUrl)
34+
test(
35+
`${chain.name}: check block explorer`,
36+
async () => {
37+
await assertExplorerUrl(explorerUrl)
38+
},
39+
{ timeout: defaultTimeout },
40+
)
41+
})
42+
43+
function isLocalNetwork(url: string): boolean {
44+
const u = new URL(url)
45+
const localNetworkRegex =
46+
/^(127\.|10\.|172\.1[6-9]\.|172\.2[0-9]\.|172\.3[0-1]\.|192\.168\.)/
47+
48+
return localNetworkRegex.test(u.hostname) || u.hostname === 'localhost'
49+
}
50+
51+
async function assertHttpRpcUrls(
52+
chainId: number,
53+
rpcUrls: readonly string[],
54+
): Promise<void> {
55+
for (const url of rpcUrls) {
56+
if (isLocalNetwork(url)) continue
57+
const response = await rpc
58+
.http(url, {
59+
body: { method: 'eth_chainId' },
60+
fetchOptions: { headers: { 'Content-Type': 'application/json' } },
61+
timeout: defaultTimeout,
62+
})
63+
.then((r) => r.result)
64+
expect(BigInt(response)).toBe(BigInt(chainId))
65+
}
66+
}
67+
68+
async function assertWebSocketRpcUrls(
69+
chainId: number,
70+
rpcUrls: readonly string[],
71+
): Promise<void> {
72+
for (const url of rpcUrls) {
73+
if (isLocalNetwork(url)) continue
74+
75+
const socket = await withTimeout(() => getSocket(url), {
76+
timeout: defaultTimeout,
77+
})
78+
const response = await rpc
79+
.webSocketAsync(socket, {
80+
body: { method: 'eth_chainId' },
81+
timeout: defaultTimeout,
82+
})
83+
.then((r) => r.result)
84+
socket.close()
85+
expect(BigInt(response)).toBe(BigInt(chainId))
86+
}
87+
}
88+
89+
async function assertExplorerUrl(explorerUrl: string): Promise<void> {
90+
await fetch(explorerUrl, { method: 'HEAD' })
91+
}

test/tsconfig.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"types": ["bun-types"],
4+
"baseUrl": ".",
5+
"paths": {
6+
"~viem/*": ["../src/*"]
7+
}
8+
}
9+
}

tsconfig.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
// This configuration is used for local development and type checking.
33
"extends": "./tsconfig.base.json",
4-
"include": ["src", "test"],
4+
"include": ["src", "test/src"],
55
"exclude": [],
66
"references": [{ "path": "./scripts/tsconfig.json" }],
77
"compilerOptions": {
88
"baseUrl": ".",
99
"paths": {
1010
"~viem/*": ["./src/*"],
11-
"~test/*": ["./test/*"],
12-
},
11+
"~test/*": ["./test/*"]
12+
}
1313
}
1414
}

0 commit comments

Comments
 (0)