Polkadot Apps
    Preparing search index...

    Module @polkadot-apps/contracts

    @polkadot-apps/contracts

    Typed contract interactions for Solidity and ink! smart contracts on Polkadot Asset Hub.

    pnpm add @polkadot-apps/contracts
    

    The fastest way to get started is ContractManager.fromClient(), which lazy-loads the Ink SDK internally:

    import { createChainClient } from "@polkadot-apps/chain-client";
    import { paseo_asset_hub } from "@polkadot-apps/descriptors/paseo-asset-hub";
    import { ContractManager } from "@polkadot-apps/contracts";
    import cdmJson from "./cdm.json";

    const client = await createChainClient({
    chains: { assetHub: paseo_asset_hub },
    rpcs: { assetHub: ["wss://sys.ibp.network/asset-hub-paseo"] },
    });

    const manager = await ContractManager.fromClient(
    cdmJson,
    client.raw.assetHub,
    { signerManager }, // from @polkadot-apps/signer
    );

    const counter = manager.getContract("@example/counter");
    const { value } = await counter.getCount.query();
    await counter.increment.tx();

    fromClient() dynamically imports @polkadot-api/sdk-ink (~4 MB of metadata). If your bundler cannot tree-shake the lazy import, construct the manager directly with a pre-created InkSdk to control exactly when that cost is paid:

    import { createChainClient } from "@polkadot-apps/chain-client";
    import { createInkSdk } from "@polkadot-api/sdk-ink";
    import { paseo_asset_hub } from "@polkadot-apps/descriptors/paseo-asset-hub";
    import { ContractManager } from "@polkadot-apps/contracts";
    import cdmJson from "./cdm.json";

    const client = await createChainClient({
    chains: { assetHub: paseo_asset_hub },
    rpcs: { assetHub: ["wss://sys.ibp.network/asset-hub-paseo"] },
    });

    const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });
    const manager = new ContractManager(cdmJson, inkSdk, {
    signerManager, // from @polkadot-apps/signer
    });

    const counter = manager.getContract("@example/counter");
    const { value } = await counter.getCount.query();
    await counter.increment.tx();

    If you don't need custom RPC endpoints, getChainAPI gives you a pre-configured client:

    import { getChainAPI } from "@polkadot-apps/chain-client";
    import { ContractManager } from "@polkadot-apps/contracts";
    import cdmJson from "./cdm.json";

    const client = await getChainAPI("paseo");
    const manager = await ContractManager.fromClient(
    cdmJson,
    client.raw.assetHub,
    );

    ContractManager reads contract addresses and ABIs from a cdm.json manifest. Each method on a contract handle exposes .query() for read-only dry-runs and .tx() for signed on-chain transactions.

    import { createChainClient } from "@polkadot-apps/chain-client";
    import { paseo_asset_hub } from "@polkadot-apps/descriptors/paseo-asset-hub";
    import { ContractManager } from "@polkadot-apps/contracts";
    import cdmJson from "./cdm.json";

    const client = await createChainClient({
    chains: { assetHub: paseo_asset_hub },
    rpcs: { assetHub: ["wss://sys.ibp.network/asset-hub-paseo"] },
    });

    const manager = await ContractManager.fromClient(cdmJson, client.raw.assetHub);

    const counter = manager.getContract("@example/counter");

    // Read-only query (dry-run, no gas cost)
    const result = await counter.getCount.query();
    console.log(result.value); // 42

    // Signed transaction
    const txResult = await counter.increment.tx();
    console.log(txResult.txHash, txResult.ok);

    Methods with arguments use positional parameters. Pass an options object as the last argument to override defaults.

    const token = manager.getContract("@example/token");

    await token.transfer.tx("0xRecipient", 1000n, {
    value: 0n,
    waitFor: "finalized",
    });

    const { value } = await token.balanceOf.query("0xOwner");

    When cdm.json contains multiple targets, the first target is selected by default. Pass targetHash to select a specific one.

    const manager = await ContractManager.fromClient(cdmJson, client.raw.assetHub, {
    targetHash: "abc123",
    });

    createContractFromClient builds a contract handle from a raw address and ABI -- no cdm.json needed. This is useful for one-off interactions or contracts not managed by CDM.

    import { createChainClient } from "@polkadot-apps/chain-client";
    import { paseo_asset_hub } from "@polkadot-apps/descriptors/paseo-asset-hub";
    import { createContractFromClient } from "@polkadot-apps/contracts";

    const client = await createChainClient({
    chains: { assetHub: paseo_asset_hub },
    rpcs: { assetHub: ["wss://sys.ibp.network/asset-hub-paseo"] },
    });

    const abi = [
    {
    type: "function",
    name: "getCount",
    inputs: [],
    outputs: [{ name: "", type: "uint32" }],
    stateMutability: "view",
    },
    {
    type: "function",
    name: "increment",
    inputs: [],
    outputs: [],
    stateMutability: "nonpayable",
    },
    ];

    const counter = await createContractFromClient(
    client.raw.assetHub,
    "0xC472...",
    abi,
    {
    defaultOrigin: "5GrwvaEF...",
    defaultSigner: signer,
    },
    );

    const { value } = await counter.getCount.query();
    await counter.increment.tx();

    Like the constructor vs fromClient() distinction, createContract takes an explicit InkSdk and avoids the dynamic import:

    import { createInkSdk } from "@polkadot-api/sdk-ink";
    import { createContract } from "@polkadot-apps/contracts";

    const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });

    const counter = createContract(inkSdk, "0xC472...", abi, {
    signerManager,
    });

    await counter.getCount.query();
    await counter.increment.tx();

    Pass a SignerManager from @polkadot-apps/signer so the currently logged-in account is used automatically as the signer and origin for all contract interactions. The account is resolved at call time, so account switches are reflected immediately.

    import { SignerManager } from "@polkadot-apps/signer";
    import { ContractManager } from "@polkadot-apps/contracts";

    const signerManager = new SignerManager();
    await signerManager.connect();
    signerManager.selectAccount(accounts[0].address);

    const manager = await ContractManager.fromClient(cdmJson, client.raw.assetHub, {
    signerManager,
    });

    // Uses the logged-in account automatically -- no manual signer wiring
    const counter = manager.getContract("@example/counter");
    await counter.increment.tx();

    Signer resolution order (highest priority wins):

    1. Explicit override in call options ({ signer })
    2. signerManager (current logged-in account)
    3. Static defaultSigner / defaultOrigin

    For read-only queries, when no origin is available from any source, a dev fallback address (Alice) is used automatically. This is safe because queries are dry-run simulations.

    You can also update defaults after construction:

    manager.setDefaults({ signerManager: newSignerManager });
    manager.setDefaults({ origin: "5NewOrigin" });

    generateContractTypes produces a TypeScript module augmentation that extends the Contracts interface with typed method signatures for each installed contract. This gives ContractManager.getContract() fully-typed handles with autocomplete for method names, arguments, and return types.

    import { generateContractTypes } from "@polkadot-apps/contracts/codegen";
    import { writeFileSync } from "node:fs";

    const source = generateContractTypes([
    { library: "@example/counter", abi },
    ]);
    writeFileSync(".cdm/contracts.d.ts", source);

    The generated file looks like:

    // Auto-generated by cdm install — do not edit
    import type { HexString, Binary, FixedSizeBinary } from "polkadot-api";

    declare module "@polkadot-apps/contracts" {
    interface Contracts {
    "@example/counter": {
    methods: {
    getCount: { args: []; response: number };
    increment: { args: []; response: undefined };
    };
    };
    }
    }

    Solidity ABI types are mapped to TypeScript equivalents: uint8/uint16/uint32 become number, larger integers become bigint, address becomes HexString, bytes becomes Binary, bytesN becomes FixedSizeBinary<N>, and tuples become inline object types.

    All errors extend ContractError. Use instanceof to catch any contract-related error, or narrow to specific types.

    import {
    ContractError,
    ContractSignerMissingError,
    ContractNotFoundError,
    } from "@polkadot-apps/contracts";

    try {
    await counter.increment.tx();
    } catch (error) {
    if (error instanceof ContractSignerMissingError) {
    console.error("No signer -- connect a wallet first");
    } else if (error instanceof ContractNotFoundError) {
    console.error(`${error.library} not in cdm.json for target ${error.targetHash}`);
    } else if (error instanceof ContractError) {
    console.error("Contract error:", error.message);
    }
    }

    Transaction calls also propagate errors from @polkadot-apps/tx (TxTimeoutError, TxDispatchError, TxSigningRejectedError) since they use submitAndWatch internally.

    Create a manager backed by a cdm.json manifest. This is the size-optimized path -- you provide the InkSdk directly.

    Parameter Type Description
    cdmJson CdmJson Parsed cdm.json manifest with targets, dependencies, and contracts.
    inkSdk InkSdk Ink SDK instance created via createInkSdk(client.raw.assetHub, { atBest: true }).
    options ContractManagerOptions Optional. See below.

    Convenience async factory that creates the InkSdk internally via a dynamic import of @polkadot-api/sdk-ink. The ~4 MB sdk-ink metadata is loaded lazily only when this method is called.

    Parameter Type Description
    cdmJson CdmJson Parsed cdm.json manifest.
    client PolkadotClient A PolkadotClient for the chain where contracts are deployed (e.g., client.raw.assetHub).
    options ContractManagerOptions Optional. See below.

    Return a typed contract handle. Each method has .query() and .tx().

    Parameter Type Description
    library string Contract library name as it appears in cdm.json (e.g. "@example/counter").

    Throws: ContractNotFoundError when the library is not in the manifest for the selected target.

    Return the on-chain address of an installed contract.

    Parameter Type Description
    library string Contract library name.

    Throws: ContractNotFoundError when the library is not found.

    Update the default origin, signer, or signerManager used by all contract handles.

    Parameter Type Description
    defaults ContractDefaults Partial defaults to merge.

    Create a contract handle from a raw address and ABI -- no cdm.json needed. This is the size-optimized standalone path.

    Parameter Type Description
    inkSdk InkSdk Ink SDK instance created via createInkSdk(client.raw.assetHub, { atBest: true }).
    address HexString On-chain contract address.
    abi AbiEntry[] Solidity-compatible ABI array.
    options ContractOptions Optional signer/origin configuration.

    Convenience async wrapper that creates the InkSdk internally via dynamic import. For size-sensitive apps, use createContract with a pre-created InkSdk.

    Parameter Type Description
    client PolkadotClient A PolkadotClient for the contracts chain (e.g., client.raw.assetHub).
    address HexString On-chain contract address.
    abi AbiEntry[] Solidity-compatible ABI array.
    options ContractOptions Optional signer/origin configuration.

    Generate a TypeScript module augmentation for typed contract handles. Exported from the @polkadot-apps/contracts/codegen subpath.

    Parameter Type Description
    contracts { library: string; abi: AbiEntry[] }[] Contracts to generate types for.

    Returns: TypeScript source string to write to .cdm/contracts.d.ts.

    interface CdmJson {
    targets: Record<string, CdmJsonTarget>;
    dependencies: Record<string, Record<string, number | string>>;
    contracts?: Record<string, Record<string, CdmJsonContract>>;
    }

    interface CdmJsonTarget {
    "asset-hub": string;
    bulletin: string;
    }

    interface CdmJsonContract {
    version: number;
    address: HexString;
    abi: AbiEntry[];
    metadataCid: string;
    }

    interface AbiEntry {
    type: string;
    name?: string;
    inputs: AbiParam[];
    outputs?: AbiParam[];
    stateMutability?: string;
    }

    interface AbiParam {
    name: string;
    type: string;
    components?: AbiParam[];
    }

    interface ContractDef {
    methods: Record<string, { args: any[]; response: any }>;
    }

    interface QueryResult<T> {
    success: boolean;
    value: T;
    gasRequired?: bigint;
    }

    interface QueryOptions {
    origin?: SS58String;
    value?: bigint;
    }

    interface TxOptions extends SubmitOptions {
    signer?: PolkadotSigner;
    origin?: SS58String;
    value?: bigint;
    gasLimit?: Weight;
    storageDepositLimit?: bigint;
    }

    interface ContractDefaults {
    origin?: SS58String;
    signer?: PolkadotSigner;
    signerManager?: SignerManager;
    }

    interface ContractOptions {
    signerManager?: SignerManager;
    defaultOrigin?: SS58String;
    defaultSigner?: PolkadotSigner;
    }

    interface ContractManagerOptions extends ContractOptions {
    targetHash?: string;
    }
    Class Extends Key properties
    ContractError Error Base class for all contract errors.
    ContractSignerMissingError ContractError No signer available for a transaction call.
    ContractNotFoundError ContractError library: string, targetHash: string

    Apache-2.0

    Modules

    codegen