Developer Documentation

Everything you need to build on Anonyma — SDK integration, JSON-RPC API, WebSocket subscriptions, chain architecture, and proof-of-concept validation.

Quick Start SDK Reference

Contents

Start Building in Minutes

Connect your dApp to Anonyma with the browser wallet extension SDK.

1

Install the Wallet

Users install the Anonyma Wallet Chrome extension from the Chrome Web Store. The extension injects the provider at window.anonyma.

2

Detect the Provider

Wait for the anonyma#initialized event or check if window.anonyma exists. The provider is ready when window.anonyma.isAnonyma === true.

3

Connect & Build

Call connect() to request wallet access. Once approved, use the full SDK to send transactions, sign messages, and query balances.

JavaScript Detect & Connect
// Wait for wallet extension to initialize
function getProvider() {
  if ('anonyma' in window) {
    return window.anonyma;
  }

  return new Promise(resolve => {
    window.addEventListener('anonyma#initialized', () => {
      resolve(window.anonyma);
    }, { once: true });
  });
}

// Connect to wallet
const provider = await getProvider();
const { address, publicKey } = await provider.connect();
console.log('Connected:', address);

Provider API

Complete reference for the window.anonyma provider injected by the wallet extension.

Properties

isAnonyma boolean

Always true. Use this to verify the Anonyma provider is present.

if (window.anonyma?.isAnonyma) {
  // Anonyma wallet is available
}

Methods

getState() async → { isLocked, hasWallet, address }

Returns the current wallet state. Use this to check if the wallet is locked, whether a wallet exists, and the current address.

Return FieldTypeDescription
isLockedbooleanWhether the wallet is currently locked
hasWalletbooleanWhether a wallet has been created
addressstring | nullCurrent wallet address, or null if locked
const state = await window.anonyma.getState();
// { isLocked: false, hasWallet: true, address: "5Kd7..." }
connect() async → { address, publicKey }

Requests wallet connection. Opens an approval popup for the user to confirm. Throws if the user rejects.

Return FieldTypeDescription
addressstringBase58-encoded wallet address
publicKeystringBase58-encoded Ed25519 public key
try {
  const { address, publicKey } = await window.anonyma.connect();
  console.log('Wallet connected:', address);
} catch (err) {
  console.log('User rejected connection');
}
disconnect() async → void

Disconnects the current site from the wallet. Emits the 'disconnect' event.

await window.anonyma.disconnect();
getWallet() async → { address, publicKey, secretKey }

Returns full wallet keypair including the secret key. Use with extreme caution — only request this if your app needs to sign transactions client-side.

Return FieldTypeDescription
addressstringBase58-encoded address
publicKeystringBase58-encoded public key
secretKeystringBase58-encoded Ed25519 secret key (64 bytes)
const wallet = await window.anonyma.getWallet();
// ⚠️ Never expose secretKey to servers or third parties
signAndSendTransaction(tx) async → { hash }

Signs a transaction with the wallet key and broadcasts it to the network. Opens an approval popup for user confirmation.

ParameterTypeRequiredDescription
tx.tostringYesRecipient address (Base58)
tx.amountnumberYesAmount in drops (1 ANM = 1,000,000 drops)
tx.datastringNoArbitrary data payload (max 16,384 bytes)
// Send 1.5 ANM
const { hash } = await window.anonyma.signAndSendTransaction({
  to: '5Kd7Rv3gmYxBaQEKbr9tcBQ5o3PdFuoCEm34fE2dajXW',
  amount: 1_500_000,  // 1.5 ANM in drops
});
console.log('TX sent:', hash);

// Send with data payload
const { hash: h2 } = await window.anonyma.signAndSendTransaction({
  to: '5Kd7Rv3gmYxBaQEKbr9tcBQ5o3PdFuoCEm34fE2dajXW',
  amount: 0,
  data: 'Hello from Anonyma!',
});
signMessage(message) async → { signature }

Signs an arbitrary string message with the wallet's Ed25519 private key. Useful for authentication and verification without sending a transaction.

ParameterTypeDescription
messagestringThe message to sign
const { signature } = await window.anonyma.signMessage('Prove wallet ownership');
console.log('Signature:', signature); // Base58-encoded Ed25519 signature
getBalance() async → { balance, balanceANM }

Returns the connected wallet's current balance in drops and ANM.

Return FieldTypeDescription
balancenumberBalance in drops (smallest unit)
balanceANMnumberBalance in ANM (balance / 1,000,000)
const { balance, balanceANM } = await window.anonyma.getBalance();
console.log(`Balance: ${balanceANM} ANM`);

Events

on(event, callback) subscribe
off(event, callback) unsubscribe

