Demo without an account (sandbox)
The fastest way to see a full purchase round-trip — no wallet, no API key./sandbox/try is public.
import { LuminaClient } from '@lumina-org/sdk'
const lumina = new LuminaClient({ apiKey: '' })
const r = await lumina.sandbox.try()
console.log('sandbox policy:', r.policyId, 'tx:', r.blockExplorer)
Onboard, save the key
import { Wallet } from 'ethers'
import { LuminaClient } from '@lumina-org/sdk'
const wallet = new Wallet(process.env.PRIVATE_KEY!)
const lumina = new LuminaClient({ apiKey: '' })
const result = await lumina.agent.onboard(wallet, { label: 'compound-bot' })
process.env.LUMINA_API_KEY = result.apiKey // store securely
console.log('keyId:', result.keyId)
Buy a policy with retry-on-network-blip
import { LuminaClient, LuminaError } from '@lumina-org/sdk'
import { randomUUID } from 'crypto'
const lumina = new LuminaClient({ apiKey: process.env.LUMINA_API_KEY! })
const idempotencyKey = randomUUID() // same key for every retry of THIS attempt
async function buyOnce(): Promise<string> {
for (let attempt = 0; attempt < 3; attempt++) {
try {
const r = await lumina.policies.purchase({
productName: 'FLASHBTC24-001', // SDK auto-resolves productId hash + asset='BTC'
buyer: process.env.BUYER!,
coverageAmount: '1000000000', // $1,000 in 6-dec USDC
idempotencyKey,
})
return r.policyId
} catch (err) {
if (!(err instanceof LuminaError)) throw err
if (err.status >= 500 || err.code === 'network_error') {
await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, attempt)))
continue
}
throw err // 4xx → bug or bad config; don't retry
}
}
throw new Error('giving up after 3 attempts')
}
console.log('policyId:', await buyOnce())
End-to-end: purchase → wait for trigger → redeem bond
import { LuminaClient } from '@lumina-org/sdk'
import { randomUUID } from 'crypto'
const lumina = new LuminaClient({ apiKey: process.env.LUMINA_API_KEY! })
// 1) Buy $1k of 24h BTC flash cover.
const purchase = await lumina.policies.purchase({
productName: 'FLASHBTC24-001',
buyer: process.env.BUYER!,
coverageAmount: '1000000000',
idempotencyKey: randomUUID(),
})
console.log('policyId:', purchase.policyId, 'premium:', purchase.premiumPaid)
// 2) Poll until the policy is triggered (or the 24h window closes).
// In production prefer the `policy_triggered` webhook over polling.
async function waitForTrigger(productId: string, policyId: string) {
for (let i = 0; i < 60; i++) {
const p = await lumina.policies.get(productId, policyId)
if (p.status === 'triggered') return p
if (p.status === 'expired') throw new Error('policy expired without trigger')
await new Promise((r) => setTimeout(r, 60_000)) // 1 min
}
throw new Error('timeout waiting for trigger')
}
const triggered = await waitForTrigger(purchase.productId, purchase.policyId)
console.log('triggered, bond minted at epoch:', triggered.bondEpochId)
// 3) Wait for the epoch to mature, then redeem ON-CHAIN via BondVault
// (there is no lumina.bonds.redeem REST method). Resolve the address at
// runtime and check the per-epoch throttle with BondQueue first.
import { BondQueue, bondVaultAbi } from '@lumina-org/sdk'
import { ethers } from 'ethers'
const { bondVault } = await lumina.getContracts()
const bv = new ethers.Contract(bondVault, bondVaultAbi, signer)
const queue = new BondQueue(bv)
const status = await queue.getRedemptionStatus(buyer, triggered.bondEpochId, 800)
if (status.status === 'ready') {
const tx = await bv.redeemBond(triggered.bondEpochId, 800) // epochId, $800 face
console.log('redeemed on-chain, tx:', tx.hash)
} else {
console.log('not settleable yet:', status.status) // 'queued' | 'matured-pending'
}
End-to-end: purchase → trigger → list bond on marketplace
If you don’t want to wait 730 days, list the bond on the marketplace and let a secondary buyer take it at a discount.import { LuminaClient } from '@lumina-org/sdk'
const lumina = new LuminaClient({ apiKey: process.env.LUMINA_API_KEY! })
// Assume policyId came back triggered (see previous example).
const myBonds = await lumina.bonds.list()
const latest = myBonds[0]
console.log('bondId:', latest.bondId, 'face:', latest.faceValueUsdc)
// Find current floor for comparable bonds.
const listings = await lumina.marketplace.listings({
sortBy: 'price-asc',
limit: 5,
})
console.log('cheapest active listings:', listings.map((l) => l.totalPriceUsdc))
// Listing the bond itself goes through the on-chain marketplace contract.
// Pull the address via getContracts() and call list() with ethers v6:
const { marketplace } = await lumina.getContracts()
// const m = new Contract(marketplace, MARKETPLACE_ABI, wallet)
// await m.list(bondId, priceUsdc)
React to triggers via webhook
const sub = await lumina.webhooks.create({
url: 'https://my-bot.example.com/webhooks/lumina',
events: ['policy_triggered'],
})
saveSecretToVault(sub.secret) // shown only once
Browse cheap listings every minute
setInterval(async () => {
const listings = await lumina.marketplace.listings({ sortBy: 'price-asc', limit: 10 })
const cheap = listings.filter(
(l) => BigInt(l.totalPriceUsdc) < (BigInt(l.amount) * 95n) / 100n
)
if (cheap.length) console.log('cheap bonds:', cheap)
}, 60_000)