Skip to main content

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
Then on the receiving server: see agents/webhooks for the HMAC-verification snippet.

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)