Publishing Anchor Metadata

By AnchorFactory ·

For your anchor to be discoverable by wallets and applications, you need to publish service metadata on-chain. This metadata tells the resolver where your anchor lives, what it supports, and how to interact with it.

How Discovery Works

Your Anchor Account
  → publishes metadata via SET_INFO block
  → metadata stored on your account's chain

Resolver
  → reads metadata from known anchor accounts
  → returns matching services when queried

Wallet / dApp
  → queries resolver: "find FX anchors supporting KTA/USDC"
  → gets your endpoint URL + capabilities
  → calls your API directly

Metadata Format

Anchor metadata is a JSON object stored as a base64-encoded string in the account's metadata field via a SET_INFO operation.

FX Anchor Example

{
  "version": 1,
  "currencyMap": {
    "$KTA": "keeta_anqdilpazdekdu4acw65fj7smltcp26wbrildkqtszqvverljpwpezmd44ssg",
    "$USDC": "keeta_amnkge74xitii5dsobstldatv3irmyimujfjotftx7plaaaseam4bntb7wnna"
  },
  "services": {
    "fx": {
      "myExchange": {
        "from": [
          { "asset": "$KTA", "network": "chain:keeta:21378" },
          { "asset": "$USDC", "network": "chain:keeta:21378" }
        ],
        "operations": ["getEstimate", "createExchange", "getExchangeStatus"]
      }
    }
  }
}

Fields

Field

Required

Description

version

Yes

Schema version (currently 1)

currencyMap

Yes

Shorthand symbol → full token address mapping

services

Yes

Service category → anchor definitions

resolvers

No

Child resolver public keys (for scaling)

Service Categories

Category

Key

Typical Operations

FX

fx

getEstimate, getQuote, createExchange, getExchangeStatus

KYC

kyc

createVerification, getVerificationStatus, getCertificates

Asset Movement

assetMovement

simulateTransfer, initiateTransfer

Banking

banking

createDeposit, createWithdrawal

Username

username

resolve, claim, release

Storage

storage

get, set, delete

Notification

notification

register, send

Publishing Metadata On-Chain

Step 1: Build the Metadata

const KeetaNet = require("@keetanetwork/keetanet-client");

const metadata = {
  version: 1,
  currencyMap: {
    "$KTA": "keeta_anqdilpazdekdu4acw65fj7smltcp26wbrildkqtszqvverljpwpezmd44ssg",
    "$USDC": "keeta_amnkge74xitii5dsobstldatv3irmyimujfjotftx7plaaaseam4bntb7wnna",
  },
  services: {
    fx: {
      myExchange: {
        from: [
          { asset: "$KTA", network: "chain:keeta:21378" },
          { asset: "$USDC", network: "chain:keeta:21378" },
        ],
        operations: ["getEstimate", "createExchange", "getExchangeStatus"],
      },
    },
  },
};

const metadataBase64 = btoa(JSON.stringify(metadata));

Step 2: Publish a SET_INFO Block

const seed = Buffer.from(process.env.SEED, "hex");
const account = KeetaNet.lib.Account.fromSeed(seed, 0);
const client = await KeetaNet.UserClient.fromNetwork("main", null, { account });

// Get current chain head
const state = await client.getAccountState(account);
const head = state.head; // previous block hash

// Build SET_INFO block
const builder = new KeetaNet.lib.Block.Builder();
builder.account = account;
builder.previous = head || KeetaNet.lib.Block.NO_PREVIOUS;
builder.network = 0x5382; // mainnet

builder.addOperation({
  type: KeetaNet.lib.Block.Operation.SET_INFO,
  name: "My FX Anchor",
  description: "Currency exchange service for KTA and USDC",
  metadata: metadataBase64,
});

const block = await builder.seal();

// Publish (requires fee block)
await client.publish([block], {
  generateFeeBlock: async (staple) => {
    return feeBuilder.computeFeeBlock(staple);
  },
});

console.log("Metadata published! Block hash:", block.hash);

Step 3: Verify Discovery

const { Resolver } = await import("@keetanetwork/anchor/lib/resolver");

const resolver = new Resolver({
  root: resolverAccount,
  client: client,
});

// Look up your anchor
const fxServices = await resolver.lookup("fx", {
  inputCurrency: "$KTA",
  outputCurrency: "$USDC",
});

console.log("Found services:", fxServices);

Updating Metadata

To update your metadata, publish a new SET_INFO block with the updated JSON. The latest block's metadata replaces the previous one.

// Change supported pairs, update endpoints, etc.
metadata.services.fx.myExchange.from.push(
  { asset: "$EURC", network: "chain:keeta:21378" }
);

// Publish new SET_INFO block (same flow as above)

Metadata Size Limits

On-chain metadata is limited to approximately 5400 characters of compressed JSON. If your anchor supports many pairs (>60 pools), you'll need to split across multiple resolver accounts.

Multi-Resolver Pattern

{
  "version": 1,
  "currencyMap": { ... },
  "services": {
    "fx": {
      "topPool1": { ... },
      "topPool2": { ... }
    }
  },
  "resolvers": [
    "keeta_child_resolver_1...",
    "keeta_child_resolver_2..."
  ]
}
  • Master resolver: top pools by market cap + references to child resolvers

  • Child resolvers: ~60 pools each, assigned by creation time

  • The resolver SDK follows resolvers references automatically

Master Metadata Endpoint

The current live metadata for all registered anchors is available at:

https://static.network.keeta.com/metadata/services

Study this to see the exact format used by production anchors.

Next Steps

More in Core SDK · All documentation