1.43.0 • Published 2 years ago

@latticexyz/network v1.43.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

Network

This package includes general low level utilities to interact with Ethereum contracts, as well as specialized MUD contract/client sync utilities.

The general utilities can be used without MUD. For the specialized but much more powerful utilities, usage of solecs is required and recs is recommended.

See mud.dev/network for the detailed API documentation.

Example

This is a real-world example from an upcoming game built with MUD.

// setupContracts.ts

import {
  createNetwork,
  createContracts,
  Mappings,
  createTxQueue,
  createSyncWorker,
  createEncoder,
  NetworkComponentUpdate,
  createSystemExecutor,
} from "@latticexyz/network";

import { Component as SolecsComponent, World as WorldContract } from "@latticexyz/solecs";
import { abi as WorldAbi } from "@latticexyz/solecs/abi/World.json";
import ComponentAbi from "@latticexyz/solecs/abi/Component.json";

import {
  Component,
  Components,
  EntityIndex,
  getComponentEntities,
  getComponentValueStrict,
  removeComponent,
  Schema,
  setComponent,
  Type,
  World,
} from "@latticexyz/recs";

import { keccak256, stretch, toEthAddress } from "@latticexyz/utils";

import { defineStringComponent } from "@latticexyz/std-client";

import { bufferTime, filter, Observable, Subject } from "rxjs";
import { computed, IComputedValue } from "mobx";
import { Contract, ethers, Signer, Wallet } from "ethers";
import { JsonRpcProvider } from "@ethersproject/providers";

import { SystemTypes } from "<contracts>/types/SystemTypes";
import { SystemAbis } from "<contracts>/types/SystemAbis.mjs";
import { config } from "./config";

export type ContractComponents = {
  [key: string]: Component<Schema, { contractId: string }>;
};

export async function setupContracts<C extends ContractComponents>(world: World, components: C) {
  const SystemsRegistry = defineStringComponent(world, {
    id: "SystemsRegistry",
    metadata: { contractId: "world.component.systems" },
  });

  const ComponentsRegistry = defineStringComponent(world, {
    id: "ComponentsRegistry",
    metadata: { contractId: "world.component.components" },
  });

  components = {
    ...components,
    SystemsRegistry,
    ComponentsRegistry,
  };

  const mappings: Mappings<C> = {};

  for (const key of Object.keys(components)) {
    const { contractId } = components[key].metadata;
    mappings[keccak256(contractId)] = key;
  }

  const network = await createNetwork(config);

  const signerOrProvider = computed(() => network.signer.get() || network.providers.get().json);

  const { contracts, config: contractsConfig } = await createContracts<{ World: WorldContract }>({
    config: { World: { abi: WorldAbi, address: config.worldAddress } },
    signerOrProvider,
  });

  const { txQueue, dispose: disposeTxQueue } = createTxQueue(contracts, network);

  const systems = createSystemExecutor<SystemTypes>(world, network, SystemsRegistry, SystemAbis, {
    devMode: config.devMode,
  });

  const { ecsEvent$, config$, dispose } = createSyncWorker<C>();

  function startSync() {
    config$.next({
      provider: networkConfig.provider,
      worldContract: contractsConfig.World,
      initialBlockNumber: config.initialBlockNumber ?? 0,
      chainId: config.chainId,
      disableCache: false,
      snapshotServiceUrl: networkConfig.snapshotServiceUrl,
      streamServiceUrl: networkConfig.streamServiceUrl,
    });
  }

  const { txReduced$ } = applyNetworkUpdates(world, components, ecsEvent$, mappings);

  const encoders = createEncoders(world, ComponentsRegistry, signerOrProvider);

  return { txQueue, txReduced$, encoders, network, startSync, systems };
}

async function createEncoders(
  world: World,
  components: Component<{ value: Type.String }>,
  signerOrProvider: IComputedValue<JsonRpcProvider | Signer>
) {
  const encoders = {} as Record<string, ReturnType<typeof createEncoder>>;

  async function fetchAndCreateEncoder(entity: EntityIndex) {
    const componentAddress = toEthAddress(world.entities[entity]);
    const componentId = getComponentValueStrict(components, entity).value;
    const componentContract = new Contract(
      componentAddress,
      ComponentAbi.abi,
      signerOrProvider.get()
    ) as SolecsComponent;
    const [componentSchemaPropNames, componentSchemaTypes] = await componentContract.getSchema();
    encoders[componentId] = createEncoder(componentSchemaPropNames, componentSchemaTypes);
  }

  // Initial setup
  for (const entity of getComponentEntities(components)) fetchAndCreateEncoder(entity);

  // Keep up to date
  const subscription = components.update$.subscribe((update) => fetchAndCreateEncoder(update.entity));
  world.registerDisposer(() => subscription?.unsubscribe());

  return encoders;
}

/**
 * Sets up synchronization between contract components and client components
 */
