/** * Shared Nonce Tracker * * Unified, file-backed nonce tracker that prevents client-side nonce conflicts * across all tx-submitting MCP tools. Replaces the separate in-memory trackers * in builder.ts and inbox.tools.ts. * * Design principles (issue #413): * - Local state is primary — no network calls on the hot path for nonce assignment * - Hiro is periodic reconciliation, not a real-time oracle * - Non-blocking — file read/write on the hot path, no network calls * - Records every submission — nonce + txid + timestamp for debugging * - Shared state file (~/.aibtc/nonce-state.json) for cross-process compatibility * with CLI skills (aibtcdev/skills#240) * * @see https://github.com/aibtcdev/aibtc-mcp-server/issues/413 */ /** A record of a single submitted transaction. */ export interface PendingTxRecord { nonce: number; txid: string; timestamp: string; } /** Per-address nonce state. */ export interface AddressNonceState { /** The highest nonce we have assigned (not yet necessarily confirmed). */ lastUsedNonce: number; /** ISO-8601 timestamp of the last nonce assignment. */ lastUpdated: string; /** Rolling log of recent submissions (bounded to MAX_PENDING_LOG). */ pending: PendingTxRecord[]; } /** On-disk file format. */ export interface NonceStateFile { version: number; addresses: Record; } /** Snapshot returned by getHealthStatus(). */ export interface NonceHealthSnapshot { address: string; local: { lastUsedNonce: number; lastUpdated: string; pendingCount: number; isStale: boolean; }; chain: { possibleNextNonce: number; lastExecutedNonce: number; lastMempoolNonce: number | null; missingNonces: number[]; mempoolNonces: number[]; }; healthy: boolean; issues: string[]; } /** * How long a locally-tracked nonce is considered fresh. After this window the * tracker falls back to the chain value on the next getNextNonce() call. * * Set to 90 seconds (~15-30 Stacks blocks post-Nakamoto, where blocks are 3-5s). * Previously 10 minutes (calibrated for Bitcoin block times). The shorter window * ensures the tracker detects external sends and chain advances promptly. */ export declare const STALE_NONCE_MS: number; /** * Get the next nonce to use for an address. * * This is the HOT PATH — no network calls. Returns the locally tracked * next nonce, or 0 if no state exists for the address (caller should then * fall back to chain query). * * @returns The next nonce to use, or `null` if no local state exists or the * local state is stale (caller should query the chain). */ export declare function getTrackedNonce(address: string): Promise; /** * Record that a nonce was used for a transaction. * * Called after successful broadcast. Updates both in-memory and on-disk state. * * @param address - The sender STX address * @param nonce - The nonce that was used * @param txid - The transaction ID from broadcast */ export declare function recordNonceUsed(address: string, nonce: number, txid: string): Promise; /** * Reconcile local state with chain data. * * If the chain's `possibleNextNonce` is ahead of our local state, update * to match (txs confirmed or external sends happened). If our local state * is ahead, keep it (chain is lagging behind mempool propagation). * * @param address - The STX address * @param chainNextNonce - The `possible_next_nonce` value from Hiro API */ export declare function reconcileWithChain(address: string, chainNextNonce: number): Promise; /** * Reset (clear) nonce state for an address. * Called on wallet unlock/lock/switch so the tracker re-syncs from chain. */ export declare function resetTrackedNonce(address: string): Promise; /** * Reset all tracked nonces (e.g., for testing or full resync). */ export declare function resetAllTrackedNonces(): Promise; /** * Get the raw state for an address (for diagnostics/health tool). */ export declare function getAddressState(address: string): Promise; /** * Get the full state file (for diagnostics). */ export declare function getFullState(): Promise; /** * Force reload state from disk (useful after external process wrote to file). * Merges disk state into memory so external writes (CLI skills) are picked up * without losing any in-memory nonce advancements. */ export declare function reloadFromDisk(): Promise; export declare const _testing: { STALE_NONCE_MS: number; MAX_PENDING_LOG: number; MAX_ADDRESSES: number; readonly NONCE_STATE_FILE: string; /** Override the state file path for test isolation. */ setStateFilePath(filePath: string): void; /** Reset state file path to default. */ resetStateFilePath(): void; /** Clear in-memory state without touching disk. */ clearMemory(): void; /** Get raw memory state ref for assertions. */ getMemoryState(): NonceStateFile | null; }; //# sourceMappingURL=nonce-tracker.d.ts.map