use-truapi
Contracts

useEnsureAccountMapped

Idempotent pallet-revive account mapping, required once before contract transactions

Before an account can call PolkaVM contracts, pallet-revive needs a one-time account mapping that gives the Substrate account an H160 identity. useEnsureAccountMapped wraps that step: ensureMapped() maps the connected account on the contract's chain and resolves with no data once done. Mutation state (error, isPending, reset) tracks the last attempt.

The operation is idempotent: if the account is already mapped it completes without submitting anything, so it is safe to call defensively (for example before the first write in a session). It only needs to succeed once per account per chain.

The manifest argument scopes the mapping to the same cached contract manager that useContract uses, so pass the same cdmJson object.

Usage

import { useContract, useContractTx, useEnsureAccountMapped } from "@use-truapi/react";
import { COUNTER_LIBRARY, cdmJson } from "./counter-contract";

function Setup() {
  const contract = useContract(cdmJson, COUNTER_LIBRARY);
  const mapAccount = useEnsureAccountMapped(cdmJson);
  const increment = useContractTx(contract.data, "increment");

  async function onFirstIncrement() {
    try {
      await mapAccount.ensureMapped(); // no-op if already mapped
      await increment.send();
    } catch {
      // failures are also reported in mapAccount.error / increment.error
    }
  }

  return (
    <>
      <button
        type="button"
        disabled={!contract.data || mapAccount.isPending || increment.isPending}
        onClick={onFirstIncrement}
      >
        {mapAccount.isPending ? "Mapping account…" : "Increment"}
      </button>
      {mapAccount.error && <p role="alert">{mapAccount.error.message}</p>}
    </>
  );
}
<script setup lang="ts">
import { useContract, useContractTx, useEnsureAccountMapped } from "@use-truapi/vue";
import { COUNTER_LIBRARY, cdmJson } from "./counter-contract";

const contract = useContract(cdmJson, COUNTER_LIBRARY);
const mapAccount = useEnsureAccountMapped(cdmJson);
const increment = useContractTx(contract, "increment");

async function onFirstIncrement() {
  try {
    await mapAccount.ensureMapped(); // no-op if already mapped
    await increment.send();
  } catch {
    // failures are also reported in mapAccount.error / increment.error
  }
}
</script>

<template>
  <button
    type="button"
    :disabled="!contract.data || mapAccount.isPending || increment.isPending"
    @click="onFirstIncrement"
  >
    {{ mapAccount.isPending ? "Mapping account…" : "Increment" }}
  </button>
  <p v-if="mapAccount.error" role="alert">{{ mapAccount.error.message }}</p>
</template>

See also

  • useContractTx is the write this mapping unlocks.
  • useContract takes the same cdmJson manifest.
  • useTx submits plain transactions, which don't require mapping.

API reference

React

function useEnsureAccountMapped(cdmJson, options?): NamedMutation<void, void> & object;

Idempotent pallet-revive account mapping — required once before contract txs: ensureMapped().

Parameters

ParameterType
cdmJsonCdmJson
options?{ chain?: string; mutation?: MutationOptions<void, void>; }
options.chain?string
options.mutation?MutationOptions<void, void>

Returns

NamedMutation<void, void> & object

Vue

function useEnsureAccountMapped(cdmJson, options?): NamedMutation<void, void> & object;

Idempotent pallet-revive account mapping — required once before contract txs: ensureMapped().

Parameters

ParameterType
cdmJsonCdmJson
options?{ chain?: string; mutation?: MutationOptions<void, void>; }
options.chain?string
options.mutation?MutationOptions<void, void>

Returns

NamedMutation<void, void> & object

On this page