Skip to content

Commit

Permalink
Merge commit '1b477d476a666ccffafc6eb266d1732b90bc28f9' into entrykit
Browse files Browse the repository at this point in the history
  • Loading branch information
holic committed Jan 9, 2025
2 parents ea3d2fc + 1b477d4 commit 0be6a43
Show file tree
Hide file tree
Showing 82 changed files with 877 additions and 1,109 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-apples-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/cli": patch
---

When upgrading an existing world, the deployer now attempts to read the deploy block number from the `worlds.json` file. If it is found, the `HelloWorld` and `HelloStore` event are fetched from this block instead of searching for the events starting from the genesis block.
5 changes: 5 additions & 0 deletions .changeset/fresh-chairs-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/explorer": patch
---

The functions in the Interact tab now display the emitted logs with the block explorer URL for the submitted transaction.
5 changes: 5 additions & 0 deletions .changeset/little-tables-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/world": patch
---

Moved TS utils over to using hardcoded ABIs instead of ones imported from `.abi.json` files to fix some internal type resolution issues.
5 changes: 5 additions & 0 deletions .changeset/sharp-lions-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/store": patch
---

Added internal `getRecord` and `getStaticDataLocation` helpers.
5 changes: 5 additions & 0 deletions .changeset/young-seals-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/paymaster": patch
---

Added `GenerousPaymaster`, a simple paymaster that sponsors all user operations for local development purposes.
31 changes: 16 additions & 15 deletions docs/pages/cli/deploy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,22 @@ Again, there are several ways to do this:

These are the command line options you can specify on `mud deploy`:

| Option | Meaning | Type | Default value |
| ----------------------- | ------------------------------------------------ | ------- | ---------------------------------------------------------- |
| `--configPath` | Path to the config file | string | `mud.config.ts` |
| `--printConfig` | Print the resolved config | boolean | `false` |
| `--saveDeployment` | Save the deployment info to a file | boolean | `true` |
| `--profile` | The foundry profile to use | string | `local` |
| `--rpc`<sup>1</sup> | The RPC URL to use | string | RPC url from `foundry.toml` |
| `--rpcBatch` | Enable batch processing of RPC requests | boolean | `false` |
| `--worldAddress` | Deploy to an existing World at the given address | string | Empty, deploy new `World` |
| `--srcDir` | Source directory | string | Foundry `src` directory |
| `--skipBuild` | Skip rebuilding the contracts before deploying | boolean | `false` |
| `--alwaysRunPostDeploy` | Run `PostDeploy.s.sol` after each deploy | boolean | `false` (run the script only when deploying a new `World`) |
| `--help` | Show help | boolean | `false` |
| `--version` | Show version number | boolean | `false` |
| `--forgeScriptOptions` | Command line options for forge | string | empty |
| Option | Meaning | Type | Default value |
| ----------------------- | -------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------- |
| `--configPath` | Path to the config file | string | `mud.config.ts` |
| `--printConfig` | Print the resolved config | boolean | `false` |
| `--saveDeployment` | Save the deployment info to a file | boolean | `true` |
| `--profile` | The foundry profile to use | string | `local` |
| `--rpc`<sup>1</sup> | The RPC URL to use | string | RPC url from `foundry.toml` |
| `--rpcBatch` | Enable batch processing of RPC requests | boolean | `false` |
| `--worldAddress` | Deploy to an existing World at the given address | string | Empty, deploy new `World` |
| `--srcDir` | Source directory | string | Foundry `src` directory |
| `--skipBuild` | Skip rebuilding the contracts before deploying | boolean | `false` |
| `--alwaysRunPostDeploy` | Run `PostDeploy.s.sol` after each deploy | boolean | `false` (run the script only when deploying a new `World`) |
| `--help` | Show help | boolean | `false` |
| `--version` | Show version number | boolean | `false` |
| `--forgeScriptOptions` | Command line options for forge | string | empty |
| `--indexerUrl` | If provided, read records from the indexer instead of fetching logs from the RPC | string | empty |

(1) The hostname `localhost` may not work. If that is the case, use `127.0.0.1` instead.

