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
uixofor 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
WithdrawPaymentAuthorizationif you act on behalf of another account — see Custom authorisations for IXO Claims.
npm install @ixo/impactxclient-sdkimport { 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
| Operation | Message | Effect |
|---|---|---|
| Cancel batches | MsgCancelToken | Owner cancels specific token batches with a reason. |
| Pause minting | MsgPauseToken | Minter halts further minting on a contract. paused: true to halt, false to resume. |
| Stop the contract | MsgStopToken | Minter 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
invalid class DID
class must be a valid entity DID for an existing token-protocol entity. Confirm the entity exists and that you control it.
cap exceeded
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.
contract paused or stopped
Minting is rejected on paused contracts and on stopped contracts. Issue MsgPauseToken with paused: false to resume; MsgStopToken cannot be reversed.