Skip to main content
The IXO Protocol token module wraps the ixo1155 smart contract — a CosmWasm implementation of the EIP-1155 multi-token standard. Each token domain has a token class (entity DID), a fixed cap, and supports both fungible and non-fungible balances within a single contract. This guide shows the chain-level message patterns. For the data model and contract details, read the x/token proto definitions and the @ixo/impactxclient-sdk README.

Before you start

You need:
  • An IXO account with sufficient uixo for fees on the network you target — see Networks and endpoints.
  • An offline signer — see the SDK README on creating signers.
  • The token protocol entity DID (the value used as class). Token class is validated against an existing entity.
  • For minting, a deployed token contract address (set via MsgCreateToken).
  • For credit transfers, a valid WithdrawPaymentAuthorization if you act on behalf of another account — see Custom authorisations for IXO Claims.
npm install @ixo/impactxclient-sdk
import { ixo, createSigningClient } from "@ixo/impactxclient-sdk";

const signingClient = await createSigningClient(RPC_ENDPOINT, offlineSigner);

Create a token

MsgCreateToken registers a new token name under a token-protocol entity. Cap "0" means unlimited supply.
const createMsg = {
  typeUrl: "/ixo.token.v1beta1.MsgCreateToken",
  value: ixo.token.v1beta1.MsgCreateToken.fromPartial({
    minter: signerAddress,
    class: "did:ixo:entity:protocol123",
    name: "CARBON",
    description: "Verified carbon offset units",
    image: "ipfs://bafy.../image.png",
    tokenType: "ixo1155",
    cap: "1000000",
  }),
};

await signingClient.signAndBroadcast(signerAddress, [createMsg], "auto");

Mint tokens

MsgMintToken issues new units in batches against an existing token contract. Each batch references a unique index and a collection DID.
const mintMsg = {
  typeUrl: "/ixo.token.v1beta1.MsgMintToken",
  value: ixo.token.v1beta1.MsgMintToken.fromPartial({
    minter: signerAddress,
    contractAddress: tokenContractAddress,
    owner: ownerAddress,
    mintBatch: [
      ixo.token.v1beta1.MintBatch.fromPartial({
        name: "CARBON",
        index: "bafkreih...",
        amount: "1000",
        collection: "did:ixo:entity:collection456",
        tokenData: [],
      }),
    ],
  }),
};

await signingClient.signAndBroadcast(signerAddress, [mintMsg], "auto");

Transfer tokens

MsgTransferToken moves balances between accounts. All tokens in a single message must belong to the same contract.
const transferMsg = {
  typeUrl: "/ixo.token.v1beta1.MsgTransferToken",
  value: ixo.token.v1beta1.MsgTransferToken.fromPartial({
    owner: signerAddress,
    recipient: recipientAddress,
    tokens: [
      ixo.token.v1beta1.TokenBatch.fromPartial({
        id: "bafkreih...",
        amount: "100",
      }),
    ],
  }),
};

await signingClient.signAndBroadcast(signerAddress, [transferMsg], "auto");

Retire tokens

MsgRetireToken permanently removes tokens from circulation while preserving their on-chain history. Use this for end-claims such as voluntary carbon retirement.
const retireMsg = {
  typeUrl: "/ixo.token.v1beta1.MsgRetireToken",
  value: ixo.token.v1beta1.MsgRetireToken.fromPartial({
    owner: signerAddress,
    tokens: [
      ixo.token.v1beta1.TokenBatch.fromPartial({
        id: "bafkreih...",
        amount: "50",
      }),
    ],
    jurisdiction: "ZA-WC-7700",
    reason: "Voluntary retirement on behalf of buyer X",
  }),
};
The jurisdiction field follows the format <country-code>[-<sub-national-code>[-<postal-code>]]. Only the country code is required.

Transfer credit (ITMO state change)

MsgTransferCredit flips tokens to a “transferred” state when units are moved off-protocol to a different registry — for example, Internationally Transferred Mitigation Outcomes (ITMOs) under Article 6.2 of the Paris Agreement. It accepts an optional authorization_id for delegated transfers.
const creditTransferMsg = {
  typeUrl: "/ixo.token.v1beta1.MsgTransferCredit",
  value: ixo.token.v1beta1.MsgTransferCredit.fromPartial({
    owner: signerAddress,
    tokens: [
      ixo.token.v1beta1.TokenBatch.fromPartial({
        id: "bafkreih...",
        amount: "100",
      }),
    ],
    jurisdiction: "CH",
    reason: "ITMO transfer to Swiss national registry",
    authorizationId: "auth-2025-001",
  }),
};

Cancel, pause, or stop a token

OperationMessageEffect
Cancel batchesMsgCancelTokenOwner cancels specific token batches with a reason.
Pause mintingMsgPauseTokenMinter halts further minting on a contract. paused: true to halt, false to resume.
Stop the contractMsgStopTokenMinter permanently stops the token contract. Irreversible.
const pauseMsg = {
  typeUrl: "/ixo.token.v1beta1.MsgPauseToken",
  value: ixo.token.v1beta1.MsgPauseToken.fromPartial({
    minter: signerAddress,
    contractAddress: tokenContractAddress,
    paused: true,
  }),
};

Verify the result

Query token state through the Blocksync GraphQL API:
query Token($id: String!) {
  token(id: $id) {
    id
    name
    description
    cap
    class
    minter
    minted
    transferred
    retired
    cancelled
  }
}
Or read directly from the token contract via the gRPC gateway API.

Troubleshooting

class must be a valid entity DID for an existing token-protocol entity. Confirm the entity exists and that you control it.
MsgMintToken will fail if the new total would exceed the token cap. Either raise the cap by recreating the token or split issuance across additional token names.
Minting is rejected on paused contracts and on stopped contracts. Issue MsgPauseToken with paused: false to resume; MsgStopToken cannot be reversed.
Delegated MsgTransferCredit requires a WithdrawPaymentAuthorization referencing the supplied authorization_id. See Custom authorisations for IXO Claims.

Next steps

IXO MultiClient SDK

Full SDK reference including signing client setup.

Token proto definitions

Source of truth for token messages.

Bonds module

Add bonding-curve pricing and reserves to tokens.

Custom authorisations

Delegate token operations safely.