use-truapi
Contracts

useContract

Typed PolkaVM contract handle from a cdm.json manifest

useContract resolves a typed contract handle from a cdm.json manifest, the file cdm deploy produces. data is a Contract handle with one property per contract method, each exposing a dry-run read and a transaction; feed it to useContractQuery and useContractTx.

Contract managers are cached per (chain, manifest) and share the runtime's signer, so any number of components can resolve the same manifest without redundant setup. The result is cached under the chain key, the manifest name (or a session-stable id when the manifest has no name), the library name and the live flag.

Addresses come from the manifest snapshot by default. Pass live: true to resolve them from the on-chain CDM registry instead, so the handle follows re-deployments without shipping a new manifest.

Usage

Keep the manifest import in one module so every component shares the same object (the cache key is derived from it):

counter-contract.ts
import type { CdmJson } from "@use-truapi/react"; // or "@use-truapi/vue"
import manifest from "../contracts/cdm.json";

export const COUNTER_LIBRARY = "@my-app/counter";
export const cdmJson = manifest as unknown as CdmJson;
import { useContract, useContractQuery } from "@use-truapi/react";
import { COUNTER_LIBRARY, cdmJson } from "./counter-contract";

function Counter() {
  const contract = useContract(cdmJson, COUNTER_LIBRARY);
  const count = useContractQuery<bigint>(contract.data, "getCount", []);

  if (contract.isPending) return <p>Resolving contract…</p>;
  if (contract.error) return <p>Failed to load contract: {contract.error.message}</p>;

  return <p>Count: {count.data?.toString() ?? "…"}</p>;
}

In React, pass the resolved handle, contract.data, to useContractQuery and useContractTx.

<script setup lang="ts">
import { useContract, useContractQuery } from "@use-truapi/vue";
import { COUNTER_LIBRARY, cdmJson } from "./counter-contract";

const contract = useContract(cdmJson, COUNTER_LIBRARY);
const count = useContractQuery<bigint>(contract, "getCount", []);
</script>

<template>
  <p v-if="contract.isPending">Resolving contract…</p>
  <p v-else-if="contract.error">Failed to load contract: {{ contract.error.message }}</p>
  <p v-else>Count: {{ count.data?.toString() ?? "…" }}</p>
</template>

In Vue, pass the whole contract query result to useContractQuery and useContractTx; they unwrap contract.data reactively.

To resolve addresses from the live registry, or build against a specific chain:

const contract = useContract(cdmJson, COUNTER_LIBRARY, {
  live: true,
  chain: "assetHub",
});

See also

API reference

React

function useContract(
   cdmJson, 
   library, 
options?): UseQueryResult<Contract<ContractDef>, Error>;

Typed contract handle from a cdm.json manifest. Managers are cached per (chain, manifest) and share the runtime's SignerManager.

Parameters

ParameterType
cdmJsonCdmJson
librarystring
options?ContractScope<string> & object

Returns

UseQueryResult<Contract<ContractDef>, Error>

Vue

function useContract(
   cdmJson, 
   library, 
options?): QueryResult<Contract<ContractDef>>;

Typed contract handle from a cdm.json manifest. Managers are cached per (chain, manifest) and share the runtime's SignerManager.

Parameters

ParameterType
cdmJsonCdmJson
librarystring
options?ContractScope<string> & object

Returns

QueryResult<Contract<ContractDef>>

On this page