Expand Down
3 changes: 0 additions & 3 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,11 @@
"@latticexyz/common": "workspace:*",
"@latticexyz/config": "workspace:*",
"@latticexyz/gas-report": "workspace:*",
"@latticexyz/paymaster": "workspace:*",
"@latticexyz/protocol-parser": "workspace:*",
"@latticexyz/schema-type": "workspace:*",
"@latticexyz/store": "workspace:*",
"@latticexyz/store-sync": "workspace:*",
"@latticexyz/utils": "workspace:*",
"@latticexyz/wiresaw": "workspace:*",
"@latticexyz/world": "workspace:*",
"@latticexyz/world-module-metadata": "workspace:*",
"abitype": "1.0.6",
Expand All @@ -68,7 +66,6 @@
"p-queue": "^7.4.1",
"p-retry": "^5.1.2",
"path": "^0.12.7",
"permissionless": "^0.2.3",
"rxjs": "7.5.5",
"throttle-debounce": "^5.0.0",
"toposort": "^2.0.2",
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/commands/dev-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const devOptions = {
alwaysRunPostDeploy: deployOptions.alwaysRunPostDeploy,
forgeScriptOptions: deployOptions.forgeScriptOptions,
worldAddress: deployOptions.worldAddress,
smartAccount: deployOptions.smartAccount,
};

