1- import { Account , Address , Chain , Client , Transport , concatHex } from "viem" ;
1+ import { Account , Address , Chain , Client , Hex , Transport , concatHex , withCache } from "viem" ;
22import { readPlan } from "./readPlan" ;
3- import { resourceToHex , resourceToLabel } from "@latticexyz/common" ;
3+ import { hexToResource , resourceToHex , resourceToLabel , spliceHex } from "@latticexyz/common" ;
44import { StoreLog } from "@latticexyz/store" ;
55import { getWorldDeploy } from "../deploy/getWorldDeploy" ;
66import { mudTables } from "@latticexyz/store-sync" ;
77import { createRecordHandler } from "./createRecordHandler" ;
88import { 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
1014export 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 ( / ^ 0 x / , "" ) ,
89+ {
90+ value : system . address . toLowerCase ( ) . replace ( / ^ 0 x / , "" ) ,
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