DEVNETYou are on Solana devnet. Funds are not real. Behavior matches mainnet.

Signed quotes

A signed quote is the atomic unit of off-chain-to-on-chain pricing in Milky. Each one is a structured message signed by the oracle's keypair that authorizes one specific borrower to open one specific loan with one specific principal against one specific card.

This page explains exactly what a quote contains and how the protocol binds it tightly enough that an attacker can't reuse it for anything other than its intended purpose.

What a quote contains

Every quote is built from the following fields:

  • Oracle public key — which oracle signed this quote. Must match an active entry in the on-chain AllowedOracle list.
  • Quote ID — a unique 32-byte identifier. Used for replay protection.
  • Pool — the pool this quote authorizes borrowing against.
  • NFT mint — the specific card NFT being collateralized.
  • Cert hash — a 32-byte digest of the grading certificate identity, binding the quote to the specific certified card the issuer minted the NFT against.
  • Asset type ID — the canonical asset identity (cert + grader + grade), used for exposure tracking.
  • Root version — the version number of the Merkle root the protocol was using when this quote was signed.
  • FMV (micro-USDC) — the fair market value the oracle determined.
  • LTV (basis points) — the maximum loan-to-value the oracle allows.
  • Term seconds — the loan duration this quote is valid for.
  • Expiry timestamp — Unix timestamp after which the quote is no longer accepted.
  • Domain tag — a constant byte string (BORROW_QUOTE_V1\0) that prevents this signature from being replayed against other Milky signature-verifying instructions.

The signature itself is Ed25519 over the canonical byte representation of the above fields.

How the protocol verifies a quote

The Solana program performs three layered checks at loan creation:

  1. Signature verification

    Solana's native Ed25519 program is invoked (via instruction introspection) to verify that the signature on the quote message is valid for the claimed oracle pubkey. The program checks that the introspected Ed25519 instruction's message bytes match the reconstructed quote.

  2. Oracle allowlisting

    The signing oracle's pubkey is checked against the AllowedOracle PDA list. If the oracle has been removed (or deactivated) since the quote was signed, the loan creation fails.

  3. Field validation

    Each field is validated against on-chain state: the pool matches the account passed, the NFT mint matches, the cert hash and asset type match the proof receipt, the root version is one the chain still recognizes, the LTV is within global and pool ceilings, the principal is within FMV × LTV / 10,000, the expiry is in the future.

If any check fails, the entire transaction reverts and no loan is created.

Replay protection

Once a quote has been used to create a loan, a UsedQuote PDA is written with seeds (b"used_quote", oracle_pubkey, quote_id). The account itself contains minimal data — its mere existence is the "used" flag.

Any future attempt to use the same (oracle pubkey, quote id) pair for another loan will fail because the PDA already exists and the "create-as-init" flag refuses to recreate it.

This means:

  • The same signed quote cannot be used to open two loans, even if both attempts are atomically issued by the same wallet.
  • An attacker who somehow gets access to a valid quote for someone else's card cannot use it to open a loan against that card more than once.
  • The replay-protection record persists indefinitely; it's part of the protocol's audit trail.

Quote expiry

Two distinct expiry checks happen:

  • At loan_create: the program rejects any quote whose expiry_ts < current_ts. This is the primary freshness guarantee.
  • At loan_draw: the program re-checks expiry_ts against the current cluster time. This closes a "create-now-draw-later" window where a borrower might create the loan with a fresh quote but delay drawing until the price had moved against the lender.

Together with the short default TTL (5 minutes), these two checks ensure that the price the lender effectively underwrites is close to the price the oracle saw.

Pending draw window

After loan_create succeeds, the borrower has a pending draw window of about 5 minutes (PENDING_WINDOW_SECS = 300). If they don't draw in that window, the loan can be cancelled and the quote becomes permanently used (the UsedQuote PDA persists). This prevents abandoned loan-create transactions from cluttering on-chain state indefinitely.

What an attacker cannot do with a leaked quote

Because every quote binds to specific accounts and a one-shot identifier, even a leaked quote has limited blast radius. An attacker who obtains a signed quote cannot:

  • Open a loan against a different card. The NFT mint, cert hash, and asset type are all in the signed message and validated.
  • Open a loan in a different pool. The pool address is signed.
  • Open a loan for a different borrower. The signer of the loan_create transaction is the borrower; the quote authorizes the loan but does not authorize the actor.
  • Reuse the quote for a second loan against the same card. The UsedQuote PDA blocks this.
  • Use the quote after the expiry timestamp.
  • Use the signature against a different protocol or instruction. The domain tag (BORROW_QUOTE_V1\0) ensures it can only be a borrow quote.