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 }

Returns the connected wallet's address and public key. Use this to identify the user's account or derive encryption keys.

Return FieldTypeDescription
addressstringBase58-encoded wallet address
publicKeystringBase58-encoded Ed25519 public key
const { address, publicKey } = await window.anonyma.getWallet();
console.log('Wallet:', address);
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 524,288 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. Failover endpoint: https://rpc2.anonyma.network (Validator 2).

// 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, validators, and connected peers.

Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "anm_chainInfo"
}
Response
{
  "chainId": "anonyma-mainnet-1",
  "chainName": "Anonyma",
  "height": 42069,
  "latestBlock": "a1b2c3...",
  "baseFee": 1000000,
  "dropsPerAnm": 1000000,
  "pendingTxs": 0,
  "accounts": 128,
  "totalFeesRecycled": 5000,
  "totalFeesRecycledANM": 0.005,
  "validators": ["EA8sUw...", "2EJMoc..."],
  "validatorCount": 2,
  "peers": 1,
  "peerAddresses": ["2EJMoc..."]
}
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
}
anm_getValidators GET

Returns the list of active validators in the network and the total validator count.

// Response
{
  "validators": ["EA8sUw...", "2EJMoc..."],
  "count": 2
}
anm_getPeers GET

Returns the list of connected P2P peer addresses and the total peer count. Only available when multi-validator mode is active.

// Response
{
  "peers": ["2EJMoc..."],
  "count": 1
}
Rate Limits

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

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 Multi-Validator Proof-of-Authority consensus, P2P networking, fee recycling, and cryptography work.

Consensus Multi-Validator Proof of Authority (PoA)
Validator Network P2P WebSocket + Round-Robin Slots
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 ANM (recycled to validator)
Max Data Size 524,288 bytes (512 KB) per TX
Max TXs per Block 2,000
Signatures Ed25519 (tweetnacl)
Hashing SHA-256
Encoding Base58 (bs58)
Encryption X25519-XSalsa20-Poly1305

Multi-Validator Proof of Authority

Anonyma uses a distributed multi-validator Proof-of-Authority consensus. Validators connect via a P2P WebSocket network and produce blocks in a deterministic round-robin schedule (producer = blockHeight % validatorCount). Each validator signs blocks with its Ed25519 key, and all peers verify signatures before accepting blocks. This eliminates single points of failure while maintaining sub-second finality.

Distributed Trust No single validator controls the chain. Multiple independent validators must operate honestly to produce blocks.
Round-Robin Consensus Deterministic block producer assignment: height mod validator count. Transparent and verifiable by any node.
P2P Networking Validators relay blocks and transactions via WebSocket. Auto-reconnect, chain sync on connect, and deduplication built in.
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. Validators produce blocks deterministically in their assigned slots.

Fee Recycling Model

