InviaInviaDocs
GitHubLaunch app
Get started/How it works

How it works

Invia is one Anchor program with three account types and four instructions. Every offer creates a small set of program-owned accounts; every fill is one Solana transaction that either succeeds in full or reverts in full.

The accounts

Each offer materializes three accounts owned by the program. Nothing else moves until one of the four instructions is called.

Offer PDARecords maker, asset mint, payment mint, side (sell or buy), original size, remaining size, price per token, min fill, created_at, and expires_at. Seeded by [b"offer", maker_pubkey, nonce].
Asset vaultProgram-owned token account. Holds tokens for sell-side offers, or receives tokens on buy-side fills.
Payment vaultProgram-owned token account in the chosen payment mint (USDC, USDT, or wSOL). Holds payment for buy-side offers, or receives payment on sell-side fills.

The vaults are PDAs derived from the offer. Only the program can sign for them, and only inside one of the four instructions below.

The four instructions

create_offer
Opens an offer and locks the maker's side into the appropriate vault. Validates that expires_at fits inside the 30-day window, that min_fill > 0, that size >= min_fill, and that price > 0. Payment mint must be USDC, USDT, or wSOL.
take_offer
Taker delivers their side, receives the maker's side, and a 0.20% fee is skimmed from the payment leg to the treasury vault. remaining_size is decremented; if it hits zero the offer and vaults close and rent is refunded to the maker.
cancel_offer
Maker-only. Returns escrowed assets to the maker and closes the accounts. No fee, no penalty.
expire_offer
Permissionless after expires_at. Returns escrowed assets to the maker and pays a small SOL bounty to the caller for triggering cleanup. This is what keeper bots run on.

Atomic settlement, in one paragraph

A fill is a single Solana transaction. Inside that transaction the program moves tokens from the asset vault to the taker, moves payment from the taker to the maker, skims the 0.20% fee to the treasury, and decrements the offer. There is no point inside that transaction at which one side has paid and the other has not. Either every step lands on chain together or none of them land. The maker cannot ghost mid-trade because there is no mid-trade.

Why this matters

Default risk is the entire reason OTC desks exist. By making settlement atomic at the protocol level, Invia removes the role of the desk. The trust model collapses to: do you trust the program bytecode? You can read it yourself.

Worked example: a sell-side fill

A maker lists 1,000,000 of token MEME at 0.0001 SOL per token, min fill 50,000, expiry 7d. The program now holds 1,000,000 MEME in the asset vault.

A taker comes in to buy 200,000:

  1. Taker submits take_offer(offer, fill_amount=200_000).
  2. The program validates: offer not expired, 200_000 >= min_fill, 200_000 <= remaining_size.
  3. Taker pays 200_000 * 0.0001 = 20 SOL from their wallet.
  4. The program splits the payment: 19.96 SOL to the maker's wallet, 0.04 SOL (20 bps) to the treasury.
  5. The program transfers 200,000 MEME from the asset vault to the taker's ATA.
  6. remaining_size drops to 800,000.

Every step is in the same transaction. If any check fails or the taker's wallet rejects, the entire transaction reverts and no state changes.

What changes after the offer is closed

When remaining_size reaches zero, or the maker calls cancel_offer, or anyone calls expire_offer after the deadline, the offer PDA and both vaults close. The rent for those accounts is refunded to the maker (or, in the expire case, the SOL bounty goes to the caller and the rest goes to the maker). The offer is no longer visible on chain, it has been physically removed.