Files
nick-doc/02 - Data Models/ScannerIntent.md
Siavash Sameni dceaf82934 audit: 2026-05-30 full-codebase audit — report, issues, docs, runbooks
Full-codebase-audit 2026-05-30 outputs:
- Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md
- 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer).
- Scanner docs from scratch (was zero): architecture, data model, API ref, payment
  flow, operations runbook + repo README.
- Doc-sync updates across API reference, data models, flows, design system.
- Secret Rotation Runbook (08 - Operations) for the exposed credentials.
- Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js.

Issues remain status:open intentionally — the code fixes are uncommitted-then-committed
working-tree changes per repo and aren't "resolved" until merged/deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:48:04 +04:00

5.2 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 Number of blocks required before confirmation (EVM). Defaults to chain config
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. Incremented each scan cycle for confirming intents
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.