mud

MUD is a framework for building ambitious onchain applications

MIT License

Downloads
318.4K
Stars
643
Committers
80

Bot releases are visible (Hide)

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Major Changes

  • #1284 939916bc Thanks @holic! - MUD dev tools is updated to latest sync stack. You must now pass in all of its data requirements rather than relying on magic globals.

    import { mount as mountDevTools } from "@latticexyz/dev-tools";
    
    - mountDevTools();
    + mountDevTools({
    +   config,
    +   publicClient,
    +   walletClient,
    +   latestBlock$,
    +   blockStorageOperations$,
    +   worldAddress,
    +   worldAbi,
    +   write$,
    +   // if you're using recs
    +   recsWorld,
    + });
    

    It's also advised to wrap dev tools so that it is only mounted during development mode. Here's how you do this with Vite:

    // https://vitejs.dev/guide/env-and-mode.html
    if (import.meta.env.DEV) {
      mountDevTools({ ... });
    }
    

Patch Changes

mud - [email protected]

Published by github-actions[bot] about 1 year ago

Major Changes

  • #1278 48c51b52 Thanks @holic! - RECS components are now dynamically created and inferred from your MUD config when using syncToRecs.

    To migrate existing projects after upgrading to this MUD version:

    1. Remove contractComponents.ts from client/src/mud

    2. Remove components argument from syncToRecs

    3. Update build:mud and dev scripts in contracts/package.json to remove tsgen

      - "build:mud": "mud tablegen && mud worldgen && mud tsgen --configPath mud.config.ts --out ../client/src/mud",
      + "build:mud": "mud tablegen && mud worldgen",
      
      - "dev": "pnpm mud dev-contracts --tsgenOutput ../client/src/mud",
      + "dev": "pnpm mud dev-contracts",
      
  • #1284 939916bc Thanks @holic! - MUD dev tools is updated to latest sync stack. You must now pass in all of its data requirements rather than relying on magic globals.

    import { mount as mountDevTools } from "@latticexyz/dev-tools";
    
    - mountDevTools();
    + mountDevTools({
    +   config,
    +   publicClient,
    +   walletClient,
    +   latestBlock$,
    +   blockStorageOperations$,
    +   worldAddress,
    +   worldAbi,
    +   write$,
    +   // if you're using recs
    +   recsWorld,
    + });
    

    It's also advised to wrap dev tools so that it is only mounted during development mode. Here's how you do this with Vite:

    // https://vitejs.dev/guide/env-and-mode.html
    if (import.meta.env.DEV) {
      mountDevTools({ ... });
    }
    

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Minor Changes

  • #1284 939916bc Thanks @holic! - createContract now has an onWrite callback so you can observe writes. This is useful for wiring up the transanction log in MUD dev tools.

    import { createContract, ContractWrite } from "@latticexyz/common";
    import { Subject } from "rxjs";
    
    const write$ = new Subject<ContractWrite>();
    creactContract({
      ...
      onWrite: (write) => write$.next(write),
    });
    
  • #1308 b8a6158d Thanks @holic! - - adds defaultPriorityFee to mudFoundry for better support with MUD's default anvil config and removes workaround in createContract

    • improves nonce error detection using viem's custom errors

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Major Changes

  • #1278 48c51b52 Thanks @holic! - RECS components are now dynamically created and inferred from your MUD config when using syncToRecs.

    To migrate existing projects after upgrading to this MUD version:

    1. Remove contractComponents.ts from client/src/mud

    2. Remove components argument from syncToRecs

    3. Update build:mud and dev scripts in contracts/package.json to remove tsgen

      - "build:mud": "mud tablegen && mud worldgen && mud tsgen --configPath mud.config.ts --out ../client/src/mud",
      + "build:mud": "mud tablegen && mud worldgen",
      
      - "dev": "pnpm mud dev-contracts --tsgenOutput ../client/src/mud",
      + "dev": "pnpm mud dev-contracts",
      

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Major Changes

  • #1308 b8a6158d Thanks @holic! - - removes our own getLogs function now that viem's getLogs supports using multiple events per RPC call.
    • removes isNonPendingBlock and isNonPendingLog helpers now that viem narrows Block and Log types based on inputs
    • simplifies groupLogsByBlockNumber types and tests

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Major Changes

  • #1198 e86fbc12 Thanks @holic! - Adds store indexer service package with utils to query the indexer service.

    You can run the indexer locally by checking out the MUD monorepo, installing/building everything, and running pnpm start:local from packages/store-indexer.

    To query the indexer in the client, you can create a tRPC client with a URL pointing to the indexer service and call the available tRPC methods:

    import { createIndexerClient } from "@latticexyz/store-sync/trpc-indexer";
    
    const indexer = createIndexerClient({ url: indexerUrl });
    const result = await indexer.findAll.query({
      chainId: publicClient.chain.id,
      address,
    });
    

    If you're using syncToRecs, you can just pass in the indexerUrl option as a shortcut to the above:

    import { syncToRecs } from "@latticexyz/store-sync/recs";
    
    syncToRecs({
      ...
      indexerUrl: "https://your.indexer.service",
    });
    

