Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(cli): move deploy utils to common #3421

Merged
merged 5 commits into from
Jan 7, 2025
Merged
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
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);
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
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
Loading