Skip to content

Commit

Permalink
refactor(cli): move deploy utils to common (#3421)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored Jan 7, 2025
1 parent 1a2b3c8 commit f2c69f4
Show file tree
Hide file tree
Showing 25 changed files with 101 additions and 77 deletions.
7 changes: 1 addition & 6 deletions packages/cli/src/deploy/common.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { Abi, Account, Address, Chain, Client, Hex, Transport, padHex } from "viem";
import { Abi, Account, Address, Chain, Client, Hex, Transport } from "viem";
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json" assert { type: "json" };
import { helloStoreEvent } from "@latticexyz/store";
import { helloWorldEvent } from "@latticexyz/world";
import { LibraryMap } from "./getLibraryMap";

export const salt = padHex("0x", { size: 32 });

// https://eips.ethereum.org/EIPS/eip-170
export const contractSizeLimit = parseInt("6000", 16);

export const worldDeployEvents = [helloStoreEvent, helloWorldEvent] as const;

export const worldAbi = IBaseWorldAbi;
Expand Down
17 changes: 11 additions & 6 deletions packages/cli/src/deploy/createPrepareDeploy.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import { DeterministicContract, LibraryPlaceholder, salt } from "./common";
import { DeterministicContract, LibraryPlaceholder } from "./common";
import { spliceHex } from "@latticexyz/common";
import { Hex, getCreate2Address, Address } from "viem";
import { Hex, Address } from "viem";
import { LibraryMap } from "./getLibraryMap";
import { getContractAddress } from "@latticexyz/common/internal";

export function createPrepareDeploy(
bytecodeWithPlaceholders: Hex,
placeholders: readonly LibraryPlaceholder[],
): DeterministicContract["prepareDeploy"] {
return function prepareDeploy(deployer: Address, libraryMap?: LibraryMap) {
return function prepareDeploy(deployerAddress: Address, libraryMap?: LibraryMap) {
let bytecode = bytecodeWithPlaceholders;

if (placeholders.length === 0) {
return { bytecode, address: getCreate2Address({ from: deployer, bytecode, salt }) };
return { bytecode, address: getContractAddress({ deployerAddress, bytecode }) };
}

if (!libraryMap) {
throw new Error("Libraries must be provided if there are placeholders");
}

for (const placeholder of placeholders) {
const address = libraryMap.getAddress({ name: placeholder.name, path: placeholder.path, deployer });
const address = libraryMap.getAddress({
name: placeholder.name,
path: placeholder.path,
deployer: deployerAddress,
});
bytecode = spliceHex(bytecode, placeholder.start, placeholder.length, address);
}
return {
bytecode,
address: getCreate2Address({ from: deployer, bytecode, salt }),
address: getContractAddress({ deployerAddress, bytecode }),
};
};
}
4 changes: 1 addition & 3 deletions packages/cli/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Address, Hex, stringToHex } from "viem";
import { ensureDeployer } from "./ensureDeployer";
import { deployWorld } from "./deployWorld";
import { ensureTables } from "./ensureTables";
import {
Expand All @@ -18,16 +17,15 @@ import { ensureModules } from "./ensureModules";
import { ensureNamespaceOwner } from "./ensureNamespaceOwner";
import { debug } from "./debug";
import { resourceToHex, resourceToLabel } from "@latticexyz/common";
import { ensureContractsDeployed } from "./ensureContractsDeployed";
import { randomBytes } from "crypto";
import { Table } from "@latticexyz/config";
import { ensureResourceTags } from "./ensureResourceTags";
import { waitForTransactions } from "./waitForTransactions";
import { ContractArtifact } from "@latticexyz/world/node";
import { World } from "@latticexyz/world";
import { deployCustomWorld } from "./deployCustomWorld";
import { uniqueBy } from "@latticexyz/common/utils";
import { getLibraryMap } from "./getLibraryMap";
import { ensureContractsDeployed, ensureDeployer, waitForTransactions } from "@latticexyz/common/internal";

type DeployOptions = {
config: World;
Expand Down
12 changes: 5 additions & 7 deletions packages/cli/src/deploy/deployCustomWorld.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Account, Chain, Client, Hex, Transport, concatHex, encodeDeployData, getCreate2Address, isHex } from "viem";
import { Account, Chain, Client, Hex, Transport, concatHex, encodeDeployData, isHex } from "viem";
import { waitForTransactionReceipt } from "viem/actions";
import { resourceToHex, sendTransaction, writeContract } from "@latticexyz/common";
import { debug } from "./debug";
import { logsToWorldDeploy } from "./logsToWorldDeploy";
import { WorldDeploy, salt, worldAbi } from "./common";
import { WorldDeploy, worldAbi } from "./common";
import { getWorldContracts } from "./getWorldContracts";
import { ensureContractsDeployed } from "./ensureContractsDeployed";
import { ensureContractsDeployed, getContractAddress, waitForTransactions } from "@latticexyz/common/internal";
import { ContractArtifact, ReferenceIdentifier } from "@latticexyz/world/node";
import { World } from "@latticexyz/world";
import { waitForTransactions } from "./waitForTransactions";

function findArtifact(ref: ReferenceIdentifier, artifacts: readonly ContractArtifact[]): ContractArtifact {
const artifact = artifacts.find((a) => a.sourcePath === ref.sourcePath && a.name === ref.name);
Expand All @@ -31,9 +30,8 @@ function getDeployable(deployerAddress: Hex, artifact: ContractArtifact, artifac
return concatHex(
artifact.bytecode.map((ref): Hex => {
if (isHex(ref)) return ref;
return getCreate2Address({
from: deployerAddress,
salt,
return getContractAddress({
deployerAddress,
bytecode: getDeployable(deployerAddress, findArtifact(ref, artifacts), artifacts),
});
}),
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/ensureModules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Module, WorldDeploy, worldAbi } from "./common";
import { debug } from "./debug";
import { isDefined } from "@latticexyz/common/utils";
import pRetry from "p-retry";
import { ensureContractsDeployed } from "./ensureContractsDeployed";
import { LibraryMap } from "./getLibraryMap";
import { ensureContractsDeployed } from "@latticexyz/common/internal";

export async function ensureModules({
client,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/ensureResourceTags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { ensureModules } from "./ensureModules";
import metadataModule from "@latticexyz/world-module-metadata/out/MetadataModule.sol/MetadataModule.json" assert { type: "json" };
import { getContractArtifact } from "../utils/getContractArtifact";
import { createPrepareDeploy } from "./createPrepareDeploy";
import { waitForTransactions } from "./waitForTransactions";
import { LibraryMap } from "./getLibraryMap";
import { getKeyTuple, getSchemaPrimitives } from "@latticexyz/protocol-parser/internal";
import { getRecords } from "@latticexyz/store-sync";
import { CommonDeployOptions } from "./common";
import { waitForTransactions } from "@latticexyz/common/internal";

const metadataModuleArtifact = getContractArtifact(metadataModule);

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/ensureSystems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { debug } from "./debug";
import { getSystems } from "./getSystems";
import { getResourceAccess } from "./getResourceAccess";
import pRetry from "p-retry";
import { ensureContractsDeployed } from "./ensureContractsDeployed";
import { LibraryMap } from "./getLibraryMap";
import { ensureContractsDeployed } from "@latticexyz/common/internal";

// TODO: move each system registration+access to batch call to be atomic

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/ensureWorldFactory.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Client, Transport, Chain, Account, Hex, Address } from "viem";
import { ensureContractsDeployed } from "./ensureContractsDeployed";
import { getWorldFactoryContracts } from "./getWorldFactoryContracts";
import { getWorldProxyFactoryContracts } from "./getWorldProxyFactoryContracts";
import { ensureContractsDeployed } from "@latticexyz/common/internal";

export async function ensureWorldFactory(
client: Client<Transport, Chain | undefined, Account>,
Expand Down
23 changes: 10 additions & 13 deletions packages/cli/src/deploy/getWorldContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,33 @@ import batchCallSystemBuild from "@latticexyz/world/out/BatchCallSystem.sol/Batc
import registrationSystemBuild from "@latticexyz/world/out/RegistrationSystem.sol/RegistrationSystem.json" assert { type: "json" };
import initModuleBuild from "@latticexyz/world/out/InitModule.sol/InitModule.json" assert { type: "json" };
import initModuleAbi from "@latticexyz/world/out/InitModule.sol/InitModule.abi.json" assert { type: "json" };
import { Hex, getCreate2Address, encodeDeployData, size } from "viem";
import { salt } from "./common";
import { Hex, encodeDeployData, size } from "viem";
import { getContractAddress } from "@latticexyz/common/internal";

export function getWorldContracts(deployerAddress: Hex) {
const accessManagementSystemDeployedBytecodeSize = size(accessManagementSystemBuild.deployedBytecode.object as Hex);
const accessManagementSystemBytecode = accessManagementSystemBuild.bytecode.object as Hex;
const accessManagementSystem = getCreate2Address({
from: deployerAddress,
const accessManagementSystem = getContractAddress({
deployerAddress,
bytecode: accessManagementSystemBytecode,
salt,
});

const balanceTransferSystemDeployedBytecodeSize = size(balanceTransferSystemBuild.deployedBytecode.object as Hex);
const balanceTransferSystemBytecode = balanceTransferSystemBuild.bytecode.object as Hex;
const balanceTransferSystem = getCreate2Address({
from: deployerAddress,
const balanceTransferSystem = getContractAddress({
deployerAddress,
bytecode: balanceTransferSystemBytecode,
salt,
});

const batchCallSystemDeployedBytecodeSize = size(batchCallSystemBuild.deployedBytecode.object as Hex);
const batchCallSystemBytecode = batchCallSystemBuild.bytecode.object as Hex;
const batchCallSystem = getCreate2Address({ from: deployerAddress, bytecode: batchCallSystemBytecode, salt });
const batchCallSystem = getContractAddress({ deployerAddress, bytecode: batchCallSystemBytecode });

const registrationDeployedBytecodeSize = size(registrationSystemBuild.deployedBytecode.object as Hex);
const registrationBytecode = registrationSystemBuild.bytecode.object as Hex;
const registration = getCreate2Address({
from: deployerAddress,
const registration = getContractAddress({
deployerAddress,
bytecode: registrationBytecode,
salt,
});

const initModuleDeployedBytecodeSize = size(initModuleBuild.deployedBytecode.object as Hex);
Expand All @@ -42,7 +39,7 @@ export function getWorldContracts(deployerAddress: Hex) {
abi: initModuleAbi,
args: [accessManagementSystem, balanceTransferSystem, batchCallSystem, registration],
});
const initModule = getCreate2Address({ from: deployerAddress, bytecode: initModuleBytecode, salt });
const initModule = getContractAddress({ deployerAddress, bytecode: initModuleBytecode });

return {
AccessManagementSystem: {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/deploy/getWorldFactoryContracts.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import worldFactoryBuild from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.json" assert { type: "json" };
import worldFactoryAbi from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.abi.json" assert { type: "json" };
import { Hex, getCreate2Address, encodeDeployData, size } from "viem";
import { salt } from "./common";
import { Hex, encodeDeployData, size } from "viem";
import { getWorldContracts } from "./getWorldContracts";
import { getContractAddress } from "@latticexyz/common/internal";

export function getWorldFactoryContracts(deployerAddress: Hex) {
const worldContracts = getWorldContracts(deployerAddress);
Expand All @@ -13,7 +13,7 @@ export function getWorldFactoryContracts(deployerAddress: Hex) {
abi: worldFactoryAbi,
args: [worldContracts.InitModule.address],
});
const worldFactory = getCreate2Address({ from: deployerAddress, bytecode: worldFactoryBytecode, salt });
const worldFactory = getContractAddress({ deployerAddress, bytecode: worldFactoryBytecode });

return {
...worldContracts,
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/deploy/getWorldProxyFactoryContracts.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import worldProxyFactoryBuild from "@latticexyz/world/out/WorldProxyFactory.sol/WorldProxyFactory.json" assert { type: "json" };
import worldProxyFactoryAbi from "@latticexyz/world/out/WorldProxyFactory.sol/WorldProxyFactory.abi.json" assert { type: "json" };
import { Hex, getCreate2Address, encodeDeployData, size } from "viem";
import { salt } from "./common";
import { Hex, encodeDeployData, size } from "viem";
import { getWorldContracts } from "./getWorldContracts";
import { getContractAddress } from "@latticexyz/common/internal";

export function getWorldProxyFactoryContracts(deployerAddress: Hex) {
const worldContracts = getWorldContracts(deployerAddress);
Expand All @@ -13,7 +13,7 @@ export function getWorldProxyFactoryContracts(deployerAddress: Hex) {
abi: worldProxyFactoryAbi,
args: [worldContracts.InitModule.address],
});
const worldProxyFactory = getCreate2Address({ from: deployerAddress, bytecode: worldProxyFactoryBytecode, salt });
const worldProxyFactory = getContractAddress({ deployerAddress, bytecode: worldProxyFactoryBytecode });

return {
...worldContracts,
Expand Down
16 changes: 7 additions & 9 deletions packages/cli/src/verify.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Chain, Client, Hex, Transport, getCreate2Address, sliceHex, zeroHash } from "viem";
import { Chain, Client, Hex, Transport, sliceHex, zeroHash } from "viem";
import { getWorldFactoryContracts } from "./deploy/getWorldFactoryContracts";
import { verifyContract } from "./verify/verifyContract";
import PQueue from "p-queue";
import { getWorldProxyFactoryContracts } from "./deploy/getWorldProxyFactoryContracts";
import { getDeployer } from "./deploy/getDeployer";
import { MUDError } from "@latticexyz/common/errors";
import { Module, salt } from "./deploy/common";
import { Module } from "./deploy/common";
import { getStorageAt } from "viem/actions";
import { execa } from "execa";
import { getContractAddress, getDeployer } from "@latticexyz/common/internal";

type VerifyOptions = {
client: Client<Transport, Chain | undefined>;
Expand Down Expand Up @@ -58,10 +58,9 @@ export async function verify({
rpc,
verifier,
verifierUrl,
address: getCreate2Address({
from: deployerAddress,
address: getContractAddress({
deployerAddress,
bytecode: bytecode,
salt,
}),
}).catch((error) => {
console.error(`Error verifying system contract ${name}:`, error);
Expand Down Expand Up @@ -93,10 +92,9 @@ export async function verify({
rpc,
verifier,
verifierUrl,
address: getCreate2Address({
from: deployerAddress,
address: getContractAddress({
deployerAddress,
bytecode: bytecode,
salt,
}),
}).catch((error) => {
console.error(`Error verifying world factory contract ${name}:`, error);
Expand Down
4 changes: 4 additions & 0 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"./type-utils": "./dist/type-utils.js",
"./utils": "./dist/utils.js",
"./kms": "./dist/kms.js",
"./internal": "./dist/internal.js",
"./tsconfig.base.json": "./tsconfig.base.json"
},
"typesVersions": {
Expand Down Expand Up @@ -49,6 +50,9 @@
],
"kms": [
"./dist/kms.d.ts"
],
"internal": [
"./dist/internal.d.ts"
]
}
},
Expand Down
7 changes: 7 additions & 0 deletions packages/common/src/deploy/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { stringToHex } from "viem";

// salt for deterministic deploys of singleton contracts
export const singletonSalt = stringToHex("", { size: 32 });

// https://eips.ethereum.org/EIPS/eip-170
export const contractSizeLimit = parseInt("6000", 16);
File renamed without changes.
10 changes: 10 additions & 0 deletions packages/common/src/deploy/debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { debug as parentDebug } from "../debug";

export const debug = parentDebug.extend("deploy");
export const error = parentDebug.extend("deploy");

// Pipe debug output to stdout instead of stderr
debug.log = console.debug.bind(console);

// Pipe error output to stderr
error.log = console.error.bind(console);
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Client, Transport, Chain, Account, concatHex, getCreate2Address, Hex } from "viem";
import { getBytecode } from "viem/actions";
import { contractSizeLimit, salt } from "./common";
import { sendTransaction } from "@latticexyz/common";
import { getCode } from "viem/actions";
import { contractSizeLimit, singletonSalt } from "./common";
import { debug } from "./debug";
import pRetry from "p-retry";
import { sendTransaction } from "../sendTransaction";

export type Contract = {
bytecode: Hex;
Expand All @@ -25,9 +24,9 @@ export async function ensureContract({
throw new Error(`Found unlinked public library in ${debugLabel} bytecode`);
}

const address = getCreate2Address({ from: deployerAddress, salt, bytecode });
const address = getCreate2Address({ from: deployerAddress, salt: singletonSalt, bytecode });

const contractCode = await getBytecode(client, { address, blockTag: "pending" });
const contractCode = await getCode(client, { address, blockTag: "pending" });
if (contractCode) {
debug("found", debugLabel, "at", address);
return [];
Expand All @@ -48,17 +47,10 @@ export async function ensureContract({

debug("deploying", debugLabel, "at", address);
return [
await pRetry(
() =>
sendTransaction(client, {
chain: client.chain ?? null,
to: deployerAddress,
data: concatHex([salt, bytecode]),
}),
{
retries: 3,
onFailedAttempt: () => debug(`failed to deploy ${debugLabel}, retrying...`),
},
),
await sendTransaction(client, {
chain: client.chain ?? null,
to: deployerAddress,
data: concatHex([singletonSalt, bytecode]),
}),
];
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Client, Transport, Chain, Account, Hex } from "viem";
import { Contract, ensureContract } from "./ensureContract";
import { uniqueBy } from "@latticexyz/common/utils";
import { waitForTransactions } from "./waitForTransactions";
import { waitForTransactions } from "../waitForTransactions";
import { uniqueBy } from "../utils/uniqueBy";

export async function ensureContractsDeployed({
client,
Expand Down
File renamed without changes.
12 changes: 12 additions & 0 deletions packages/common/src/deploy/getContractAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Hex, getCreate2Address } from "viem";
import { singletonSalt } from "./common";

export function getContractAddress({
deployerAddress,
bytecode,
}: {
readonly deployerAddress: Hex;
readonly bytecode: Hex;
}): Hex {
return getCreate2Address({ from: deployerAddress, bytecode, salt: singletonSalt });
}
Loading

0 comments on commit f2c69f4

Please sign in to comment.