Subscribe or unsubscribe to wallet events.

EventCallback ArgsDescription
'connect'{ address }Wallet connected to site
'disconnect'Wallet disconnected
'accountChanged'{ address }Active account changed
// Listen for account changes
window.anonyma.on('accountChanged', ({ address }) => {
  console.log('Account changed:', address);
});

// Listen for disconnect
window.anonyma.on('disconnect', () => {
  console.log('Wallet disconnected');
});

RPC Methods

Direct HTTP JSON-RPC 2.0 interface to the Anonyma chain node.

POST https://rpc.anonyma.network

All requests use POST with Content-Type: application/json. Body follows the JSON-RPC 2.0 specification.

// Base request format
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "anm_chainInfo",
  "params": {}
}
anm_chainInfo GET

Returns chain metadata: height, block hash, pending transactions, account count, fee recycling stats.

Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "anm_chainInfo"
}
Response
{
  "chainId": "anonyma-mainnet-1",
  "chainName": "Anonyma",
  "height": 42069,
  "latestBlock": "a1b2c3...",
  "baseFee": 1,
  "dropsPerAnm": 1000000,
  "pendingTxs": 0,
  "accounts": 128,
  "totalFeesRecycled": 5000,
  "totalFeesRecycledANM": 0.005
}
anm_getBalance GET

Returns the balance for a given address in both drops and ANM.

Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "anm_getBalance",
  "params": {
    "address": "5Kd7Rv..."
  }
}
Response
{
  "address": "5Kd7Rv...",
  "balance": 1500000000,
  "balanceANM": 1500.0
}
anm_getAccount GET

Returns full account info including balance, nonce, and ANM balance.

Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "anm_getAccount",
  "params": {
    "address": "5Kd7Rv..."
  }
}
Response
{
  "address": "5Kd7Rv...",
  "balance": 1500000000,
  "balanceANM": 1500.0,
  "nonce": 42
}
anm_getNonce GET

Returns the next nonce to use for a transaction from the given address. Includes pending (mempool) transactions.

Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "anm_getNonce",
  "params": {
    "address": "5Kd7Rv..."
  }
}
Response
{
  "address": "5Kd7Rv...",
  "nonce": 43
}
anm_sendTransaction SEND

Submit a signed transaction to the network mempool. The transaction must be pre-signed with a valid Ed25519 signature.

Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "anm_sendTransaction",
  "params": {
    "transaction": {
      "from": "5Kd7Rv...",
      "to": "3xQ9Fm...",
      "amount": 1000000,
      "fee": 1,
      "nonce": 0,
      "timestamp": 1700000000000,
      "data": "",
      "hash": "abc123...",
      "signature": "def456..."
    }
  }
}
Response
// Success
{
  "success": true,
  "hash": "abc123..."
}

// Error
{
  "success": false,
  "error": "Insufficient balance"
}
anm_getTransaction GET

Retrieve a transaction by its hash. Returns null if not found.

Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "anm_getTransaction",
  "params": {
    "hash": "abc123..."
  }
}
Response
{
  "transaction": {
    "from": "5Kd7Rv...",
    "to": "3xQ9Fm...",
    "amount": 1000000,
    "fee": 1,
    "hash": "abc123...",
    ...
  },
  "blockIndex": 1234
}
anm_getTransactions GET

Get recent transactions for an address. Default limit is 50.

// Params
{ "address": "5Kd7Rv...", "limit": 20 }
anm_searchTransactions GET

Search transactions for an address with optional data content filter. Default limit is 100.

// Params
{ "address": "5Kd7Rv...", "dataContains": "hello", "limit": 50 }
anm_getBlock GET

Retrieve a full block by index, including all transactions.

// Params
{ "index": 1234 }
anm_getBlockCount GET

Returns the current block height.

// Response
{ "height": 42069 }
anm_getLatestBlock GET

Returns the latest block header (without full transaction data).

// Response
{
  "index": 42069,
  "timestamp": 1700000000000,
  "hash": "a1b2c3...",
  "previousHash": "d4e5f6...",
  "validator": "5Kd7Rv...",
  "txCount": 3
}
Rate Limits

The RPC endpoint is rate-limited to 100 requests per 10 seconds per IP address. Request body size is limited to 256 KB. Exceeding these limits returns HTTP 429.

Real-Time Subscriptions

Subscribe to live block production and address-specific transaction events.

WS wss://rpc.anonyma.network

Connect via WebSocket to the same RPC endpoint. Send JSON messages to subscribe/unsubscribe to event channels.

Subscribe to Blocks

Receive a notification every time a new block is produced (~400ms).