Minor Changes

  • #1234 131c63e5 Thanks @holic! - - Accept a plain viem PublicClient (instead of requiring a Chain to be set) in store-sync and store-indexer functions. These functions now fetch chain ID using publicClient.getChainId() when no publicClient.chain.id is present.

    • Allow configuring store-indexer with a set of RPC URLs (RPC_HTTP_URL and RPC_WS_URL) instead of CHAIN_ID.
  • #1235 582388ba Thanks @holic! - Export singletonEntity as const rather than within the syncToRecs result.

    - const { singletonEntity, ... } = syncToRecs({ ... });
    + import { singletonEntity, syncToRecs } from "@latticexyz/store-sync/recs";
    + const { ... } = syncToRecs({ ... });
    

Patch Changes

  • #1228 57a52608 Thanks @holic! - Adds latestBlockNumber and lastBlockNumberProcessed to internal SyncProgress component

  • #1197 9e5baf4f Thanks @holic! - Add RECS sync strategy and corresponding utils

    import { createPublicClient, http } from 'viem';
    import { syncToRecs } from '@latticexyz/store-sync';
    import storeConfig from 'contracts/mud.config';
    import { defineContractComponents } from './defineContractComponents';
    
    const publicClient = createPublicClient({
      chain,
      transport: http(),
      pollingInterval: 1000,
    });
    
    const { components, singletonEntity, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({
      world,
      config: storeConfig,
      address: '0x...',
      publicClient,
      components: defineContractComponents(...),
    });
    
  • #1235 582388ba Thanks @holic! - Add startBlock option to syncToRecs.

    import { syncToRecs } from "@latticexyz/store-sync/recs";
    import worlds from "contracts/worlds.json";
    
    syncToRecs({
      startBlock: worlds['31337'].blockNumber,
      ...
    });
    
  • #1258 6c673325 Thanks @holic! - Add tableIdToHex and hexToTableId pure functions and move/deprecate TableId.

  • Updated dependencies [c963b46c, 3fb9ce28, 35c9f33d, 5c965a91, b02f9d0e, 60cfd089, 6071163f, 6c673325, cd5abcc3, afdba793, cc2c8da0]:

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Major Changes

  • #1198 e86fbc12 Thanks @holic! - Adds store indexer service package with utils to query the indexer service.

    You can run the indexer locally by checking out the MUD monorepo, installing/building everything, and running pnpm start:local from packages/store-indexer.

    To query the indexer in the client, you can create a tRPC client with a URL pointing to the indexer service and call the available tRPC methods:

    import { createIndexerClient } from "@latticexyz/store-sync/trpc-indexer";
    
    const indexer = createIndexerClient({ url: indexerUrl });
    const result = await indexer.findAll.query({
      chainId: publicClient.chain.id,
      address,
    });
    

    If you're using syncToRecs, you can just pass in the indexerUrl option as a shortcut to the above:

    import { syncToRecs } from "@latticexyz/store-sync/recs";
    
    syncToRecs({
      ...
      indexerUrl: "https://your.indexer.service",
    });
    

Minor Changes

  • #1234 131c63e5 Thanks @holic! - - Accept a plain viem PublicClient (instead of requiring a Chain to be set) in store-sync and store-indexer functions. These functions now fetch chain ID using publicClient.getChainId() when no publicClient.chain.id is present.
    • Allow configuring store-indexer with a set of RPC URLs (RPC_HTTP_URL and RPC_WS_URL) instead of CHAIN_ID.

Patch Changes

  • #1214 60cfd089 Thanks @holic! - Templates and examples now use MUD's new sync packages, all built on top of viem. This greatly speeds up and stabilizes our networking code and improves types throughout.

    These new sync packages come with support for our recs package, including encodeEntity and decodeEntity utilities for composite keys.

    If you're using store-cache and useRow/useRows, you should wait to upgrade until we have a suitable replacement for those libraries. We're working on a sql.js-powered sync module that will replace store-cache.

    Migrate existing RECS apps to new sync packages

    As you migrate, you may find some features replaced, removed, or not included by default. Please open an issue and let us know if we missed anything.

    1. Add @latticexyz/store-sync package to your app's client package and make sure viem is pinned to version 1.3.1 (otherwise you may get type errors)

    2. In your supportedChains.ts, replace foundry chain with our new mudFoundry chain.

      - import { foundry } from "viem/chains";
      - import { MUDChain, latticeTestnet } from "@latticexyz/common/chains";
      + import { MUDChain, latticeTestnet, mudFoundry } from "@latticexyz/common/chains";
      
      - export const supportedChains: MUDChain[] = [foundry, latticeTestnet];
      + export const supportedChains: MUDChain[] = [mudFoundry, latticeTestnet];
      
    3. In getNetworkConfig.ts, remove the return type (to let TS infer it for now), remove now-unused config values, and add the viem chain object.

      - export async function getNetworkConfig(): Promise<NetworkConfig> {
      + export async function getNetworkConfig() {
      
        const initialBlockNumber = params.has("initialBlockNumber")
          ? Number(params.get("initialBlockNumber"))
      -   : world?.blockNumber ?? -1; // -1 will attempt to find the block number from RPC
      +   : world?.blockNumber ?? 0n;
      
      + return {
      +   privateKey: getBurnerWallet().value,
      +   chain,
      +   worldAddress,
      +   initialBlockNumber,
      +   faucetServiceUrl: params.get("faucet") ?? chain.faucetUrl,
      + };
      
    4. In setupNetwork.ts, replace setupMUDV2Network with syncToRecs.

      - import { setupMUDV2Network } from "@latticexyz/std-client";
      - import { createFastTxExecutor, createFaucetService, getSnapSyncRecords } from "@latticexyz/network";
      + import { createFaucetService } from "@latticexyz/network";
      + import { createPublicClient, fallback, webSocket, http, createWalletClient, getContract, Hex, parseEther, ClientConfig } from "viem";
      + import { encodeEntity, syncToRecs } from "@latticexyz/store-sync/recs";
      + import { createBurnerAccount, createContract, transportObserver } from "@latticexyz/common";
      
      - const result = await setupMUDV2Network({
      -   ...
      - });
      
      + const clientOptions = {
      +   chain: networkConfig.chain,
      +   transport: transportObserver(fallback([webSocket(), http()])),
      +   pollingInterval: 1000,
      + } as const satisfies ClientConfig;
      
      + const publicClient = createPublicClient(clientOptions);
      
      + const burnerAccount = createBurnerAccount(networkConfig.privateKey as Hex);
      + const burnerWalletClient = createWalletClient({
      +   ...clientOptions,
      +   account: burnerAccount,
      + });
      
      + const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({
      +   world,
      +   config: storeConfig,
      +   address: networkConfig.worldAddress as Hex,
      +   publicClient,
      +   components: contractComponents,
      +   startBlock: BigInt(networkConfig.initialBlockNumber),
      +   indexerUrl: networkConfig.indexerUrl ?? undefined,
      + });
      
      + const worldContract = createContract({
      +   address: networkConfig.worldAddress as Hex,
      +   abi: IWorld__factory.abi,
      +   publicClient,
      +   walletClient: burnerWalletClient,
      + });
      
        // Request drip from faucet
      - const signer = result.network.signer.get();
      - if (networkConfig.faucetServiceUrl && signer) {
      -   const address = await signer.getAddress();
      + if (networkConfig.faucetServiceUrl) {
      +   const address = burnerAccount.address;
      
        const requestDrip = async () => {
      -   const balance = await signer.getBalance();
      +   const balance = await publicClient.getBalance({ address });
          console.info(`[Dev Faucet]: Player balance -> ${balance}`);
      -   const lowBalance = balance?.lte(utils.parseEther("1"));
      +   const lowBalance = balance < parseEther("1");
      

      You can remove the previous ethers worldContract, snap sync code, and fast transaction executor.

      The return of setupNetwork is a bit different than before, so you may have to do corresponding app changes.

      + return {
      +   world,
      +   components,
      +   playerEntity: encodeEntity({ address: "address" }, { address: burnerWalletClient.account.address }),
      +   publicClient,
      +   walletClient: burnerWalletClient,
      +   latestBlock$,
      +   blockStorageOperations$,
      +   waitForTransaction,
      +   worldContract,
      + };
      
    5. Update createSystemCalls with the new return type of setupNetwork.

        export function createSystemCalls(
      -   { worldSend, txReduced$, singletonEntity }: SetupNetworkResult,
      +   { worldContract, waitForTransaction }: SetupNetworkResult,
          { Counter }: ClientComponents
        ) {
           const increment = async () => {
      -      const tx = await worldSend("increment", []);
      -      await awaitStreamValue(txReduced$, (txHash) => txHash === tx.hash);
      +      const tx = await worldContract.write.increment();
      +      await waitForTransaction(tx);
             return getComponentValue(Counter, singletonEntity);
           };
      
    6. (optional) If you still need a clock, you can create it with:

      import { map, filter } from "rxjs";
      import { createClock } from "@latticexyz/network";
      
      const clock = createClock({
        period: 1000,
        initialTime: 0,
        syncInterval: 5000,
      });
      
      world.registerDisposer(() => clock.dispose());
      
      latestBlock$
        .pipe(
          map((block) => Number(block.timestamp) * 1000), // Map to timestamp in ms
          filter((blockTimestamp) => blockTimestamp !== clock.lastUpdateTime), // Ignore if the clock was already refreshed with this block
          filter((blockTimestamp) => blockTimestamp !== clock.currentTime) // Ignore if the current local timestamp is correct
        )
        .subscribe(clock.update); // Update the local clock
      

    If you're using the previous LoadingState component, you'll want to migrate to the new SyncProgress:

    import { SyncStep, singletonEntity } from "@latticexyz/store-sync/recs";
    
    const syncProgress = useComponentValue(SyncProgress, singletonEntity, {
      message: "Connecting",
      percentage: 0,
      step: SyncStep.INITIALIZE,
    });
    
    if (syncProgress.step === SyncStep.LIVE) {
      // we're live!
    }
    
  • Updated dependencies [c963b46c, e86fbc12, 3fb9ce28, 35c9f33d, 57a52608, 9e5baf4f, 131c63e5, 5c965a91, 582388ba, 582388ba, 60cfd089, 6071163f, 6c673325, cd5abcc3, cc2c8da0]:

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Patch Changes

mud - [email protected]

Published by github-actions[bot] about 1 year ago

mud - [email protected]

Published by github-actions[bot] about 1 year ago

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Patch Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Minor Changes

mud - @latticexyz/[email protected]

Published by github-actions[bot] about 1 year ago

Patch Changes

  • #1214 60cfd089 Thanks @holic! - Templates and examples now use MUD's new sync packages, all built on top of viem. This greatly speeds up and stabilizes our networking code and improves types throughout.

    These new sync packages come with support for our recs package, including encodeEntity and decodeEntity utilities for composite keys.

    If you're using store-cache and useRow/useRows, you should wait to upgrade until we have a suitable replacement for those libraries. We're working on a sql.js-powered sync module that will replace store-cache.

    Migrate existing RECS apps to new sync packages

    As you migrate, you may find some features replaced, removed, or not included by default. Please open an issue and let us know if we missed anything.

    1. Add @latticexyz/store-sync package to your app's client package and make sure viem is pinned to version 1.3.1 (otherwise you may get type errors)

    2. In your supportedChains.ts, replace foundry chain with our new mudFoundry chain.

      - import { foundry } from "viem/chains";
      - import { MUDChain, latticeTestnet } from "@latticexyz/common/chains";
      + import { MUDChain, latticeTestnet, mudFoundry } from "@latticexyz/common/chains";
      
      - export const supportedChains: MUDChain[] = [foundry, latticeTestnet];
      + export const supportedChains: MUDChain[] = [mudFoundry, latticeTestnet];
      
    3. In getNetworkConfig.ts, remove the return type (to let TS infer it for now), remove now-unused config values, and add the viem chain object.

      - export async function getNetworkConfig(): Promise<NetworkConfig> {
      + export async function getNetworkConfig() {
      
        const initialBlockNumber = params.has("initialBlockNumber")
          ? Number(params.get("initialBlockNumber"))
      -   : world?.blockNumber ?? -1; // -1 will attempt to find the block number from RPC
      +   : world?.blockNumber ?? 0n;
      
      + return {
      +   privateKey: getBurnerWallet().value,
      +   chain,
      +   worldAddress,
      +   initialBlockNumber,
      +   faucetServiceUrl: params.get("faucet") ?? chain.faucetUrl,
      + };
      
    4. In setupNetwork.ts, replace setupMUDV2Network with syncToRecs.

      - import { setupMUDV2Network } from "@latticexyz/std-client";
      - import { createFastTxExecutor, createFaucetService, getSnapSyncRecords } from "@latticexyz/network";
      + import { createFaucetService } from "@latticexyz/network";
      + import { createPublicClient, fallback, webSocket, http, createWalletClient, getContract, Hex, parseEther, ClientConfig } from "viem";
      + import { encodeEntity, syncToRecs } from "@latticexyz/store-sync/recs";
      + import { createBurnerAccount, createContract, transportObserver } from "@latticexyz/common";
      
      - const result = await setupMUDV2Network({
      -   ...
      - });
      
      + const clientOptions = {
      +   chain: networkConfig.chain,
      +   transport: transportObserver(fallback([webSocket(), http()])),
      +   pollingInterval: 1000,
      + } as const satisfies ClientConfig;
      
      + const publicClient = createPublicClient(clientOptions);
      
      + const burnerAccount = createBurnerAccount(networkConfig.privateKey as Hex);
      + const burnerWalletClient = createWalletClient({
      +   ...clientOptions,
      +   account: burnerAccount,
      + });
      
      + const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({
      +   world,
      +   config: storeConfig,
      +   address: networkConfig.worldAddress as Hex,
      +   publicClient,
      +   components: contractComponents,
      +   startBlock: BigInt(networkConfig.initialBlockNumber),
      +   indexerUrl: networkConfig.indexerUrl ?? undefined,
      + });
      
      + const worldContract = createContract({
      +   address: networkConfig.worldAddress as Hex,
      +   abi: IWorld__factory.abi,
      +   publicClient,
      +   walletClient: burnerWalletClient,
      + });
      
        // Request drip from faucet
      - const signer = result.network.signer.get();
      - if (networkConfig.faucetServiceUrl && signer) {
      -   const address = await signer.getAddress();
      + if (networkConfig.faucetServiceUrl) {
      +   const address = burnerAccount.address;
      
        const requestDrip = async () => {
      -   const balance = await signer.getBalance();
      +   const balance = await publicClient.getBalance({ address });
          console.info(`[Dev Faucet]: Player balance -> ${balance}`);
      -   const lowBalance = balance?.lte(utils.parseEther("1"));
      +   const lowBalance = balance < parseEther("1");
      

      You can remove the previous ethers worldContract, snap sync code, and fast transaction executor.

      The return of setupNetwork is a bit different than before, so you may have to do corresponding app changes.

      + return {
      +   world,
      +   components,
      +   playerEntity: encodeEntity({ address: "address" }, { address: burnerWalletClient.account.address }),
      +   publicClient,
      +   walletClient: burnerWalletClient,
      +   latestBlock$,
      +   blockStorageOperations$,
      +   waitForTransaction,
      +   worldContract,
      + };
      
    5. Update createSystemCalls with the new return type of setupNetwork.

        export function createSystemCalls(
      -   { worldSend, txReduced$, singletonEntity }: SetupNetworkResult,
      +   { worldContract, waitForTransaction }: SetupNetworkResult,
          { Counter }: ClientComponents
        ) {
           const increment = async () => {
      -      const tx = await worldSend("increment", []);
      -      await awaitStreamValue(txReduced$, (txHash) => txHash === tx.hash);
      +      const tx = await worldContract.write.increment();
      +      await waitForTransaction(tx);
             return getComponentValue(Counter, singletonEntity);
           };
      
    6. (optional) If you still need a clock, you can create it with:

      import { map, filter } from "rxjs";
      import { createClock } from "@latticexyz/network";
      
      const clock = createClock({
        period: 1000,
        initialTime: 0,
        syncInterval: 5000,
      });
      
      world.registerDisposer(() => clock.dispose());
      
      latestBlock$
        .pipe(
          map((block) => Number(block.timestamp) * 1000), // Map to timestamp in ms
          filter((blockTimestamp) => blockTimestamp !== clock.lastUpdateTime), // Ignore if the clock was already refreshed with this block
          filter((blockTimestamp) => blockTimestamp !== clock.currentTime) // Ignore if the current local timestamp is correct
        )
        .subscribe(clock.update); // Update the local clock
      

    If you're using the previous LoadingState component, you'll want to migrate to the new SyncProgress:

    import { SyncStep, singletonEntity } from "@latticexyz/store-sync/recs";
    
    const syncProgress = useComponentValue(SyncProgress, singletonEntity, {
      message: "Connecting",
      percentage: 0,
      step: SyncStep.INITIALIZE,
    });
    
    if (syncProgress.step === SyncStep.LIVE) {
      // we're live!
    }
    
  • #1195 afdba793 Thanks @holic! - Update RECS components with v2 key/value schemas. This helps with encoding/decoding composite keys and strong types for keys/values.

    This may break if you were previously dependent on component.id, component.metadata.componentId, or component.metadata.tableId:

    • component.id is now the on-chain bytes32 hex representation of the table ID
    • component.metadata.componentName is the table name (e.g. Position)
    • component.metadata.tableName is the namespaced table name (e.g. myworld:Position)
    • component.metadata.keySchema is an object with key names and their corresponding ABI types
    • component.metadata.valueSchema is an object with field names and their corresponding ABI types
  • Updated dependencies [b02f9d0e]: