Skip to main content

Overview

This reference documents the database schema used by LeoKit for storing quotes, transactions, tokens, and operational data. Use this schema for backend integrations, analytics, and custom implementations.

Core Tables

quotes Table

Stores all quote requests and responses with timing information.
CREATE TABLE quotes (
  id UUID PRIMARY KEY,
  timestamp BIGINT,
  created_at TIMESTAMPTZ,
  response_time NUMERIC,
  from_address TEXT,
  to_address TEXT,
  quote_offers JSONB,
  client_api TEXT,
  quoting_type TEXT,
  from_asset TEXT,
  to_asset TEXT,
  amount TEXT
);

Field Descriptions

FieldTypeDescriptionConstraints
idUUIDUnique quote identifierPRIMARY KEY
timestampBIGINTUnix timestamp (milliseconds)-
created_atTIMESTAMPTZTimestamp with timezone-
response_timeNUMERICAPI response time in ms-
from_addressTEXTSource wallet addressNullable
to_addressTEXTDestination wallet addressNOT NULL
quote_offersJSONBArray of protocol quotesNOT NULL
client_apiTEXTClient API key identifierNOT NULL
quoting_typeTEXT’http’ or ‘streaming’NOT NULL
from_assetTEXTSource asset (CHAIN.SYMBOL-ADDRESS)NOT NULL
to_assetTEXTDestination asset (CHAIN.SYMBOL-ADDRESS)NOT NULL
amountTEXTInput amount in base unitsNOT NULL

Example Row

INSERT INTO quotes VALUES (
  'f47ac10b-58cc-4372-a567-0e02b2c3d479',
  1704067200000,
  '2024-01-01 00:00:00+00',
  2350.5,
  'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  '[
    {
      "protocol": "thorchain",
      "data": {
        "expected_amount_out": "9850000000",
        "total_swap_seconds": 180,
        "fees": [...]
      }
    }
  ]'::jsonb,
  'client_abc123',
  'http',
  'BTC.BTC',
  'ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  '100000000'
);

Indexes

CREATE INDEX idx_quotes_client_api ON quotes(client_api);
CREATE INDEX idx_quotes_created_at ON quotes(created_at);
CREATE INDEX idx_quotes_from_asset ON quotes(from_asset);
CREATE INDEX idx_quotes_to_asset ON quotes(to_asset);
CREATE INDEX idx_quotes_timestamp ON quotes(timestamp);

deposits Table

Stores deposit transaction details after quote selection.
CREATE TABLE deposits (
  id SERIAL PRIMARY KEY,
  client_api TEXT,
  quote_id UUID UNIQUE,
  req_id TEXT,
  transaction JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

Field Descriptions

FieldTypeDescriptionConstraints
idSERIALAuto-incrementing IDPRIMARY KEY
client_apiTEXTClient API key identifierNOT NULL
quote_idUUIDReference to quotes.idUNIQUE, NOT NULL
req_idTEXTCustom request identifierNullable
transactionJSONBUnsigned transaction dataNOT NULL
created_atTIMESTAMPTZCreation timestampDEFAULT NOW()

Example Row

INSERT INTO deposits (client_api, quote_id, req_id, transaction) VALUES (
  'client_abc123',
  'f47ac10b-58cc-4372-a567-0e02b2c3d479',
  'user_request_001',
  '{
    "protocol": "thorchain",
    "unsigned_transactions": [{
      "to": "bc1qxy2...",
      "value": "100000000",
      "memo": "=:ETH.USDC-0xA0b...:0x742d35..."
    }],
    "chain": "BTC"
  }'::jsonb
);

Indexes

CREATE INDEX idx_deposits_client_api ON deposits(client_api);
CREATE INDEX idx_deposits_quote_id ON deposits(quote_id);
CREATE INDEX idx_deposits_created_at ON deposits(created_at);

tx_status Table

