From c8ecf8cb159b519203b33f63baac72ac6b9273bd Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 5 Sep 2024 15:07:14 +0000 Subject: [PATCH 1/6] wip: enable automine during deploy --- packages/cli/src/runDeploy.ts | 18 +++++++- packages/cli/src/utils/getBlockTime.ts | 36 ++++++++++++++++ packages/cli/src/utils/miningMode.ts | 57 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/utils/getBlockTime.ts create mode 100644 packages/cli/src/utils/miningMode.ts diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 1dc7be3cb8..8873a84fc4 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -16,6 +16,8 @@ import { WorldDeploy } from "./deploy/common"; import { build } from "./build"; import { kmsKeyToAccount } from "@latticexyz/common/kms"; import { configToModules } from "./deploy/configToModules"; +import { anvil } from "viem/chains"; +import { MiningMode, getMiningMode, setMiningMode } from "./utils/miningMode"; export const deployOptions = { configPath: { type: "string", desc: "Path to the MUD config file" }, @@ -131,6 +133,15 @@ export async function runDeploy(opts: DeployOptions): Promise { account, }); + // If we're on a local anvil chain, attempt to enable automine for the duration of the deploy to speed up + const chainId = await getChainId(client); + let prevMiningMode: MiningMode | undefined = undefined; + if (chainId === anvil.id) { + console.log("Enabling automine during deploy"); + prevMiningMode = await getMiningMode(rpc); + await setMiningMode(rpc, { type: "automine" }); + } + console.log("Deploying from", client.account.address); const startTime = Date.now(); @@ -157,13 +168,18 @@ export async function runDeploy(opts: DeployOptions): Promise { } console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds")); + // Reset mining mode after deploy + if (chainId === anvil.id && prevMiningMode?.type === "interval") { + await setMiningMode(rpc, prevMiningMode); + console.log("Mining mode reset to previous state"); + } + const deploymentInfo = { worldAddress: worldDeploy.address, blockNumber: Number(worldDeploy.deployBlock), }; if (opts.saveDeployment) { - const chainId = await getChainId(client); const deploysDir = path.join(config.deploy.deploysDirectory, chainId.toString()); mkdirSync(deploysDir, { recursive: true }); writeFileSync(path.join(deploysDir, "latest.json"), JSON.stringify(deploymentInfo, null, 2)); diff --git a/packages/cli/src/utils/getBlockTime.ts b/packages/cli/src/utils/getBlockTime.ts new file mode 100644 index 0000000000..29ba7cb030 --- /dev/null +++ b/packages/cli/src/utils/getBlockTime.ts @@ -0,0 +1,36 @@ +export async function getBlockTime(rpc: string): Promise { + async function getBlock(blockNumber: number | string) { + const response = await fetch(rpc, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [blockNumber, false], + id: 1, + }), + }); + const data = await response.json(); + console.log("got block", data); + return data.result; + } + + // Get the latest block + const latestBlock = await getBlock("latest"); + const latestBlockNumber = parseInt(latestBlock.number, 16); + const latestBlockTimestamp = parseInt(latestBlock.timestamp, 16); + + // Get the previous block + const previousBlock = await getBlock(latestBlockNumber - 1); + const previousBlockTimestamp = parseInt(previousBlock.timestamp, 16); + + // Calculate the block time + const blockTime = latestBlockTimestamp - previousBlockTimestamp; + + console.log(`Current block time: ${blockTime} seconds`); + console.log(`Latest block number: ${latestBlockNumber}`); + console.log(`Latest block timestamp: ${latestBlockTimestamp}`); + console.log(`Previous block timestamp: ${previousBlockTimestamp}`); + + return blockTime; +} diff --git a/packages/cli/src/utils/miningMode.ts b/packages/cli/src/utils/miningMode.ts new file mode 100644 index 0000000000..c1c315f37f --- /dev/null +++ b/packages/cli/src/utils/miningMode.ts @@ -0,0 +1,57 @@ +import { getBlockTime } from "./getBlockTime"; + +export type MiningMode = + | { + type: "automine"; + } + | { + type: "interval"; + blockTime: number; + }; + +export function setMiningMode(rpcUrl: string, miningMode: MiningMode) { + const payload = + miningMode.type === "automine" + ? { + jsonrpc: "2.0", + method: "evm_setAutomine", + params: [true], + id: 1, + } + : { + jsonrpc: "2.0", + method: "evm_setIntervalMining", + params: [miningMode.blockTime], + id: 1, + }; + + return fetch(rpcUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); +} + +export async function getMiningMode(rpcUrl: string): Promise { + const { result: isAutomine } = await fetch(rpcUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "anvil_getAutomine", + params: [], + id: 1, + }), + }).then((res) => res.json()); + + if (isAutomine) { + return { type: "automine" }; + } + + const blockTime = await getBlockTime(rpcUrl); + return { type: "interval", blockTime }; +} From 4aab55258e6dfc5ba5484587b32d30880226c297 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 5 Sep 2024 18:15:20 +0000 Subject: [PATCH 2/6] make it more elegant --- packages/cli/src/runDeploy.ts | 20 ++--- packages/cli/src/utils/enableAutomine.ts | 107 +++++++++++++++++++++++ packages/cli/src/utils/getBlockTime.ts | 36 -------- packages/cli/src/utils/miningMode.ts | 57 ------------ 4 files changed, 112 insertions(+), 108 deletions(-) create mode 100644 packages/cli/src/utils/enableAutomine.ts delete mode 100644 packages/cli/src/utils/getBlockTime.ts delete mode 100644 packages/cli/src/utils/miningMode.ts diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 8873a84fc4..75abc72522 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -16,8 +16,7 @@ import { WorldDeploy } from "./deploy/common"; import { build } from "./build"; import { kmsKeyToAccount } from "@latticexyz/common/kms"; import { configToModules } from "./deploy/configToModules"; -import { anvil } from "viem/chains"; -import { MiningMode, getMiningMode, setMiningMode } from "./utils/miningMode"; +import { enableAutomine } from "./utils/enableAutomine"; export const deployOptions = { configPath: { type: "string", desc: "Path to the MUD config file" }, @@ -133,17 +132,10 @@ export async function runDeploy(opts: DeployOptions): Promise { account, }); - // If we're on a local anvil chain, attempt to enable automine for the duration of the deploy to speed up - const chainId = await getChainId(client); - let prevMiningMode: MiningMode | undefined = undefined; - if (chainId === anvil.id) { - console.log("Enabling automine during deploy"); - prevMiningMode = await getMiningMode(rpc); - await setMiningMode(rpc, { type: "automine" }); - } + // Attempt to enable automine for the duration of the deploy. Noop if automine is not available. + const { reset: resetMiningMode } = await enableAutomine(rpc); console.log("Deploying from", client.account.address); - const startTime = Date.now(); const worldDeploy = await deploy({ deployerAddress: opts.deployerAddress as Hex | undefined, @@ -169,10 +161,7 @@ export async function runDeploy(opts: DeployOptions): Promise { console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds")); // Reset mining mode after deploy - if (chainId === anvil.id && prevMiningMode?.type === "interval") { - await setMiningMode(rpc, prevMiningMode); - console.log("Mining mode reset to previous state"); - } + resetMiningMode(); const deploymentInfo = { worldAddress: worldDeploy.address, @@ -180,6 +169,7 @@ export async function runDeploy(opts: DeployOptions): Promise { }; if (opts.saveDeployment) { + const chainId = await getChainId(client); const deploysDir = path.join(config.deploy.deploysDirectory, chainId.toString()); mkdirSync(deploysDir, { recursive: true }); writeFileSync(path.join(deploysDir, "latest.json"), JSON.stringify(deploymentInfo, null, 2)); diff --git a/packages/cli/src/utils/enableAutomine.ts b/packages/cli/src/utils/enableAutomine.ts new file mode 100644 index 0000000000..ca3d77ff04 --- /dev/null +++ b/packages/cli/src/utils/enableAutomine.ts @@ -0,0 +1,107 @@ +import { debug } from "../debug"; + +type MiningMode = + | { + type: "automine"; + } + | { + type: "interval"; + blockTime: number; + }; + +export type EnableAutomineResult = { reset: () => Promise }; + +export async function enableAutomine(rpcUrl: string): Promise { + try { + debug("Enabling automine"); + const prevMiningMode = await getMiningMode(rpcUrl); + await setMiningMode(rpcUrl, { type: "automine" }); + return { + reset: () => { + debug("Disabling automine"); + return setMiningMode(rpcUrl, prevMiningMode); + }, + }; + } catch (e) { + debug("Skipping automine"); + return { reset: async () => void 0 }; + } +} + +async function getMiningMode(rpcUrl: string): Promise { + const { result: isAutomine } = await fetch(rpcUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "anvil_getAutomine", + params: [], + id: 1, + }), + }).then((res) => res.json()); + + if (isAutomine) { + return { type: "automine" }; + } + + const blockTime = await getBlockTime(rpcUrl); + return { type: "interval", blockTime }; +} + +async function setMiningMode(rpcUrl: string, miningMode: MiningMode): Promise { + const payload = + miningMode.type === "automine" + ? { + jsonrpc: "2.0", + method: "evm_setAutomine", + params: [true], + id: 1, + } + : { + jsonrpc: "2.0", + method: "evm_setIntervalMining", + params: [miningMode.blockTime], + id: 1, + }; + + await fetch(rpcUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); +} + +async function getBlockTime(rpc: string): Promise { + async function getBlock(blockNumber: number | string) { + const response = await fetch(rpc, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [blockNumber, false], + id: 1, + }), + }); + const data = await response.json(); + return data.result; + } + + // Get the latest block + const latestBlock = await getBlock("latest"); + const latestBlockNumber = parseInt(latestBlock.number, 16); + const latestBlockTimestamp = parseInt(latestBlock.timestamp, 16); + + // Get the previous block + const previousBlock = await getBlock(latestBlockNumber - 1); + const previousBlockTimestamp = parseInt(previousBlock.timestamp, 16); + + // Calculate the block time + const blockTime = latestBlockTimestamp - previousBlockTimestamp; + + return blockTime; +} diff --git a/packages/cli/src/utils/getBlockTime.ts b/packages/cli/src/utils/getBlockTime.ts deleted file mode 100644 index 29ba7cb030..0000000000 --- a/packages/cli/src/utils/getBlockTime.ts +++ /dev/null @@ -1,36 +0,0 @@ -export async function getBlockTime(rpc: string): Promise { - async function getBlock(blockNumber: number | string) { - const response = await fetch(rpc, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "eth_getBlockByNumber", - params: [blockNumber, false], - id: 1, - }), - }); - const data = await response.json(); - console.log("got block", data); - return data.result; - } - - // Get the latest block - const latestBlock = await getBlock("latest"); - const latestBlockNumber = parseInt(latestBlock.number, 16); - const latestBlockTimestamp = parseInt(latestBlock.timestamp, 16); - - // Get the previous block - const previousBlock = await getBlock(latestBlockNumber - 1); - const previousBlockTimestamp = parseInt(previousBlock.timestamp, 16); - - // Calculate the block time - const blockTime = latestBlockTimestamp - previousBlockTimestamp; - - console.log(`Current block time: ${blockTime} seconds`); - console.log(`Latest block number: ${latestBlockNumber}`); - console.log(`Latest block timestamp: ${latestBlockTimestamp}`); - console.log(`Previous block timestamp: ${previousBlockTimestamp}`); - - return blockTime; -} diff --git a/packages/cli/src/utils/miningMode.ts b/packages/cli/src/utils/miningMode.ts deleted file mode 100644 index c1c315f37f..0000000000 --- a/packages/cli/src/utils/miningMode.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { getBlockTime } from "./getBlockTime"; - -export type MiningMode = - | { - type: "automine"; - } - | { - type: "interval"; - blockTime: number; - }; - -export function setMiningMode(rpcUrl: string, miningMode: MiningMode) { - const payload = - miningMode.type === "automine" - ? { - jsonrpc: "2.0", - method: "evm_setAutomine", - params: [true], - id: 1, - } - : { - jsonrpc: "2.0", - method: "evm_setIntervalMining", - params: [miningMode.blockTime], - id: 1, - }; - - return fetch(rpcUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(payload), - }); -} - -export async function getMiningMode(rpcUrl: string): Promise { - const { result: isAutomine } = await fetch(rpcUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "anvil_getAutomine", - params: [], - id: 1, - }), - }).then((res) => res.json()); - - if (isAutomine) { - return { type: "automine" }; - } - - const blockTime = await getBlockTime(rpcUrl); - return { type: "interval", blockTime }; -} From c8a6c8862fb127570a60cabba5db9f3e7a3977d8 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 5 Sep 2024 18:22:49 +0000 Subject: [PATCH 3/6] readd console log --- packages/cli/src/runDeploy.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 75abc72522..d5b6e2a6ca 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -163,6 +163,8 @@ export async function runDeploy(opts: DeployOptions): Promise { // Reset mining mode after deploy resetMiningMode(); + console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds")); + const deploymentInfo = { worldAddress: worldDeploy.address, blockNumber: Number(worldDeploy.deployBlock), From 8cd44397c30ff54d0e623a898823d727d08b6ad3 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 5 Sep 2024 22:18:14 +0000 Subject: [PATCH 4/6] self-review --- packages/cli/src/runDeploy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index d5b6e2a6ca..0cf7303529 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -132,10 +132,11 @@ export async function runDeploy(opts: DeployOptions): Promise { account, }); + console.log("Deploying from", client.account.address); + // Attempt to enable automine for the duration of the deploy. Noop if automine is not available. const { reset: resetMiningMode } = await enableAutomine(rpc); - console.log("Deploying from", client.account.address); const startTime = Date.now(); const worldDeploy = await deploy({ deployerAddress: opts.deployerAddress as Hex | undefined, @@ -158,7 +159,6 @@ export async function runDeploy(opts: DeployOptions): Promise { opts.kms ? true : false, ); } - console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds")); // Reset mining mode after deploy resetMiningMode(); From 81498cc9b55e2788d947b4f52fe5e0111ee2c119 Mon Sep 17 00:00:00 2001 From: alvarius Date: Thu, 5 Sep 2024 23:21:42 +0100 Subject: [PATCH 5/6] Create real-pianos-yell.md --- .changeset/real-pianos-yell.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/real-pianos-yell.md diff --git a/.changeset/real-pianos-yell.md b/.changeset/real-pianos-yell.md new file mode 100644 index 0000000000..130da6a63b --- /dev/null +++ b/.changeset/real-pianos-yell.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/cli": patch +--- + +Speed up deployment in development by temporarily enabling automine mode for the duration of the deployment. From ad2d4a4515c2c8649caa6430833fece2d971901d Mon Sep 17 00:00:00 2001 From: alvrs Date: Fri, 6 Sep 2024 16:32:04 +0000 Subject: [PATCH 6/6] use viem --- packages/cli/src/runDeploy.ts | 4 +- packages/cli/src/utils/enableAutomine.ts | 96 ++++++------------------ 2 files changed, 26 insertions(+), 74 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 0cf7303529..09566279bd 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -135,7 +135,7 @@ export async function runDeploy(opts: DeployOptions): Promise { console.log("Deploying from", client.account.address); // Attempt to enable automine for the duration of the deploy. Noop if automine is not available. - const { reset: resetMiningMode } = await enableAutomine(rpc); + const { reset: resetMiningMode } = await enableAutomine(client); const startTime = Date.now(); const worldDeploy = await deploy({ @@ -161,7 +161,7 @@ export async function runDeploy(opts: DeployOptions): Promise { } // Reset mining mode after deploy - resetMiningMode(); + await resetMiningMode(); console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds")); diff --git a/packages/cli/src/utils/enableAutomine.ts b/packages/cli/src/utils/enableAutomine.ts index ca3d77ff04..15c7a2e498 100644 --- a/packages/cli/src/utils/enableAutomine.ts +++ b/packages/cli/src/utils/enableAutomine.ts @@ -1,4 +1,7 @@ -import { debug } from "../debug"; +import { getAutomine, getBlock, setAutomine, setIntervalMining } from "viem/actions"; +import { debug, error } from "../debug"; +import { Client } from "viem"; +import { getAction } from "viem/utils"; type MiningMode = | { @@ -11,97 +14,46 @@ type MiningMode = export type EnableAutomineResult = { reset: () => Promise }; -export async function enableAutomine(rpcUrl: string): Promise { +export async function enableAutomine(client: Client): Promise { try { debug("Enabling automine"); - const prevMiningMode = await getMiningMode(rpcUrl); - await setMiningMode(rpcUrl, { type: "automine" }); + const prevMiningMode = await getMiningMode(client); + await setMiningMode(client, { type: "automine" }); return { reset: () => { debug("Disabling automine"); - return setMiningMode(rpcUrl, prevMiningMode); + return setMiningMode(client, prevMiningMode); }, }; } catch (e) { debug("Skipping automine"); + error(e); return { reset: async () => void 0 }; } } -async function getMiningMode(rpcUrl: string): Promise { - const { result: isAutomine } = await fetch(rpcUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "anvil_getAutomine", - params: [], - id: 1, - }), - }).then((res) => res.json()); - +async function getMiningMode(client: Client): Promise { + const localClient = { mode: "anvil", ...client }; // set default mode to "anvil", potential error is caught by enableAutomine + const isAutomine = await getAction(localClient, getAutomine, "getAutomine")({}); if (isAutomine) { return { type: "automine" }; } - const blockTime = await getBlockTime(rpcUrl); + const blockTime = await getBlockTime(client); return { type: "interval", blockTime }; } -async function setMiningMode(rpcUrl: string, miningMode: MiningMode): Promise { - const payload = - miningMode.type === "automine" - ? { - jsonrpc: "2.0", - method: "evm_setAutomine", - params: [true], - id: 1, - } - : { - jsonrpc: "2.0", - method: "evm_setIntervalMining", - params: [miningMode.blockTime], - id: 1, - }; - - await fetch(rpcUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(payload), - }); -} - -async function getBlockTime(rpc: string): Promise { - async function getBlock(blockNumber: number | string) { - const response = await fetch(rpc, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "eth_getBlockByNumber", - params: [blockNumber, false], - id: 1, - }), - }); - const data = await response.json(); - return data.result; +async function setMiningMode(client: Client, miningMode: MiningMode): Promise { + if (miningMode.type === "automine") { + await getAction(client, setAutomine, "setAutomine")(true); + } else { + await getAction(client, setIntervalMining, "setIntervalMining")({ interval: miningMode.blockTime }); } +} - // Get the latest block - const latestBlock = await getBlock("latest"); - const latestBlockNumber = parseInt(latestBlock.number, 16); - const latestBlockTimestamp = parseInt(latestBlock.timestamp, 16); - - // Get the previous block - const previousBlock = await getBlock(latestBlockNumber - 1); - const previousBlockTimestamp = parseInt(previousBlock.timestamp, 16); - - // Calculate the block time - const blockTime = latestBlockTimestamp - previousBlockTimestamp; - - return blockTime; +async function getBlockTime(client: Client): Promise { + const latestBlock = await getAction(client, getBlock, "getBlock")({ blockTag: "latest" }); + const previousBlock = await getAction(client, getBlock, "getBlock")({ blockNumber: latestBlock.number - 1n }); + const blockTime = latestBlock.timestamp - previousBlock.timestamp; + return Number(blockTime); }