Subscribe
// Send
{ "action": "subscribe", "channel": "block" }

// Unsubscribe
{ "action": "unsubscribe", "channel": "block" }
Event
{
  "event": "newBlock",
  "data": {
    "index": 42069,
    "timestamp": 1700000000000,
    "hash": "a1b2c3...",
    "validator": "5Kd7...",
    "txCount": 3
  }
}
Subscribe to Address Transactions

Receive a notification whenever a transaction involves a specific address (as sender or recipient).

Subscribe
// Watch incoming/outgoing txs
{
  "action": "subscribe",
  "channel": "tx:5Kd7Rv..."
}
Event
{
  "event": "newTransaction",
  "data": {
    "transaction": { ... },
    "blockIndex": 42069
  }
}
JavaScript Full WebSocket Example
const ws = new WebSocket('wss://rpc.anonyma.network');

ws.addEventListener('open', () => {
  // Subscribe to new blocks
  ws.send(JSON.stringify({ action: 'subscribe', channel: 'block' }));

  // Subscribe to txs for a specific address
  ws.send(JSON.stringify({
    action: 'subscribe',
    channel: 'tx:5Kd7Rv3gmYxBaQEKbr9tcBQ5o3PdFuoCEm34fE2dajXW'
  }));
});

ws.addEventListener('message', (e) => {
  const msg = JSON.parse(e.data);

  if (msg.event === 'newBlock') {
    console.log('New block:', msg.data.index, 'txs:', msg.data.txCount);
  }

  if (msg.event === 'newTransaction') {
    console.log('New tx:', msg.data.transaction.hash);
  }
});

Chain Architecture

How Anonyma's Proof-of-Authority consensus, fee recycling, and cryptography work.

Consensus Proof of Authority (PoA)
Chain ID anonyma-mainnet-1
Block Time 400ms
Total Supply 1,000,000,000 ANM
Denomination 1 ANM = 1,000,000 drops
Base Fee 1 drop (recycled to validator)
Max Data Size 16,384 bytes per TX
Max TXs per Block 2,000
Signatures Ed25519 (tweetnacl)
Hashing SHA-256
Encoding Base58 (bs58)
Encryption X25519-XSalsa20-Poly1305

Proof of Authority

Anonyma uses a single trusted validator to produce blocks. This eliminates energy-intensive mining and provides instant block finality at ~400ms intervals. The validator node is configured at genesis and signs every block with its Ed25519 key.

Instant Finality No forks, no confirmations needed. A transaction is final as soon as it's included in a block.
Sub-Second Blocks 400ms block interval means transactions confirm almost instantly.
Zero Energy Waste No mining puzzles. The validator produces blocks deterministically.

Fee Recycling Model

Transaction fees (1 drop per TX) are recycled back to the validator rather than burned. This creates a sustainable economic model where no tokens are permanently removed from circulation.

Why Recycle?

Fee recycling maintains total supply at exactly 1 billion ANM. The validator is compensated for operating the network infrastructure while preserving the token economy's stability.

Transaction Structure

Every transaction contains the following fields. The hash is computed from the raw fields, then signed with the sender's Ed25519 private key.

FieldTypeDescription
fromstringSender address (Base58)
tostringRecipient address (Base58)
amountnumberAmount in drops
feenumberFee in drops (must be ≥ BASE_FEE = 1)
noncenumberSequential counter (prevents replay attacks)
timestampnumberUnix timestamp in milliseconds
datastringOptional arbitrary data (max 16,384 bytes)
hashstringSHA-256 hash of raw transaction fields
signaturestringEd25519 signature of the hash (Base58)
// Transaction hash is computed as:
// SHA-256( from + to + amount + fee + nonce + timestamp + data )
// Then signed with Ed25519: sign(hash, secretKey)

Block Structure

FieldTypeDescription
indexnumberBlock height (0 = genesis)
timestampnumberBlock creation time (unix ms)
previousHashstringHash of the previous block
hashstringSHA-256 of block header
txRootstringMerkle-like root of included transactions
stateRootstringHash of state after applying block
validatorstringAddress of the block producer
txCountnumberNumber of transactions in block
transactionsarrayArray of signed transactions

Cryptography Stack

Ed25519

All keys, transaction signatures, and block signatures use Ed25519 elliptic-curve cryptography via tweetnacl. Keys are 32-byte public / 64-byte secret.

SHA-256

All hashing — transaction hashes, block hashes, state roots — uses SHA-256 from the Web Crypto API for performance and security.

X25519-XSalsa20-Poly1305

End-to-end encrypted messaging in Palavia uses tweetnacl-js box. Messages are encrypted with the recipient's public key and can only be decrypted by the recipient.

