A simple escrow contract built using Solana and the Anchor framework. It enables two parties to trustlessly exchange tokens. If the trade is not completed, the maker can reclaim their tokens — ensuring a fair and fail-safe process.
The whole code explanation can be found here
- 🔒 PDA-controlled vaults for full security
- ✅ SPL token validation via
transfer_checked
- ↻ Refundable offer if no taker appears
- 💥 Closes ATAs to reclaim rent
- 💳 Supports SPL tokens via
token-interface
.
├── programs/
│ └── escrow/
│ ├── src/
│ │ ├── lib.rs # Entrypoint for the program
│ │ ├── instructions/
│ │ │ ├── make_offer.rs # Handles offer creation
│ │ │ ├── take_offer.rs # Handles trade acceptance
│ │ │ ├── refund_offer.rs # Handles refund logic
│ │ │ └── shared.rs # Reusable helpers (transfers, close)
│ │ └── state/
│ │ └── offer.rs # Offer account definition
├── migrations/
├── tests/
├── Cargo.toml
└── Anchor.toml
The maker initiates an offer by doing the following:
-
Creates a PDA-owned
Offer
account using seeds["offer", maker_pubkey, id]
-
Transfers a specified amount of Token A from their wallet to a vault ATA owned by the
Offer
PDA -
Vault address is a derived associated token account with:
mint = token_mint_a
authority = offer PDA
Once created, this offer waits for a taker.
A taker accepts the offer:
- Sends the expected Token B amount directly to the maker's token account
- Receives Token A from the vault (vault ➔ taker)
- This transaction uses PDA signer seeds to allow the vault (PDA-owned) to transfer Token A
- Finally, the vault ATA is closed, and rent is reclaimed
- The offer account is also closed
If no one takes the offer, the maker can:
- Reclaim their Token A from the vault
- Vault is closed after refund
- Offer account is closed too
This ensures the maker doesn’t lose tokens to stuck or expired offers.
Account | Description |
---|---|
Offer |
Stores trade data: maker, token A/B mints, amount wanted, bump |
Vault |
PDA-owned ATA holding Token A |
taker_token_account_b |
ATA of taker for sending Token B |
maker_token_account_b |
ATA of maker to receive Token B |
taker_token_account_a |
ATA of taker to receive Token A |
Safely moves SPL tokens using transfer_checked
CPI, with optional signer seeds for PDA authority.
Closes an ATA and sends the lamports to the specified destination. Uses PDA authority if needed.
anchor install
anchor build
anchor localnet
anchor deploy
anchor test
- Vault is strictly controlled by a PDA (no keypairs)
- Transfer amounts are validated with mint precision using
transfer_checked
- Vaults are only created using
init_if_needed
and ATAs, reducing errors - All accounts are closed after use to free up space and reclaim rent