Add two timestamp columns to the ido table, both sourced from Delphi NFT
commitments (48-bit LE unix seconds):
- created_at (NOT NULL, default 0): from the Delphi NFT in the preinit's
first output, set at IDO creation. 0 if the commitment is too short.
- launched_at (NULL): from the Delphi NFT in output#3 of the launch
transaction, set in lockstep with launch_txid. Null until launched.
Both are threaded through the parse/context/update paths and exposed on
IdoRpcRecord.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an optional owner_nfthash query param to GET /<id>/entries. Accepts
up to 20 comma-delimited 32-byte hex hashes; an entry matches any listed
value via an owner_nfthash IN (...) clause. Rejects malformed hex,
wrong-length values, and >20 filters with a 400.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sum demand/supply amounts and count entries for an IDO, used to show
"raised so far" for active offerings where on-chain state keeps no
running total.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update ido/mod.rs for the bitcoincash 0.32 API: Script -> ScriptBuf,
push_slice via &PushBytes (new pb() helper), as_byte_array/as_bytes.
Fix update_mempool to diff against cauldron_txs instead of defi_txs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stamp mempool tracker entries with the negative of the highest known
block height instead of a fixed -1 sentinel, and trim on abs(height) so
entries whose tx is invalidated before confirming (e.g. by a double
spend) age out after TRACKER_MAX_BLOCK_DEPTH like confirmed ones. The
height is still replaced with the real one once the tx confirms.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- electrum mempool fetch gains an ido filter: state machine spends
(IDO_SIGNATURE in scriptsig) union preinit announcements
(IDO_PREINIT_ANNOUNCEMENT_SIGNATURE in scriptpubkey)
- split tx scanning out of index_block into index_txs and add
index_mempool, which indexes with a -1 sentinel height;
txchain_trim_tracker leaves negative heights alone and the real
height replaces the sentinel once the tx confirms in a block
- block indexing now skips re-creating an ido already seen in the
mempool, only updating its tracker height
- mempool txs are kahn-sorted so txchain parents index before children
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The preinit tx layout changed: the first output is now the Delphi NFT and
the announcement OP_RETURN is the last output. Read the announcement from the
last output in both is_preinit_broadcast and parse_ido_preinit_tx_params.
Add validity checks on the first output's Delphi NFT: its category must match
the announcement's delphiCategory, and the 48-bit commitment timestamp (current
time) must place launchConditions.expiresAt within a 1-30 day window.
The two PREINIT_TEST_TX vectors use the old layout, so the three preinit parse
tests are marked #[ignore] pending new-format sample transactions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Change get_txchain_tx to take the public txid (a unique field) instead of
the non-public internal txchain item id, and promote it to a production
endpoint. Mount the two remaining debug endpoints (list_ido_txchain,
list_txchain_tracker_map) only when `debug = true` in the config.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The IDO RPC record no longer leaks internal txchain row ids. Drop the
txchain_entrypoint field entirely, and change txchain_head from the
internal ido_txchain.id to the head record's txid (display hex).
list_idos and get_ido_by_offering_token_id resolve this in a single
query via LEFT JOIN ido_txchain ON head_tx.id = ido.txchain_head; a
NULL head yields null. Adds tests for both the resolved and null cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename ido.id -> ido.internal_id in the schema and IdoDBRecord, and
expose the preinit_txid hex as the public "id" in all RPC responses.
Public API shape:
- IdoRpcRecord.id: i64 -> String (hex of preinit_txid). preinit_txid
field is preserved unchanged.
- IdoEntryRpcRecord.ido_id: i64 -> String (hex of parent preinit_txid).
- IdoTxChainRpcRecord.ido_id: i64 -> String (hex of parent preinit_txid).
- IdoTxChainRpcRecord gains ido_internal_id: i64 (debug endpoint only).
- Routes /<id>/entries and /<id>/txchain accept the preinit_txid hex
as the path id; returns 404 IDO_NOT_FOUND on miss.
Internals:
- New lookup_internal_id_by_preinit_txid helper.
- list_ido_entries / list_ido_txchain take preinit_txid_hex as input
so the caller (which already parsed it from the path) avoids the
extra "preinit_txid by internal_id" lookup.
- Child table FK columns (ido_entry.ido_id, ido_txchain.ido_id) keep
their names; only the parent PK and field accesses were renamed.
Notes:
- Breaking API change for /ido/* endpoints. Clients reading "id" or
"ido_id" as integers must switch to strings.
- ido.db has no migration framework: drop the file and re-index on
deploy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
List every active TokenToken pool (no pair filter) so the frontend can
present the set of pooled token pairs without probing each candidate pair.
Mirrors db_active_pools_for_pair minus the pair WHERE clause; served at a
distinct /pools path (the param'd /pool/active already matches param-less
requests via its Option guards, so reusing it would collide).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migrate off the deprecated bitcoincash 0.29 API (Amount/Version types,
FromHex -> FromStr/parse, non-exhaustive Network) across the indexer and
tests.
Add indexing of native token-A <-> token-B (TokenToken) AMM pools:
- tokentoken_pool / tokentoken_pool_history_entry tables in cauldron.db,
created via an always-run idempotent migration (no DB_VERSION bump, so
existing databases upgrade in place)
- block-path indexing sharing the cauldron write transaction and reorg
undo, with creation/swap/withdrawal state tracking and reserve deltas
- mempool indexing: electrum mempool.get filters on the tokentoken
contract code (spends) and the CONJURE op_return hint (creations);
first_seen_timestamp reconciles with mtp on confirmation
- RPC endpoints /tokentoken/pool/active (pair lookup, order-insensitive)
and /tokentoken/tokens
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This makes the application fully async; freeing web server threads to
handle new connections when waiting on SQL queries.
Additionally sqlx will allow easier move to a different database if
needed in the future.
Includes some SQL optimizations as well (slow queries more easliy identifiable
with sqlx).
The previous commit tried to query bcmr_data table from the crc20
database connection, but bcmr_data exists only in the bcmr database.
Fix by introducing STATE_SKIP_HAS_BCMR to mark tokens that have BCMR
data. The CRC20 fetcher now queries the bcmr database separately to
find candidates with BCMR data, marks them in the crc20 database, and
skips them in future iterations.
- Refactor update_changes_score_volume_and_ranking to use read connections
for read/compute phases, only acquiring write connection for final writes
- Change first_pool_creation to use try_get() for opportunistic caching,
preventing thread exhaustion if write pool is busy
- Add linter to flag blocking _w.get() calls in RPC handlers
Migrate all 32-byte hash fields from TEXT (64-char hex) to BLOB
(32 bytes) for ~50% storage savings on hash columns.
Changes:
- Add src/db/blob.rs with ToBlob/FromBlob traits for hash types
- Update all table schemas to use BLOB for hash columns
- Replace .to_hex() with .to_blob() for DB inserts
- Replace ::from_hex() with ::from_blob() for DB reads
- Use SQL hex() function where API responses need hex strings
- Bump DB_VERSION from 4 to 5
Affected tables: tx, pool, pool_history_entry, utxo_spending,
utxo_funding, user_action, auth_chain_entry, bcmr_data,
bcmr_failure, bcmr_well_known, crc20, crc20_candidates,
delphi_entry, cached_token_metrics
Fixes#1
When a token has both an auth_chain_entry (without BCMR) and a CRC20
entry (with name/symbol), the dedup logic now keeps the metadata
instead of returning NULL.