Manage bonds

Create and manage algorithmic bonds, trade them, and settle outcome payments on the IXO Protocol.

The IXO Protocol bonds module provides universal token bonding-curve functions to mint, burn, swap, and settle bonds. Bonds price their tokens via a bonding curve (function_type and function_parameters), accept one or more reserve_tokens, and pass through a lifecycle of HATCH → OPEN → SETTLE (or FAILED). Alpha bonds add risk-adjustment via the oracle_did-controlled MsgSetNextAlpha.

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.
  • For MsgCreateBond: a controller DID, a creator DID, optional oracle DID for alpha bonds, and reserve token denoms.
  • For MsgWithdrawReserve: the bond's allow_reserve_withdrawals flag must be true.
npm install @ixo/impactxclient-sdk
import { ixo, createSigningClient } from "@ixo/impactxclient-sdk";

const signingClient = await createSigningClient(RPC_ENDPOINT, offlineSigner);

Create a bond

const createBondMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgCreateBond",
  value: ixo.bonds.v1beta1.MsgCreateBond.fromPartial({
    bondDid: "did:ixo:bond:abc123",
    token: "BOND",
    name: "Solar Farm Alpha Bond",
    description: "Outcome-linked bond for verified solar production",
    functionType: "augmented_function",
    functionParameters: [
      ixo.bonds.v1beta1.FunctionParam.fromPartial({ param: "p0", value: "1" }),
    ],
    creatorDid: "did:ixo:creator123",
    controllerDid: "did:ixo:controller123",
    oracleDid: "did:ixo:oracle123",
    reserveTokens: ["uixo"],
    txFeePercentage: "0.5",
    exitFeePercentage: "1.0",
    feeAddress: feeAddress,
    reserveWithdrawalAddress: reserveAddress,
    maxSupply: { denom: "BOND", amount: "1000000" },
    orderQuantityLimits: [],
    sanityRate: "0",
    sanityMarginPercentage: "0",
    allowSells: true,
    allowReserveWithdrawals: false,
    alphaBond: true,
    batchBlocks: "1",
    outcomePayment: "0",
    creatorAddress: signerAddress,
  }),
};

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

Edit, set alpha, transition state

const editMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgEditBond",
  value: ixo.bonds.v1beta1.MsgEditBond.fromPartial({
    bondDid: "did:ixo:bond:abc123",
    name: "Solar Farm Alpha Bond v2",
    description: "Updated description",
    orderQuantityLimits: "",
    sanityRate: "",
    sanityMarginPercentage: "",
    editorDid: "did:ixo:editor123",
    editorAddress: signerAddress,
  }),
};

const setAlphaMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgSetNextAlpha",
  value: ixo.bonds.v1beta1.MsgSetNextAlpha.fromPartial({
    bondDid: "did:ixo:bond:abc123",
    alpha: "0.85",
    delta: "0.05",
    oracleDid: "did:ixo:oracle123",
    oracleAddress: oracleSignerAddress,
  }),
};

const updateStateMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgUpdateBondState",
  value: ixo.bonds.v1beta1.MsgUpdateBondState.fromPartial({
    bondDid: "did:ixo:bond:abc123",
    state: "OPEN",
    editorDid: "did:ixo:editor123",
    editorAddress: signerAddress,
  }),
};

Bond states are: HATCH (initial fundraising), OPEN (active trading), SETTLE (settlement), FAILED.

Buy, sell, swap

const buyMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgBuy",
  value: ixo.bonds.v1beta1.MsgBuy.fromPartial({
    buyerDid: "did:ixo:buyer123",
    amount: { denom: "BOND", amount: "1000000" },
    maxPrices: [{ denom: "uixo", amount: "100" }],
    bondDid: "did:ixo:bond:abc123",
    buyerAddress: signerAddress,
  }),
};

const sellMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgSell",
  value: ixo.bonds.v1beta1.MsgSell.fromPartial({
    sellerDid: "did:ixo:seller123",
    amount: { denom: "BOND", amount: "1000000" },
    bondDid: "did:ixo:bond:abc123",
    sellerAddress: signerAddress,
  }),
};

const swapMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgSwap",
  value: ixo.bonds.v1beta1.MsgSwap.fromPartial({
    swapperDid: "did:ixo:swapper123",
    bondDid: "did:ixo:bond:abc123",
    from: { denom: "uixo", amount: "1000" },
    toToken: "uatom",
    swapperAddress: signerAddress,
  }),
};

Outcome payments and withdrawals

const outcomePayMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgMakeOutcomePayment",
  value: ixo.bonds.v1beta1.MsgMakeOutcomePayment.fromPartial({
    senderDid: "did:ixo:funder123",
    amount: "1000000",
    bondDid: "did:ixo:bond:abc123",
    senderAddress: signerAddress,
  }),
};

const withdrawShareMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgWithdrawShare",
  value: ixo.bonds.v1beta1.MsgWithdrawShare.fromPartial({
    recipientDid: "did:ixo:holder123",
    bondDid: "did:ixo:bond:abc123",
    recipientAddress: signerAddress,
  }),
};

const withdrawReserveMsg = {
  typeUrl: "/ixo.bonds.v1beta1.MsgWithdrawReserve",
  value: ixo.bonds.v1beta1.MsgWithdrawReserve.fromPartial({
    withdrawerDid: "did:ixo:treasury123",
    amount: [{ denom: "uixo", amount: "500000" }],
    bondDid: "did:ixo:bond:abc123",
    withdrawerAddress: signerAddress,
  }),
};

Verify the result

Query bond state through the Blocksync GraphQL API or ixo.bonds.v1beta1 query endpoints on the gRPC gateway API.

Troubleshooting

invalid function type or parameters

function_type must be one of the supported curve types (power_function, sigmoid_function, swapper_function, augmented_function). Each requires a specific set of function_parameters — see the bonds proto.

state transition rejected

State transitions are constrained by the bond's lifecycle. HATCH → OPEN requires the hatch threshold to be met. OPEN → SETTLE is typically called once the outcome obligation is met or the bond expires.

reserve withdrawal not allowed

MsgWithdrawReserve fails when allow_reserve_withdrawals is false on MsgCreateBond. This flag is immutable.

alpha update unauthorized

Only the bond's oracle_address (resolving from oracle_did) can broadcast MsgSetNextAlpha. Confirm the signer matches.

Next steps