Skip to content

Commit

Permalink
register functions
Browse files Browse the repository at this point in the history
  • Loading branch information
holic committed Oct 6, 2023
1 parent 29e0506 commit 862eb42
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 85 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions examples/minimal/packages/contracts/src/systems/DoubleSystem.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21;
import { console } from "forge-std/console.sol";
import { System } from "@latticexyz/world/src/System.sol";
import { CounterTable } from "../codegen/index.sol";

contract DoubleSystem is System {
function double() public returns (uint32) {
uint32 counter = CounterTable.get();
uint32 newValue = counter * 2;
CounterTable.set(newValue);
return newValue;
}
}
3 changes: 2 additions & 1 deletion packages/cli/src/commands/deploy2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const commandModule: CommandModule<DeployOptions, DeployOptions> = {
.filter((sig) => !baseSystemFunctions.includes(sig))
.map((sig): WorldFunction => {
// TODO: figure out how to not duplicate contract behavior (https://github.com/latticexyz/mud/issues/1708)
const worldSignature = namespace === "" ? `${namespace}_${name}_${sig}` : sig;
const worldSignature = namespace === "" ? sig : `${namespace}_${name}_${sig}`;
return {
signature: worldSignature,
selector: getFunctionSelector(worldSignature),
Expand Down Expand Up @@ -110,6 +110,7 @@ const commandModule: CommandModule<DeployOptions, DeployOptions> = {
})
);
await deploy({
worldAddress: args.worldAddress as Hex | undefined,
client,
config: {
tables: configToTables(config),
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/deploy/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export type WorldDeploy = {
address: Address;
worldVersion: string;
storeVersion: string;
blockNumber: bigint;
/** Block number where the world was deployed */
fromBlock: bigint;
/** Block number at the time of fetching world deploy, to keep further queries aligned to the same block number */
toBlock: bigint;
};

export type WorldFunction = {
Expand Down
11 changes: 9 additions & 2 deletions packages/cli/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ensureSystems } from "./ensureSystems";
import { waitForTransactionReceipt } from "viem/actions";
import { getResourceIds } from "./getResourceIds";
import { getWorldDeploy } from "./getWorldDeploy";
import { ensureFunctions } from "./ensureFunctions";

type DeployOptions<configInput extends ConfigInput> = {
client: Client<Transport, Chain | undefined, Account>;
Expand All @@ -26,6 +27,8 @@ export async function deploy<configInput extends ConfigInput>({
: await deployWorld(client);
// TODO: check that world/store versions are compatible with our deploy

// TODO: update RPC get calls to use `worldDeploy.toBlock` to align the block number everywhere

const tableTxs = await ensureTables({
client,
worldDeploy,
Expand All @@ -36,9 +39,13 @@ export async function deploy<configInput extends ConfigInput>({
worldDeploy,
systems: Object.values(config.systems),
});
const functionTxs = await ensureFunctions({
client,
worldDeploy,
functions: Object.values(config.systems).flatMap((system) => system.functions),
});

const receipts = await Promise.all(
[...tableTxs, ...systemTxs].map((tx) => waitForTransactionReceipt(client, { hash: tx }))
[...tableTxs, ...systemTxs, ...functionTxs].map((tx) => waitForTransactionReceipt(client, { hash: tx }))
);
// console.log(config);
}
4 changes: 2 additions & 2 deletions packages/cli/src/deploy/deployWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export async function deployWorld(client: Client<Transport, Chain | undefined, A

// TODO: fix receipt log type in viem to not be pending
const deploy = logsToWorldDeploy(receipt.logs.map((log) => log as Log<bigint, number, false>));
debug("deployed world to", deploy.address, "at block", deploy.blockNumber);
debug("deployed world to", deploy.address, "at block", deploy.fromBlock);

return deploy;
return { ...deploy, toBlock: deploy.fromBlock };
}
63 changes: 63 additions & 0 deletions packages/cli/src/deploy/ensureFunctions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Client, Transport, Chain, Account, Hex, getFunctionSelector } from "viem";
import { hexToResource, writeContract } from "@latticexyz/common";
import { System, WorldDeploy, WorldFunction, worldAbi } from "./common";
import { debug } from "./debug";
import { getFunctions } from "./getFunctions";

export async function ensureFunctions({
client,
worldDeploy,
functions,
}: {
client: Client<Transport, Chain | undefined, Account>;
worldDeploy: WorldDeploy;
functions: WorldFunction[];
}): Promise<Hex[]> {
const worldFunctions = await getFunctions({ client, worldDeploy });
const worldSelectorToFunction = Object.fromEntries(worldFunctions.map((func) => [func.selector, func]));

const toSkip = functions.filter((func) => worldSelectorToFunction[func.selector]);
const toAdd = functions.filter((func) => !toSkip.includes(func));

if (toSkip.length) {
debug("functions already registered:", toSkip.map((func) => func.signature).join(", "));
const wrongSystem = toSkip.filter((func) => func.systemId !== worldSelectorToFunction[func.selector]?.systemId);
if (wrongSystem.length) {
console.warn(
"found",
wrongSystem.length,
"functions already registered but pointing at a different system ID:",
wrongSystem.map((func) => func.signature).join(", ")
);
}
}

if (!toAdd.length) return [];

debug("registering functions:", toAdd.map((func) => func.signature).join(", "));

return Promise.all(
toAdd.map((func) => {
const { namespace } = hexToResource(func.systemId);
if (namespace === "") {
return writeContract(client, {
chain: client.chain ?? null,
address: worldDeploy.address,
abi: worldAbi,
// TODO: replace with batchCall
functionName: "registerRootFunctionSelector",
// TODO: why do we have to specify the selector here?
args: [func.systemId, func.systemFunctionSignature, func.systemFunctionSelector],
});
}
return writeContract(client, {
chain: client.chain ?? null,
address: worldDeploy.address,
abi: worldAbi,
// TODO: replace with batchCall
functionName: "registerFunctionSelector",
args: [func.systemId, func.systemFunctionSignature],
});
})
);
}
51 changes: 0 additions & 51 deletions packages/cli/src/deploy/ensureSystemFunctions.ts

This file was deleted.

10 changes: 1 addition & 9 deletions packages/cli/src/deploy/ensureSystems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ensureContract } from "./ensureContract";
import { debug } from "./debug";
import { resourceLabel } from "./resourceLabel";
import { getSystems } from "./getSystems";
import { ensureSystemFunctions } from "./ensureSystemFunctions";

export async function ensureSystems({
client,
Expand Down Expand Up @@ -70,12 +69,5 @@ export async function ensureSystems({
)
);

// then register system functions
const functionTxs = await Promise.all(
missing.map((system) => ensureSystemFunctions({ client, worldDeploy, system }))
);

const txs = [...contractTxs, ...registerTxs, ...functionTxs];

return (await Promise.all(txs)).flat();
return (await Promise.all([...contractTxs, ...registerTxs])).flat();
}
6 changes: 2 additions & 4 deletions packages/cli/src/deploy/getFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Client, Hex, getFunctionSelector, parseAbiItem } from "viem";
import { Client, getFunctionSelector, parseAbiItem } from "viem";
import { WorldDeploy, WorldFunction, worldTables } from "./common";
import { debug } from "./debug";
import { storeSetRecordEvent } from "@latticexyz/store";
Expand All @@ -7,8 +7,6 @@ import { decodeValueArgs } from "@latticexyz/protocol-parser";
import { getTableValue } from "./getTableValue";
import { hexToResource } from "@latticexyz/common";

console.log(worldTables);

export async function getFunctions({
client,
worldDeploy,
Expand All @@ -20,7 +18,7 @@ export async function getFunctions({
debug("looking up function signatures for", worldDeploy.address);
const logs = await getLogs(client, {
strict: true,
fromBlock: worldDeploy.blockNumber,
fromBlock: worldDeploy.fromBlock,
address: worldDeploy.address,
event: parseAbiItem(storeSetRecordEvent),
args: { tableId: worldTables.world_FunctionSignatures.tableId },
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/src/deploy/getResourceIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ import { debug } from "./debug";

export async function getResourceIds({
client,
worldDeploy: { address, blockNumber },
worldDeploy,
}: {
client: Client;
worldDeploy: WorldDeploy;
}): Promise<Hex[]> {
// This assumes we only use `ResourceIds._setExists(true)`, which is true as of this writing.
// TODO: PR to viem's getLogs to accept topics array so we can filter on all store events and quickly recreate this table's current state

debug("looking up resource IDs for", address);
debug("looking up resource IDs for", worldDeploy.address);
const logs = await getLogs(client, {
strict: true,
fromBlock: blockNumber,
address,
address: worldDeploy.address,
fromBlock: worldDeploy.fromBlock,
event: parseAbiItem(storeSpliceStaticDataEvent),
args: { tableId: storeTables.store_ResourceIds.tableId },
});

const resourceIds = logs.map((log) => log.args.keyTuple[0]);
debug("found", resourceIds.length, "resource IDs for", address);
debug("found", resourceIds.length, "resource IDs for", worldDeploy.address);

return resourceIds;
}
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/getTables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function getTables({
debug("looking up tables for", worldDeploy.address);
const logs = await getLogs(client, {
strict: true,
fromBlock: worldDeploy.blockNumber,
fromBlock: worldDeploy.fromBlock,
address: worldDeploy.address,
event: parseAbiItem(storeSetRecordEvent),
args: { tableId: storeTables.store_Tables.tableId },
Expand Down
13 changes: 10 additions & 3 deletions packages/cli/src/deploy/getWorldDeploy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Client, Address, getAddress, parseAbi } from "viem";
import { getLogs } from "viem/actions";
import { getBlockNumber, getLogs } from "viem/actions";
import { WorldDeploy, helloStoreEvent, helloWorldEvent } from "./common";
import { debug } from "./debug";
import { logsToWorldDeploy } from "./logsToWorldDeploy";
Expand All @@ -15,16 +15,23 @@ export async function getWorldDeploy(client: Client, worldAddress: Address): Pro
}

debug("looking up world deploy for", address);

const toBlock = await getBlockNumber(client);
const logs = await getLogs(client, {
strict: true,
address,
events: parseAbi([helloWorldEvent, helloStoreEvent]),
fromBlock: "earliest",
toBlock,
});

deploy = logsToWorldDeploy(logs);
deploy = {
...logsToWorldDeploy(logs),
toBlock,
};
deploys.set(address, deploy);
debug("found world deploy for", address, "at block", deploy.blockNumber);

debug("found world deploy for", address, "at block", deploy.fromBlock);

return deploy;
}
10 changes: 5 additions & 5 deletions packages/cli/src/deploy/logsToWorldDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AbiEventSignatureNotFoundError, Log, decodeEventLog, hexToString, parse
import { WorldDeploy, helloStoreEvent, helloWorldEvent } from "./common";
import { isDefined } from "@latticexyz/common/utils";

export function logsToWorldDeploy(logs: Log<bigint, number, false>[]): WorldDeploy {
export function logsToWorldDeploy(logs: Log<bigint, number, false>[]): Omit<WorldDeploy, "toBlock"> {
const deployLogs = logs
.map((log) => {
try {
Expand All @@ -25,11 +25,11 @@ export function logsToWorldDeploy(logs: Log<bigint, number, false>[]): WorldDepl
.filter(isDefined);

// TODO: should this test for/validate that only one of each of these events is present? and that the address/block number don't change between each?
const { address, blockNumber, worldVersion, storeVersion } = deployLogs.reduce<Partial<WorldDeploy>>(
const { address, fromBlock, worldVersion, storeVersion } = deployLogs.reduce<Partial<WorldDeploy>>(
(deploy, log) => ({
...deploy,
address: log.address,
blockNumber: log.blockNumber,
fromBlock: log.blockNumber,
...(log.eventName === "HelloWorld"
? { worldVersion: hexToString(trim(log.args.worldVersion, { dir: "right" })) }
: null),
Expand All @@ -41,9 +41,9 @@ export function logsToWorldDeploy(logs: Log<bigint, number, false>[]): WorldDepl
);

if (address == null) throw new Error("could not find world address");
if (blockNumber == null) throw new Error("could not find world deploy block number");
if (fromBlock == null) throw new Error("could not find world deploy block number");
if (worldVersion == null) throw new Error("could not find world version");
if (storeVersion == null) throw new Error("could not find store version");

return { address, blockNumber, worldVersion, storeVersion };
return { address, fromBlock, worldVersion, storeVersion };
}

0 comments on commit 862eb42

Please sign in to comment.