Transaction fees (1 ANM per TX) are recycled back to the block's 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. Validators are compensated for operating 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,000,000 = 1 ANM)
noncenumberSequential counter (prevents replay attacks)
timestampnumberUnix timestamp in milliseconds
datastringOptional arbitrary data (max 524,288 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. Group and channel profiles (name + avatar) are also encrypted on-chain using NaCl SecretBox with domain-separated keys.

Base58

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

Running a Validator

Configure and operate a validator node in Anonyma's multi-validator PoA network.

Overview

Anonyma v2.0 introduced multi-validator consensus. 2 validators are live in production — primary at rpc.anonyma.network and backup at rpc2.anonyma.network. Validators connect to each other via a P2P WebSocket network, relay blocks and transactions, and produce blocks in a deterministic round-robin schedule. All frontend apps automatically fail over to Val 2 if Val 1 is unreachable (3-second timeout). Any node can join the network by setting the appropriate environment variables.

Environment Variables

VariableDescriptionExample
ANONYMA_PEERSComma-separated list of peer validators (host:port)anonyma-val2.railway.internal:7779
ANONYMA_VALIDATORSComma-separated validator addresses (all known validators)2EJMoc...,3xLBWo...
ANONYMA_P2P_PORTP2P WebSocket listen port7779
PORTHTTP JSON-RPC server port8080
ANONYMA_VALIDATOR_KEYValidator's Ed25519 secret key (Base64) — overrides disk key(base64 string)
ANONYMA_DATA_DIRBlockchain data directory (persistent volume)/app/data

How It Works

1. Connect On startup, the validator connects to all peers specified in ANONYMA_PEERS via WebSocket and performs a HELLO handshake.
2. Sync After connecting, the validator requests any missing blocks from peers to catch up with the chain tip.
3. Produce In each 400ms slot, the assigned validator (height % validatorCount) produces a block and broadcasts it to all peers.
4. Relay Transactions submitted to any validator are gossipped to all peers. Blocks are relayed and verified (signature + slot assignment) before acceptance.
Backward Compatible

Without ANONYMA_PEERS and ANONYMA_VALIDATORS, the node runs in solo validator mode — identical behavior to v1. No configuration changes needed for existing single-validator deployments.

Bridge & Swap Integration

Integrate ANM token swaps into your application. The Bridge API enables cross-chain swaps between ETH, SOL and ANM with constant-product AMM pricing.

Base URL
https://bridge.anonyma.network
GET /api/pools

Returns all liquidity pools with current reserves and live USD prices.

Response

[
  {
    "pair": "ETH_ANM",
    "tokenA": "ETH",
    "tokenB": "ANM",
    "reserveA": 15.0,
    "reserveB": 5000000.0,
    "priceANM_USD": 0.0085,
    "priceETH_USD": 2800.0,
    "priceSOL_USD": 145.0
  },
  {
    "pair": "SOL_ANM",
    "tokenA": "SOL",
    "tokenB": "ANM",
    "reserveA": 300.0,
    "reserveB": 5000000.0,
    "priceANM_USD": 0.0085,
    "priceETH_USD": 2800.0,
    "priceSOL_USD": 145.0
  }
]
GET /api/prices

Returns current USD prices for ANM, ETH and SOL. Prices are fetched from CoinGecko every 60 seconds.

{
  "ANM": 0.0085,
  "ETH": 2800.0,
  "SOL": 145.0
}
GET /api/quote

Get a price quote for a swap. Uses constant-product AMM (x × y = k) with 0.3% fee.

Query Parameters

ParamTypeDescription
pairstringETH_ANM or SOL_ANM
directionstringbuy (token → ANM) or sell (ANM → token)
amountnumberAmount of input token

Example

// Quote: Buy ANM with 0.1 ETH
GET /api/quote?pair=ETH_ANM&direction=buy&amount=0.1

{
  "amountOut": 33222.59,
  "fee": 0.0003,
  "feeToken": "ETH",
  "priceImpact": 0.0067,
  "rate": 333333.33,
  "minAmountOut": 33056.48
}
GET /api/deposit-address

Get the bridge deposit address for a given pair and direction. Users send funds to this address before submitting a swap.

Query Parameters

ParamTypeDescription
pairstringETH_ANM or SOL_ANM
directionstringbuy or sell
GET /api/deposit-address?pair=ETH_ANM&direction=buy

{ "address": "0x..." }
POST /api/swap

Submit a swap after depositing funds to the bridge address. The bridge verifies the deposit on-chain, executes the AMM trade, and sends the output token to the receiver.

Request Body (JSON)

FieldTypeDescription
pairstringETH_ANM or SOL_ANM
directionstringbuy or sell
amountInnumberAmount sent to bridge
senderAddressstringYour sending address (verified against TX)
receiverAddressstringAddress to receive output tokens
txHashstringDeposit transaction hash

Example

// Buy ANM with ETH
const res = await fetch('https://bridge.anonyma.network/api/swap', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    pair: 'ETH_ANM',
    direction: 'buy',
    amountIn: 0.1,
    senderAddress: '0xYourEthAddress...',
    receiverAddress: 'AnmReceiverAddress...',
    txHash: '0xDepositTxHash...',
  }),
});

const data = await res.json();
// { swapId, status, amountOut, payoutTxHash }
GET /api/swap/:id

Check the status of a swap by its ID.

GET /api/swap/abc123

{
  "id": "abc123",
  "pair": "ETH_ANM",
  "direction": "buy",
  "status": "completed",
  "amountIn": 0.1,
  "amountOut": 33222.59,
  "feeAmount": 0.0003,
  "depositTxHash": "0x...",
  "payoutTxHash": "5abc...",
  "createdAt": 1708900000000,
  "completedAt": 1708900005000
}

