AI-assisted, non-custodial escrow for P2P marketplace deals — built on Solana.
TrustLayer doesn't try to replace Facebook Marketplace, Telegram, or Discord — it sits on top of them. A seller shares a TrustLayer link instead of their bank details or a "friends & family" payment. The buyer's payment is locked in a real Solana smart contract instead of being held by any platform, and an AI model gives an advisory trust signal on the listing photos and, later, on delivery evidence — a helper, not a gate. Funds only ever move when the buyer chooses to release them on-chain.
Built in a 3-hour hackathon. Two-person team: one frontend/AI engineer, one Solana/Anchor engineer.
- How it works
- Architecture
- Tech stack
- Project layout
- Setup
- Environment variables
- Token setup
- Anchor program
- Toolchain notes
- Demo script
- Roadmap
- Seller creates a listing (title, description, price, 1-3 photos). Gemini reviews the
listing and returns an advisory risk score + reasons. The listing is saved to
localStorageand gets a shareable/listing/<id>link — no signup, no database. - Seller connects a wallet and "activates" the listing, which calls
initialize_escrowon the Anchor program, deriving a PDA escrow account and a PDA-owned token vault for that listing. - Buyer opens the link, connects a different wallet, and deposits the listed price (in USDC
or a demo fallback token) into the vault via the
depositinstruction. Funds now sit in the program, not with TrustLayer or the seller. - Seller simulates delivery by uploading a "proof of delivery" photo. Gemini compares it against the original listing photos and flags whether they plausibly show the same item.
- Buyer reviews the comparison result and calls
release, which pays the seller directly from the vault. TrustLayer's backend never touches the funds at any point.
The AI is explicitly advisory: the UI never says "verified safe," and no AI result blocks any
escrow action — a vision model can't reliably prove ownership, authenticity, or delivery. See
lib/ai/risk.ts for the prompts and the deterministic mock fallback used when
no Gemini API key is configured.
flowchart LR
Seller[Seller Wallet] --> Web[Next.js App]
Buyer[Buyer Wallet] --> Web
Web --> AiRoute[Gemini Vision Route]
Web --> AnchorClient[Anchor Client]
AnchorClient --> Program[Escrow Program]
Program --> Vault[PDA Token Vault]
Web --> LocalStore[Browser-local Demo Storage]
- Web app — Next.js (App Router) + TypeScript + Tailwind CSS.
- Wallet / chain client — Solana wallet adapter + a typed Anchor client wrapper in
lib/solana/. - AI — one server route (
app/api/analyze) calling Gemini multimodal structured output, with a deterministic mock fallback so demos never depend on external API availability. - On-chain program — Anchor, one escrow PDA + one PDA-owned associated token vault per listing.
- Persistence — listing metadata and compressed image previews live in the browser's
localStorage; only a listing ID goes in the shared URL. No images or listing text touch the chain.
Next.js · TypeScript · Tailwind CSS · Solana web3.js · Anchor (Rust) · SPL Token · Google Gemini
(@google/generative-ai) · Solana Wallet Adapter
app/ Next.js pages
page.tsx Create-listing flow (seller)
listing/[id]/page.tsx Transaction hub: activate / deposit / deliver / release
api/analyze/route.ts AI risk-analysis endpoint (Gemini + mock fallback)
components/ Trust score card, escrow timeline, image dropzone, wallet button, Explorer links
lib/
ai/risk.ts AI prompts, schema, and deterministic mock scorer
solana/constants.ts RPC / program / token configuration
solana/client.ts Typed Anchor helpers: createEscrow, depositEscrow, releaseEscrow, fetchEscrow
storage.ts localStorage-backed listing persistence
programs/trustlayer/ Anchor program (initialize_escrow, deposit, release)
tests/trustlayer.ts Anchor/Mocha integration tests
scripts/create-demo-mint.ts One-off script to mint a fallback "Demo USDC" SPL token
scripts/mint-demo-usdc.ts Top up an existing Demo USDC mint for more wallets/testing
target/idl/, target/types/ Hand-maintained IDL + TS types (see Toolchain notes)
npm install
cp .env.example .env.local # fill in values, see below
npm run devOpen http://localhost:3000. The app runs out of the box in AI-mock mode; you only need real credentials for a live devnet demo (see below).
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SOLANA_RPC_URL |
Devnet RPC endpoint (public default works fine). |
NEXT_PUBLIC_PROGRAM_ID |
Deployed Anchor program ID. |
NEXT_PUBLIC_DEMO_MINT |
SPL token mint used for deposits. Leave blank until you've created/obtained one (see below); the deposit button is disabled without it. |
NEXT_PUBLIC_TOKEN_DECIMALS / NEXT_PUBLIC_TOKEN_LABEL |
Cosmetic + math config for the token above. |
GEMINI_API_KEY |
Enables real Gemini vision analysis. Omit to use the deterministic mock scorer. |
MOCK_AI |
Set to true to force the mock scorer even with a key set (useful for a guaranteed-reliable demo). |
The escrow accepts any SPL token; preference order is real devnet USDC, falling back to a project-owned six-decimal "Demo USDC" mint:
# Only if you have real devnet USDC: paste its mint address into NEXT_PUBLIC_DEMO_MINT.
# Otherwise, mint your own demo token and fund one or more wallets:
npm run create-demo-mint -- <buyerWalletPubkey> [sellerWalletPubkey...]
# then paste the printed mint address into NEXT_PUBLIC_DEMO_MINT
# To top up more Demo USDC to wallets later (reuses the existing mint):
npm run mint-demo-usdc -- <pubkey> [pubkey...] --amount 1000 --mint <mintAddress>This requires keys/deployer.json (the program's fee-payer keypair, gitignored) to hold a small
amount of devnet SOL — see Toolchain notes if the public faucet is rate-limited.
anchor build --no-idl # see Toolchain notes for why --no-idl
anchor test --skip-build
anchor deploy --provider.cluster devnetOn-chain design — one PDA escrow account + one PDA-owned associated token vault per listing:
initialize_escrow(id, amount)— seller-signed. Records seller, buyer slot, mint, amount, setsCreated.deposit()— buyer-signed. Transfersamountofmintfrom the buyer into the vault, setsFunded.release()— buyer-signed. Transfers the vault balance to the seller's associated token account, setsReleased.
Enforced invariants (see tests/trustlayer.ts): only the recorded buyer can release, deposits must
use the escrow's own mint, and every instruction checks the escrow is in the expected status before
acting.
A few environment quirks came up building this; documenting them so nobody has to re-debug them:
anchor build's automatic IDL generation can fail on newer nightly Rust toolchains (ananchor-synmacro depends on an unstableproc_macro2API that has since moved). If you hit a "could not create temp file" / nightly sync error, build withanchor build --no-idland use the hand-writtentarget/idl/trustlayer.json/target/types/trustlayer.tsin this repo instead of regenerating them. Keep both files' account/type names in sync manually if you changelib.rs: the raw IDL JSON uses Rust's snake_case/PascalCase, but the.tstypes file must use the camelCase names Anchor's JS client actually looks up (initializeEscrow,escrow,escrowStatus, etc).- The devnet airdrop faucet rate-limits aggressively from shared/CI-like IPs. If
solana airdropkeeps failing, retry with delays between attempts, use a browser-based faucet (faucet.solana.com, QuickNode's devnet faucet, etc.) from a different network, or generate a fresh keypair — some faucets also rate-limit per destination address. - Solana CLI keypair generation can fail with "Operation not permitted" if your environment blocks
writes to
~/.config/solana/. This repo instead keeps a project-local deploy keypair atkeys/deployer.json(gitignored) and pointsAnchor.toml'swalletfield at it.
- Explain the problem: P2P deals on Facebook/Telegram/Discord have no neutral settlement layer.
- Create a listing, show the AI's advisory review (not a verdict).
- Switch wallets, deposit into escrow, show the vault balance + Explorer transaction proving TrustLayer never held the funds.
- Simulate delivery, upload the evidence photo, show the AI's match assessment.
- Release funds as the buyer, show the seller's balance change + Explorer transaction.
- Close: "Next steps are dispute arbitration, shipping-provider attestations, and production stablecoin support."
- Dispute arbitration / partial refunds
- Shipping-provider delivery attestations instead of simulated delivery
- Production stablecoin support (real USDC, mainnet)
- Multi-listing seller dashboard, notifications, and account history