function applyNetworkUpdates<C extends Components>(
  world: World,
  components: C,
  ecsEvent$: Observable<NetworkComponentUpdate<C>>,
  mappings: Mappings<C>
) {
  const txReduced$ = new Subject<string>();

  const ecsEventSub = ecsEvent$.subscribe((update) => {
    const entityIndex = world.entityToIndex.get(update.entity) ?? world.registerEntity({ id: update.entity });
    const componentKey = mappings[update.component];

    if (update.value === undefined) {
      // undefined value means component removed
      removeComponent(components[componentKey] as Component<Schema>, entityIndex);
    } else {
      setComponent(components[componentKey] as Component<Schema>, entityIndex, update.value);
    }

    if (update.lastEventInTx) txReduced$.next(update.txHash);
  });

  return { txReduced$: txReduced$.asObservable() };
}
2.0.0-next.2

2 years ago

2.0.0-next.0

2 years ago

2.0.0-next.1

2 years ago

2.0.0-alpha.1.97

2 years ago

2.0.0-alpha.1.98

2 years ago

2.0.0-alpha.1.95

2 years ago

2.0.0-alpha.1.96

2 years ago

2.0.0-alpha.1.59

2 years ago

2.0.0-alpha.1.57

2 years ago

2.0.0-alpha.1.58

2 years ago

2.0.0-alpha.1.55

2 years ago

2.0.0-alpha.1.56

2 years ago

2.0.0-alpha.1.53

2 years ago

2.0.0-alpha.1.54

2 years ago

2.0.0-alpha.1.51

2 years ago

2.0.0-alpha.1.52

2 years ago

2.0.0-alpha.1.60

2 years ago

2.0.0-alpha.1.61

2 years ago

2.0.0-alpha.1.68

2 years ago

2.0.0-alpha.1.69

2 years ago

2.0.0-alpha.1.66

2 years ago

2.0.0-alpha.1.67

2 years ago

2.0.0-alpha.1.64

2 years ago

2.0.0-alpha.1.65

2 years ago

2.0.0-alpha.1.62

2 years ago

2.0.0-alpha.1.63

2 years ago

2.0.0-alpha.1.71

2 years ago

2.0.0-alpha.1.72

2 years ago

2.0.0-alpha.1.70

2 years ago

2.0.0-alpha.1.79

2 years ago

2.0.0-alpha.1.77

2 years ago

2.0.0-alpha.1.78

2 years ago

2.0.0-alpha.1.75

2 years ago

2.0.0-alpha.1.76

2 years ago

2.0.0-alpha.1.73

2 years ago

2.0.0-alpha.1.74

2 years ago

2.0.0-alpha.1.82

2 years ago

2.0.0-alpha.1.83

2 years ago

2.0.0-alpha.1.80

2 years ago

2.0.0-alpha.1.81

2 years ago

2.0.0-alpha.1.88

2 years ago

2.0.0-alpha.1.89

2 years ago

2.0.0-alpha.1.86

2 years ago

2.0.0-alpha.1.87

2 years ago

2.0.0-alpha.1.84

2 years ago

2.0.0-alpha.1.85

2 years ago

2.0.0-alpha.1.93

2 years ago

2.0.0-alpha.1.94

2 years ago

2.0.0-alpha.1.91

2 years ago

2.0.0-alpha.1.92

2 years ago

2.0.0-alpha.1.90

2 years ago

2.0.0-alpha.1.39

2 years ago

2.0.0-alpha.1.37

2 years ago

2.0.0-alpha.1.38

2 years ago

2.0.0-alpha.1.35

2 years ago

2.0.0-alpha.1.36

2 years ago

2.0.0-alpha.1.34

2 years ago

2.0.0-alpha.1.48

2 years ago

2.0.0-alpha.1.49

2 years ago

2.0.0-alpha.1.46

2 years ago

2.0.0-alpha.1.47

2 years ago

2.0.0-alpha.1.44

2 years ago

2.0.0-alpha.1.45

2 years ago

2.0.0-alpha.1.42

2 years ago

2.0.0-alpha.1.43

2 years ago

2.0.0-alpha.1.41

2 years ago

2.0.0-alpha.1.50

2 years ago

2.0.0-alpha.1.13

2 years ago

2.0.0-alpha.1.11

2 years ago

2.0.0-alpha.1.12

2 years ago

2.0.0-alpha.7

2 years ago

2.0.0-alpha.1.10

2 years ago

2.0.0-alpha.8

2 years ago

2.0.0-alpha.9

2 years ago

2.0.0-alpha.3

2 years ago

2.0.0-alpha.4

2 years ago

2.0.0-alpha.5

2 years ago

1.41.1-alpha.0

2 years ago

2.0.0-alpha.6

2 years ago

2.0.0-alpha.94

2 years ago

2.0.0-alpha.0

2 years ago

2.0.0-alpha.1

2 years ago

2.0.0-alpha.93

2 years ago

2.0.0-alpha.2

2 years ago

2.0.0-alpha.92

2 years ago

1.40.0

2 years ago

2.0.0-alpha.1.28