Integration Flow

Complete swap lifecycle for integrating into your application:

1

Get Quote

Call GET /api/quote to show the user the expected output amount, fee, and price impact before they confirm.

2

Get Deposit Address

Call GET /api/deposit-address to get the bridge wallet address the user should send funds to.

3

User Deposits

User sends the input token (ETH, SOL, or ANM) to the bridge deposit address and obtains the transaction hash.

4

Submit Swap

Call POST /api/swap with the deposit TX hash. The bridge verifies on-chain, executes the AMM trade, and sends the output token.

5

Poll Status

Optionally poll GET /api/swap/:id to track completion and get the payout transaction hash.

AMM Pricing Model

Anonyma Swap uses a constant-product AMM (x × y = k), the same model as Uniswap V2.

PropertyValue
Fee0.3% per swap
FormulaamountOut = (reserveOut × amountIn × 0.997) / (reserveIn + amountIn × 0.997)
PairsETH_ANM, SOL_ANM
Directionsbuy (token → ANM), sell (ANM → token)
VerificationDeposits are verified on-chain (ETH via Etherscan, SOL via Solana RPC, ANM via Anonyma RPC)
Sender checkThe sender address in the deposit TX must match senderAddress
TX reuseEach deposit TX hash can only be used once

Admin: Add Liquidity

Top up pool reserves without resetting swap history. Requires Authorization: Bearer <ADMIN_SECRET> header.

POST /api/add-liquidity

Add liquidity to a pool. Provide one side for proportional add (price unchanged), or both sides for custom ratio (price may shift).

Body (JSON)

FieldTypeRequiredDescription
pairstringYesETH_ANM or SOL_ANM
amountAnumberOne of A/BAmount of ETH or SOL to add
amountBnumberOne of A/BAmount of ANM to add

Modes

InputBehaviorPrice Impact
Only amountAAuto-calculates proportional ANMNone (recommended)
Only amountBAuto-calculates proportional ETH/SOLNone (recommended)
Both amountA + amountBAdds both as-isMay shift price

Response

{
  "ok": true,
  "pair": "SOL_ANM",
  "added": { "amountA": "proportional", "amountB": 100000 },
  "reserves": { "tokenA": 3.5993, "ANM": 300000 },
  "k": 1079775,
  "priceBefore": 0.000011998,
  "priceAfter": 0.000011998,
  "priceANM_USD": 0.000939
}

Error Responses

All errors return JSON with an error field:

{ "error": "Deposit transaction not found or not confirmed..." }
HTTPMeaning
400Missing or invalid parameters
403Unauthorized (admin endpoints: /api/reset-pools, /api/add-liquidity)
404Swap or resource not found
409Deposit TX hash already used
500Internal error

Swap Widget (Embed)

Embed the full Anonyma Swap interface into your own app using an iframe. The widget auto-detects when embedded and hides redundant navigation.

Basic Embed

<iframe
  src="https://swap.anonyma.network"
  title="Anonyma Swap"
  width="100%"
  height="680"
  style="border: none; border-radius: 24px;"
  allow="clipboard-write"
/>

Pre-fill ANM Address

Pass the user's ANM wallet address via the anm query parameter. The swap form will auto-fill it into the correct field based on swap direction:

<iframe
  src="https://swap.anonyma.network?anm=ANM_WALLET_ADDRESS"
  title="Anonyma Swap"
  width="100%"
  height="680"
  style="border: none; border-radius: 24px;"
  allow="clipboard-write"
/>

URL Parameters

ParameterTypeDescription
anmstringANM wallet address to pre-fill in the swap form

Embed Behavior

FeatureStandaloneEmbedded (iframe)
Footer linksVisibleHidden
Bridge status dotVisibleHidden
Pool infoVisibleVisible
Vertical padding48px16px (compact)

Content Security Policy

The swap widget allows embedding from these origins:

frame-ancestors 'self' https://anonyma.palavia.chat https://*.vercel.app http://localhost:*

To embed from a custom domain, contact us to add your origin to the allowlist.

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 524,288 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.