@@ -8,20 +8,30 @@ import chalk from "chalk";
88import { ExecaError } from "execa" ;
99import getPort , { portNumbers } from "get-port" ;
1010import ora from "ora" ;
11- import { type Address , type Hex , numberToHex } from "viem" ;
11+ import {
12+ type Address ,
13+ createPublicClient ,
14+ type Hex ,
15+ http ,
16+ numberToHex ,
17+ } from "viem" ;
1218import { getMachineHash , getProjectName } from "../base.js" ;
19+ import type { ForkConfig } from "../compose/anvil.js" ;
1320import { DEFAULT_SDK_VERSION , PREFERRED_PORT } from "../config.js" ;
1421import {
1522 AVAILABLE_SERVICES ,
16- type RollupsDeployment ,
1723 deployApplication ,
1824 removeApplication ,
25+ type RollupsDeployment ,
1926 startEnvironment ,
2027 stopEnvironment ,
2128 waitHealthyEnvironment ,
2229} from "../exec/rollups.js" ;
2330import { keySelect } from "../prompts.js" ;
2431
32+ const DEFAULT_FORK_URL = "https://ethereum.reth.rs/rpc" ;
33+ const DEFAULT_FORK_BLOCK_NUMBER = 24000000 ;
34+
2535const commaSeparatedList = ( value : string ) => value . split ( "," ) ;
2636
2737const shell = async ( options : {
@@ -165,6 +175,36 @@ const deploy = async (options: {
165175 return application ;
166176} ;
167177
178+ const configureFork = async ( options : {
179+ fork ?: true | undefined ;
180+ forkUrl ?: string ;
181+ forkBlockNumber ?: number ;
182+ } ) : Promise < ForkConfig | undefined > => {
183+ // determine fork mode: explicit --fork flag or --fork-url provided
184+ const isFork = options . fork || options . forkUrl !== undefined ;
185+
186+ // assign default fork url
187+ const forkUrl = options . forkUrl ?? DEFAULT_FORK_URL ;
188+
189+ // if --fork-url explicitly defined than explicit --flock-block-number is required
190+ if ( options . forkUrl && ! options . forkBlockNumber ) {
191+ throw new Error ( "required --fork-block-number" ) ;
192+ }
193+
194+ const forkBlockNumber =
195+ options . forkBlockNumber ?? DEFAULT_FORK_BLOCK_NUMBER ;
196+
197+ // need to query fork chainId if forkUrl is specified
198+ const client = createPublicClient ( {
199+ transport : http ( forkUrl ) ,
200+ } ) ;
201+ const chainId = await client . getChainId ( ) ;
202+
203+ return isFork
204+ ? { blockNumber : forkBlockNumber , chainId, url : forkUrl }
205+ : undefined ;
206+ } ;
207+
168208export const createRunCommand = ( ) => {
169209 return new Command ( "run" )
170210 . description ( "Run a local cartesi node for the application." )
@@ -197,6 +237,17 @@ export const createRunCommand = () => {
197237 . default ( "latest" ) ,
198238 )
199239 . option ( "--dry-run" , "show the docker compose configuration" , false )
240+ . option (
241+ "--fork" ,
242+ "fork from a live chain instead of using devnet state" ,
243+ )
244+ . option ( "--fork-url <url>" , "RPC URL to fork from (implies --fork)" )
245+ . addOption (
246+ new Option (
247+ "--fork-block-number <number>" ,
248+ "block number to fork from" ,
249+ ) . argParser ( Number ) ,
250+ )
200251 . addOption (
201252 new Option (
202253 "--memory <number>" ,
@@ -265,12 +316,16 @@ export const createRunCommand = () => {
265316 port : portNumbers ( PREFERRED_PORT , PREFERRED_PORT + 10 ) ,
266317 } ) ) ;
267318
319+ // configure optional anvil fork
320+ const forkConfig = await configureFork ( options ) ;
321+
268322 // run compose environment (detached)
269323 const { address, config } = await startEnvironment ( {
270324 blockTime,
271325 cpus,
272326 defaultBlock,
273327 dryRun,
328+ forkConfig,
274329 memory,
275330 port,
276331 projectName,
0 commit comments