2 years ago

2.0.0-alpha.1.29

2 years ago

2.0.0-alpha.1.26

2 years ago

2.0.0-alpha.1.27

2 years ago

2.0.0-alpha.91

2 years ago

2.0.0-alpha.90

2 years ago

2.0.0-alpha.1.22

2 years ago

2.0.0-alpha.1.23

2 years ago

2.0.0-alpha.1.21

2 years ago

2.0.0-alpha.88

2 years ago

2.0.0-alpha.87

2 years ago

2.0.0-alpha.86

2 years ago

2.0.0-alpha.85

2 years ago

2.0.0-alpha.84

2 years ago

2.0.0-alpha.83

2 years ago

2.0.0-alpha.81

2 years ago

2.0.0-alpha.89

2 years ago

2.0.0-alpha.80

2 years ago

2.0.0-alpha.1.33

2 years ago

2.0.0-alpha.1.31

2 years ago

2.0.0-alpha.1.32

2 years ago

2.0.0-alpha.1.30

2 years ago

2.0.0-alpha.76

2 years ago

2.0.0-alpha.75

2 years ago

2.0.0-alpha.74

2 years ago

2.0.0-alpha.73

2 years ago

2.0.0-alpha.72

2 years ago

1.41.0

2 years ago

2.0.0-alpha.79

2 years ago

2.0.0-alpha.78

2 years ago

2.0.0-alpha.66

2 years ago

2.0.0-alpha.65

2 years ago

2.0.0-alpha.64

2 years ago

2.0.0-alpha.63

2 years ago

2.0.0-alpha.62

2 years ago

2.0.0-alpha.61

2 years ago

2.0.0-alpha.60

2 years ago

2.0.0-alpha.69

2 years ago

2.0.0-alpha.68

2 years ago

2.0.0-alpha.67

2 years ago

2.0.0-alpha.55

2 years ago

2.0.0-alpha.54

2 years ago

2.0.0-alpha.53

2 years ago

2.0.0-alpha.52

2 years ago

2.0.0-alpha.51

2 years ago

2.0.0-alpha.50

2 years ago

1.42.0

2 years ago

2.0.0-alpha.59

2 years ago

2.0.0-alpha.58

2 years ago

2.0.0-alpha.57

2 years ago

2.0.0-alpha.56

2 years ago

2.0.0-alpha.44

2 years ago

2.0.0-alpha.43

2 years ago

2.0.0-alpha.41

2 years ago

2.0.0-alpha.40

2 years ago

2.0.0-alpha.49

2 years ago

2.0.0-alpha.48

2 years ago

2.0.0-alpha.47

2 years ago

2.0.0-alpha.46

2 years ago

2.0.0-alpha.45

2 years ago

2.0.0-alpha.39

2 years ago

2.0.0-alpha.38

2 years ago

2.0.0-alpha.37

2 years ago

2.0.0-alpha.36

2 years ago

1.43.0

2 years ago

1.41.1-alpha.41

2 years ago

1.37.0

2 years ago

1.35.0

2 years ago

1.37.1

2 years ago

1.39.0

2 years ago

1.36.1

2 years ago

1.34.0

2 years ago

1.38.0

2 years ago

1.33.1

2 years ago

1.31.3

3 years ago

1.29.0

3 years ago

1.27.1

3 years ago

1.32.0

2 years ago

1.30.0

3 years ago

1.30.1

3 years ago

1.26.0

3 years ago

1.28.1

3 years ago

1.28.0

3 years ago

1.31.1

3 years ago

1.31.2

3 years ago

1.31.0

3 years ago

1.15.0

3 years ago

1.2.0

3 years ago

1.1.0

3 years ago

1.13.0

3 years ago

1.0.0

3 years ago

1.12.0

3 years ago

1.19.0

3 years ago

1.18.0

3 years ago

1.17.0

3 years ago

1.16.0

3 years ago

1.14.2

3 years ago

1.9.0

3 years ago

1.8.0

3 years ago

1.7.1

3 years ago

1.7.0

3 years ago

1.6.0

3 years ago

1.5.1

3 years ago

1.5.0

3 years ago

1.4.1

3 years ago

1.4.0

3 years ago

1.3.0

3 years ago

0.16.3

3 years ago

0.16.4

3 years ago

1.21.0

3 years ago

1.22.0

3 years ago

1.20.0

3 years ago

1.24.1

3 years ago

1.25.1

3 years ago

1.23.0

3 years ago

1.24.0

3 years ago

1.23.1

3 years ago

1.11.0

3 years ago

1.10.0

3 years ago

0.16.2

3 years ago

0.16.1

3 years ago

0.16.0

3 years ago

0.15.1

3 years ago

0.15.0

3 years ago

0.14.2

3 years ago

0.14.1

3 years ago

0.14.0

3 years ago

0.13.0

3 years ago

0.12.0

3 years ago

0.11.1

3 years ago

0.10.0

3 years ago

0.9.0

3 years ago