Skip to content
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
8 changes: 8 additions & 0 deletions .changeset/twelve-melons-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@latticexyz/cli": patch
"@latticexyz/common": patch
---

The `mud deploy` command now includes the addresses of all deployed contracts and libraries in the deployment file. Previously, it only included the world address.

The `mud test` command now includes an optional 'saveDeployment' flag to enable the deployment info from the test run to be saved to a file.
2 changes: 1 addition & 1 deletion packages/cli/src/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const testOptions = {
"Address of an existing world contract. If provided, deployment is skipped and the RPC provided in the foundry.toml is used for fork testing.",
},
forgeOptions: { type: "string", description: "Options to pass to forge test" },
saveDeployment: { type: "boolean", desc: "Save the deployment info to a file", default: false },
} as const satisfies Record<string, Options>;

type TestOptions = InferredOptionTypes<typeof testOptions>;
Expand Down Expand Up @@ -45,7 +46,6 @@ const commandModule: CommandModule<typeof testOptions, TestOptions> = {
(
await runDeploy({
...opts,
saveDeployment: false,
rpc: forkRpc,
})
).address;
Expand Down
20 changes: 17 additions & 3 deletions packages/cli/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,15 @@ export async function deploy({
deployerAddress: initialDeployerAddress,
indexerUrl,
chainId,
}: DeployOptions): Promise<WorldDeploy> {
}: DeployOptions): Promise<
WorldDeploy & {
/** Addresses of the deployed contracts */
readonly contracts: readonly {
readonly label: string;
readonly address: Address;
}[];
}
> {
const deployerAddress = initialDeployerAddress ?? (await ensureDeployer(client));

const worldDeploy = existingWorldAddress
Expand Down Expand Up @@ -105,7 +113,7 @@ export async function deploy({
}

const libraryMap = getLibraryMap(libraries);
await ensureContractsDeployed({
const deployedContracts = await ensureContractsDeployed({
...commonDeployOptions,
deployerAddress,
contracts: [
Expand Down Expand Up @@ -203,5 +211,11 @@ export async function deploy({
});

debug("deploy complete");
return worldDeploy;
return {
...worldDeploy,
contracts: deployedContracts.map(({ contract, deployedAddress }) => ({
label: contract.debugLabel ?? "unknown",
address: deployedAddress,
})),
};
}
13 changes: 7 additions & 6 deletions packages/cli/src/runDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export async function runDeploy(opts: DeployOptions): Promise<WorldDeploy> {
const automine = await enableAutomine(client);

const startTime = Date.now();
const worldDeploy = await deploy({
const deployResult = await deploy({
config,
deployerAddress: opts.deployerAddress as Hex | undefined,
salt,
Expand All @@ -176,7 +176,7 @@ export async function runDeploy(opts: DeployOptions): Promise<WorldDeploy> {
if (opts.worldAddress == null || opts.alwaysRunPostDeploy) {
await postDeploy(
config.deploy.postDeployScript,
worldDeploy.address,
deployResult.address,
rpc,
profile,
opts.forgeScriptOptions,
Expand All @@ -190,8 +190,9 @@ export async function runDeploy(opts: DeployOptions): Promise<WorldDeploy> {
console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds"));

const deploymentInfo = {
worldAddress: worldDeploy.address,
blockNumber: Number(worldDeploy.deployBlock),
worldAddress: deployResult.address,
blockNumber: Number(deployResult.deployBlock),
contracts: deployResult.contracts,
};

if (opts.saveDeployment) {
Expand Down Expand Up @@ -219,9 +220,9 @@ export async function runDeploy(opts: DeployOptions): Promise<WorldDeploy> {
);
}

console.log(deploymentInfo);
console.log({ worldAddress: deploymentInfo.worldAddress, blockNumber: deploymentInfo.blockNumber });

return worldDeploy;
return deployResult;
}

function getWorldDeployBlock({
Expand Down
32 changes: 23 additions & 9 deletions packages/common/src/deploy/ensureContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export type Contract = {
salt?: Hex;
};

export type EnsureContractResult = {
contract: Contract;
deployedAddress: Hex;
txHash: readonly Hex[];
};

export async function ensureContract({
client,
deployerAddress,
Expand All @@ -21,7 +27,7 @@ export async function ensureContract({
}: {
readonly client: Client<Transport, Chain | undefined, Account>;
readonly deployerAddress: Hex;
} & Contract): Promise<readonly Hex[]> {
} & Contract): Promise<EnsureContractResult> {
if (bytecode.includes("__$")) {
throw new Error(`Found unlinked public library in ${debugLabel} bytecode`);
}
Expand All @@ -31,7 +37,11 @@ export async function ensureContract({
const contractCode = await getCode(client, { address, blockTag: "pending" });
if (contractCode) {
debug("found", debugLabel, "at", address);
return [];
return {
contract: { bytecode, deployedBytecodeSize, debugLabel, salt },
deployedAddress: address,
txHash: [],
};
}

if (deployedBytecodeSize != null) {
Expand All @@ -52,11 +62,15 @@ export async function ensureContract({
}

debug("deploying", debugLabel, "at", address);
return [
await sendTransaction(client, {
chain: client.chain ?? null,
to: deployerAddress,
data: concatHex([salt, bytecode]),
}),
];
return {
contract: { bytecode, deployedBytecodeSize, debugLabel, salt },
deployedAddress: address,
txHash: [
await sendTransaction(client, {
chain: client.chain ?? null,
to: deployerAddress,
data: concatHex([salt, bytecode]),
}),
],
};
}
10 changes: 5 additions & 5 deletions packages/common/src/deploy/ensureContractsDeployed.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Client, Transport, Chain, Account, Hex } from "viem";
import { Contract, ensureContract } from "./ensureContract";
import { Contract, ensureContract, EnsureContractResult } from "./ensureContract";
import { waitForTransactions } from "../waitForTransactions";
import { uniqueBy } from "../utils/uniqueBy";

Expand All @@ -11,19 +11,19 @@ export async function ensureContractsDeployed({
readonly client: Client<Transport, Chain | undefined, Account>;
readonly deployerAddress: Hex;
readonly contracts: readonly Contract[];
}): Promise<readonly Hex[]> {
}): Promise<EnsureContractResult[]> {
// Deployments assume a deterministic deployer, so we only need to deploy the unique bytecode
const uniqueContracts = uniqueBy(contracts, (contract) => contract.bytecode);

const txs = (
const deployedContracts = (
await Promise.all(uniqueContracts.map((contract) => ensureContract({ client, deployerAddress, ...contract })))
).flat();

await waitForTransactions({
client,
hashes: txs,
hashes: deployedContracts.map(({ txHash }) => txHash).flat(),
debugLabel: "contract deploys",
});

return txs;
return deployedContracts;
}
Loading