From 258b10e1cddd7f471146b742347da197ee99e1b0 Mon Sep 17 00:00:00 2001 From: y77cao Date: Tue, 19 Sep 2023 11:49:51 -0400 Subject: [PATCH 1/6] basic setup --- packages/common/src/chains/mudFoundry.ts | 6 +++--- packages/common/src/createContract.ts | 1 - templates/react/packages/client/package.json | 4 +++- templates/react/packages/client/src/App.tsx | 5 +++++ templates/react/packages/client/src/index.tsx | 15 ++++++++++++--- .../packages/client/src/mud/setupNetwork.ts | 17 +++++++++++++++++ 6 files changed, 40 insertions(+), 8 deletions(-) diff --git a/packages/common/src/chains/mudFoundry.ts b/packages/common/src/chains/mudFoundry.ts index 1754534d8d..e971901277 100644 --- a/packages/common/src/chains/mudFoundry.ts +++ b/packages/common/src/chains/mudFoundry.ts @@ -3,7 +3,7 @@ import { MUDChain } from "./types"; export const mudFoundry = { ...foundry, - fees: { - defaultPriorityFee: 0n, - }, + // fees: { + // defaultPriorityFee: 0n, + // }, } as const satisfies MUDChain; diff --git a/packages/common/src/createContract.ts b/packages/common/src/createContract.ts index c327709352..46cc0ed236 100644 --- a/packages/common/src/createContract.ts +++ b/packages/common/src/createContract.ts @@ -15,7 +15,6 @@ import { getAbiItem, getContract, getFunctionSelector, - trim, } from "viem"; import pRetry from "p-retry"; import { AbiFunction } from "abitype"; diff --git a/templates/react/packages/client/package.json b/templates/react/packages/client/package.json index 1855fbf09e..831345ae19 100644 --- a/templates/react/packages/client/package.json +++ b/templates/react/packages/client/package.json @@ -20,11 +20,13 @@ "@latticexyz/store-sync": "link:../../../../packages/store-sync", "@latticexyz/utils": "link:../../../../packages/utils", "@latticexyz/world": "link:../../../../packages/world", + "@rainbow-me/rainbowkit": "^1.0.11", "contracts": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0", "rxjs": "7.5.5", - "viem": "1.6.0" + "viem": "1.6.0", + "wagmi": "^1.4.2" }, "devDependencies": { "@types/react": "18.2.22", diff --git a/templates/react/packages/client/src/App.tsx b/templates/react/packages/client/src/App.tsx index a9660de463..9363888f32 100644 --- a/templates/react/packages/client/src/App.tsx +++ b/templates/react/packages/client/src/App.tsx @@ -1,3 +1,6 @@ +import "@rainbow-me/rainbowkit/styles.css"; +import { ConnectButton } from "@rainbow-me/rainbowkit"; +import { useWalletClient } from "wagmi"; import { useComponentValue } from "@latticexyz/react"; import { useMUD } from "./MUDContext"; import { singletonEntity } from "@latticexyz/store-sync/recs"; @@ -7,6 +10,7 @@ export const App = () => { components: { Counter }, systemCalls: { increment }, } = useMUD(); + const walletClient = useWalletClient(); const counter = useComponentValue(Counter, singletonEntity); @@ -24,6 +28,7 @@ export const App = () => { > Increment + ); }; diff --git a/templates/react/packages/client/src/index.tsx b/templates/react/packages/client/src/index.tsx index da8d70f020..e59a46c0eb 100644 --- a/templates/react/packages/client/src/index.tsx +++ b/templates/react/packages/client/src/index.tsx @@ -1,4 +1,6 @@ import ReactDOM from "react-dom/client"; +import { RainbowKitProvider } from "@rainbow-me/rainbowkit"; +import { WagmiConfig } from "wagmi"; import { App } from "./App"; import { setup } from "./mud/setup"; import { MUDProvider } from "./MUDContext"; @@ -10,10 +12,17 @@ const root = ReactDOM.createRoot(rootElement); // TODO: figure out if we actually want this to be async or if we should render something else in the meantime setup().then(async (result) => { + const { + network: { wagmiConfig, chain }, + } = result; root.render( - - - + + + + + + + ); // https://vitejs.dev/guide/env-and-mode.html diff --git a/templates/react/packages/client/src/mud/setupNetwork.ts b/templates/react/packages/client/src/mud/setupNetwork.ts index 59f5663277..7871825f32 100644 --- a/templates/react/packages/client/src/mud/setupNetwork.ts +++ b/templates/react/packages/client/src/mud/setupNetwork.ts @@ -6,10 +6,13 @@ import { createPublicClient, fallback, webSocket, http, createWalletClient, Hex, parseEther, ClientConfig } from "viem"; import { createFaucetService } from "@latticexyz/services/faucet"; import { encodeEntity, syncToRecs } from "@latticexyz/store-sync/recs"; +import { createConfig } from "wagmi"; +import { getDefaultWallets } from "@rainbow-me/rainbowkit"; import { getNetworkConfig } from "./getNetworkConfig"; import { world } from "./world"; import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; + import { createBurnerAccount, createContract, transportObserver, ContractWrite } from "@latticexyz/common"; import { Subject, share } from "rxjs"; @@ -51,6 +54,18 @@ export async function setupNetwork() { account: burnerAccount, }); + const { connectors } = getDefaultWallets({ + appName: "MUD", + projectId: "MUD", + chains: [networkConfig.chain], + }); + + const wagmiConfig = createConfig({ + autoConnect: true, + connectors, + publicClient, + }); + /* * Create an observable for contract writes that we can * pass into MUD dev tools for transaction observability. @@ -117,6 +132,8 @@ export async function setupNetwork() { playerEntity: encodeEntity({ address: "address" }, { address: burnerWalletClient.account.address }), publicClient, walletClient: burnerWalletClient, + wagmiConfig, + chain: networkConfig.chain, latestBlock$, storedBlockLogs$, waitForTransaction, From 26312fa3944fde0d31170ff14bc5ba38be0cc69b Mon Sep 17 00:00:00 2001 From: y77cao Date: Tue, 19 Sep 2023 14:40:31 -0400 Subject: [PATCH 2/6] create system calls after connect --- templates/react/packages/client/src/App.tsx | 26 ++++++++++---- .../packages/client/src/mud/constants.ts | 7 ++++ .../client/src/mud/createSystemCalls.ts | 36 ++++++++++++++++--- .../react/packages/client/src/mud/setup.ts | 3 -- .../packages/client/src/mud/setupNetwork.ts | 16 ++------- .../packages/client/src/mud/useSystemCalls.ts | 25 +++++++++++++ 6 files changed, 85 insertions(+), 28 deletions(-) create mode 100644 templates/react/packages/client/src/mud/constants.ts create mode 100644 templates/react/packages/client/src/mud/useSystemCalls.ts diff --git a/templates/react/packages/client/src/App.tsx b/templates/react/packages/client/src/App.tsx index 9363888f32..d236e99d8e 100644 --- a/templates/react/packages/client/src/App.tsx +++ b/templates/react/packages/client/src/App.tsx @@ -4,15 +4,17 @@ import { useWalletClient } from "wagmi"; import { useComponentValue } from "@latticexyz/react"; import { useMUD } from "./MUDContext"; import { singletonEntity } from "@latticexyz/store-sync/recs"; +import { useSystemCalls } from "./mud/useSystemCalls"; export const App = () => { - const { - components: { Counter }, - systemCalls: { increment }, - } = useMUD(); - const walletClient = useWalletClient(); + const { network, components } = useMUD(); + const { playerEntity, walletClient: burnerWalletClient } = network; + const { Counter, Delegations } = components; + const walletClient = useWalletClient(); + const systemCalls = useSystemCalls(network, components, walletClient.data); const counter = useComponentValue(Counter, singletonEntity); + const delegatee = useComponentValue(Delegations, playerEntity); return ( <> @@ -23,12 +25,24 @@ export const App = () => { type="button" onClick={async (event) => { event.preventDefault(); - console.log("new counter value:", await increment()); + console.log("new counter value:", await systemCalls?.increment()); }} > Increment + {walletClient.data && !delegatee ? ( + + ) : null} ); }; diff --git a/templates/react/packages/client/src/mud/constants.ts b/templates/react/packages/client/src/mud/constants.ts new file mode 100644 index 0000000000..78c56c0cf2 --- /dev/null +++ b/templates/react/packages/client/src/mud/constants.ts @@ -0,0 +1,7 @@ +import { encodePacked, toHex } from "viem"; + +const ROOT_NAMESPACE = 0; + +const encodedRootSpace = toHex(ROOT_NAMESPACE, { size: 16 }); +const encodedDelegationId = toHex("unlimited.d", { size: 16 }); +export const UNLIMITED_DELEGATION = encodePacked(["bytes16", "bytes16"], [encodedRootSpace, encodedDelegationId]); diff --git a/templates/react/packages/client/src/mud/createSystemCalls.ts b/templates/react/packages/client/src/mud/createSystemCalls.ts index 9858910ed2..b5e9d95c9e 100644 --- a/templates/react/packages/client/src/mud/createSystemCalls.ts +++ b/templates/react/packages/client/src/mud/createSystemCalls.ts @@ -2,11 +2,14 @@ * Create the system calls that the client can use to ask * for changes in the World state (using the System contracts). */ - +import { Hex, WalletClient, Transport, Chain, Account } from "viem"; import { getComponentValue } from "@latticexyz/recs"; -import { ClientComponents } from "./createClientComponents"; -import { SetupNetworkResult } from "./setupNetwork"; import { singletonEntity } from "@latticexyz/store-sync/recs"; +import { SetupNetworkResult } from "./setupNetwork"; +import { ClientComponents } from "./createClientComponents"; +import { UNLIMITED_DELEGATION } from "./constants"; +import { createContract } from "@latticexyz/common"; +import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; export type SystemCalls = ReturnType; @@ -28,9 +31,22 @@ export function createSystemCalls( * through createClientComponents.ts, but it originates in * syncToRecs (https://github.com/latticexyz/mud/blob/26dabb34321eedff7a43f3fcb46da4f3f5ba3708/templates/react/packages/client/src/mud/setupNetwork.ts#L39). */ - { worldContract, waitForTransaction }: SetupNetworkResult, - { Counter }: ClientComponents + { worldAddress, waitForTransaction, getResourceSelector, publicClient, write$ }: SetupNetworkResult, + { Counter }: ClientComponents, + walletClient: WalletClient ) { + /* + * Create an object for communicating with the deployed World. + */ + const worldContract = createContract({ + address: worldAddress as Hex, + abi: IWorldAbi, + publicClient, + walletClient, + onWrite: (write) => write$.next(write), + getResourceSelector, + }); + const increment = async () => { /* * Because IncrementSystem @@ -43,7 +59,17 @@ export function createSystemCalls( return getComponentValue(Counter, singletonEntity); }; + const registerDelegation = async (delegatee: Hex) => { + const callData = "0x"; + console.log({ delegatee, UNLIMITED_DELEGATION, callData }); + const tx = await worldContract.write.registerDelegation([delegatee, UNLIMITED_DELEGATION, callData], { + gas: 15000000n, + }); + await waitForTransaction(tx); + }; + return { increment, + registerDelegation, }; } diff --git a/templates/react/packages/client/src/mud/setup.ts b/templates/react/packages/client/src/mud/setup.ts index 8f9fdbab34..c95fa97b68 100644 --- a/templates/react/packages/client/src/mud/setup.ts +++ b/templates/react/packages/client/src/mud/setup.ts @@ -3,7 +3,6 @@ */ import { createClientComponents } from "./createClientComponents"; -import { createSystemCalls } from "./createSystemCalls"; import { setupNetwork } from "./setupNetwork"; export type SetupResult = Awaited>; @@ -11,11 +10,9 @@ export type SetupResult = Awaited>; export async function setup() { const network = await setupNetwork(); const components = createClientComponents(network); - const systemCalls = createSystemCalls(network, components); return { network, components, - systemCalls, }; } diff --git a/templates/react/packages/client/src/mud/setupNetwork.ts b/templates/react/packages/client/src/mud/setupNetwork.ts index 7871825f32..304fa4d338 100644 --- a/templates/react/packages/client/src/mud/setupNetwork.ts +++ b/templates/react/packages/client/src/mud/setupNetwork.ts @@ -11,7 +11,6 @@ import { getDefaultWallets } from "@rainbow-me/rainbowkit"; import { getNetworkConfig } from "./getNetworkConfig"; import { world } from "./world"; -import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; import { createBurnerAccount, createContract, transportObserver, ContractWrite } from "@latticexyz/common"; @@ -86,18 +85,6 @@ export async function setupNetwork() { startBlock: BigInt(networkConfig.initialBlockNumber), }); - /* - * Create an object for communicating with the deployed World. - */ - const worldContract = createContract({ - address: networkConfig.worldAddress as Hex, - abi: IWorldAbi, - publicClient, - walletClient: burnerWalletClient, - onWrite: (write) => write$.next(write), - getResourceSelector, - }); - /* * If there is a faucet, request (test) ETH if you have * less than 1 ETH. Repeat every 20 seconds to ensure you don't @@ -128,6 +115,7 @@ export async function setupNetwork() { return { world, + worldAddress: networkConfig.worldAddress, components, playerEntity: encodeEntity({ address: "address" }, { address: burnerWalletClient.account.address }), publicClient, @@ -137,7 +125,7 @@ export async function setupNetwork() { latestBlock$, storedBlockLogs$, waitForTransaction, - worldContract, + getResourceSelector, write$: write$.asObservable().pipe(share()), }; } diff --git a/templates/react/packages/client/src/mud/useSystemCalls.ts b/templates/react/packages/client/src/mud/useSystemCalls.ts new file mode 100644 index 0000000000..aa01881034 --- /dev/null +++ b/templates/react/packages/client/src/mud/useSystemCalls.ts @@ -0,0 +1,25 @@ +import { Schema } from "@latticexyz/recs"; +import { useEffect, useState } from "react"; +import { WalletClient, Transport, Chain, Account } from "viem"; +import { createSystemCalls } from "./createSystemCalls"; +import { SetupNetworkResult } from "./setupNetwork"; +import { ClientComponents } from "./createClientComponents"; + +export function useSystemCalls( + network: SetupNetworkResult, + components: ClientComponents, + client: WalletClient | undefined +) { + const [systemCalls, setSystemCalls] = useState(undefined); + + useEffect(() => { + if (!client) return; + + const systemCalls = createSystemCalls(network, components, client); + setSystemCalls(systemCalls); + + // TODO what's the cleanup fn? + }, [client]); + + return systemCalls; +} From d7bb9f5568682d98c05f1b514cb8658ebf3bdcc2 Mon Sep 17 00:00:00 2001 From: y77cao Date: Tue, 19 Sep 2023 22:23:43 -0400 Subject: [PATCH 3/6] delegationControl hook --- templates/react/packages/client/src/App.tsx | 12 +++-- templates/react/packages/client/src/index.tsx | 8 ++-- .../client/src/mud/createSystemCalls.ts | 2 +- .../packages/client/src/mud/setupNetwork.ts | 9 ++-- .../client/src/mud/useDelegationControl.ts | 47 +++++++++++++++++++ .../packages/client/src/mud/useSystemCalls.ts | 6 +-- 6 files changed, 66 insertions(+), 18 deletions(-) create mode 100644 templates/react/packages/client/src/mud/useDelegationControl.ts diff --git a/templates/react/packages/client/src/App.tsx b/templates/react/packages/client/src/App.tsx index d236e99d8e..7d865d5d5a 100644 --- a/templates/react/packages/client/src/App.tsx +++ b/templates/react/packages/client/src/App.tsx @@ -5,16 +5,18 @@ import { useComponentValue } from "@latticexyz/react"; import { useMUD } from "./MUDContext"; import { singletonEntity } from "@latticexyz/store-sync/recs"; import { useSystemCalls } from "./mud/useSystemCalls"; +import { useDelegationControl } from "./mud/useDelegationControl"; export const App = () => { const { network, components } = useMUD(); - const { playerEntity, walletClient: burnerWalletClient } = network; - const { Counter, Delegations } = components; + // TODO rename to delegatee + const { walletClient: burnerWalletClient } = network; + const { Counter } = components; const walletClient = useWalletClient(); const systemCalls = useSystemCalls(network, components, walletClient.data); + const delegationControlId = useDelegationControl(walletClient.data, burnerWalletClient, components); const counter = useComponentValue(Counter, singletonEntity); - const delegatee = useComponentValue(Delegations, playerEntity); return ( <> @@ -31,13 +33,13 @@ export const App = () => { Increment - {walletClient.data && !delegatee ? ( + {walletClient.data && !delegationControlId ? ( - {walletClient.data && !delegationControlId ? ( + {account.isConnected && !delegationControlId ? (