• zk-verified ✓atlas treasury
  • 11.84%kamino · usdc
  • 18.20%drift · ksol
  • 9.40%marginfi · usdc
  • 14.10%jupiter · jlp
  • 7.80%kamino · jitosol
  • 8.92%drift · usdc
  • 22.40%orca · sol-usdc
  • 24.10%raydium · sol-usdc
  • 6.20%marginfi · sol
  • 12.30%kamino · pyusd
  • 19.80%jupiter · jlp-perp
  • 5.40%meteora · usdc-usdt
AtlasAtlas/docs
Open App

Powered by Dodo Payments

Treasury payment rails — HMAC-signed invoice webhooks, slot-rate pre-warm, same-slot Solana settlement.

HMAC-SHA256 · constant-time Same-slot settlementView live payments panel

How it works

Webhook

invoice.paid

HMAC verify

5-step gate

Pre-warm

slot-rate formula

Tx

SPL transfer · same slot

Webhook payload schema

Dodo signs the canonicalized body with HMAC-SHA256 using the shared secret. Atlas rejects any payload that fails the 5-step verification or replays an already-seen invoice_id.

#[derive(Deserialize)]
pub struct DodoWebhookPayload {
    pub event:       String,      // "invoice.paid"
    pub invoice_id:  String,      // "inv_8c2..."
    pub amount:      u64,         // base units
    pub mint:        Pubkey,      // PUSD / USDC mint
    pub destination: Pubkey,      // treasury ATA
    pub memo:        Option<String>,
    pub signed_at:   i64,         // unix seconds — replay window anchor
    pub signature:   String,      // HMAC-SHA256 hex of canonicalized body
}

5-step verification

  1. 1

    Canonicalize JSON

    Sort keys, drop whitespace. Hash input is the canonical byte string, not the wire body.

  2. 2

    HMAC-SHA256 compute

    Compute HMAC-SHA256(secret, canonical_body) once. Reused for verify + audit log.

  3. 3

    Constant-time compare

    Use subtle::ConstantTimeEq against the supplied hex signature — never == on bytes.

  4. 4

    10-minute replay window

    Reject if now() - signed_at > 600s. Clock skew tolerance ±60s baked in.

  5. 5

    Idempotency check

    Dedup by invoice_id in a 24h LRU. Duplicate webhooks accept silently — no double-settle.

Slot-rate pre-warm formula

The pre-warm target is the floor of the buffer ratio plus the slot-rate term — the yield Atlas expects to capture before the obligation deadline. This keeps the rebalance window open even when the idle buffer alone is short.

prewarm_target_pct  =  min( 100,  ⌊ (idle_buffer ÷ obligation) × 100 ⌋
                                 +  slot_rate_bps ÷ 100 )

slot_rate_bps        =  EMA₆₄(yield_bps_per_slot) × slots_until_deadline
  • EMA₆₄ — 64-slot exponential moving average of realised yield bps per slot.
  • slots_until_deadline — slots between now() and the payout's settlement target.
  • Both terms are unit-checked; floors prevent off-by-one over-warming.

6 failure modes

ModeOutcomeReason
bad signature rejectconstant-time compare failed
stale signed_at rejectoutside 10-minute replay window
duplicate invoice_id accept · no-opidempotent settlement
forbidden mint rejectmint not in vault allowlist
insufficient liquidity defensive pre-warmpre-warm capped at idle buffer cap
Solana RPC degraded retry · exponentialresubmits same canonical body

Source

⌘ I