-- 062: Crypto payment ledger — immutable money ledger -- -- Every crypto→USD movement writes one row here: the initial SHKeeper -- receive, any mid-path swaps (e.g. ETH→USDC), the Coinbase Prime -- offramp, the Relay bank deposit, card authorizations + settlements, -- and cold-wallet sweeps. Rows are never mutated after creation — -- corrections use a `movement_type='adjustment'` or `'reversal'` entry -- that links via related_ledger_id. -- -- This table is the source of truth for IRS Form 8949 cost-basis -- reconciliation: every `receive` captures acquired_at + fx_rate_usd -- (cost basis); every `offramp` captures disposed_at + proceeds_usd. CREATE TABLE IF NOT EXISTS crypto_payment_ledger ( id BIGSERIAL PRIMARY KEY, order_id TEXT NOT NULL, order_type TEXT NOT NULL, -- compliance | formation | canada_crtc | bundle coin TEXT NOT NULL, -- BTC | ETH | MATIC | LTC | TRX | BNB | DOGE | USDC | USD movement_type TEXT NOT NULL CHECK (movement_type IN ( 'receive', -- customer → SHKeeper (ledger entry on webhook confirm) 'swap', -- within exchange, coin → coin (e.g., ETH → USDC) 'offramp', -- coin → USD via Coinbase Prime market-sell 'bank_deposit', -- USD arrival at RelayFi (matched from IMAP monitor) 'card_authorization', -- Relay debit card auth event (card_settlement follows) 'card_settlement', -- Relay debit card final posting 'sweep', -- hot → cold wallet transfer 'reversal', -- corrective entry undoing a prior row 'adjustment' -- manual accounting correction )), amount_coin NUMERIC(36,18), -- signed; +in, -out. NULL for USD-only rows. amount_usd_cents BIGINT NOT NULL, -- signed USD-equivalent at entry time fx_rate_usd NUMERIC(24,10), -- coin→USD rate captured at entry basis_usd_cents BIGINT, -- cost basis for disposal rows (FIFO) proceeds_usd_cents BIGINT, -- proceeds for disposal rows acquired_at TIMESTAMPTZ, -- when this coin was originally received (receive rows) disposed_at TIMESTAMPTZ, -- when this coin was sold (offramp rows) provider TEXT, -- shkeeper | coinbase_prime | relay provider_ref TEXT, -- tx hash | Prime order id | Relay tx id provider_status TEXT, state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','confirmed','failed','reversed')), idempotency_key TEXT UNIQUE, -- e.g. "shkeeper:" | "prime:" | "sweep:" related_ledger_id BIGINT REFERENCES crypto_payment_ledger(id), notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_cpl_order ON crypto_payment_ledger (order_id); CREATE INDEX IF NOT EXISTS idx_cpl_pending ON crypto_payment_ledger (state) WHERE state = 'pending'; CREATE INDEX IF NOT EXISTS idx_cpl_disposal ON crypto_payment_ledger (disposed_at) WHERE disposed_at IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_cpl_movement_type ON crypto_payment_ledger (movement_type); CREATE INDEX IF NOT EXISTS idx_cpl_provider_ref ON crypto_payment_ledger (provider, provider_ref) WHERE provider_ref IS NOT NULL;