Base58

All addresses, public keys, signatures, and hashes are encoded in Base58 (Bitcoin-style) for human-readability and URL safety.

Verification & Integrity

How Anonyma ensures every transaction is valid and the chain is tamper-proof.

Signature Verification

Every transaction includes an Ed25519 signature. The chain verifies that the signature matches the sender's public key before accepting the transaction. Invalid signatures are rejected immediately.

Hash Integrity

Transaction hashes are recomputed and compared. A transaction with a modified field will produce a different SHA-256 hash, causing signature verification to fail — making transactions tamper-proof.

Chain Linking

Each block references the previous block's hash, forming an immutable linked chain. Altering any historical block would invalidate all subsequent block hashes.

Nonce Ordering

Nonces are sequential per account, preventing replay attacks. A transaction with a duplicate or out-of-order nonce is rejected by the mempool and block validator.

Balance Checks

Before inclusion in a block, every transaction is validated against the sender's current balance. The amount + fee must not exceed available balance. Overdrafts are impossible.

State Root

Each block includes a state root — a hash of the entire account state after applying all transactions. This allows any node to verify state consistency independently.

JavaScript Verify a Transaction Signature (Client-Side)
import nacl from 'tweetnacl';
import bs58 from 'bs58';

function verifyTransaction(tx) {
  // Decode the signature and public key from Base58
  const signature = bs58.decode(tx.signature);
  const publicKey = bs58.decode(tx.from); // address = public key
  const hashBytes = bs58.decode(tx.hash);

  // Verify Ed25519 signature
  const valid = nacl.sign.detached.verify(hashBytes, signature, publicKey);

  return valid; // true if signature is authentic
}

// Verify a message signature
function verifyMessage(message, signature, address) {
  const msgBytes = new TextEncoder().encode(message);
  const sigBytes = bs58.decode(signature);
  const pubKey  = bs58.decode(address);

  return nacl.sign.detached.verify(msgBytes, sigBytes, pubKey);
}

RPC Error Codes

CodeConstantDescription
-32700PARSE_ERRORInvalid JSON received
-32600INVALID_REQUESTNot a valid JSON-RPC 2.0 request
-32601METHOD_NOT_FOUNDUnknown method name
-32602INVALID_PARAMSMissing or invalid parameters
-32603INTERNAL_ERRORInternal server error
-32000TX_INVALIDTransaction validation failed
-32001INSUFFICIENT_BALInsufficient balance for tx + fee
-32002INVALID_NONCENonce mismatch (too low or too high)
-32003INVALID_SIGEd25519 signature verification failed
-32004DATA_TOO_LARGEData payload exceeds 16,384 bytes

Full Integration

Complete example: detect wallet, connect, send a transaction, and listen for confirmation.

JavaScript Complete dApp Integration
// ── 1. Detect & Connect ──
async function initAnonyma() {
  // Wait for extension to inject provider
  const provider = window.anonyma ?? await new Promise(r =>
    window.addEventListener('anonyma#initialized', () => r(window.anonyma), { once: true })
  );

  // Check wallet state
  const state = await provider.getState();
  if (!state.hasWallet) {
    alert('Please create a wallet in the Anonyma extension');
    return;
  }

  // Request connection
  const { address } = await provider.connect();
  console.log('Connected:', address);

  return provider;
}

// ── 2. Send Transaction ──
async function sendPayment(provider, recipient, amountANM) {
  const amountDrops = amountANM * 1_000_000;

  const { hash } = await provider.signAndSendTransaction({
    to: recipient,
    amount: amountDrops,
  });

  console.log('TX submitted:', hash);
  return hash;
}

// ── 3. Listen for Confirmation via WebSocket ──
function waitForConfirmation(txHash, address) {
  return new Promise((resolve) => {
    const ws = new WebSocket('wss://rpc.anonyma.network');

    ws.addEventListener('open', () => {
      ws.send(JSON.stringify({
        action: 'subscribe',
        channel: `tx:${address}`,
      }));
    });

    ws.addEventListener('message', (e) => {
      const msg = JSON.parse(e.data);
      if (msg.data?.transaction?.hash === txHash) {
        console.log('Confirmed in block', msg.data.blockIndex);
        ws.close();
        resolve(msg.data);
      }
    });
  });
}

// ── 4. Usage ──
const provider = await initAnonyma();
const hash = await sendPayment(provider, 'RecipientAddress...', 10);
const confirmation = await waitForConfirmation(hash, 'SenderAddress...');
console.log('All done!', confirmation);

Start Building on Anonyma

Install the wallet extension, connect to the RPC, and ship your first dApp.