Skip to content

Commit

Permalink
feat(common): add viem actions that work the same as the current wrap…
Browse files Browse the repository at this point in the history
…pers (#2347)

Co-authored-by: Kevin Ingersoll <kingersoll@gmail.com>
  • Loading branch information
tash-2s and holic committed Feb 29, 2024
1 parent db314a7 commit 5926765
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 50 deletions.
25 changes: 25 additions & 0 deletions .changeset/moody-shirts-carry.md
@@ -0,0 +1,25 @@
---
"@latticexyz/common": minor
"create-mud": minor
---

Added viem custom client actions that work the same as MUD's now-deprecated `getContract`, `writeContract`, and `sendTransaction` wrappers. Templates have been updated to reflect the new patterns.

You can migrate your own code like this:

```diff
-import { createWalletClient } from "viem";
-import { getContract, writeContract, sendTransaction } from "@latticexyz/common";
+import { createWalletClient, getContract } from "viem";
+import { transactionQueue, writeObserver } from "@latticexyz/common/actions";

-const walletClient = createWalletClient(...);
+const walletClient = createWalletClient(...)
+ .extend(transactionQueue())
+ .extend(writeObserver({ onWrite });

const worldContract = getContract({
client: { publicClient, walletClient },
- onWrite,
});
```
4 changes: 4 additions & 0 deletions packages/common/package.json
Expand Up @@ -11,6 +11,7 @@
"type": "module",
"exports": {
".": "./dist/index.js",
"./actions": "./dist/actions.js",
"./chains": "./dist/chains.js",
"./codegen": "./dist/codegen.js",
"./errors": "./dist/errors.js",
Expand All @@ -23,6 +24,9 @@
"index": [
"./src/index.ts"
],
"actions": [
"./src/actions/index.ts"
],
"chains": [
"./src/chains/index.ts"
],
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/actions/index.ts
@@ -0,0 +1,2 @@
export * from "./transactionQueue";
export * from "./writeObserver";
14 changes: 14 additions & 0 deletions packages/common/src/actions/transactionQueue.ts
@@ -0,0 +1,14 @@
import type { Transport, Chain, Account, WalletActions, WalletClient } from "viem";
import { writeContract as mud_writeContract } from "../writeContract";
import { sendTransaction as mud_sendTransaction } from "../sendTransaction";

export function transactionQueue<TChain extends Chain, TAccount extends Account>(): (
client: WalletClient<Transport, TChain, TAccount>,
) => Pick<WalletActions<TChain, TAccount>, "writeContract" | "sendTransaction"> {
return (client) => ({
// Applies to: `client.writeContract`, `getContract(client, ...).write`
writeContract: (args) => mud_writeContract(client, args),
// Applies to: `client.sendTransaction`
sendTransaction: (args) => mud_sendTransaction(client, args),
});
}
34 changes: 34 additions & 0 deletions packages/common/src/actions/writeObserver.ts
@@ -0,0 +1,34 @@
import type {
WriteContractParameters,
Transport,
Chain,
Account,
WalletActions,
WalletClient,
WriteContractReturnType,
} from "viem";
import { getAction } from "viem/utils";
import { writeContract } from "viem/actions";
import { type ContractWrite } from "../getContract";

type WriteObserverParameters = { onWrite: (write: ContractWrite) => void };

export function writeObserver<TChain extends Chain, TAccount extends Account>({
onWrite,
}: WriteObserverParameters): (
client: WalletClient<Transport, TChain, TAccount>,
) => Pick<WalletActions<TChain, TAccount>, "writeContract"> {
let nextWriteId = 0;

return (client) => ({
// Applies to: `client.writeContract`, `getContract(client, ...).write`
writeContract: (args): Promise<WriteContractReturnType> => {
const result = getAction(client, writeContract, "writeContract")(args);

const id = `${client.chain.id}:${client.account.address}:${nextWriteId++}`;
onWrite({ id, request: args as WriteContractParameters, result });

return result;
},
});
}
1 change: 1 addition & 0 deletions packages/common/src/getContract.ts
Expand Up @@ -56,6 +56,7 @@ export type GetContractOptions<

// TODO: migrate away from this approach once we can hook into viem: https://github.com/wagmi-dev/viem/discussions/1230

/** @deprecated Use `walletClient.extend(transactionQueue()).extend(writeObserver({ onWrite }))` and viem's `getContract` instead. */
export function getContract<
TTransport extends Transport,
TAddress extends Address,
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/sendTransaction.ts
Expand Up @@ -17,6 +17,7 @@ const debug = parentDebug.extend("sendTransaction");

// TODO: migrate away from this approach once we can hook into viem's nonce management: https://github.com/wagmi-dev/viem/discussions/1230

/** @deprecated Use `walletClient.extend(transactionQueue())` instead. */
export async function sendTransaction<
TChain extends Chain | undefined,
TAccount extends Account | undefined,
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/writeContract.ts
Expand Up @@ -20,6 +20,7 @@ const debug = parentDebug.extend("writeContract");

// TODO: migrate away from this approach once we can hook into viem's nonce management: https://github.com/wagmi-dev/viem/discussions/1230

/** @deprecated Use `walletClient.extend(transactionQueue())` instead. */
export async function writeContract<
chain extends Chain | undefined,
account extends Account | undefined,
Expand Down
1 change: 1 addition & 0 deletions packages/common/tsup.config.ts
Expand Up @@ -3,6 +3,7 @@ import { defineConfig } from "tsup";
export default defineConfig({
entry: {
index: "src/index.ts",
actions: "src/actions/index.ts",
chains: "src/chains/index.ts",
codegen: "src/codegen/index.ts",
errors: "src/errors/index.ts",
Expand Down
32 changes: 22 additions & 10 deletions templates/phaser/packages/client/src/mud/setupNetwork.ts
Expand Up @@ -3,13 +3,24 @@
* (https://viem.sh/docs/getting-started.html).
* This line imports the functions we need from it.
*/
import { createPublicClient, fallback, webSocket, http, createWalletClient, Hex, parseEther, ClientConfig } from "viem";
import {
createPublicClient,
fallback,
webSocket,
http,
createWalletClient,
Hex,
parseEther,
ClientConfig,
getContract,
} from "viem";
import { createFaucetService } from "@latticexyz/services/faucet";
import { encodeEntity, syncToRecs } from "@latticexyz/store-sync/recs";
import { getNetworkConfig } from "./getNetworkConfig";
import { world } from "./world";
import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json";
import { createBurnerAccount, getContract, transportObserver, ContractWrite } from "@latticexyz/common";
import { createBurnerAccount, transportObserver, ContractWrite } from "@latticexyz/common";
import { transactionQueue, writeObserver } from "@latticexyz/common/actions";
import { Subject, share } from "rxjs";

/*
Expand Down Expand Up @@ -39,6 +50,12 @@ export async function setupNetwork() {

const publicClient = createPublicClient(clientOptions);

/*
* Create an observable for contract writes that we can
* pass into MUD dev tools for transaction observability.
*/
const write$ = new Subject<ContractWrite>();

/*
* Create a temporary wallet and a viem client for it
* (see https://viem.sh/docs/clients/wallet.html).
Expand All @@ -47,13 +64,9 @@ export async function setupNetwork() {
const burnerWalletClient = createWalletClient({
...clientOptions,
account: burnerAccount,
});

/*
* Create an observable for contract writes that we can
* pass into MUD dev tools for transaction observability.
*/
const write$ = new Subject<ContractWrite>();
})
.extend(transactionQueue())
.extend(writeObserver({ onWrite: (write) => write$.next(write) }));

/*
* Create an object for communicating with the deployed World.
Expand All @@ -62,7 +75,6 @@ export async function setupNetwork() {
address: networkConfig.worldAddress as Hex,
abi: IWorldAbi,
client: { public: publicClient, wallet: burnerWalletClient },
onWrite: (write) => write$.next(write),
});

/*
Expand Down
32 changes: 22 additions & 10 deletions templates/react-ecs/packages/client/src/mud/setupNetwork.ts
Expand Up @@ -3,14 +3,25 @@
* (https://viem.sh/docs/getting-started.html).
* This line imports the functions we need from it.
*/
import { createPublicClient, fallback, webSocket, http, createWalletClient, Hex, parseEther, ClientConfig } from "viem";
import {
createPublicClient,
fallback,
webSocket,
http,
createWalletClient,
Hex,
parseEther,
ClientConfig,
getContract,
} from "viem";
import { createFaucetService } from "@latticexyz/services/faucet";
import { encodeEntity, syncToRecs } from "@latticexyz/store-sync/recs";

import { getNetworkConfig } from "./getNetworkConfig";
import { world } from "./world";
import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json";
import { createBurnerAccount, getContract, transportObserver, ContractWrite } from "@latticexyz/common";
import { createBurnerAccount, transportObserver, ContractWrite } from "@latticexyz/common";
import { transactionQueue, writeObserver } from "@latticexyz/common/actions";

import { Subject, share } from "rxjs";

Expand Down Expand Up @@ -41,6 +52,12 @@ export async function setupNetwork() {

const publicClient = createPublicClient(clientOptions);

/*
* Create an observable for contract writes that we can
* pass into MUD dev tools for transaction observability.
*/
const write$ = new Subject<ContractWrite>();

/*
* Create a temporary wallet and a viem client for it
* (see https://viem.sh/docs/clients/wallet.html).
Expand All @@ -49,13 +66,9 @@ export async function setupNetwork() {
const burnerWalletClient = createWalletClient({
...clientOptions,
account: burnerAccount,
});

/*
* Create an observable for contract writes that we can
* pass into MUD dev tools for transaction observability.
*/
const write$ = new Subject<ContractWrite>();
})
.extend(transactionQueue())
.extend(writeObserver({ onWrite: (write) => write$.next(write) }));

/*
* Create an object for communicating with the deployed World.
Expand All @@ -64,7 +77,6 @@ export async function setupNetwork() {
address: networkConfig.worldAddress as Hex,
abi: IWorldAbi,
client: { public: publicClient, wallet: burnerWalletClient },
onWrite: (write) => write$.next(write),
});

/*
Expand Down
32 changes: 22 additions & 10 deletions templates/react/packages/client/src/mud/setupNetwork.ts
Expand Up @@ -3,12 +3,23 @@
* (https://viem.sh/docs/getting-started.html).
* This line imports the functions we need from it.
*/
import { createPublicClient, fallback, webSocket, http, createWalletClient, Hex, parseEther, ClientConfig } from "viem";
import {
createPublicClient,
fallback,
webSocket,
http,
createWalletClient,
Hex,
parseEther,
ClientConfig,
getContract,
} from "viem";
import { createFaucetService } from "@latticexyz/services/faucet";
import { syncToZustand } from "@latticexyz/store-sync/zustand";
import { getNetworkConfig } from "./getNetworkConfig";
import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json";
import { createBurnerAccount, getContract, transportObserver, ContractWrite } from "@latticexyz/common";
import { createBurnerAccount, transportObserver, ContractWrite } from "@latticexyz/common";
import { transactionQueue, writeObserver } from "@latticexyz/common/actions";
import { Subject, share } from "rxjs";

/*
Expand Down Expand Up @@ -38,6 +49,12 @@ export async function setupNetwork() {

const publicClient = createPublicClient(clientOptions);

/*
* Create an observable for contract writes that we can
* pass into MUD dev tools for transaction observability.
*/
const write$ = new Subject<ContractWrite>();

/*
* Create a temporary wallet and a viem client for it
* (see https://viem.sh/docs/clients/wallet.html).
Expand All @@ -46,13 +63,9 @@ export async function setupNetwork() {
const burnerWalletClient = createWalletClient({
...clientOptions,
account: burnerAccount,
});

/*
* Create an observable for contract writes that we can
* pass into MUD dev tools for transaction observability.
*/
const write$ = new Subject<ContractWrite>();
})
.extend(transactionQueue())
.extend(writeObserver({ onWrite: (write) => write$.next(write) }));

/*
* Create an object for communicating with the deployed World.
Expand All @@ -61,7 +74,6 @@ export async function setupNetwork() {
address: networkConfig.worldAddress as Hex,
abi: IWorldAbi,
client: { public: publicClient, wallet: burnerWalletClient },
onWrite: (write) => write$.next(write),
});

/*
Expand Down
32 changes: 22 additions & 10 deletions templates/threejs/packages/client/src/mud/setupNetwork.ts
Expand Up @@ -3,13 +3,24 @@
* (https://viem.sh/docs/getting-started.html).
* This line imports the functions we need from it.
*/
import { createPublicClient, fallback, webSocket, http, createWalletClient, Hex, parseEther, ClientConfig } from "viem";
import {
createPublicClient,
fallback,
webSocket,
http,
createWalletClient,
Hex,
parseEther,
ClientConfig,
getContract,
} from "viem";
import { createFaucetService } from "@latticexyz/services/faucet";
import { encodeEntity, syncToRecs } from "@latticexyz/store-sync/recs";
import { getNetworkConfig } from "./getNetworkConfig";
import { world } from "./world";
import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json";
import { createBurnerAccount, getContract, transportObserver, ContractWrite } from "@latticexyz/common";
import { createBurnerAccount, transportObserver, ContractWrite } from "@latticexyz/common";
import { transactionQueue, writeObserver } from "@latticexyz/common/actions";
import { Subject, share } from "rxjs";

/*
Expand Down Expand Up @@ -39,6 +50,12 @@ export async function setupNetwork() {

const publicClient = createPublicClient(clientOptions);

/*
* Create an observable for contract writes that we can
* pass into MUD dev tools for transaction observability.
*/
const write$ = new Subject<ContractWrite>();

/*
* Create a temporary wallet and a viem client for it
* (see https://viem.sh/docs/clients/wallet.html).
Expand All @@ -47,13 +64,9 @@ export async function setupNetwork() {
const burnerWalletClient = createWalletClient({
...clientOptions,
account: burnerAccount,
});

/*
* Create an observable for contract writes that we can
* pass into MUD dev tools for transaction observability.
*/
const write$ = new Subject<ContractWrite>();
})
.extend(transactionQueue())
.extend(writeObserver({ onWrite: (write) => write$.next(write) }));

/*
* Create an object for communicating with the deployed World.
Expand All @@ -62,7 +75,6 @@ export async function setupNetwork() {
address: networkConfig.worldAddress as Hex,
abi: IWorldAbi,
client: { public: publicClient, wallet: burnerWalletClient },
onWrite: (write) => write$.next(write),
});

/*
Expand Down

0 comments on commit 5926765

Please sign in to comment.