const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof devOptions>> = {
Expand Down
9 changes: 2 additions & 7 deletions packages/cli/src/commands/trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { getChainId, readContract } from "viem/actions";
import { World as WorldConfig } from "@latticexyz/world";
import { resolveSystems } from "@latticexyz/world/node";
import { worldAbi } from "../deploy/common";
import { getAction } from "viem/utils";

const systemsTableId = worldConfig.namespaces.world.tables.Systems.tableId;

Expand Down Expand Up @@ -64,19 +63,15 @@ const commandModule: CommandModule<Options, Options> = {
const config = (await loadConfig(configPath)) as WorldConfig;

const client = createClient({ transport: http(rpc) });
const chainId = await getAction(client, getChainId, "getChainId")({});
const chainId = await getChainId(client);
const worldAddress = (args.worldAddress ?? getWorldAddress(config.deploy.worldsFile, chainId)) as Hex;

const systems = await resolveSystems({ rootDir, config });

const labels = await Promise.all(
systems.map(async (system) => ({
label: system.label,
address: await getAction(
client,
readContract,
"readContract",
)({
address: await readContract(client, {
abi: worldAbi,
address: worldAddress,
functionName: "getField",
Expand Down
7 changes: 1 addition & 6 deletions packages/cli/src/deploy/common.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { Abi, Account, Address, Chain, Client, Hex, Transport, padHex } from "viem";
import { Abi, Account, Address, Chain, Client, Hex, Transport } from "viem";
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json" assert { type: "json" };
import { helloStoreEvent } from "@latticexyz/store";
import { helloWorldEvent } from "@latticexyz/world";
import { LibraryMap } from "./getLibraryMap";

export const salt = padHex("0x", { size: 32 });

// https://eips.ethereum.org/EIPS/eip-170
export const contractSizeLimit = parseInt("6000", 16);

export const worldDeployEvents = [helloStoreEvent, helloWorldEvent] as const;

export const worldAbi = IBaseWorldAbi;
Expand Down
17 changes: 11 additions & 6 deletions packages/cli/src/deploy/createPrepareDeploy.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import { DeterministicContract, LibraryPlaceholder, salt } from "./common";
import { DeterministicContract, LibraryPlaceholder } from "./common";
import { spliceHex } from "@latticexyz/common";
import { Hex, getCreate2Address, Address } from "viem";
import { Hex, Address } from "viem";
import { LibraryMap } from "./getLibraryMap";
import { getContractAddress } from "@latticexyz/common/internal";

export function createPrepareDeploy(
bytecodeWithPlaceholders: Hex,
placeholders: readonly LibraryPlaceholder[],
): DeterministicContract["prepareDeploy"] {
return function prepareDeploy(deployer: Address, libraryMap?: LibraryMap) {
return function prepareDeploy(deployerAddress: Address, libraryMap?: LibraryMap) {
let bytecode = bytecodeWithPlaceholders;

if (placeholders.length === 0) {
return { bytecode, address: getCreate2Address({ from: deployer, bytecode, salt }) };
return { bytecode, address: getContractAddress({ deployerAddress, bytecode }) };
}

if (!libraryMap) {
throw new Error("Libraries must be provided if there are placeholders");
}

for (const placeholder of placeholders) {
const address = libraryMap.getAddress({ name: placeholder.name, path: placeholder.path, deployer });
const address = libraryMap.getAddress({
name: placeholder.name,
path: placeholder.path,
deployer: deployerAddress,
});
bytecode = spliceHex(bytecode, placeholder.start, placeholder.length, address);
}
return {
bytecode,
address: getCreate2Address({ from: deployer, bytecode, salt }),
address: getContractAddress({ deployerAddress, bytecode }),
};
};
}
12 changes: 8 additions & 4 deletions packages/cli/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Address, Hex, stringToHex } from "viem";
import { ensureDeployer } from "./ensureDeployer";
import { deployWorld } from "./deployWorld";
import { ensureTables } from "./ensureTables";
import {
Expand All @@ -18,16 +17,15 @@ import { ensureModules } from "./ensureModules";
import { ensureNamespaceOwner } from "./ensureNamespaceOwner";
import { debug } from "./debug";
import { resourceToHex, resourceToLabel } from "@latticexyz/common";
import { ensureContractsDeployed } from "./ensureContractsDeployed";
import { randomBytes } from "crypto";
import { Table } from "@latticexyz/config";
import { ensureResourceTags } from "./ensureResourceTags";
import { waitForTransactions } from "./waitForTransactions";
import { ContractArtifact } from "@latticexyz/world/node";
import { World } from "@latticexyz/world";
import { deployCustomWorld } from "./deployCustomWorld";
import { uniqueBy } from "@latticexyz/common/utils";
import { getLibraryMap } from "./getLibraryMap";
import { ensureContractsDeployed, ensureDeployer, waitForTransactions } from "@latticexyz/common/internal";

type DeployOptions = {
config: World;
Expand All @@ -38,6 +36,11 @@ type DeployOptions = {
artifacts: readonly ContractArtifact[];
salt?: Hex;
worldAddress?: Address;
/**
* Block number of an existing world deployment.
* Only used if `worldAddress` is provided.
*/
worldDeployBlock?: bigint;
/**
* Address of determinstic deployment proxy: https://github.com/Arachnid/deterministic-deployment-proxy
* By default, we look for a deployment at 0x4e59b44847b379578588920ca78fbf26c0b4956c and, if not, deploy one.
Expand All @@ -64,14 +67,15 @@ export async function deploy({
artifacts,
salt,
worldAddress: existingWorldAddress,
worldDeployBlock,
deployerAddress: initialDeployerAddress,
indexerUrl,
chainId,
}: DeployOptions): Promise<WorldDeploy> {
const deployerAddress = initialDeployerAddress ?? (await ensureDeployer(client));

const worldDeploy = existingWorldAddress
? await getWorldDeploy(client, existingWorldAddress)
? await getWorldDeploy(client, existingWorldAddress, worldDeployBlock)
: config.deploy.customWorld
? await deployCustomWorld({
client,
Expand Down
43 changes: 12 additions & 31 deletions packages/cli/src/deploy/deployCustomWorld.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Account, Chain, Client, Hex, Transport, concatHex, encodeDeployData, getCreate2Address, isHex } from "viem";
import { waitForTransactionReceipt, writeContract, sendTransaction } from "viem/actions";
import { resourceToHex } from "@latticexyz/common";
import { Account, Chain, Client, Hex, Transport, concatHex, encodeDeployData, isHex } from "viem";
import { waitForTransactionReceipt } from "viem/actions";
import { resourceToHex, sendTransaction, writeContract } from "@latticexyz/common";
import { debug } from "./debug";
import { logsToWorldDeploy } from "./logsToWorldDeploy";
import { WorldDeploy, salt, worldAbi } from "./common";
import { WorldDeploy, worldAbi } from "./common";
import { getWorldContracts } from "./getWorldContracts";
import { ensureContractsDeployed } from "./ensureContractsDeployed";
import { ensureContractsDeployed, getContractAddress, waitForTransactions } from "@latticexyz/common/internal";
import { ContractArtifact, ReferenceIdentifier } from "@latticexyz/world/node";
import { World } from "@latticexyz/world";
import { waitForTransactions } from "./waitForTransactions";
import { getAction } from "viem/utils";

function findArtifact(ref: ReferenceIdentifier, artifacts: readonly ContractArtifact[]): ContractArtifact {
const artifact = artifacts.find((a) => a.sourcePath === ref.sourcePath && a.name === ref.name);
Expand All @@ -32,9 +30,8 @@ function getDeployable(deployerAddress: Hex, artifact: ContractArtifact, artifac
return concatHex(
artifact.bytecode.map((ref): Hex => {
if (isHex(ref)) return ref;
return getCreate2Address({
from: deployerAddress,
salt,
return getContractAddress({
deployerAddress,
bytecode: getDeployable(deployerAddress, findArtifact(ref, artifacts), artifacts),
});
}),
Expand Down Expand Up @@ -79,23 +76,17 @@ export async function deployCustomWorld({

// Deploy custom world without deterministic deployer for now
debug("deploying custom world");
const deployTx = await getAction(
client,
sendTransaction,
"sendTransaction",
)({
const deployTx = await sendTransaction(client, {
chain: client.chain ?? null,
account: client.account,
data: encodeDeployData({
abi: worldArtifact.abi,
args: [], // TODO (https://github.com/latticexyz/mud/issues/3150)
bytecode: getDeployable(deployerAddress, worldArtifact, artifacts),
}),
gas: 10_000_000n,
});

debug("waiting for custom world deploy at tx", deployTx);
const receipt = await getAction(client, waitForTransactionReceipt, "waitForTransactionReceipt")({ hash: deployTx });
debug("waiting for custom world deploy");
const receipt = await waitForTransactionReceipt(client, { hash: deployTx });
if (receipt.status !== "success") {
console.error("world deploy failed", receipt);
throw new Error("world deploy failed");
Expand All @@ -105,13 +96,8 @@ export async function deployCustomWorld({
debug("deployed custom world to", deploy.address, "at block", deploy.deployBlock);

// initialize world via init module
const initTx = await getAction(
client,
writeContract,
"writeContract",
)({
const initTx = await writeContract(client, {
chain: client.chain ?? null,
account: client.account,
address: deploy.address,
abi: worldAbi,
functionName: "initialize",
Expand All @@ -120,13 +106,8 @@ export async function deployCustomWorld({
await waitForTransactions({ client, hashes: [initTx], debugLabel: "world init" });

// transfer root namespace to deployer after init module is installed so `transferOwnership` method is available
const transferOwnershipTx = await getAction(
client,
writeContract,
"writeContract",
)({
const transferOwnershipTx = await writeContract(client, {
chain: client.chain ?? null,
account: client.account,
address: deploy.address,
abi: worldAbi,
functionName: "transferOwnership",
Expand Down
29 changes: 6 additions & 23 deletions packages/cli/src/deploy/deployWorld.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Account, Chain, Client, Hex, Transport, parseEventLogs } from "viem";
import { waitForTransactionReceipt, writeContract } from "viem/actions";
import { Account, Chain, Client, Hex, Transport } from "viem";
import { waitForTransactionReceipt } from "viem/actions";
import { ensureWorldFactory } from "./ensureWorldFactory";
import WorldFactoryAbi from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.abi.json" assert { type: "json" };
import { writeContract } from "@latticexyz/common";
import { debug } from "./debug";
import { logsToWorldDeploy } from "./logsToWorldDeploy";
import { WorldDeploy } from "./common";
import { getAction } from "viem/utils";
import { entryPoint07Abi } from "viem/account-abstraction";

export async function deployWorld(
client: Client<Transport, Chain | undefined, Account>,
Expand All @@ -17,35 +16,19 @@ export async function deployWorld(
const worldFactory = await ensureWorldFactory(client, deployerAddress, withWorldProxy);

debug("deploying world");
const tx = await getAction(
client,
writeContract,
"writeContract",
)({
const tx = await writeContract(client, {
chain: client.chain ?? null,
account: client.account,
address: worldFactory,
abi: WorldFactoryAbi,
functionName: "deployWorld",
args: [salt],
});

debug("waiting for world deploy at tx", tx);
const receipt = await getAction(client, waitForTransactionReceipt, "waitForTransactionReceipt")({ hash: tx });
debug("waiting for world deploy");
const receipt = await waitForTransactionReceipt(client, { hash: tx });
if (receipt.status !== "success") {
console.error("world deploy failed", receipt);
throw new Error("world deploy failed");
} else if (client.account.type === "smart") {
// TODO: lift this out into something better that doesn't assume one tx
const parsedLogs = parseEventLogs({
logs: receipt.logs,
abi: entryPoint07Abi,
eventName: "UserOperationEvent" as const,
});
if (!parsedLogs[0].args.success) {
console.error("world deploy failed");
throw new Error("world deploy failed");
}
}

const deploy = logsToWorldDeploy(receipt.logs);
Expand Down
Loading

0 comments on commit 0be6a43

Please sign in to comment.