From 706f2e0192d20a8ea95f5b8e701dc51e2ffcf497 Mon Sep 17 00:00:00 2001 From: alvarius Date: Mon, 6 Jan 2025 15:12:17 +0100 Subject: [PATCH 1/2] docs: add indexerUrl to deploy CLI docs (#3416) --- docs/pages/cli/deploy.mdx | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/pages/cli/deploy.mdx b/docs/pages/cli/deploy.mdx index a81a0b56cc..676d363e60 100644 --- a/docs/pages/cli/deploy.mdx +++ b/docs/pages/cli/deploy.mdx @@ -37,21 +37,22 @@ Again, there are several ways to do this: These are the command line options you can specify on `mud deploy`: -| Option | Meaning | Type | Default value | -| ----------------------- | ------------------------------------------------ | ------- | ---------------------------------------------------------- | -| `--configPath` | Path to the config file | string | `mud.config.ts` | -| `--printConfig` | Print the resolved config | boolean | `false` | -| `--saveDeployment` | Save the deployment info to a file | boolean | `true` | -| `--profile` | The foundry profile to use | string | `local` | -| `--rpc`1 | The RPC URL to use | string | RPC url from `foundry.toml` | -| `--rpcBatch` | Enable batch processing of RPC requests | boolean | `false` | -| `--worldAddress` | Deploy to an existing World at the given address | string | Empty, deploy new `World` | -| `--srcDir` | Source directory | string | Foundry `src` directory | -| `--skipBuild` | Skip rebuilding the contracts before deploying | boolean | `false` | -| `--alwaysRunPostDeploy` | Run `PostDeploy.s.sol` after each deploy | boolean | `false` (run the script only when deploying a new `World`) | -| `--help` | Show help | boolean | `false` | -| `--version` | Show version number | boolean | `false` | -| `--forgeScriptOptions` | Command line options for forge | string | empty | +| Option | Meaning | Type | Default value | +| ----------------------- | -------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------- | +| `--configPath` | Path to the config file | string | `mud.config.ts` | +| `--printConfig` | Print the resolved config | boolean | `false` | +| `--saveDeployment` | Save the deployment info to a file | boolean | `true` | +| `--profile` | The foundry profile to use | string | `local` | +| `--rpc`1 | The RPC URL to use | string | RPC url from `foundry.toml` | +| `--rpcBatch` | Enable batch processing of RPC requests | boolean | `false` | +| `--worldAddress` | Deploy to an existing World at the given address | string | Empty, deploy new `World` | +| `--srcDir` | Source directory | string | Foundry `src` directory | +| `--skipBuild` | Skip rebuilding the contracts before deploying | boolean | `false` | +| `--alwaysRunPostDeploy` | Run `PostDeploy.s.sol` after each deploy | boolean | `false` (run the script only when deploying a new `World`) | +| `--help` | Show help | boolean | `false` | +| `--version` | Show version number | boolean | `false` | +| `--forgeScriptOptions` | Command line options for forge | string | empty | +| `--indexerUrl` | If provided, read records from the indexer instead of fetching logs from the RPC | string | empty | (1) The hostname `localhost` may not work. If that is the case, use `127.0.0.1` instead. From 1e092407da570f8ba52e89e73dda50cdff161a93 Mon Sep 17 00:00:00 2001 From: Jackie Xu Date: Mon, 6 Jan 2025 17:09:01 +0100 Subject: [PATCH 2/2] feat(cli): fetch world deploy block number if available (#3417) Co-authored-by: alvrs --- .changeset/curly-apples-bake.md | 5 +++++ packages/cli/src/deploy/deploy.ts | 8 +++++++- packages/cli/src/deploy/getWorldDeploy.ts | 17 ++++++++++++----- packages/cli/src/runDeploy.ts | 22 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 .changeset/curly-apples-bake.md diff --git a/.changeset/curly-apples-bake.md b/.changeset/curly-apples-bake.md new file mode 100644 index 0000000000..4f2a8075f7 --- /dev/null +++ b/.changeset/curly-apples-bake.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/cli": patch +--- + +When upgrading an existing world, the deployer now attempts to read the deploy block number from the `worlds.json` file. If it is found, the `HelloWorld` and `HelloStore` event are fetched from this block instead of searching for the events starting from the genesis block. diff --git a/packages/cli/src/deploy/deploy.ts b/packages/cli/src/deploy/deploy.ts index 6d6e7c427a..d2d00f3906 100644 --- a/packages/cli/src/deploy/deploy.ts +++ b/packages/cli/src/deploy/deploy.ts @@ -38,6 +38,11 @@ type DeployOptions = { artifacts: readonly ContractArtifact[]; salt?: Hex; worldAddress?: Address; + /** + * Block number of an existing world deployment. + * Only used if `worldAddress` is provided. + */ + worldDeployBlock?: bigint; /** * Address of determinstic deployment proxy: https://github.com/Arachnid/deterministic-deployment-proxy * By default, we look for a deployment at 0x4e59b44847b379578588920ca78fbf26c0b4956c and, if not, deploy one. @@ -64,6 +69,7 @@ export async function deploy({ artifacts, salt, worldAddress: existingWorldAddress, + worldDeployBlock, deployerAddress: initialDeployerAddress, indexerUrl, chainId, @@ -71,7 +77,7 @@ export async function deploy({ const deployerAddress = initialDeployerAddress ?? (await ensureDeployer(client)); const worldDeploy = existingWorldAddress - ? await getWorldDeploy(client, existingWorldAddress) + ? await getWorldDeploy(client, existingWorldAddress, worldDeployBlock) : config.deploy.customWorld ? await deployCustomWorld({ client, diff --git a/packages/cli/src/deploy/getWorldDeploy.ts b/packages/cli/src/deploy/getWorldDeploy.ts index 86982740ff..5259a7488f 100644 --- a/packages/cli/src/deploy/getWorldDeploy.ts +++ b/packages/cli/src/deploy/getWorldDeploy.ts @@ -7,7 +7,11 @@ import { logsToWorldDeploy } from "./logsToWorldDeploy"; const deploys = new Map(); -export async function getWorldDeploy(client: Client, worldAddress: Address): Promise { +export async function getWorldDeploy( + client: Client, + worldAddress: Address, + deployBlock?: bigint, +): Promise { const address = getAddress(worldAddress); let deploy = deploys.get(address); @@ -20,10 +24,9 @@ export async function getWorldDeploy(client: Client, worldAddress: Address): Pro debug("looking up world deploy for", address); - const [fromBlock, toBlock] = await Promise.all([ - getBlock(client, { blockTag: "earliest" }), - getBlock(client, { blockTag: "latest" }), - ]); + const [fromBlock, toBlock] = deployBlock + ? [{ number: deployBlock }, { number: deployBlock }] + : await Promise.all([getBlock(client, { blockTag: "earliest" }), getBlock(client, { blockTag: "latest" })]); const blockLogs = await fetchBlockLogs({ publicClient: client, @@ -34,6 +37,10 @@ export async function getWorldDeploy(client: Client, worldAddress: Address): Pro maxBlockRange: 100_000n, }); + if (blockLogs.length === 0) { + throw new Error("could not find `HelloWorld` or `HelloStore` event"); + } + deploy = { ...logsToWorldDeploy(blockLogs.flatMap((block) => block.logs)), stateBlock: toBlock.number, diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 4f66c180d0..b2c1ce4c0d 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -144,6 +144,13 @@ export async function runDeploy(opts: DeployOptions): Promise { const chainId = await getChainId(client); const indexerUrl = opts.indexerUrl ?? defaultChains.find((chain) => chain.id === chainId)?.indexerUrl; + const worldDeployBlock = opts.worldAddress + ? getWorldDeployBlock({ + worldAddress: opts.worldAddress, + worldsFile: config.deploy.worldsFile, + chainId, + }) + : undefined; console.log("Deploying from", client.account.address); @@ -156,6 +163,7 @@ export async function runDeploy(opts: DeployOptions): Promise { deployerAddress: opts.deployerAddress as Hex | undefined, salt, worldAddress: opts.worldAddress as Hex | undefined, + worldDeployBlock, client, tables, systems, @@ -215,3 +223,17 @@ export async function runDeploy(opts: DeployOptions): Promise { return worldDeploy; } + +function getWorldDeployBlock({ + chainId, + worldAddress, + worldsFile, +}: { + chainId: number; + worldAddress: string; + worldsFile: string; +}): bigint | undefined { + const deploys = existsSync(worldsFile) ? JSON.parse(readFileSync(worldsFile, "utf-8")) : {}; + const worldDeployBlock = deploys[chainId]?.address === worldAddress ? deploys[chainId].blockNumber : undefined; + return worldDeployBlock ? BigInt(worldDeployBlock) : undefined; +}