Skip to main content
OnchainDB stores JSON documents with verifiable storage and automatic payment handling through the x402 protocol.

How It Works

The SDK automatically handles HTTP 402 responses internally. When you pass a payment callback as the second parameter to store(), the SDK:
  1. Attempts the store operation
  2. If the server returns 402 (payment required), the SDK invokes your callback with the payment quote
  3. Your callback executes the blockchain payment and returns { txHash, network }
  4. The SDK automatically retries the store operation with the payment proof
You do NOT need to catch 402 errors or make a second API call - the SDK handles the entire flow.

Store with Payment Callback

// Store with payment callback (x402 flow)
const result = await client.store(
  {
    collection: 'posts',
    data: [{
      title: 'My First Post',
      content: 'Stored on blockchain!',
      author: 'alice'
    }]
  },
  // Payment callback - SDK invokes this when server returns 402
  async (quote) => {
    console.log('Payment required:', quote.totalCostTia, 'TIA');
    console.log('Pay to:', quote.brokerAddress);

    // Execute blockchain payment (e.g., via Keplr)
    const txHash = await wallet.sendTokens(
      quote.brokerAddress,
      quote.totalCostTia
    );

    // Return format is required - SDK uses this to retry the store
    return {
      txHash: txHash,
      network: 'mocha-4' // or 'celestia' for mainnet
    };
  },
  true // waitForConfirmation
);

// SDK automatically retried with payment proof - result contains confirmed data
console.log(`Confirmed at block ${result.block_height}`);

Store Without Payment Callback

If Auto-Pay is enabled (userKey with authz), no callback needed:
// With Auto-Pay enabled, payment happens automatically
const result = await client.store({
  collection: 'data',
  data: [{ example: 'data' }]
});

Wait for Confirmation

The waitForConfirmation option ensures data is confirmed on-chain before returning:
// Without confirmation (returns immediately with ticket)
const quick = await client.store(
  { collection: 'products', data: products },
  paymentCallback,
  false  // Returns immediately
);

console.log('Task ticket:', quick.ticket_id);

// Check status later
const status = await client.getTaskStatus(quick.ticket_id);
console.log('Status:', status.status);

// With confirmation (waits for blockchain)
const confirmed = await client.store(
  { collection: 'products', data: products },
  paymentCallback,
  true  // Waits for on-chain confirmation
);

console.log('Data confirmed at height:', confirmed.block_height);

Payment Methods

OnchainDB supports multiple payment methods:
await client.store(
  { collection: 'data', data: [{ content: 'example' }] },
  async (quote) => {
    const txHash = await wallet.pay(quote.brokerAddress, quote.totalCostTia);
    return { txHash, network: 'mocha-4' };
  }
);

Method 2: Auto-Pay

// Just store without callback - payment is automatic
await client.store({
  collection: 'data',
  data: [{ content: 'example' }]
});

Method 3: Pre-paid with payment_tx_hash

await client.store({
  collection: 'data',
  data: [{ content: 'example' }],
  payment_tx_hash: 'ABC123...'
});

Method 4: Signed Transaction

await client.store({
  collection: 'data',
  data: [{ content: 'example' }],
  signed_payment_tx: {
    signed_tx_bytes: 'base64_encoded_tx',
    user_address: 'celestia1...',
    broker_address: 'celestia1broker...',
    amount_utia: 100000,
    purpose: 'data_storage'
  }
});

Browser CORS Considerations

When using the SDK in a browser, blockchain RPC/REST endpoints don’t include CORS headers. You must create server-side API routes to proxy these requests:
// Required API routes for browser-based apps:
// /api/wallet/account-info - Proxy to blockchain REST API for account info
// /api/wallet/broadcast    - Proxy to blockchain REST API for tx broadcast

// Example Next.js API route: /api/wallet/broadcast
export async function POST(request: Request) {
  const { tx_bytes, mode } = await request.json();

  const response = await fetch('https://api-mocha.pops.one/cosmos/tx/v1beta1/txs', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ tx_bytes, mode })
  });

  return Response.json(await response.json());
}

Keplr Wallet Integration

When integrating with Keplr wallet for payments:
import { Registry } from '@cosmjs/proto-signing';
import { defaultRegistryTypes } from '@cosmjs/stargate';
import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx';

// Use Amino signer (more compatible)
const signer = await window.keplr.getOfflineSignerOnlyAmino(chainId);
const accounts = await signer.getAccounts();

// Create and sign transaction
const registry = new Registry(defaultRegistryTypes);
const signedTx = await client.sign(
  accounts[0].address,
  [sendMsg],
  fee,
  memo
);

// Convert to protobuf and broadcast via REST API
const txBytes = TxRaw.encode(signedTx).finish();
const txBytesBase64 = Buffer.from(txBytes).toString('base64');

// Broadcast through your API route (avoids CORS)
const result = await fetch('/api/wallet/broadcast', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ tx_bytes: txBytesBase64, mode: 'BROADCAST_MODE_SYNC' })
});

Append-Only Storage

OnchainDB uses append-only storage on a Data Availability layer:
OperationBehavior
CreateAppends a new record to the blockchain with a unique ID and timestamp
ReadQueries return the latest version of records by default (based on timestamp)
UpdateDoes NOT modify existing data. Instead, appends a NEW record with the same ID but a newer timestamp
DeletePerforms a soft delete by appending a new record with deleted: true
Benefits:
  • Complete audit trail - all historical versions are preserved on-chain
  • Immutable history - past states cannot be altered
  • Blockchain verifiability - every change is recorded on-chain

Data Retention

OnchainDB allows you to configure automatic data cleanup policies per collection to manage storage costs.

Retention Options

SettingDescriptionCost
PermanentData stored indefinitely, no automatic cleanupStandard storage fees
30 days or lessFree tier - no additional retention costsFree
31+ daysExtended retention with monthly cost per KBVariable

How Retention Works

  1. Default behavior: Collections have permanent storage (no automatic cleanup)
  2. Setting retention: Configure a retention period in days per collection
  3. Automatic cleanup: Data older than the retention period is automatically removed
  4. Cost calculation: Monthly costs are based on data size (KB) and retention period
The first 30 days of retention are free for all collections. Costs only apply when you configure retention periods beyond 30 days.

Configuring Retention

From the Dashboard:
  1. Navigate to your application
  2. Select the Retention tab
  3. Expand a collection to view/edit its settings
  4. Enter the retention period in days (leave empty for permanent)
  5. Click Save to apply changes

Retention Configuration

Each collection’s retention config includes:
FieldDescription
retention_daysNumber of days to keep data (null = permanent)
monthly_cost_per_kbCost per KB per month for extended retention
status’active’ (has retention policy) or ‘permanent’
last_cleanupTimestamp of last automatic cleanup

Cost Estimation

The Dashboard shows projected costs for each collection:
  • Current Size: Amount of data currently stored
  • Monthly Cost: Current monthly retention cost in TIA
  • Projected Size: Estimated size based on growth trends
  • Projected Cost: Estimated next month’s cost
Use shorter retention periods for temporary data (logs, sessions, caches) to reduce storage costs. Keep permanent storage for critical business data.

Use Cases

Data TypeRecommended Retention
Session data7-14 days
User activity logs30-90 days
Analytics events30-180 days
Transaction recordsPermanent
User profilesPermanent
Audit logsPermanent or compliance-required period
Once data is cleaned up by the retention policy, it cannot be recovered. Ensure you have appropriate backups for critical data before setting retention limits.

Next Steps