The most common operation on Sigil: move the native currency from one DID to another. On mainnet this is MINT, on the Evolve canary it is BITS, on testnet it is test SIGIL. The wire-level transaction is identical — only the chain id and amount semantics differ.

When to use this

  • Paying a counterparty in the native currency.
  • Funding a service account from a treasury.
  • Moving stake-eligible balance between DIDs you control.
For custom-token transfers (e.g. a stablecoin minted on Sigil) see Create a custom token.

Prerequisites

1

A funded sender DID

Run the Quickstart on testnet to mint a DID and drip from the faucet.
2

A recipient DID

Any valid did:oas:sigil:<kind>:<32-byte-blake3-hex> accepted by the chain.
3

Environment variables

export SIGIL_RPC_URL=https://testnet-rpc.sigil.ml/rpc
export SIGIL_CHAIN_ID=sigil-testnet-1
export SIGIL_SIGNING_KEY_HEX=...
export SIGIL_SENDER_DID=did:oas:sigil:agent:...
export RECIPIENT_DID=did:oas:sigil:agent:...
export AMOUNT_BASE_UNITS=1000000   # 1 test SIGIL

Script

import * as ed25519 from '@noble/ed25519';
import { blake3 } from '@noble/hashes/blake3';
import { sha512 } from '@noble/hashes/sha512';
import { bytesToHex, hexToBytes } from '@noble/ciphers/utils';

ed25519.etc.sha512Sync = (...m) => {
  const h = sha512.create();
  for (const part of m) h.update(part);
  return h.digest();
};

const TX_DOMAIN_TAG = 'sigil/transaction/v1\n';

async function rpc<T>(method: string, params: unknown): Promise<T> {
  const res = await fetch(process.env.SIGIL_RPC_URL!, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
  });
  const body = await res.json() as { result?: T; error?: { code: number; message: string } };
  if (body.error) throw new Error(`${body.error.code}: ${body.error.message}`);
  if (body.result === undefined) throw new Error('missing result');
  return body.result;
}

async function main() {
  const privateKey = hexToBytes(process.env.SIGIL_SIGNING_KEY_HEX!);
  const publicKey = await ed25519.getPublicKey(privateKey);
  const senderDid = process.env.SIGIL_SENDER_DID!;
  const recipientDid = process.env.RECIPIENT_DID!;
  const amount = Number(process.env.AMOUNT_BASE_UNITS);

  const { nonce } = await rpc<{ nonce: number }>('sigil_getBalance', { did: senderDid });

  const tx = {
    chain_id: process.env.SIGIL_CHAIN_ID,
    sender_did: senderDid,
    nonce,
    gas_limit: 200_000,
    gas_price: 1,
    tx_type: {
      Transfer: {
        recipient_did: recipientDid,
        amount,
        memo: 'cookbook/transfer',
      },
    },
    timestamp: new Date().toISOString(),
  };

  const transcript = TX_DOMAIN_TAG + JSON.stringify(tx);
  const digest = blake3(new TextEncoder().encode(transcript));
  const signature = await ed25519.sign(digest, privateKey);

  const { tx_hash } = await rpc<{ tx_hash: string }>('sigil_sendTransaction', {
    tx,
    signature: bytesToHex(signature),
    signer_public_key: bytesToHex(publicKey),
  });
  console.log('submitted:', tx_hash);

  for (let i = 0; i < 20; i += 1) {
    const r = await rpc<{ status: string; block_height: number | null }>(
      'sigil_getTransactionReceipt',
      { tx_hash },
    );
    if (r && r.status !== 'pending') {
      console.log('final:', r);
      return;
    }
    await new Promise((resolve) => setTimeout(resolve, 1_500));
  }
  throw new Error('receipt did not finalise');
}

main().catch((err) => { console.error(err); process.exit(1); });

Expected output

submitted: 0xe7d3...
final: { status: 'final', block_height: 184201, gas_used: 21000, error: null }

Common errors

SymptomCauseFix
RPC -32602: invalid paramstx_type is the wrong shapeThe enum is externally tagged: { "Transfer": {...} }, not { "type": "Transfer", ... }
RPC -32603: insufficient balanceSender lacks amount + gas_limit * gas_priceTop up via faucet or another transfer
RPC -32603: nonce too lowTwo transfers racedRe-fetch nonce after each submission
Receipt stuck on pendingMempool full or sender quota exhaustedWait one block; back off exponentially

See also