Tracks transaction status and updates over time.
CREATE TABLE tx_status (
  id SERIAL PRIMARY KEY,
  quote_id UUID,
  deposit_id INTEGER REFERENCES deposits(id),
  tx_hash TEXT,
  req_id TEXT,
  status TEXT,
  protocol TEXT,
  response JSONB,
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

Field Descriptions

FieldTypeDescriptionConstraints
idSERIALAuto-incrementing IDPRIMARY KEY
quote_idUUIDReference to quotes.idNullable
deposit_idINTEGERReference to deposits.idFOREIGN KEY
tx_hashTEXTBlockchain transaction hashNullable
req_idTEXTCustom request identifierNullable
statusTEXTTransaction statusNOT NULL
protocolTEXTProtocol nameNOT NULL
responseJSONBStatus response from protocolNOT NULL
updated_atTIMESTAMPTZLast update timestampDEFAULT NOW()

Status Values

StatusDescription
pendingTransaction submitted but not confirmed
processingTransaction confirmed, swap in progress
completedSwap completed successfully
failedTransaction or swap failed
refundedTransaction refunded due to error

Example Row

INSERT INTO tx_status (quote_id, deposit_id, tx_hash, status, protocol, response) VALUES (
  'f47ac10b-58cc-4372-a567-0e02b2c3d479',
  1,
  'abc123...def456',
  'processing',
  'thorchain',
  '{
    "stages": {
      "inbound_observed": { "completed": true },
      "swap_finalised": { "completed": true },
      "outbound_signed": { "completed": false }
    }
  }'::jsonb
);

Indexes

CREATE INDEX idx_tx_status_quote_id ON tx_status(quote_id);
CREATE INDEX idx_tx_status_tx_hash ON tx_status(tx_hash);
CREATE INDEX idx_tx_status_status ON tx_status(status);
CREATE INDEX idx_tx_status_updated_at ON tx_status(updated_at);

Asset & Pricing Tables

tokens Table

Stores token metadata and pricing information.
CREATE TABLE tokens (
  id SERIAL PRIMARY KEY,
  blockchain TEXT,
  symbol TEXT,
  address TEXT,
  decimals INTEGER,
  price_usd NUMERIC,
  coingecko_id TEXT,
  is_popular BOOLEAN,
  updated_at TIMESTAMPTZ,
  icon_url TEXT,
  UNIQUE(blockchain, address)
);

Field Descriptions

FieldTypeDescriptionConstraints
idSERIALAuto-incrementing IDPRIMARY KEY
blockchainTEXTBlockchain short name (ETH, BSC, etc.)NOT NULL
symbolTEXTToken symbolNOT NULL
addressTEXTContract address (NULL for native)Nullable
decimalsINTEGERToken decimalsNOT NULL
price_usdNUMERICCurrent price in USDNullable
coingecko_idTEXTCoinGecko API identifierNullable
is_popularBOOLEANFlag for popular tokensDEFAULT FALSE
updated_atTIMESTAMPTZLast price update-
icon_urlTEXTToken icon URLNullable

Example Rows

-- Native asset
INSERT INTO tokens (blockchain, symbol, address, decimals, price_usd, coingecko_id, is_popular, icon_url) VALUES
('BTC', 'BTC', NULL, 8, 65432.50, 'bitcoin', true, 'https://assets.coingecko.com/coins/images/1/large/bitcoin.png');

