Wallet SDK

The @insureco/wallet is the official TypeScript client for the iec-wallet service. Use it to check balances, charge gas, credit tokens, query the ledger, and validate deploy reserves — all with full type safety and built-in retry logic.

Installation

npm install @insureco/wallet

The SDK has two entry points:

// Production — client, types, constants
import { WalletClient, WalletError, deriveWalletId } from '@insureco/wallet'

// Testing — in-memory mock client
import { MockWalletClient } from '@insureco/wallet/testing'
Zero dependencies. The SDK uses only native Node.js fetch, crypto, and AbortSignal. No external packages required.

Quick Start

Create a client and ensure a wallet exists:

import { WalletClient } from '@insureco/wallet'

// From explicit config
const wallet = new WalletClient({
  baseUrl: process.env.IEC_WALLET_URL || 'http://localhost:3002',
  serviceKey: process.env.INTERNAL_SERVICE_KEY,
})

// Or from environment variables
const wallet = WalletClient.fromEnv()

// Ensure a wallet exists (idempotent — safe for concurrent calls)
const { wallet: w } = await wallet.ensureWallet({
  ownerId: 'my-org',
  ownerType: 'organization',
  ownerName: 'My Organization',
})
// w.id === 'wallet-my-org'
// w.totalBalance === 50000  (org gets 50K intro tokens)

Environment Variables

VariableRequiredDescription
IEC_WALLET_URLYes*Base URL of the wallet service
WALLET_URLFallbackAlternative if IEC_WALLET_URL is not set
INTERNAL_SERVICE_KEYNoService-to-service auth key (X-Internal-Key header)

Wallet Operations

Check Balance

const balance = await wallet.getBalance('wallet-my-org')
// {
//   walletId: 'wallet-my-org',
//   totalBalance: 50000,
//   balances: [
//     { tokenType: 'intro', balance: 50000 },
//     { tokenType: 'purchased', balance: 0 },
//     { tokenType: 'earned', balance: 0 },
//   ]
// }

Credit Tokens

await wallet.credit('wallet-my-org', {
  amount: 10000,
  tokenType: 'purchased',
  reason: 'Token purchase (self-service)',
  referenceId: 'purchase-1708300000',
})

Query Ledger

const ledger = await wallet.getLedger('wallet-my-org', {
  page: 1,
  limit: 20,
  serviceId: 'my-api',  // optional filter
})
// { entries: [...], total: 42, page: 1, limit: 20 }

Charging Gas

There are two types of gas on the platform. The SDK handles token restrictions automatically based on the gasType field.

Gas TypeWhat It Pays ForUses Intro Tokens?
hostingPod uptime (charged hourly)Yes
transactionAPI calls through JanusNo — automatically skipped

Hosting Gas

// Uses default spend order: intro → purchased → earned
await wallet.debit('wallet-my-org', {
  amount: 5,
  reason: 'hosting_gas',
  gasType: 'hosting',
  serviceId: 'my-api',
  referenceId: 'hosting-my-org-2026020118',
})

Transaction Gas

// Automatically skips intro tokens
await wallet.debit('wallet-my-org', {
  amount: 10,
  reason: 'transaction_gas',
  gasType: 'transaction',
  serviceId: 'my-api',
  referenceId: 'req-abc123',
})

Spend Order

When debiting without specifying a tokenType, tokens are consumed in order: intro → purchased → earned. If one type runs out mid-debit, it automatically splits across types.

See the full token economics on the Gas & Wallets page.

Deploy Gate

Before every tawa deploy, the builder checks your wallet for 3 months of hosting reserve. Use the SDK to implement this check:

import { WalletClient, calculateReserve } from '@insureco/wallet'

const wallet = WalletClient.fromEnv()

const { sufficient, shortfall } = await wallet.checkReserve('wallet-my-org', {
  podTier: 'nano',
  months: 3,  // optional, defaults to 3
})

if (!sufficient) {
  throw new Error(`Need ${shortfall} more tokens to deploy`)
}

// Or calculate locally without an API call
const requiredTokens = calculateReserve('nano')  // 10800
Fail-open: If the wallet service is unreachable, the deploy gate allows the deploy to proceed. This ensures platform resilience — deploys are never blocked by a wallet outage.

Error Handling

The SDK throws WalletError for all failures, with structured error codes:

import { WalletError } from '@insureco/wallet'

try {
  await wallet.debit('wallet-my-org', { amount: 999999, reason: 'test' })
} catch (err) {
  if (err instanceof WalletError) {
    err.code       // 'INSUFFICIENT_BALANCE'
    err.statusCode // 402
    err.message    // 'Insufficient balance in wallet...'
  }
}
Error CodeHTTPCause
WALLET_NOT_FOUND404Wallet ID does not exist
INSUFFICIENT_BALANCE402Not enough tokens for the debit
INVALID_TOKEN_USAGE400Intro tokens used for transaction gas
DUPLICATE_WALLET409Wallet already exists for this owner
NETWORK_ERROR0Wallet service unreachable

Retry Behavior

The SDK retries on network errors, 5xx responses, and JSON parse failures. It does not retry on 4xx client errors. Default: 2 retries with exponential backoff (capped at 5 seconds).

const wallet = new WalletClient({
  baseUrl: 'http://localhost:3002',
  retries: 3,        // override retry count
  timeoutMs: 15000,  // override request timeout (default: 10s)
})

Testing with MockWalletClient

The SDK includes a full in-memory mock that enforces the same business rules as the real service — spend order, token restrictions, insufficient balance errors, and reserve calculations.

import { MockWalletClient } from '@insureco/wallet/testing'

const mock = new MockWalletClient()

// Same API as WalletClient — no HTTP calls needed
await mock.ensureWallet({
  ownerId: 'acme',
  ownerType: 'organization',
  ownerName: 'Acme Corp',
})

await mock.debit('wallet-acme', {
  amount: 100,
  reason: 'hosting_gas',
  gasType: 'hosting',
})

// Inspect internals for assertions
const entries = mock.getLedgerEntries()
expect(entries[0].type).toBe('debit')

// Reset between tests
mock.reset()
Tip: Use MockWalletClient in your integration tests so you don't need a running wallet service. The mock tracks balances, enforces rules, and records ledger entries just like production.

Constants & Helpers

import {
  deriveWalletId,       // 'insureco' → 'wallet-insureco'
  calculateReserve,     // ('nano', 3) → 10800
  getPodTierConfig,     // 'nano' → { tier, cpu, memoryMb, hostingGasPerHour }
  TOKEN_VALUE_USD,      // 0.01
  INTRO_TOKEN_GRANT,    // 50000
  DEPLOY_RESERVE_MONTHS,// 3
  PLATFORM_WALLET_ID,   // 'platform-wallet-000'
  POD_TIER_CONFIGS,     // Array of all 5 tier configs
  TOKEN_SPEND_ORDER,    // ['intro', 'purchased', 'earned']
} from '@insureco/wallet'

API Reference

MethodDescription
createWallet(req)Create wallet with UUID-based ID
ensureWallet(req)Idempotent create-or-get (wallet-{ownerId})
getWallet(id)Get full wallet details
getBalance(id)Balance breakdown by token type
debit(id, req)Deduct tokens (respects spend order and gas type)
credit(id, req)Add tokens by type
getLedger(id, opts?)Paginated transaction history
checkReserve(id, req)Verify deploy gate reserve