Skip to content

Commit

Permalink
fix(cli): deploy systems/modules before registering/installing them (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored Oct 13, 2023
1 parent e1dc88e commit 61c6ab7
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 54 deletions.
5 changes: 5 additions & 0 deletions .changeset/loud-mayflies-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/cli": patch
---

Changed deploy order so that system/module contracts are fully deployed before registering/installing them on the world.
16 changes: 8 additions & 8 deletions packages/cli/src/deploy/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,25 @@ export type WorldFunction = {
readonly systemFunctionSelector: Hex;
};

export type System = {
export type DeterministicContract = {
readonly address: Address;
readonly bytecode: Hex;
readonly abi: Abi;
};

export type System = DeterministicContract & {
readonly namespace: string;
readonly name: string;
readonly systemId: Hex;
readonly allowAll: boolean;
readonly allowedAddresses: readonly Hex[];
readonly address: Address;
readonly bytecode: Hex;
readonly abi: Abi;
readonly functions: readonly WorldFunction[];
};

export type Module = {
export type Module = DeterministicContract & {
readonly name: string;
readonly installAsRoot: boolean;
readonly installData: Hex; // TODO: figure out better naming for this
readonly address: Address;
readonly bytecode: Hex;
readonly abi: Abi;
};

export type ConfigInput = StoreConfig & WorldConfig;
Expand Down
26 changes: 25 additions & 1 deletion packages/cli/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { ensureModules } from "./ensureModules";
import { Table } from "./configToTables";
import { assertNamespaceOwner } from "./assertNamespaceOwner";
import { debug } from "./debug";
import { resourceLabel } from "./resourceLabel";
import { ensureContract } from "./ensureContract";
import { uniqueBy } from "@latticexyz/common/utils";

type DeployOptions<configInput extends ConfigInput> = {
client: Client<Transport, Chain | undefined, Account>;
Expand Down Expand Up @@ -51,6 +54,27 @@ export async function deploy<configInput extends ConfigInput>({
resourceIds: [...tables.map((table) => table.tableId), ...systems.map((system) => system.systemId)],
});

// deploy all dependent contracts, because system registration, module install, etc. all expect these contracts to be callable.
const contractTxs = (
await Promise.all([
...uniqueBy(systems, (system) => system.address).map((system) =>
ensureContract({ client, bytecode: system.bytecode, label: `${resourceLabel(system)} system` })
),
...uniqueBy(config.modules, (mod) => mod.address).map((mod) =>
ensureContract({ client, bytecode: mod.bytecode, label: `${mod.name} module` })
),
])
).flat();

if (contractTxs.length) {
debug("waiting for contracts");
// wait for each tx separately/serially, because parallelizing results in RPC errors
for (const tx of contractTxs) {
await waitForTransactionReceipt(client, { hash: tx });
// TODO: throw if there was a revert?
}
}

const tableTxs = await ensureTables({
client,
worldDeploy,
Expand All @@ -75,7 +99,7 @@ export async function deploy<configInput extends ConfigInput>({
const txs = [...tableTxs, ...systemTxs, ...functionTxs, ...moduleTxs];

// wait for each tx separately/serially, because parallelizing results in RPC errors
debug("waiting for transactions to confirm");
debug("waiting for all transactions to confirm");
for (const tx of txs) {
await waitForTransactionReceipt(client, { hash: tx });
// TODO: throw if there was a revert?
Expand Down
13 changes: 2 additions & 11 deletions packages/cli/src/deploy/ensureModules.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Client, Transport, Chain, Account, Hex } from "viem";
import { writeContract } from "@latticexyz/common";
import { Module, WorldDeploy, worldAbi } from "./common";
import { ensureContract } from "./ensureContract";
import { debug } from "./debug";
import { uniqueBy, wait } from "@latticexyz/common/utils";
import { wait } from "@latticexyz/common/utils";
import pRetry from "p-retry";

export async function ensureModules({
Expand All @@ -17,14 +16,6 @@ export async function ensureModules({
}): Promise<readonly Hex[]> {
if (!modules.length) return [];

// kick off contract deployments first, otherwise installing modules can fail
const contractTxs = await Promise.all(
uniqueBy(modules, (mod) => mod.address).map((mod) =>
ensureContract({ client, bytecode: mod.bytecode, label: `${mod.name} module` })
)
);

// then start installing modules
debug("installing modules:", modules.map((mod) => mod.name).join(", "));
const installTxs = await Promise.all(
modules.map((mod) =>
Expand Down Expand Up @@ -70,5 +61,5 @@ export async function ensureModules({
)
);

return (await Promise.all([...contractTxs, ...installTxs])).flat();
return await Promise.all(installTxs);
}
57 changes: 23 additions & 34 deletions packages/cli/src/deploy/ensureSystems.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Client, Transport, Chain, Account, Hex, getAddress } from "viem";
import { writeContract } from "@latticexyz/common";
import { System, WorldDeploy, worldAbi } from "./common";
import { ensureContract } from "./ensureContract";
import { debug } from "./debug";
import { resourceLabel } from "./resourceLabel";
import { getSystems } from "./getSystems";
import { getResourceAccess } from "./getResourceAccess";
import { uniqueBy, wait } from "@latticexyz/common/utils";
import { wait } from "@latticexyz/common/utils";
import pRetry from "p-retry";

export async function ensureSystems({
Expand Down Expand Up @@ -53,7 +52,7 @@ export async function ensureSystems({
debug("adding", accessToAdd.length, "access grants");
}

const accessTxs = await Promise.all([
const accessTxs = [
...accessToRemove.map((access) =>
pRetry(
() =>
Expand Down Expand Up @@ -94,7 +93,7 @@ export async function ensureSystems({
}
)
),
]);
];

const existingSystems = systems.filter((system) =>
worldSystems.some(
Expand Down Expand Up @@ -127,37 +126,27 @@ export async function ensureSystems({
debug("registering new systems", systemsToAdd.map(resourceLabel).join(", "));
}

// kick off contract deployments first, otherwise registering systems can fail
const contractTxs = await Promise.all(
uniqueBy(missingSystems, (system) => system.address).map((system) =>
ensureContract({ client, bytecode: system.bytecode, label: `${resourceLabel(system)} system` })
)
);

// then start registering systems
const registerTxs = await Promise.all(
missingSystems.map((system) =>
pRetry(
() =>
writeContract(client, {
chain: client.chain ?? null,
address: worldDeploy.address,
abi: worldAbi,
// TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
functionName: "registerSystem",
args: [system.systemId, system.address, system.allowAll],
}),
{
retries: 3,
onFailedAttempt: async (error) => {
const delay = error.attemptNumber * 500;
debug(`failed to register system ${resourceLabel(system)}, retrying in ${delay}ms...`);
await wait(delay);
},
}
)
const registerTxs = missingSystems.map((system) =>
pRetry(
() =>
writeContract(client, {
chain: client.chain ?? null,
address: worldDeploy.address,
abi: worldAbi,
// TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
functionName: "registerSystem",
args: [system.systemId, system.address, system.allowAll],
}),
{
retries: 3,
onFailedAttempt: async (error) => {
const delay = error.attemptNumber * 500;
debug(`failed to register system ${resourceLabel(system)}, retrying in ${delay}ms...`);
await wait(delay);
},
}
)
);

return (await Promise.all([...accessTxs, ...contractTxs, ...registerTxs])).flat();
return await Promise.all([...accessTxs, ...registerTxs]);
}

0 comments on commit 61c6ab7

Please sign in to comment.