-- ERC20 token
INSERT INTO tokens (blockchain, symbol, address, decimals, price_usd, coingecko_id, is_popular, icon_url) VALUES
('ETH', 'USDC', '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 1.00, 'usd-coin', true, 'https://assets.coingecko.com/coins/images/6319/large/USD_Coin_icon.png');

Indexes

CREATE INDEX idx_tokens_blockchain ON tokens(blockchain);
CREATE INDEX idx_tokens_symbol ON tokens(symbol);
CREATE INDEX idx_tokens_is_popular ON tokens(is_popular);
CREATE UNIQUE INDEX idx_tokens_blockchain_address ON tokens(blockchain, address);

gas_prices Table

Stores historical gas price data per chain.
CREATE TABLE gas_prices (
  id SERIAL PRIMARY KEY,
  chain TEXT,
  price NUMERIC,
  timestamp BIGINT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

Field Descriptions

FieldTypeDescriptionConstraints
idSERIALAuto-incrementing IDPRIMARY KEY
chainTEXTBlockchain short nameNOT NULL
priceNUMERICGas price in chain unitsNOT NULL
timestampBIGINTUnix timestamp (milliseconds)NOT NULL
created_atTIMESTAMPTZRecord creation timeDEFAULT NOW()

Gas Price Units by Chain

Chain TypeUnitDescription
EVMgwei10^9 wei
Bitcoinsat/vbyteSatoshis per virtual byte
CosmosnativeNative token units
NEARTGasTeraGas units

Example Rows

INSERT INTO gas_prices (chain, price, timestamp) VALUES
('ETH', 25.5, 1704067200000),    -- 25.5 gwei
('BTC', 10, 1704067200000),      -- 10 sat/vbyte
('ARB', 0.1, 1704067200000),     -- 0.1 gwei
('THOR', 0.02, 1704067200000);   -- 0.02 RUNE

Indexes

CREATE INDEX idx_gas_prices_chain ON gas_prices(chain);
CREATE INDEX idx_gas_prices_timestamp ON gas_prices(timestamp);
CREATE INDEX idx_gas_prices_created_at ON gas_prices(created_at);

Error & Debug Tables

error_logs Table

Stores error traces for debugging and support.
CREATE TABLE error_logs (
  id SERIAL PRIMARY KEY,
  trace_id TEXT UNIQUE,
  client_api TEXT,
  error_details JSONB,
  endpoint TEXT,
  request_params JSONB,
  timestamp TIMESTAMPTZ DEFAULT NOW()
);

Field Descriptions

FieldTypeDescriptionConstraints
idSERIALAuto-incrementing IDPRIMARY KEY
trace_idTEXTUnique error trace IDUNIQUE, NOT NULL
client_apiTEXTClient API key identifierNOT NULL
error_detailsJSONBError message and stackNOT NULL
endpointTEXTAPI endpoint that erroredNOT NULL
request_paramsJSONBRequest parametersNullable
timestampTIMESTAMPTZError occurrence timeDEFAULT NOW()

Example Row

INSERT INTO error_logs (trace_id, client_api, error_details, endpoint, request_params) VALUES (
  'err_abc123def456',
  'client_abc123',
  '{
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient balance (including gas fees).",
    "status": 400,
    "context": {
      "required": "1.05 ETH",
      "available": "1.00 ETH"
    }
  }'::jsonb,
  '/leokit/deposit',
  '{
    "quote_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "selected_protocol": "thorchain"
  }'::jsonb
);

Indexes

CREATE INDEX idx_error_logs_trace_id ON error_logs(trace_id);
CREATE INDEX idx_error_logs_client_api ON error_logs(client_api);
CREATE INDEX idx_error_logs_timestamp ON error_logs(timestamp);

Cleanup Policy

Error logs older than 30 days are automatically purged:
DELETE FROM error_logs WHERE timestamp < NOW() - INTERVAL '30 days';

Client Configuration

clients Table (Reference)

