--- title: ScannerIntent (Scanner DB model) tags: [data-model, scanner, payment] created: 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 ```sql 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 ```sql 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. --- ## Related - [Scanner Architecture](../01%20-%20Architecture/Scanner%20Architecture.md) - [Scanner API](../03%20-%20API%20Reference/Scanner%20API.md) - [Payment Flow - Scanner](../04%20-%20Flows/Payment%20Flow%20-%20Scanner.md) - [Payment](Payment.md) — the backend MongoDB model that triggers intent creation