From 44de9c83acd68c607c8382a14d61692d39e128b5 Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Wed, 25 Mar 2026 17:45:52 +0200 Subject: [PATCH 1/9] Add Westend live network smoke tests for runtime API compatibility --- deno.json | 4 +- src/test-setup.ts | 6 +- src/util.ts | 67 +++++++++++------ src/westend-tests.ts | 14 ++++ src/westend.test.ts | 167 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 230 insertions(+), 28 deletions(-) create mode 100644 src/westend-tests.ts create mode 100644 src/westend.test.ts diff --git a/deno.json b/deno.json index 6a37677..5fb360f 100644 --- a/deno.json +++ b/deno.json @@ -18,7 +18,9 @@ "test:update": "deno task build --solcOnly && USE_BYTECODE=evm START_GETH=true deno test -P -- --update", "test:polkadot": "USE_BYTECODE=evm START_ASSET_HUB_WESTEND=true START_ETH_RPC=true deno test -P src/polkadot-tests.ts", "test:polkadot:manual": "USE_BYTECODE=evm deno test -P src/polkadot-tests.ts", - "lint": "deno fmt --check && deno task build && deno check && deno lint" + "test:westend": "USE_BYTECODE=evm START_ETH_RPC_WESTEND=true deno test -P src/westend-tests.ts", + "test:westend:manual": "USE_BYTECODE=evm deno test -P src/westend-tests.ts", + "lint": "deno fmt --check && deno lint" }, "test": { "include": ["src/all-tests.ts"] diff --git a/src/test-setup.ts b/src/test-setup.ts index e2d80e3..c52cdf1 100644 --- a/src/test-setup.ts +++ b/src/test-setup.ts @@ -1,4 +1,4 @@ -import { killProcessOnPort, waitForHealth } from './util.ts' +import { getRpcUrl, killProcessOnPort, waitForHealth } from './util.ts' let processes: Deno.ChildProcess[] = [] let setupComplete = false @@ -142,9 +142,7 @@ export async function setupTests() { } // Detect and set the PLATFORM variable - const port = Deno.env.get('RPC_PORT') ?? '8545' - const url = `http://localhost:${port}` - await detectAndSetPlatform(url) + await detectAndSetPlatform(getRpcUrl()) } export function cleanupTests() { diff --git a/src/util.ts b/src/util.ts index 6d10777..356f28d 100644 --- a/src/util.ts +++ b/src/util.ts @@ -90,29 +90,29 @@ function getEnvName(): string { export const jsonRpcErrors: JsonRpcError[] = [] -export type Env = Awaited> -export async function getEnv() { +export function getRpcUrl() { const port = Deno.env.get('RPC_PORT') ?? '8545' - const url = `http://localhost:${port}` - const name = getEnvName() + return `http://localhost:${port}` +} - const id = await (async (): Promise => { - const resp = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - jsonrpc: '2.0', - method: 'eth_chainId', - id: 1, - }), - }) - const { result } = (await resp.json()) as { result: Hex } - return hexToNumber(result) - })() +async function fetchChainId(url: string): Promise { + const resp = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'eth_chainId', + id: 1, + }), + }) + const { result } = (await resp.json()) as { result: Hex } + return hexToNumber(result) +} - const chain = defineChain({ +function createChain(id: number, name: string, url: string) { + return defineChain({ id, name, nativeCurrency: { @@ -127,8 +127,10 @@ export async function getEnv() { }, testnet: true, }) +} - const transport = http(url, { +function createTransport(url: string) { + return http(url, { onFetchResponse: async (response) => { const raw = await response.clone().json() if (raw.error) { @@ -136,6 +138,11 @@ export async function getEnv() { } }, }) +} + +export type Env = Awaited> +export async function getEnv() { + const { chain, publicClient, transport, name } = await getReadOnlyEnv() const waitForTransactionReceiptExtension = (client: { getTransactionReceipt: (args: { @@ -252,8 +259,6 @@ export async function getEnv() { } } - const publicClient = createPublicClient({ chain, transport }) - const debugClient = createClient({ chain, transport, @@ -429,6 +434,22 @@ export function memoizedDeploy(env: Env, transact: () => Promise) { } } +export async function getReadOnlyEnv() { + const url = getRpcUrl() + const name = getEnvName() + const id = await fetchChainId(url) + const chain = createChain(id, name, url) + const transport = createTransport(url) + const publicClient = createPublicClient({ chain, transport }) + + return { + chain, + publicClient, + transport, + name, + } +} + export function computeMappingSlot(addressKey: Hex, slotIndex: number) { const keyBytes = pad(hexToBytes(addressKey), { size: 32 }) const slotBytes = pad(hexToBytes(`0x${slotIndex.toString(16)}`), { diff --git a/src/westend-tests.ts b/src/westend-tests.ts new file mode 100644 index 0000000..ffe4ea6 --- /dev/null +++ b/src/westend-tests.ts @@ -0,0 +1,14 @@ +// Entry point for Westend live network tests. +// These tests run stateless RPC queries against eth-rpc connected to +// the live Westend asset-hub, validating runtime API compatibility. +// +// Run with: deno task test:westend + +import { cleanupTests, setupTests } from './test-setup.ts' + +await setupTests() +await import('./westend.test.ts') + +globalThis.addEventListener('unload', () => { + cleanupTests() +}) diff --git a/src/westend.test.ts b/src/westend.test.ts new file mode 100644 index 0000000..caf2161 --- /dev/null +++ b/src/westend.test.ts @@ -0,0 +1,167 @@ +import { type Hex, hexToBigInt, hexToNumber } from 'viem' +import { getReadOnlyEnv, sanitizeOpts as opts } from './util.ts' +import { expect } from '@std/expect' + +// Lightweight read-only environment — no wallets, no funding, no deployments. +const env = await getReadOnlyEnv() + +const ZERO_ADDR = '0x0000000000000000000000000000000000000000' as const +const REVIVE_DEPLOY_BLOCK = 13169391n + +const untypedRequest = (method: string, params?: unknown[]) => + // deno-lint-ignore no-explicit-any + env.publicClient.request({ method, params } as any) + +Deno.test('westend: eth_accounts', opts, async () => { + const addresses = await untypedRequest('eth_accounts') + expect(Array.isArray(addresses)).toBe(true) +}) + +Deno.test('westend: eth_blockNumber', opts, async () => { + const res = await env.publicClient.request({ + method: 'eth_blockNumber', + }) + expect(typeof hexToBigInt(res)).toEqual('bigint') +}) + +Deno.test('westend: eth_chainId', opts, async () => { + const res = await env.publicClient.request({ + method: 'eth_chainId', + }) + expect(hexToNumber(res)).toEqual(env.chain.id) +}) + +Deno.test('westend: eth_gasPrice', opts, async () => { + const res = await env.publicClient.request({ + method: 'eth_gasPrice', + }) + expect(hexToBigInt(res) > 0n).toBe(true) +}) + +Deno.test('westend: eth_getBalance', opts, async () => { + const balance = await env.publicClient.getBalance({ + address: ZERO_ADDR, + }) + expect(typeof balance).toEqual('bigint') +}) + +Deno.test('westend: eth_getBlockByNumber (latest)', opts, async () => { + const block = await env.publicClient.getBlock({ blockTag: 'latest' }) + expect(block).toBeTruthy() + expect(block.number).toBeTruthy() + expect(block.hash).toBeTruthy() + expect(block.timestamp).toBeTruthy() +}) + +Deno.test( + 'westend: eth_getBlockByNumber (known block)', + opts, + async () => { + const block = await env.publicClient.getBlock({ + blockNumber: REVIVE_DEPLOY_BLOCK, + }) + expect(block).toBeTruthy() + expect(block.number).toEqual(REVIVE_DEPLOY_BLOCK) + }, +) + +Deno.test('westend: eth_getBlockByHash', opts, async () => { + const latest = await env.publicClient.getBlock({ blockTag: 'latest' }) + const byHash = await env.publicClient.getBlock({ + blockHash: latest.hash!, + }) + expect(byHash.number).toEqual(latest.number) +}) + +Deno.test( + 'westend: eth_getBlockTransactionCount', + opts, + async () => { + const count = await env.publicClient.getBlockTransactionCount({ + blockTag: 'latest', + }) + expect(typeof count).toEqual('number') + }, +) + +Deno.test('westend: eth_getTransactionCount', opts, async () => { + const count = await env.publicClient.getTransactionCount({ + address: ZERO_ADDR, + }) + expect(typeof count).toEqual('number') +}) + +Deno.test('westend: eth_getCode', opts, async () => { + const code = await env.publicClient.getCode({ + address: ZERO_ADDR, + }) + // Zero address is an EOA — expect no code + expect(code === undefined || code === '0x').toBe(true) +}) + +// On a live network, querying storage at a non-contract address returns +// an RPC error ("Contract not found") rather than a zero value. +// We verify the runtime API is reachable by accepting either outcome. +Deno.test('westend: eth_getStorageAt', opts, async () => { + try { + const storage = await env.publicClient.getStorageAt({ + address: ZERO_ADDR, + slot: + '0x0000000000000000000000000000000000000000000000000000000000000000', + }) + expect(typeof storage).toEqual('string') + } catch (err) { + // "Contract not found" is an expected response for non-contract addresses + expect(String(err)).toContain('Contract not found') + } +}) + +Deno.test('westend: eth_syncing', opts, async () => { + const res = await untypedRequest('eth_syncing') + expect(res).toEqual(false) +}) + +Deno.test('westend: eth_feeHistory', opts, async () => { + const feeHistory = await env.publicClient.getFeeHistory({ + blockCount: 4, + blockTag: 'latest', + rewardPercentiles: [25, 75], + }) + expect(feeHistory.oldestBlock).toBeGreaterThanOrEqual(0) + expect(feeHistory.gasUsedRatio.length).toBeGreaterThanOrEqual(0) + expect(feeHistory.baseFeePerGas).toBeTruthy() +}) + +Deno.test('westend: eth_maxPriorityFeePerGas', opts, async () => { + const res = await untypedRequest('eth_maxPriorityFeePerGas') as Hex + expect(typeof hexToBigInt(res)).toEqual('bigint') +}) + +Deno.test('westend: net_version', opts, async () => { + const res = await untypedRequest('net_version') + expect(res).toBeTruthy() +}) + +Deno.test('westend: web3_clientVersion', opts, async () => { + const res = await untypedRequest('web3_clientVersion') + expect(res).toBeTruthy() +}) + +// eth_estimateGas calls the eth_estimate_gas runtime API. +// Without .unvalidated() (polkadot-sdk#11468), this fails with metadata mismatch +// when eth-rpc is compiled against a newer runtime than the live chain. +Deno.test('westend: eth_estimateGas', opts, async () => { + const gas = await env.publicClient.estimateGas({ + account: ZERO_ADDR, + to: '0x0000000000000000000000000000000000000001', + }) + expect(gas > 0n).toBe(true) +}) + +Deno.test('westend: eth_call', opts, async () => { + const result = await env.publicClient.call({ + to: '0x0000000000000000000000000000000000000001', + }) + // Just verify it returns without RPC error + expect(typeof result).toEqual('object') +}) From 59d5bc9791206fb3dd70ed22a2e599f96d748d29 Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Wed, 25 Mar 2026 18:14:46 +0200 Subject: [PATCH 2/9] Remove comment --- src/westend.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/westend.test.ts b/src/westend.test.ts index caf2161..57a70ce 100644 --- a/src/westend.test.ts +++ b/src/westend.test.ts @@ -147,9 +147,6 @@ Deno.test('westend: web3_clientVersion', opts, async () => { expect(res).toBeTruthy() }) -// eth_estimateGas calls the eth_estimate_gas runtime API. -// Without .unvalidated() (polkadot-sdk#11468), this fails with metadata mismatch -// when eth-rpc is compiled against a newer runtime than the live chain. Deno.test('westend: eth_estimateGas', opts, async () => { const gas = await env.publicClient.estimateGas({ account: ZERO_ADDR, From d5bd15e80016a5a8d8d2adb57c58da8e54cf110a Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Wed, 25 Mar 2026 18:26:17 +0200 Subject: [PATCH 3/9] Improve westend test assertions and add eth_getLogs coverage --- src/westend.test.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/westend.test.ts b/src/westend.test.ts index 57a70ce..1127e02 100644 --- a/src/westend.test.ts +++ b/src/westend.test.ts @@ -6,6 +6,7 @@ import { expect } from '@std/expect' const env = await getReadOnlyEnv() const ZERO_ADDR = '0x0000000000000000000000000000000000000000' as const +// Block at which pallet-revive was deployed on Westend Asset Hub. const REVIVE_DEPLOY_BLOCK = 13169391n const untypedRequest = (method: string, params?: unknown[]) => @@ -116,9 +117,19 @@ Deno.test('westend: eth_getStorageAt', opts, async () => { } }) +Deno.test('westend: eth_getLogs', opts, async () => { + const latest = await env.publicClient.getBlockNumber() + const logs = await env.publicClient.getLogs({ + fromBlock: latest - 10n, + toBlock: latest, + }) + expect(Array.isArray(logs)).toBe(true) +}) + Deno.test('westend: eth_syncing', opts, async () => { const res = await untypedRequest('eth_syncing') - expect(res).toEqual(false) + // false when synced, object with sync progress otherwise + expect(res === false || typeof res === 'object').toBe(true) }) Deno.test('westend: eth_feeHistory', opts, async () => { @@ -159,6 +170,5 @@ Deno.test('westend: eth_call', opts, async () => { const result = await env.publicClient.call({ to: '0x0000000000000000000000000000000000000001', }) - // Just verify it returns without RPC error - expect(typeof result).toEqual('object') + expect(result).toBeTruthy() }) From b0ce86232907d1cfe273ef97e78acc61caa6afdf Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Thu, 26 Mar 2026 14:58:45 +0200 Subject: [PATCH 4/9] Support downloading live Westend runtime for omninode testing --- deno.json | 2 +- src/test-setup.ts | 45 ++++++++++++++++++++++++++++++++++++++++++--- src/westend.test.ts | 12 ++++++++---- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/deno.json b/deno.json index 5fb360f..d561c06 100644 --- a/deno.json +++ b/deno.json @@ -18,7 +18,7 @@ "test:update": "deno task build --solcOnly && USE_BYTECODE=evm START_GETH=true deno test -P -- --update", "test:polkadot": "USE_BYTECODE=evm START_ASSET_HUB_WESTEND=true START_ETH_RPC=true deno test -P src/polkadot-tests.ts", "test:polkadot:manual": "USE_BYTECODE=evm deno test -P src/polkadot-tests.ts", - "test:westend": "USE_BYTECODE=evm START_ETH_RPC_WESTEND=true deno test -P src/westend-tests.ts", + "test:westend": "USE_BYTECODE=evm START_ASSET_HUB_WESTEND=true USE_LIVE_RUNTIME=true START_ETH_RPC=true deno test -P src/westend-tests.ts", "test:westend:manual": "USE_BYTECODE=evm deno test -P src/westend-tests.ts", "lint": "deno fmt --check && deno lint" }, diff --git a/src/test-setup.ts b/src/test-setup.ts index c52cdf1..095f444 100644 --- a/src/test-setup.ts +++ b/src/test-setup.ts @@ -76,7 +76,12 @@ export async function setupTests() { `${Deno.env.get('HOME')}/polkadot-sdk` const omniNode = Deno.env.get('OMNI_NODE_PATH') ?? `${sdkDir}/target/release/polkadot-omni-node` - const chainSpec = await buildAssetHubWestendSpec(sdkDir, omniNode) + const useLiveRuntime = !!Deno.env.get('USE_LIVE_RUNTIME') + const chainSpec = await buildAssetHubWestendSpec( + sdkDir, + omniNode, + useLiveRuntime, + ) const nodeArgs = [ '--dev', @@ -198,12 +203,46 @@ async function runCommand( return new TextDecoder().decode(output.stdout) } +const WESTEND_ASSET_HUB_RPC = 'https://westend-asset-hub-rpc.polkadot.io' +const CODE_STORAGE_KEY = toHex(':code') + +async function downloadLiveRuntime(rpcUrl: string): Promise { + console.log(`📦 Downloading runtime from ${rpcUrl} ...`) + const resp = await fetch(rpcUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'state_getStorage', + params: [CODE_STORAGE_KEY], + id: 1, + }), + }) + const { result } = (await resp.json()) as { result: string } + if (!result || result === '0x') { + throw new Error('Failed to fetch runtime wasm from live network') + } + const runtimeCode = hexToBytes(result as `0x${string}`) + const path = '/tmp/westend-asset-hub-runtime.wasm' + await Deno.writeFile(path, runtimeCode) + console.log( + `📦 Runtime downloaded (${ + (runtimeCode.length / 1024 / 1024).toFixed(1) + } MB)`, + ) + return path +} + async function buildAssetHubWestendSpec( sdkDir: string, omniNode: string, + useLiveRuntime = false, ): Promise { - const runtime = - `${sdkDir}/target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm` + const runtime = useLiveRuntime + ? await downloadLiveRuntime( + Deno.env.get('WESTEND_RPC_URL') ?? WESTEND_ASSET_HUB_RPC, + ) + : `${sdkDir}/target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm` const basePath = '/tmp/ah-westend-base.json' const rawPath = '/tmp/ah-westend-raw.json' diff --git a/src/westend.test.ts b/src/westend.test.ts index 1127e02..71c27a5 100644 --- a/src/westend.test.ts +++ b/src/westend.test.ts @@ -49,14 +49,17 @@ Deno.test('westend: eth_getBalance', opts, async () => { Deno.test('westend: eth_getBlockByNumber (latest)', opts, async () => { const block = await env.publicClient.getBlock({ blockTag: 'latest' }) expect(block).toBeTruthy() - expect(block.number).toBeTruthy() + expect(typeof block.number).toEqual('bigint') expect(block.hash).toBeTruthy() - expect(block.timestamp).toBeTruthy() + expect(typeof block.timestamp).toEqual('bigint') }) Deno.test( 'westend: eth_getBlockByNumber (known block)', - opts, + { + ...opts, + ignore: !!Deno.env.get('START_ASSET_HUB_WESTEND'), + }, async () => { const block = await env.publicClient.getBlock({ blockNumber: REVIVE_DEPLOY_BLOCK, @@ -119,8 +122,9 @@ Deno.test('westend: eth_getStorageAt', opts, async () => { Deno.test('westend: eth_getLogs', opts, async () => { const latest = await env.publicClient.getBlockNumber() + const fromBlock = latest > 10n ? latest - 10n : 0n const logs = await env.publicClient.getLogs({ - fromBlock: latest - 10n, + fromBlock, toBlock: latest, }) expect(Array.isArray(logs)).toBe(true) From caa06d0d464b2c45f800bf9e90a2293e9caddd83 Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Thu, 26 Mar 2026 16:44:32 +0200 Subject: [PATCH 5/9] Upgrade westend tests to full read-write env and consolidate test cases --- deno.json | 2 +- src/test-setup.ts | 9 ++ src/util.ts | 23 ++--- src/westend.test.ts | 216 ++++++++++++++++---------------------------- 4 files changed, 93 insertions(+), 157 deletions(-) diff --git a/deno.json b/deno.json index d561c06..dbd0a34 100644 --- a/deno.json +++ b/deno.json @@ -18,7 +18,7 @@ "test:update": "deno task build --solcOnly && USE_BYTECODE=evm START_GETH=true deno test -P -- --update", "test:polkadot": "USE_BYTECODE=evm START_ASSET_HUB_WESTEND=true START_ETH_RPC=true deno test -P src/polkadot-tests.ts", "test:polkadot:manual": "USE_BYTECODE=evm deno test -P src/polkadot-tests.ts", - "test:westend": "USE_BYTECODE=evm START_ASSET_HUB_WESTEND=true USE_LIVE_RUNTIME=true START_ETH_RPC=true deno test -P src/westend-tests.ts", + "test:westend": "deno task build --solcOnly && USE_BYTECODE=evm START_ASSET_HUB_WESTEND=true USE_LIVE_RUNTIME=true START_ETH_RPC=true deno test -P src/westend-tests.ts", "test:westend:manual": "USE_BYTECODE=evm deno test -P src/westend-tests.ts", "lint": "deno fmt --check && deno lint" }, diff --git a/src/test-setup.ts b/src/test-setup.ts index 095f444..ee1b41b 100644 --- a/src/test-setup.ts +++ b/src/test-setup.ts @@ -271,6 +271,15 @@ async function buildAssetHubWestendSpec( baseSpec.genesis.runtimeGenesis ??= {} baseSpec.genesis.runtimeGenesis.patch ??= {} baseSpec.genesis.runtimeGenesis.patch.sudo = { key: ALICE_SS58 } + // Fund alith so eth_accounts[0] has balance. + const ALITH_SS58 = '5HYRCKHYJN9z5xUtfFkyMj4JUhsAwWyvuU8vKB1FcnYTf9ZQ' + const balances = + baseSpec.genesis.runtimeGenesis.patch.balances?.balances ?? [] + balances.push([ALITH_SS58, Number.MAX_SAFE_INTEGER]) + baseSpec.genesis.runtimeGenesis.patch.balances = { + ...baseSpec.genesis.runtimeGenesis.patch.balances, + balances, + } await Deno.writeTextFile(basePath, JSON.stringify(baseSpec, null, 2)) // Step 2: Convert to raw format diff --git a/src/util.ts b/src/util.ts index 356f28d..e6b947f 100644 --- a/src/util.ts +++ b/src/util.ts @@ -142,7 +142,12 @@ function createTransport(url: string) { export type Env = Awaited> export async function getEnv() { - const { chain, publicClient, transport, name } = await getReadOnlyEnv() + const url = getRpcUrl() + const name = getEnvName() + const id = await fetchChainId(url) + const chain = createChain(id, name, url) + const transport = createTransport(url) + const publicClient = createPublicClient({ chain, transport }) const waitForTransactionReceiptExtension = (client: { getTransactionReceipt: (args: { @@ -434,22 +439,6 @@ export function memoizedDeploy(env: Env, transact: () => Promise) { } } -export async function getReadOnlyEnv() { - const url = getRpcUrl() - const name = getEnvName() - const id = await fetchChainId(url) - const chain = createChain(id, name, url) - const transport = createTransport(url) - const publicClient = createPublicClient({ chain, transport }) - - return { - chain, - publicClient, - transport, - name, - } -} - export function computeMappingSlot(addressKey: Hex, slotIndex: number) { const keyBytes = pad(hexToBytes(addressKey), { size: 32 }) const slotBytes = pad(hexToBytes(`0x${slotIndex.toString(16)}`), { diff --git a/src/westend.test.ts b/src/westend.test.ts index 71c27a5..e08bbfc 100644 --- a/src/westend.test.ts +++ b/src/westend.test.ts @@ -1,178 +1,116 @@ -import { type Hex, hexToBigInt, hexToNumber } from 'viem' -import { getReadOnlyEnv, sanitizeOpts as opts } from './util.ts' +import { type Hex, hexToBigInt, hexToNumber, parseEther } from 'viem' +import { sanitizeOpts as opts } from './util.ts' import { expect } from '@std/expect' - -// Lightweight read-only environment — no wallets, no funding, no deployments. -const env = await getReadOnlyEnv() - -const ZERO_ADDR = '0x0000000000000000000000000000000000000000' as const -// Block at which pallet-revive was deployed on Westend Asset Hub. -const REVIVE_DEPLOY_BLOCK = 13169391n +import { TesterAbi } from '../codegen/abi/Tester.ts' +import { EventExampleAbi } from '../codegen/abi/EventExample.ts' +import { env, getEventExampleAddr, getTesterAddr } from './deploy_contracts.ts' const untypedRequest = (method: string, params?: unknown[]) => // deno-lint-ignore no-explicit-any env.publicClient.request({ method, params } as any) -Deno.test('westend: eth_accounts', opts, async () => { - const addresses = await untypedRequest('eth_accounts') - expect(Array.isArray(addresses)).toBe(true) -}) +Deno.test('westend: eth_accounts and eth_chainId', opts, async () => { + const addresses = (await untypedRequest('eth_accounts')) as string[] + expect(addresses.length).toBeGreaterThanOrEqual(1) + expect(addresses[0].toLowerCase()).toEqual( + env.serverWallet.account.address.toLowerCase(), + ) -Deno.test('westend: eth_blockNumber', opts, async () => { - const res = await env.publicClient.request({ - method: 'eth_blockNumber', - }) - expect(typeof hexToBigInt(res)).toEqual('bigint') -}) - -Deno.test('westend: eth_chainId', opts, async () => { const res = await env.publicClient.request({ method: 'eth_chainId', }) expect(hexToNumber(res)).toEqual(env.chain.id) }) -Deno.test('westend: eth_gasPrice', opts, async () => { - const res = await env.publicClient.request({ +Deno.test('westend: eth_fees', opts, async () => { + const gasPrice = await env.publicClient.request({ method: 'eth_gasPrice', }) - expect(hexToBigInt(res) > 0n).toBe(true) -}) - -Deno.test('westend: eth_getBalance', opts, async () => { - const balance = await env.publicClient.getBalance({ - address: ZERO_ADDR, - }) - expect(typeof balance).toEqual('bigint') -}) + expect(hexToBigInt(gasPrice) > 0n).toBe(true) -Deno.test('westend: eth_getBlockByNumber (latest)', opts, async () => { - const block = await env.publicClient.getBlock({ blockTag: 'latest' }) - expect(block).toBeTruthy() - expect(typeof block.number).toEqual('bigint') - expect(block.hash).toBeTruthy() - expect(typeof block.timestamp).toEqual('bigint') -}) + const maxPriority = await untypedRequest( + 'eth_maxPriorityFeePerGas', + ) as Hex + expect(hexToBigInt(maxPriority) >= 0n).toBe(true) -Deno.test( - 'westend: eth_getBlockByNumber (known block)', - { - ...opts, - ignore: !!Deno.env.get('START_ASSET_HUB_WESTEND'), - }, - async () => { - const block = await env.publicClient.getBlock({ - blockNumber: REVIVE_DEPLOY_BLOCK, - }) - expect(block).toBeTruthy() - expect(block.number).toEqual(REVIVE_DEPLOY_BLOCK) - }, -) - -Deno.test('westend: eth_getBlockByHash', opts, async () => { - const latest = await env.publicClient.getBlock({ blockTag: 'latest' }) - const byHash = await env.publicClient.getBlock({ - blockHash: latest.hash!, + const feeHistory = await env.publicClient.getFeeHistory({ + blockCount: 4, + blockTag: 'latest', + rewardPercentiles: [25, 75], }) - expect(byHash.number).toEqual(latest.number) + expect(feeHistory.gasUsedRatio.length).toBeGreaterThan(0) + expect(feeHistory.baseFeePerGas).toBeTruthy() }) -Deno.test( - 'westend: eth_getBlockTransactionCount', - opts, - async () => { - const count = await env.publicClient.getBlockTransactionCount({ - blockTag: 'latest', - }) - expect(typeof count).toEqual('number') - }, -) - -Deno.test('westend: eth_getTransactionCount', opts, async () => { - const count = await env.publicClient.getTransactionCount({ - address: ZERO_ADDR, +Deno.test('westend: eth_getBlock', opts, async () => { + const earliest = await env.publicClient.getBlock({ + blockTag: 'earliest', }) - expect(typeof count).toEqual('number') -}) + expect(earliest).toBeTruthy() + expect(earliest.number).toEqual(0n) -Deno.test('westend: eth_getCode', opts, async () => { - const code = await env.publicClient.getCode({ - address: ZERO_ADDR, + const byHash = await env.publicClient.getBlock({ + blockHash: earliest.hash!, }) - // Zero address is an EOA — expect no code - expect(code === undefined || code === '0x').toBe(true) + expect(byHash.number).toEqual(earliest.number) + expect(byHash.timestamp).toEqual(earliest.timestamp) }) -// On a live network, querying storage at a non-contract address returns -// an RPC error ("Contract not found") rather than a zero value. -// We verify the runtime API is reachable by accepting either outcome. -Deno.test('westend: eth_getStorageAt', opts, async () => { - try { - const storage = await env.publicClient.getStorageAt({ - address: ZERO_ADDR, - slot: - '0x0000000000000000000000000000000000000000000000000000000000000000', - }) - expect(typeof storage).toEqual('string') - } catch (err) { - // "Contract not found" is an expected response for non-contract addresses - expect(String(err)).toContain('Contract not found') - } -}) +Deno.test('westend: eth_contractState', opts, async () => { + const address = await getTesterAddr() -Deno.test('westend: eth_getLogs', opts, async () => { - const latest = await env.publicClient.getBlockNumber() - const fromBlock = latest > 10n ? latest - 10n : 0n - const logs = await env.publicClient.getLogs({ - fromBlock, - toBlock: latest, - }) - expect(Array.isArray(logs)).toBe(true) -}) + const code = await env.publicClient.getCode({ address }) + expect(code).toBeTruthy() + expect(code !== '0x').toBe(true) -Deno.test('westend: eth_syncing', opts, async () => { - const res = await untypedRequest('eth_syncing') - // false when synced, object with sync progress otherwise - expect(res === false || typeof res === 'object').toBe(true) + const storage = await env.serverWallet.getStorageAt({ + address, + slot: '0x01', + }) + expect(typeof storage).toEqual('string') + expect(storage !== '0x').toBe(true) }) -Deno.test('westend: eth_feeHistory', opts, async () => { - const feeHistory = await env.publicClient.getFeeHistory({ - blockCount: 4, - blockTag: 'latest', - rewardPercentiles: [25, 75], +Deno.test('westend: eth_call', opts, async () => { + const value = await env.serverWallet.readContract({ + address: await getTesterAddr(), + abi: TesterAbi, + functionName: 'value', }) - expect(feeHistory.oldestBlock).toBeGreaterThanOrEqual(0) - expect(feeHistory.gasUsedRatio.length).toBeGreaterThanOrEqual(0) - expect(feeHistory.baseFeePerGas).toBeTruthy() + expect(value).toEqual(42n) }) -Deno.test('westend: eth_maxPriorityFeePerGas', opts, async () => { - const res = await untypedRequest('eth_maxPriorityFeePerGas') as Hex - expect(typeof hexToBigInt(res)).toEqual('bigint') -}) +Deno.test('westend: eth_sendTransaction', opts, async () => { + const txParams = { + to: env.serverWallet.account.address, + value: parseEther('0.01'), + } as const -Deno.test('westend: net_version', opts, async () => { - const res = await untypedRequest('net_version') - expect(res).toBeTruthy() -}) + const gas = await env.accountWallet.estimateGas(txParams) + expect(gas > 0n).toBe(true) -Deno.test('westend: web3_clientVersion', opts, async () => { - const res = await untypedRequest('web3_clientVersion') - expect(res).toBeTruthy() -}) + const hash = await env.accountWallet.sendTransaction(txParams) + const receipt = await env.serverWallet.waitForTransactionReceipt(hash) + expect(receipt.status).toEqual('success') -Deno.test('westend: eth_estimateGas', opts, async () => { - const gas = await env.publicClient.estimateGas({ - account: ZERO_ADDR, - to: '0x0000000000000000000000000000000000000001', - }) - expect(gas > 0n).toBe(true) + const tx = await env.serverWallet.getTransaction({ hash }) + expect(tx).toBeTruthy() + expect(tx.hash).toEqual(hash) }) -Deno.test('westend: eth_call', opts, async () => { - const result = await env.publicClient.call({ - to: '0x0000000000000000000000000000000000000001', +Deno.test('westend: eth_getLogs', opts, async () => { + const address = await getEventExampleAddr() + const { request } = await env.serverWallet.simulateContract({ + address, + abi: EventExampleAbi, + functionName: 'triggerEvent', + }) + const hash = await env.serverWallet.writeContract(request) + const receipt = await env.serverWallet.waitForTransactionReceipt(hash) + const logs = await env.publicClient.getLogs({ + address, + blockHash: receipt.blockHash, }) - expect(result).toBeTruthy() + expect(logs.length).toBeGreaterThanOrEqual(1) + expect(logs[0].transactionHash).toEqual(hash) }) From a5c51e858efc4c7ea995301edbf897d330d40d71 Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Thu, 26 Mar 2026 16:58:42 +0200 Subject: [PATCH 6/9] Consolidate westend smoke tests --- src/test-setup.ts | 24 +++++-- src/westend.test.ts | 156 ++++++++++++++++++++++++-------------------- 2 files changed, 103 insertions(+), 77 deletions(-) diff --git a/src/test-setup.ts b/src/test-setup.ts index ee1b41b..f4b968b 100644 --- a/src/test-setup.ts +++ b/src/test-setup.ts @@ -206,7 +206,10 @@ async function runCommand( const WESTEND_ASSET_HUB_RPC = 'https://westend-asset-hub-rpc.polkadot.io' const CODE_STORAGE_KEY = toHex(':code') -async function downloadLiveRuntime(rpcUrl: string): Promise { +async function downloadLiveRuntime( + tmpDir: string, + rpcUrl: string, +): Promise { console.log(`📦 Downloading runtime from ${rpcUrl} ...`) const resp = await fetch(rpcUrl, { method: 'POST', @@ -218,12 +221,17 @@ async function downloadLiveRuntime(rpcUrl: string): Promise { id: 1, }), }) + if (!resp.ok) { + throw new Error( + `RPC request failed: ${resp.status} ${resp.statusText}`, + ) + } const { result } = (await resp.json()) as { result: string } if (!result || result === '0x') { throw new Error('Failed to fetch runtime wasm from live network') } const runtimeCode = hexToBytes(result as `0x${string}`) - const path = '/tmp/westend-asset-hub-runtime.wasm' + const path = `${tmpDir}/westend-asset-hub-runtime.wasm` await Deno.writeFile(path, runtimeCode) console.log( `📦 Runtime downloaded (${ @@ -238,13 +246,15 @@ async function buildAssetHubWestendSpec( omniNode: string, useLiveRuntime = false, ): Promise { + const tmpDir = await Deno.makeTempDir({ prefix: 'ah-westend-' }) const runtime = useLiveRuntime ? await downloadLiveRuntime( + tmpDir, Deno.env.get('WESTEND_RPC_URL') ?? WESTEND_ASSET_HUB_RPC, ) : `${sdkDir}/target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm` - const basePath = '/tmp/ah-westend-base.json' - const rawPath = '/tmp/ah-westend-raw.json' + const basePath = `${tmpDir}/ah-westend-base.json` + const rawPath = `${tmpDir}/ah-westend-raw.json` // Step 1: Generate base chain spec (writes to file via --chain-spec-path) console.log('📋 Generating asset-hub-westend chain spec ...') @@ -271,10 +281,10 @@ async function buildAssetHubWestendSpec( baseSpec.genesis.runtimeGenesis ??= {} baseSpec.genesis.runtimeGenesis.patch ??= {} baseSpec.genesis.runtimeGenesis.patch.sudo = { key: ALICE_SS58 } - // Fund alith so eth_accounts[0] has balance. + // Fund alith (0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac) so eth_accounts[0] has balance. const ALITH_SS58 = '5HYRCKHYJN9z5xUtfFkyMj4JUhsAwWyvuU8vKB1FcnYTf9ZQ' - const balances = - baseSpec.genesis.runtimeGenesis.patch.balances?.balances ?? [] + const balances = baseSpec.genesis.runtimeGenesis.patch.balances?.balances ?? + [] balances.push([ALITH_SS58, Number.MAX_SAFE_INTEGER]) baseSpec.genesis.runtimeGenesis.patch.balances = { ...baseSpec.genesis.runtimeGenesis.patch.balances, diff --git a/src/westend.test.ts b/src/westend.test.ts index e08bbfc..7bc4b44 100644 --- a/src/westend.test.ts +++ b/src/westend.test.ts @@ -22,41 +22,49 @@ Deno.test('westend: eth_accounts and eth_chainId', opts, async () => { expect(hexToNumber(res)).toEqual(env.chain.id) }) -Deno.test('westend: eth_fees', opts, async () => { - const gasPrice = await env.publicClient.request({ - method: 'eth_gasPrice', - }) - expect(hexToBigInt(gasPrice) > 0n).toBe(true) - - const maxPriority = await untypedRequest( - 'eth_maxPriorityFeePerGas', - ) as Hex - expect(hexToBigInt(maxPriority) >= 0n).toBe(true) - - const feeHistory = await env.publicClient.getFeeHistory({ - blockCount: 4, - blockTag: 'latest', - rewardPercentiles: [25, 75], - }) - expect(feeHistory.gasUsedRatio.length).toBeGreaterThan(0) - expect(feeHistory.baseFeePerGas).toBeTruthy() -}) - -Deno.test('westend: eth_getBlock', opts, async () => { - const earliest = await env.publicClient.getBlock({ - blockTag: 'earliest', - }) - expect(earliest).toBeTruthy() - expect(earliest.number).toEqual(0n) - - const byHash = await env.publicClient.getBlock({ - blockHash: earliest.hash!, - }) - expect(byHash.number).toEqual(earliest.number) - expect(byHash.timestamp).toEqual(earliest.timestamp) -}) - -Deno.test('westend: eth_contractState', opts, async () => { +Deno.test( + 'westend: eth_gasPrice, eth_maxPriorityFeePerGas, eth_feeHistory', + opts, + async () => { + const gasPrice = await env.publicClient.request({ + method: 'eth_gasPrice', + }) + expect(hexToBigInt(gasPrice) > 0n).toBe(true) + + const maxPriority = await untypedRequest( + 'eth_maxPriorityFeePerGas', + ) as Hex + expect(hexToBigInt(maxPriority) >= 0n).toBe(true) + + const feeHistory = await env.publicClient.getFeeHistory({ + blockCount: 4, + blockTag: 'latest', + rewardPercentiles: [25, 75], + }) + expect(feeHistory.gasUsedRatio.length).toBeGreaterThan(0) + expect(feeHistory.baseFeePerGas).toBeTruthy() + }, +) + +Deno.test( + 'westend: eth_getBlockByNumber and eth_getBlockByHash', + opts, + async () => { + const earliest = await env.publicClient.getBlock({ + blockTag: 'earliest', + }) + expect(earliest).toBeTruthy() + expect(earliest.number).toEqual(0n) + + const byHash = await env.publicClient.getBlock({ + blockHash: earliest.hash!, + }) + expect(byHash.number).toEqual(earliest.number) + expect(byHash.timestamp).toEqual(earliest.timestamp) + }, +) + +Deno.test('westend: eth_getCode and eth_getStorageAt', opts, async () => { const address = await getTesterAddr() const code = await env.publicClient.getCode({ address }) @@ -71,7 +79,7 @@ Deno.test('westend: eth_contractState', opts, async () => { expect(storage !== '0x').toBe(true) }) -Deno.test('westend: eth_call', opts, async () => { +Deno.test('westend: eth_call reads contract state', opts, async () => { const value = await env.serverWallet.readContract({ address: await getTesterAddr(), abi: TesterAbi, @@ -80,37 +88,45 @@ Deno.test('westend: eth_call', opts, async () => { expect(value).toEqual(42n) }) -Deno.test('westend: eth_sendTransaction', opts, async () => { - const txParams = { - to: env.serverWallet.account.address, - value: parseEther('0.01'), - } as const - - const gas = await env.accountWallet.estimateGas(txParams) - expect(gas > 0n).toBe(true) - - const hash = await env.accountWallet.sendTransaction(txParams) - const receipt = await env.serverWallet.waitForTransactionReceipt(hash) - expect(receipt.status).toEqual('success') - - const tx = await env.serverWallet.getTransaction({ hash }) - expect(tx).toBeTruthy() - expect(tx.hash).toEqual(hash) -}) - -Deno.test('westend: eth_getLogs', opts, async () => { - const address = await getEventExampleAddr() - const { request } = await env.serverWallet.simulateContract({ - address, - abi: EventExampleAbi, - functionName: 'triggerEvent', - }) - const hash = await env.serverWallet.writeContract(request) - const receipt = await env.serverWallet.waitForTransactionReceipt(hash) - const logs = await env.publicClient.getLogs({ - address, - blockHash: receipt.blockHash, - }) - expect(logs.length).toBeGreaterThanOrEqual(1) - expect(logs[0].transactionHash).toEqual(hash) -}) +Deno.test( + 'westend: eth_estimateGas, eth_sendTransaction, eth_getTransactionByHash', + opts, + async () => { + const txParams = { + to: env.serverWallet.account.address, + value: parseEther('0.01'), + } as const + + const gas = await env.accountWallet.estimateGas(txParams) + expect(gas > 0n).toBe(true) + + const hash = await env.accountWallet.sendTransaction(txParams) + const receipt = await env.serverWallet.waitForTransactionReceipt(hash) + expect(receipt.status).toEqual('success') + + const tx = await env.serverWallet.getTransaction({ hash }) + expect(tx).toBeTruthy() + expect(tx.hash).toEqual(hash) + }, +) + +Deno.test( + 'westend: eth_getLogs returns events from contract call', + opts, + async () => { + const address = await getEventExampleAddr() + const { request } = await env.serverWallet.simulateContract({ + address, + abi: EventExampleAbi, + functionName: 'triggerEvent', + }) + const hash = await env.serverWallet.writeContract(request) + const receipt = await env.serverWallet.waitForTransactionReceipt(hash) + const logs = await env.publicClient.getLogs({ + address, + blockHash: receipt.blockHash, + }) + expect(logs.length).toBeGreaterThanOrEqual(1) + expect(logs[0].transactionHash).toEqual(hash) + }, +) From b96f041bc7ea84197f86897c5ef8a4bae9d74b41 Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Thu, 26 Mar 2026 17:10:17 +0200 Subject: [PATCH 7/9] Clean up temp dirs on shutdown and add timeout to runtime download --- src/test-setup.ts | 13 ++++++++++++- src/westend-tests.ts | 6 ++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/test-setup.ts b/src/test-setup.ts index f4b968b..fbebffb 100644 --- a/src/test-setup.ts +++ b/src/test-setup.ts @@ -1,6 +1,7 @@ import { getRpcUrl, killProcessOnPort, waitForHealth } from './util.ts' let processes: Deno.ChildProcess[] = [] +let tmpDirs: string[] = [] let setupComplete = false async function detectAndSetPlatform(url: string) { @@ -151,7 +152,7 @@ export async function setupTests() { } export function cleanupTests() { - if (processes.length === 0) { + if (processes.length === 0 && tmpDirs.length === 0) { return } console.log('🔌 Shutting down servers...') @@ -162,7 +163,15 @@ export function cleanupTests() { // Process might already be dead } } + for (const dir of tmpDirs) { + try { + Deno.removeSync(dir, { recursive: true }) + } catch { + // Directory might already be gone + } + } processes = [] + tmpDirs = [] } // --------------------------------------------------------------------------- @@ -214,6 +223,7 @@ async function downloadLiveRuntime( const resp = await fetch(rpcUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, + signal: AbortSignal.timeout(120_000 /* 2 min */), body: JSON.stringify({ jsonrpc: '2.0', method: 'state_getStorage', @@ -247,6 +257,7 @@ async function buildAssetHubWestendSpec( useLiveRuntime = false, ): Promise { const tmpDir = await Deno.makeTempDir({ prefix: 'ah-westend-' }) + tmpDirs.push(tmpDir) const runtime = useLiveRuntime ? await downloadLiveRuntime( tmpDir, diff --git a/src/westend-tests.ts b/src/westend-tests.ts index ffe4ea6..f0ba88e 100644 --- a/src/westend-tests.ts +++ b/src/westend-tests.ts @@ -1,7 +1,5 @@ -// Entry point for Westend live network tests. -// These tests run stateless RPC queries against eth-rpc connected to -// the live Westend asset-hub, validating runtime API compatibility. -// +// Westend live runtime smoke tests — validates eth-rpc requests +// against the live Westend asset-hub runtime. // Run with: deno task test:westend import { cleanupTests, setupTests } from './test-setup.ts' From d5ee98b6310cab860df99724bb4adb5145bc4c9a Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Thu, 26 Mar 2026 17:18:37 +0200 Subject: [PATCH 8/9] Document westend test commands and env vars in CLAUDE.md --- CLAUDE.md | 46 ++++++++++++++++++++++++--------------------- src/test-setup.ts | 1 + src/westend.test.ts | 14 +++++++------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 402f164..b3cb5b0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,17 +4,19 @@ Deno 2.x test suite that validates Ethereum JSON-RPC compatibility for Polkadot' ## Quick Reference -| Command | What it does | -| ---------------------------- | -------------------------------------------------------------------- | -| `deno task test:evm` | Build + test EVM bytecode on Revive (auto-starts dev-node + eth-rpc) | -| `deno task test:pvm` | Build + test PVM bytecode on Revive (auto-starts dev-node + eth-rpc) | -| `deno task test:geth` | Build + test EVM bytecode on Geth (reference) | -| `deno task test:evm:manual` | EVM tests without auto-starting services | -| `deno task test:pvm:manual` | PVM tests without auto-starting services | -| `deno task test:update` | Update snapshots from Geth | -| `deno task build` | Compile contracts (solc + resolc) | -| `deno task build --solcOnly` | Compile contracts (solc only, faster) | -| `deno task lint` | Format check + lint | +| Command | What it does | +| ------------------------------- | -------------------------------------------------------------------- | +| `deno task test:evm` | Build + test EVM bytecode on Revive (auto-starts dev-node + eth-rpc) | +| `deno task test:pvm` | Build + test PVM bytecode on Revive (auto-starts dev-node + eth-rpc) | +| `deno task test:geth` | Build + test EVM bytecode on Geth (reference) | +| `deno task test:evm:manual` | EVM tests without auto-starting services | +| `deno task test:pvm:manual` | PVM tests without auto-starting services | +| `deno task test:westend` | Build + test against live Westend runtime (auto-starts services) | +| `deno task test:westend:manual` | Westend tests without auto-starting services | +| `deno task test:update` | Update snapshots from Geth | +| `deno task build` | Compile contracts (solc + resolc) | +| `deno task build --solcOnly` | Compile contracts (solc only, faster) | +| `deno task lint` | Format check + lint | ## Architecture @@ -26,16 +28,18 @@ Deno 2.x test suite that validates Ethereum JSON-RPC compatibility for Polkadot' ## Environment Variables -| Variable | Purpose | Default | -| ----------------------- | ----------------------- | --------------------------------------------- | -| `USE_BYTECODE` | `evm` or `pvm` | — | -| `START_REVIVE_DEV_NODE` | Auto-start dev-node | — | -| `START_ETH_RPC` | Auto-start eth-rpc | — | -| `START_GETH` | Auto-start geth | — | -| `REVIVE_DEV_NODE_PATH` | Path to dev-node binary | `~/polkadot-sdk/target/debug/revive-dev-node` | -| `ETH_RPC_PATH` | Path to eth-rpc binary | `~/polkadot-sdk/target/debug/eth-rpc` | -| `GETH_PATH` | Path to geth binary | `geth` | -| `RPC_PORT` | JSON-RPC port | `8545` | +| Variable | Purpose | Default | +| ----------------------- | ----------------------------- | --------------------------------------------- | +| `USE_BYTECODE` | `evm` or `pvm` | — | +| `START_REVIVE_DEV_NODE` | Auto-start dev-node | — | +| `START_ETH_RPC` | Auto-start eth-rpc | — | +| `START_GETH` | Auto-start geth | — | +| `REVIVE_DEV_NODE_PATH` | Path to dev-node binary | `~/polkadot-sdk/target/debug/revive-dev-node` | +| `ETH_RPC_PATH` | Path to eth-rpc binary | `~/polkadot-sdk/target/debug/eth-rpc` | +| `GETH_PATH` | Path to geth binary | `geth` | +| `RPC_PORT` | JSON-RPC port | `8545` | +| `USE_LIVE_RUNTIME` | Download live Westend runtime | — | +| `WESTEND_RPC_URL` | Westend Asset Hub RPC URL | `https://westend-asset-hub-rpc.polkadot.io` | ## CI Jobs (polkadot-sdk) diff --git a/src/test-setup.ts b/src/test-setup.ts index fbebffb..2c19bd9 100644 --- a/src/test-setup.ts +++ b/src/test-setup.ts @@ -1,3 +1,4 @@ +import { hexToBytes, toHex } from 'viem/utils' import { getRpcUrl, killProcessOnPort, waitForHealth } from './util.ts' let processes: Deno.ChildProcess[] = [] diff --git a/src/westend.test.ts b/src/westend.test.ts index 7bc4b44..b7fbce9 100644 --- a/src/westend.test.ts +++ b/src/westend.test.ts @@ -50,17 +50,17 @@ Deno.test( 'westend: eth_getBlockByNumber and eth_getBlockByHash', opts, async () => { - const earliest = await env.publicClient.getBlock({ - blockTag: 'earliest', + const latest = await env.publicClient.getBlock({ + blockTag: 'latest', }) - expect(earliest).toBeTruthy() - expect(earliest.number).toEqual(0n) + expect(latest).toBeTruthy() + expect(latest.hash).toBeTruthy() const byHash = await env.publicClient.getBlock({ - blockHash: earliest.hash!, + blockHash: latest.hash!, }) - expect(byHash.number).toEqual(earliest.number) - expect(byHash.timestamp).toEqual(earliest.timestamp) + expect(byHash.number).toEqual(latest.number) + expect(byHash.timestamp).toEqual(latest.timestamp) }, ) From 884e92b6847a0762f79d59b6067c71c187618bbf Mon Sep 17 00:00:00 2001 From: Marian Radu Date: Fri, 27 Mar 2026 20:05:45 +0200 Subject: [PATCH 9/9] Restore lint task validation and inline module constants --- deno.json | 2 +- src/test-setup.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/deno.json b/deno.json index dbd0a34..a48ed98 100644 --- a/deno.json +++ b/deno.json @@ -20,7 +20,7 @@ "test:polkadot:manual": "USE_BYTECODE=evm deno test -P src/polkadot-tests.ts", "test:westend": "deno task build --solcOnly && USE_BYTECODE=evm START_ASSET_HUB_WESTEND=true USE_LIVE_RUNTIME=true START_ETH_RPC=true deno test -P src/westend-tests.ts", "test:westend:manual": "USE_BYTECODE=evm deno test -P src/westend-tests.ts", - "lint": "deno fmt --check && deno lint" + "lint": "deno fmt --check && deno task build && deno check && deno lint" }, "test": { "include": ["src/all-tests.ts"] diff --git a/src/test-setup.ts b/src/test-setup.ts index 2c19bd9..5dedaa7 100644 --- a/src/test-setup.ts +++ b/src/test-setup.ts @@ -213,13 +213,11 @@ async function runCommand( return new TextDecoder().decode(output.stdout) } -const WESTEND_ASSET_HUB_RPC = 'https://westend-asset-hub-rpc.polkadot.io' -const CODE_STORAGE_KEY = toHex(':code') - async function downloadLiveRuntime( tmpDir: string, rpcUrl: string, ): Promise { + const codeStorageKey = toHex(':code') console.log(`📦 Downloading runtime from ${rpcUrl} ...`) const resp = await fetch(rpcUrl, { method: 'POST', @@ -228,7 +226,7 @@ async function downloadLiveRuntime( body: JSON.stringify({ jsonrpc: '2.0', method: 'state_getStorage', - params: [CODE_STORAGE_KEY], + params: [codeStorageKey], id: 1, }), }) @@ -262,7 +260,8 @@ async function buildAssetHubWestendSpec( const runtime = useLiveRuntime ? await downloadLiveRuntime( tmpDir, - Deno.env.get('WESTEND_RPC_URL') ?? WESTEND_ASSET_HUB_RPC, + Deno.env.get('WESTEND_RPC_URL') ?? + 'https://westend-asset-hub-rpc.polkadot.io', ) : `${sdkDir}/target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm` const basePath = `${tmpDir}/ah-westend-base.json`