Stores API key configurations and permissions.
CREATE TABLE clients (
  id SERIAL PRIMARY KEY,
  api_key TEXT UNIQUE NOT NULL,
  name TEXT,
  enabled_protocols TEXT[],
  fee_recipient TEXT,
  affiliate_bps INTEGER,
  rate_limit INTEGER,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

Field Descriptions

FieldTypeDescription
idSERIALAuto-incrementing ID
api_keyTEXTUnique API key
nameTEXTClient name/organization
enabled_protocolsTEXT[]Array of enabled protocols
fee_recipientTEXTAffiliate fee recipient address
affiliate_bpsINTEGERDefault affiliate fee (BPS)
rate_limitINTEGERRequests per minute limit
created_atTIMESTAMPTZAccount creation time
updated_atTIMESTAMPTZLast configuration update

Relationships

Entity Relationship Diagram

quotes (1) ──────── (1) deposits
  │                      │
  │                      │
  └────────────────┬─────┘


                  (N)
              tx_status

quotes (N) ──────── (1) clients
deposits (N) ──────── (1) clients
error_logs (N) ──────── (1) clients

tokens (N) ──────── (1) gas_prices (by blockchain)

Join Examples

Get Quote with Deposit and Status

SELECT
  q.id AS quote_id,
  q.from_asset,
  q.to_asset,
  q.amount,
  d.transaction AS deposit_data,
  t.tx_hash,
  t.status,
  t.response AS status_response
FROM quotes q
LEFT JOIN deposits d ON q.id = d.quote_id
LEFT JOIN tx_status t ON d.id = t.deposit_id
WHERE q.id = 'f47ac10b-58cc-4372-a567-0e02b2c3d479';

Get Client Analytics

SELECT
  c.name AS client_name,
  COUNT(q.id) AS total_quotes,
  COUNT(d.id) AS total_deposits,
  COUNT(CASE WHEN t.status = 'completed' THEN 1 END) AS completed_swaps,
  SUM(q.response_time) / COUNT(q.id) AS avg_response_time_ms
FROM clients c
LEFT JOIN quotes q ON q.client_api = c.api_key
LEFT JOIN deposits d ON d.quote_id = q.id
LEFT JOIN tx_status t ON t.deposit_id = d.id
WHERE q.created_at >= NOW() - INTERVAL '30 days'
GROUP BY c.id, c.name;
SELECT
  t.blockchain,
  t.symbol,
  t.price_usd,
  COUNT(DISTINCT q.id) AS quote_count
FROM tokens t
LEFT JOIN quotes q ON (
  q.from_asset LIKE t.blockchain || '.' || t.symbol || '%' OR
  q.to_asset LIKE t.blockchain || '.' || t.symbol || '%'
)
WHERE t.is_popular = true
  AND q.created_at >= NOW() - INTERVAL '7 days'
GROUP BY t.id, t.blockchain, t.symbol, t.price_usd
ORDER BY quote_count DESC
LIMIT 20;

Data Retention

TableRetention PeriodCleanup Query
quotes90 daysDELETE FROM quotes WHERE created_at < NOW() - INTERVAL '90 days'
deposits90 daysDELETE FROM deposits WHERE created_at < NOW() - INTERVAL '90 days'
tx_status90 daysDELETE FROM tx_status WHERE updated_at < NOW() - INTERVAL '90 days'
error_logs30 daysDELETE FROM error_logs WHERE timestamp < NOW() - INTERVAL '30 days'
gas_prices7 daysDELETE FROM gas_prices WHERE created_at < NOW() - INTERVAL '7 days'
tokensIndefiniteN/A (update pricing only)

Archival Strategy

For long-term analytics, consider archiving old records to separate tables:
-- Archive old quotes
INSERT INTO quotes_archive
SELECT * FROM quotes
WHERE created_at < NOW() - INTERVAL '90 days';

DELETE FROM quotes
WHERE created_at < NOW() - INTERVAL '90 days';

Performance Optimization

Query Optimization Tips

  1. Use appropriate indexes for your query patterns
  2. Partition large tables by date (monthly partitions recommended)
  3. Use JSONB indexes for frequently queried JSON fields
  4. Analyze query plans with EXPLAIN ANALYZE
  5. Vacuum regularly to maintain table performance

JSONB Indexing Examples

-- Index for quote offers protocol lookup
CREATE INDEX idx_quotes_offers_protocol ON quotes
USING GIN ((quote_offers -> 'protocol'));

-- Index for transaction status
CREATE INDEX idx_tx_status_response ON tx_status
USING GIN (response);

-- Index for error codes
CREATE INDEX idx_error_details_code ON error_logs
((error_details ->> 'code'));

Backup & Recovery

Backup Strategy

# Daily full backup
pg_dump leokit_db > backup_$(date +%Y%m%d).sql

# Continuous archiving (PITR)
# Configure in postgresql.conf:
# wal_level = replica
# archive_mode = on
# archive_command = 'cp %p /backup/wal/%f'

Recovery Example

# Restore from backup
psql leokit_db < backup_20240101.sql

# Point-in-time recovery
pg_restore -d leokit_db -t quotes backup_20240101.sql

Migration Scripts

Adding New Fields

-- Add new field to quotes table
ALTER TABLE quotes
ADD COLUMN user_agent TEXT;

-- Add index
CREATE INDEX idx_quotes_user_agent ON quotes(user_agent);

Schema Versioning

Maintain schema version in dedicated table:
CREATE TABLE schema_version (
  version INTEGER PRIMARY KEY,
  applied_at TIMESTAMPTZ DEFAULT NOW(),
  description TEXT
);

INSERT INTO schema_version (version, description) VALUES
(1, 'Initial schema'),
(2, 'Added user_agent to quotes table');