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/walletThe 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'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
| Variable | Required | Description |
|---|---|---|
IEC_WALLET_URL | Yes* | Base URL of the wallet service |
WALLET_URL | Fallback | Alternative if IEC_WALLET_URL is not set |
INTERNAL_SERVICE_KEY | No | Service-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 Type | What It Pays For | Uses Intro Tokens? |
|---|---|---|
hosting | Pod uptime (charged hourly) | Yes |
transaction | API calls through Janus | No — 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') // 10800Error 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 Code | HTTP | Cause |
|---|---|---|
WALLET_NOT_FOUND | 404 | Wallet ID does not exist |
INSUFFICIENT_BALANCE | 402 | Not enough tokens for the debit |
INVALID_TOKEN_USAGE | 400 | Intro tokens used for transaction gas |
DUPLICATE_WALLET | 409 | Wallet already exists for this owner |
NETWORK_ERROR | 0 | Wallet 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()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
| Method | Description |
|---|---|
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 |