Files
nick-doc/02 - Data Models/ScannerIntent.md

5.4 KiB

title, tags, created
title tags created
ScannerIntent (Scanner DB model)
data-model
scanner
payment
2026-05-30

ScannerIntent

SQLite row in the AMN Pay Scanner's intents table. One row per payment intent registered by the backend. This is internal scanner state — it is not a Mongoose model and lives in a separate SQLite database (/data/scanner.db).


Schema

CREATE TABLE intents (
    intent_id              TEXT PRIMARY KEY,
    chain_id               INTEGER NOT NULL,
    chain_type             TEXT NOT NULL DEFAULT 'evm',
    token_address          TEXT NOT NULL,
    destination            TEXT NOT NULL,
    amount                 TEXT NOT NULL,
    payment_reference      TEXT NOT NULL,
    topic_ref              TEXT,
    status                 TEXT NOT NULL DEFAULT 'pending',
    callback_url           TEXT NOT NULL,
    callback_secret        TEXT NOT NULL,
    confirmations_required INTEGER NOT NULL DEFAULT 12,
    tx_hash                TEXT,
    log_index              INTEGER,
    block_number           INTEGER,
    confirmations          INTEGER NOT NULL DEFAULT 0,
    salt                   TEXT NOT NULL,
    webhook_delivered_at   TEXT,
    created_at             DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at             DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Fields

Field Type Description
intent_id TEXT PK Caller-supplied unique ID (typically the backend Payment _id)
chain_id INTEGER Numeric chain ID. EVM standard (56, 137, 1, 42161, 8453), Tron (728126428), TON (1100)
chain_type TEXT evm / tron / ton. Determines which worker handles this intent
token_address TEXT ERC20 / TRC20 contract address. EVM/Tron: lowercase 0x hex. TON: exact base64url
destination TEXT Recipient wallet address. EVM/Tron: lowercase 0x hex. TON: base64url (case-sensitive)
amount TEXT Required amount in smallest token unit (wei / 10^decimals), stored as base-10 integer string
payment_reference TEXT 8-byte hex EVM payment reference (0x + 16 hex chars). Derived as last8(keccak256(intentId + salt + destination))
topic_ref TEXT keccak256(paymentReferenceBytes) — matches Topics[1] in EVM logs. Pre-computed for indexed DB lookup. NULL for Tron/TON
status TEXT Intent lifecycle state (see below)
callback_url TEXT URL the scanner POSTs to on confirmation
callback_secret TEXT HMAC-SHA256 key for webhook signature. Never returned in API responses
confirmations_required INTEGER Accepted confirmation floor for the intent. Defaults to chain config and cannot be lowered below the chain floor by the create-intent request
tx_hash TEXT NULL Transaction hash once a matching transfer is detected
log_index INTEGER NULL Log position within the transaction (EVM only; 0 for Tron/TON)
block_number INTEGER NULL Block number (EVM/Tron) or Unix timestamp seconds (TON) when the tx was seen
confirmations INTEGER Current confirmation depth while confirming; capped at confirmations_required once the intent is confirmed
salt TEXT 32-byte random hex. Combined with intent_id and destination to derive payment_reference. Prevents reference collisions across retried payments
webhook_delivered_at TEXT NULL RFC3339 timestamp when the webhook was successfully delivered. Used for startup crash recovery
created_at / updated_at DATETIME UTC timestamps

Status values

Status Description
pending Registered; scanner is watching for a matching on-chain transfer
confirming EVM only — matching tx seen, waiting for confirmations_required blocks
confirmed Payment confirmed; webhook delivery attempted
expired TTL exceeded while still in pending or confirming
webhook_failed All webhook delivery retries exhausted; manual retry or periodic auto-retry needed

Indexes

CREATE INDEX idx_intents_status       ON intents(status);
CREATE INDEX idx_intents_chain_status ON intents(chain_id, status);
CREATE INDEX idx_intents_payment_ref  ON intents(payment_reference);
CREATE INDEX idx_intents_topic_ref    ON intents(topic_ref);
CREATE UNIQUE INDEX idx_intents_tx_log ON intents(tx_hash, log_index)
    WHERE tx_hash IS NOT NULL;

idx_intents_topic_ref is the performance-critical index — the EVM scanner's inner loop does a single indexed lookup per log entry.

The unique index on (tx_hash, log_index) prevents two intents being confirmed from the same on-chain event (double-spend protection).


Migrations

Three additive migrations run at startup (idempotent):

  1. ADD COLUMN topic_ref TEXT — added after initial schema
  2. ADD COLUMN chain_type TEXT NOT NULL DEFAULT 'evm' — added for Tron/TON support
  3. ADD COLUMN webhook_delivered_at TEXT — added for crash recovery

A backfill pass recomputes topic_ref for existing EVM intents that had it as NULL.