Skip to content

Commit a8f0c8f

Browse files
committed
deploy systems, replace addresses in table data
1 parent cc822ce commit a8f0c8f

File tree

2 files changed

+74
-11
lines changed

2 files changed

+74
-11
lines changed

packages/cli/src/mirror/createMirrorPlan.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { mkdir, rm } from "fs/promises";
1212
import { mirrorPlansDirectory } from "./common";
1313
import { getSystems } from "../deploy/getSystems";
1414
import { getDeployedBytecode } from "./getDeployedBytecode";
15+
import { debug } from "./debug";
1516

1617
// TODO: attempt to create world the same way as it was originally created, thus preserving world address
1718
// TODO: set up table to track migrated records with original metadata (block number/timestamp) and for lazy migrations
@@ -39,15 +40,15 @@ export async function createMirrorPlan({
3940
const makePlan = (async () => {
4041
const worldDeploy = await getWorldDeploy(from.client, from.world, from.block);
4142

42-
console.log("getting systems");
43+
debug("getting systems");
4344
const systems = await getSystems({
4445
client: from.client,
4546
worldDeploy,
4647
indexerUrl: from.indexer,
4748
chainId: fromChainId,
4849
});
4950

50-
console.log("getting bytecode for", systems.length, "systems");
51+
debug("getting bytecode for", systems.length, "systems");
5152
const systemsWithBytecode = await Promise.all(
5253
systems.map(async (system) => {
5354
const bytecode = await getDeployedBytecode({
@@ -85,13 +86,13 @@ export async function createMirrorPlan({
8586
chainId: fromChainId,
8687
}),
8788
);
88-
console.log("got", logs.length, "logs for", resourceToLabel(table));
89+
debug("got", logs.length, "logs for", resourceToLabel(table));
8990
for (const log of logs) {
9091
plan.write({ step: "setRecord", record: log.args });
9192
}
9293
count += logs.length;
9394
}
94-
console.log("got", count, "total record logs");
95+
debug("got", count, "total record logs");
9596
})();
9697

9798
try {

packages/cli/src/mirror/executeMirrorPlan.ts

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { Account, Address, Chain, Client, Transport, concatHex } from "viem";
1+
import { Account, Address, Chain, Client, Hex, Transport, concatHex, withCache } from "viem";
22
import { readPlan } from "./readPlan";
3-
import { resourceToHex, resourceToLabel } from "@latticexyz/common";
3+
import { hexToResource, resourceToHex, resourceToLabel, spliceHex } from "@latticexyz/common";
44
import { StoreLog } from "@latticexyz/store";
55
import { getWorldDeploy } from "../deploy/getWorldDeploy";
66
import { mudTables } from "@latticexyz/store-sync";
77
import { createRecordHandler } from "./createRecordHandler";
88
import { wait } from "@latticexyz/common/utils";
9+
import { debug } from "./debug";
10+
import { DeployedBytecode } from "./common";
11+
import { DeployedSystem } from "../deploy/common";
12+
import { ensureContract, ensureDeployer, getContractAddress, waitForTransactions } from "@latticexyz/common/internal";
913

1014
export type StoreRecord = Extract<StoreLog, { eventName: "Store_SetRecord" }>["args"];
1115

@@ -22,7 +26,6 @@ export async function executeMirrorPlan({
2226
for await (const step of readPlan(planFilename)) {
2327
if (step.step === "deploySystem") {
2428
totalSystems += 1;
25-
console.log(step);
2629
}
2730
if (step.step === "setRecord") {
2831
totalRecords += 1;
@@ -37,18 +40,71 @@ export async function executeMirrorPlan({
3740
await wait(5_000);
3841

3942
const worldDeploy = await getWorldDeploy(client, worldAddress);
40-
console.log("found world deploy at", worldDeploy.address);
43+
debug("found world deploy at", worldDeploy.address);
4144

45+
// TODO: lift into CLI to allow overriding?
46+
const deployerAddress = await ensureDeployer(client);
47+
debug("deploying systems via", deployerAddress);
48+
49+
const systems: { system: DeployedSystem; address: Address; previousAddress: Address }[] = [];
4250
for await (const step of readPlan(planFilename)) {
4351
await (async () => {
4452
if (step.step !== "deploySystem") return;
4553

46-
console.log("deploying system", resourceToLabel(step.system));
47-
// TODO:
48-
return;
54+
async function deploy(bytecode: DeployedBytecode) {
55+
let initCode = bytecode.initCode;
56+
for (const lib of bytecode.libraries) {
57+
debug("deploying referenced library");
58+
initCode = spliceHex(initCode, lib.offset, 20, await deploy(lib.reference));
59+
}
60+
const address = getContractAddress({
61+
deployerAddress,
62+
bytecode: initCode,
63+
});
64+
65+
await withCache(
66+
async () => {
67+
const hashes = await ensureContract({ client, deployerAddress, bytecode: initCode });
68+
return waitForTransactions({ client, hashes, debugLabel: "contract deploy" });
69+
},
70+
{ cacheKey: `deploy:${address}` },
71+
);
72+
73+
return address;
74+
}
75+
76+
debug(`deploying ${resourceToLabel(step.system)} system`);
77+
const address = await deploy(step.bytecode);
78+
systems.push({
79+
system: step.system,
80+
address,
81+
previousAddress: step.bytecode.address,
82+
});
4983
})();
5084
}
5185

86+
const systemReplacements = new Map(
87+
systems.map((system) => [
88+
system.previousAddress.toLowerCase().replace(/^0x/, ""),
89+
{
90+
value: system.address.toLowerCase().replace(/^0x/, ""),
91+
debugLabel: `${resourceToLabel(system.system)} system address`,
92+
},
93+
]),
94+
);
95+
const systemReplacementsPattern = new RegExp(Array.from(systemReplacements.keys()).join("|"), "ig");
96+
97+
function replaceSystems(data: Hex, debugLabel: string): Hex {
98+
return data.replaceAll(systemReplacementsPattern, (match) => {
99+
const replacement = systemReplacements.get(match);
100+
// this should never happen, this is here just in case I messed up the logic
101+
if (!replacement) throw new Error(`No replacement for match: ${match}`);
102+
103+
debug("replacing", replacement.debugLabel, "in", debugLabel, `(0x${match} => 0x${replacement.value})`);
104+
return replacement.value;
105+
}) as never;
106+
}
107+
52108
const recordHandler = createRecordHandler({
53109
client,
54110
worldAddress: worldDeploy.address,
@@ -60,6 +116,12 @@ export async function executeMirrorPlan({
60116
await (async () => {
61117
if (step.step !== "setRecord") return;
62118

119+
// Update system addresses if found in record
120+
const tableLabel = resourceToLabel(hexToResource(step.record.tableId));
121+
step.record.keyTuple = step.record.keyTuple.map((key) => replaceSystems(key, `key of ${tableLabel}`));
122+
step.record.staticData = replaceSystems(step.record.staticData, `static data of ${tableLabel}`);
123+
step.record.dynamicData = replaceSystems(step.record.dynamicData, `dynamic data of ${tableLabel}`);
124+
63125
// Defer updating root namespace owner so we can set all records
64126
// before reverting it to the original owner.
65127
// This allows mirroring any world, not just ones you own.

0 commit comments

Comments
 (0)