Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/actions/src/eth/chainIdHandler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,16 @@ describe(chainIdHandler.name, () => {
// Accept either 11155111n (actual sepolia) or test environment value
expect(typeof chainId === 'bigint').toBe(true)
})

it('should return overridden chain ID from fork options', async () => {
const customChainId = 999
const node = createTevmNode({
fork: {
transport: transports.optimism,
chainId: customChainId,
},
})
const chainId = await chainIdHandler(node)({})
expect(chainId).toBe(BigInt(customChainId))
})
})
2 changes: 2 additions & 0 deletions packages/memory-client/src/MemoryClientOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type { TevmRpcSchema } from './TevmRpcSchema.js'
* fork: {
* transport: http("https://mainnet.optimism.io")({}),
* blockTag: '0xa6a63cd70fbbe396321ca6fe79e1b6735760c03538208b50d7e3a5dac5226435',
* chainId: 1337, // Optional: Override chain ID to avoid wallet confusion
* },
* // Chain configuration
* common: optimism,
Expand Down Expand Up @@ -67,6 +68,7 @@ import type { TevmRpcSchema } from './TevmRpcSchema.js'
* @property {Object} [fork] - The configuration for forking a network.
* @property {Function} [fork.transport] - The transport function for connecting to the fork source network.
* @property {string|number|bigint} [fork.blockTag] - The specific block tag to fork from (can be number, hash, or named tag like 'latest').
* @property {number} [fork.chainId] - Optional chain ID override to avoid wallet confusion when forking chains with the same ID.
* @property {Object} [miningConfig] - Configuration for how blocks are mined.
* @property {'manual'|'auto'|'interval'} [miningConfig.type] - The mining mode (manual requires calling mine(), auto mines after each tx, interval mines on a timer).
* @property {number} [miningConfig.interval] - For interval mining, how often to mine blocks in milliseconds.
Expand Down
27 changes: 27 additions & 0 deletions packages/memory-client/src/createMemoryClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,33 @@ describe('createMemoryClient', () => {
expect(blockNumber).toBeGreaterThan(0n)
})

it('should use custom chain ID when forking with chainId override', async () => {
const customChainId = 999
const client = createMemoryClient({
fork: {
transport: transports.optimism,
chainId: customChainId,
},
})

await client.tevmReady()
const chainId = await client.getChainId()
expect(chainId).toBe(customChainId)
})

it('should use auto-detected chain ID when forking without chainId override', async () => {
const client = createMemoryClient({
fork: {
transport: transports.optimism,
},
})

await client.tevmReady()
const chainId = await client.getChainId()
// Optimism chain ID should be 10
expect(chainId).toBe(10)
})

it('should auto-mine transactions when miningMode is set to auto', async () => {
const testAddress = '0x1234567890123456789012345678901234567890'
const client = createMemoryClient({
Expand Down
8 changes: 8 additions & 0 deletions packages/node/src/createTevmNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,21 @@ export const createTevmNode = (options = {}) => {
}

const chainIdPromise = (async () => {
// Fork chainId override takes highest priority
if (transport && options.fork?.chainId !== undefined) {
logger.debug({ chainId: options.fork.chainId }, 'Using overridden chainId from fork options')
return options.fork.chainId
}
// Then use common chainId if provided
if (options?.common) {
return options?.common.id
}
// Then auto-detect from transport
if (transport) {
const id = await getChainId(transport)
return id
}
// Finally fallback to default
return DEFAULT_CHAIN_ID
})().then((chainId) => {
logger.debug({ chainId }, 'Creating client with chainId')
Expand Down
42 changes: 42 additions & 0 deletions packages/node/src/createTevmNode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,46 @@ describe('createTevmNode', () => {
expect(result.execResult.executionGasUsed).toBe(9999n)
})
})

describe('Fork with custom chain ID', () => {
it('should override chain ID when provided in fork options', async () => {
const customChainId = 999
const client = createTevmNode({
fork: {
transport: transports.optimism,
chainId: customChainId,
},
})
await client.ready()
const vm = await client.getVm()
expect(vm.common.id).toBe(customChainId)
})

it('should use auto-detected chain ID when not provided in fork options', async () => {
const client = createTevmNode({
fork: {
transport: transports.optimism,
},
})
await client.ready()
const vm = await client.getVm()
// Optimism chain ID should be 10
expect(vm.common.id).toBe(10)
})

it('should prioritize fork chainId over common chainId when both are provided', async () => {
const customCommon = createCommon({ ...mainnet, id: 1, hardfork: 'prague', eips: [], loggingLevel: 'warn' })
const customChainId = 999
const client = createTevmNode({
common: customCommon,
fork: {
transport: transports.optimism,
chainId: customChainId,
},
})
await client.ready()
const vm = await client.getVm()
expect(vm.common.id).toBe(customChainId)
})
})
})
57 changes: 57 additions & 0 deletions packages/state/src/state-types/ForkOptions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { http } from 'viem'
import { describe, expect, it } from 'vitest'
import type { ForkOptions } from './ForkOptions.js'

describe('ForkOptions', () => {
it('should accept minimal fork options with transport only', () => {
const options: ForkOptions = {
transport: http('https://mainnet.infura.io/v3/your-api-key'),
}
expect(options.transport).toBeDefined()
expect(options.blockTag).toBeUndefined()
expect(options.chainId).toBeUndefined()
})

it('should accept fork options with blockTag', () => {
const options: ForkOptions = {
transport: http('https://mainnet.infura.io/v3/your-api-key'),
blockTag: 'latest',
}
expect(options.transport).toBeDefined()
expect(options.blockTag).toBe('latest')
expect(options.chainId).toBeUndefined()
})

it('should accept fork options with custom chainId', () => {
const customChainId = 999
const options: ForkOptions = {
transport: http('https://mainnet.infura.io/v3/your-api-key'),
chainId: customChainId,
}
expect(options.transport).toBeDefined()
expect(options.chainId).toBe(customChainId)
expect(options.blockTag).toBeUndefined()
})

it('should accept fork options with all properties', () => {
const customChainId = 1337
const options: ForkOptions = {
transport: http('https://mainnet.infura.io/v3/your-api-key'),
blockTag: 12345678n,
chainId: customChainId,
}
expect(options.transport).toBeDefined()
expect(options.blockTag).toBe(12345678n)
expect(options.chainId).toBe(customChainId)
})

it('should enforce number type for chainId', () => {
// This test verifies TypeScript compilation - if it compiles, the types are correct
const validChainId: number = 1337
const options: ForkOptions = {
transport: http('https://mainnet.infura.io/v3/your-api-key'),
chainId: validChainId,
}
expect(typeof options.chainId).toBe('number')
})
})
9 changes: 8 additions & 1 deletion packages/state/src/state-types/ForkOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ import { type EIP1193RequestFn, type Transport } from 'viem'
*
* const value: ForkOptions = {
* transport: http('https://mainnet.infura.io/v3/your-api-key'),
* blockTag: 'latest'
* blockTag: 'latest',
* chainId: 1337 // Optional: Override the chain ID instead of auto-detecting
* }
* ```
*/
export interface ForkOptions {
transport: { request: EIP1193RequestFn } | Transport
blockTag?: BlockTag | bigint
/**
* Optional chain ID override. If not provided, the chain ID will be auto-detected
* from the forked network. This is useful to avoid wallet confusion when working
* with forked chains that have the same chain ID as the original network.
*/
chainId?: number
}
Loading