Permissionless, intent-based on-chain ↔ off-chain interoperability.
FreeFlo is currently on MVP stage. Meaning that some trade-offs had to be made to prove this actually works:
- The current provider for Instant SEPA transfers doesn't allow for API execution of payment for non-trusted beneficiaries. Receiver's details have to be stored with the provider pre-transfer.
- Attestation Service is running on a dedicated server creating a single point of failure. The goal is to have this running on a TEE.
- The current Solver implementation allows solvers to set their own ForEx rate. Future implementation will have an enshrined oracle for each currency.
FreeFlo is a trust-minimized, intent-powered off-ramp protocol enabling USDC ↔ Fiat swaps.
Unlike cross-chain protocols where both input and output occur on-chain, FreeFlo settles the output leg off-chain via real-time payment networks (RTPNs), with fulfillment proven cryptographically using TLSNotary.
FreeFlo's MVP currently supports just the one RTPN: Instant SEPA which enables real-time EURO payments across european banks.
Note: MVP end-to-end settlement ( testnetUSDC <> EURO swap): ~10-15 seconds.
- Permissionless: Anyone with an account form supported RTPNs can run a solver.
- Trust-minimized: Payment verification via zkTLS proofs, not trusted third-parties required.
- Non-custodial: Users crypto is escrowed in smart contracts and only released if off-chain payment is cryptographically proven.
- RTPN-agnostic: Extensible to any real-time payment network with API ( for automation) and TLS ( for proof generation) capabilities.
| Term | Definition |
|---|---|
| Intent | A user's request to convert USDC to fiat at a specified destination: RTPN, fiat currency, and recipient details. |
| Solver | An entity that fulfills intents by sending fiat and proving payment. |
| Quote | A solver's offer to fulfill an intent, specifying the amount to be sent in the requested fiat currency |
| Commitment | User's selection of a specific quote, locking USDC for the duration of the fulfillment window |
| Fulfillment | Completing an intent: sending fiat, generating proof, submitting it on-chain, and claiming USDC |
| Attestation | An EIP-712 signed statement from an authorized witness confirming payment was verified via zkTLS |
| Witness | The authorized signer (controlled by FreeFlo Protocol) that validates zkTLS proofs and signs attestations |
| RTPN | Real-Time Payment Network: an off-chain payment network with near-instant settlement (e.g., Revolut, Wise...) |
| Nullifier | A unique payment identifier preventing double-claims, derived from the provider's transaction ID |
| Component | Location | Responsibility |
|---|---|---|
| OffRampV3 | contracts/src/OffRampV3.sol |
Intent lifecycle, USDC escrow, quote management |
| PaymentVerifier | contracts/src/PaymentVerifier.sol |
EIP-712 verification, witness registry, nullifier tracking |
| Attestation Service | attestation/ |
zkTLS proof validation, on-chain state verification, attestation signing |
| Solver | solver/ |
Quote API, fiat execution, proof generation, intent monitoring |
| Prover | providers/prover/ |
TLSNotary proof generation per RTPN |
| Frontend | frontend/ |
Web application for users |
-
PENDING_QUOTE — User calls
createIntent(), deposits USDC. Solvers callsubmitQuote()with competing offers. -
COMMITTED — User calls
selectQuote(), locks funds to chosen solver. Solver has fulfillment window to complete. -
FULFILLED — Solver sends fiat, proves via zkTLS, calls
fulfillIntentWithProof(). Contract verifies attestation, releases USDC to solver. -
CANCELLED / EXPIRED — Timeout reached without fulfillment. User calls
cancelIntent(), USDC returned.
FreeFlo separates concerns between untrusted solvers and trusted infrastructure:
| Actor | Trust | Responsibility |
|---|---|---|
| Solver | Untrusted | Execute fiat transfers, generate proofs |
| Attestation Service | Trusted (FreeFlo) | Validate proofs, verify on-chain state, sign attestations |
| Smart Contracts | Trustless | Verify signatures, enforce time windows, release funds |
Solvers cannot:
- Forge payment proofs : TLSNotary proofs are cryptographically bound to the TLS session
- Claim without valid attestation : On-chain signature verification required
- Double-claim payments : Nullifier (payment ID) tracked on-chain
- Claim for wrong intent : Attestation includes intent hash
Users are protected against:
- Solver non-fulfillment : Funds auto-release after timeout via
cancelIntent() - Quote manipulation : Committed quote amount verified in attestation
- Incorrect payment amount : Attestation service validates proof amount ≥ committed amount
| Risk | Mitigation |
|---|---|
| Attestation service downtime | Solvers cannot claim, but user funds remain safe. Timeout allows cancellation. |
| Proof-generation failure | No mitigation yet but leaning towards extension of fulfillment window and optimistic settlement ( requires slashing) |
| Solver commits but never sends fiat | Commitment timeout returns funds to user automatically. |
| Solver underpayment | Attestation service validates on-chain committed amount against proof. |
| Witness key compromise | Single point of failure. Future: threshold signatures or TEE. |
- Node.js 20+
- Rust 1.75+ (for attestation service and TLSNotary)
- Foundry
# Clone
git clone https://github.com/MontaguSandwich/FreeFlo.git
cd FreeFlo
# Install dependencies
cd contracts && forge install && cd ..
cd solver && npm install && cd ..
cd frontend && npm install && cd ..
# Configure environment
cp solver/env.example solver/.env
cp frontend/env.example frontend/.env.local
# Edit .env files with your values
# Start services
cd solver && npm run dev:v3 # Terminal 1: Solver
cd frontend && npm run dev # Terminal 2: Frontend
# Open http://localhost:3000See Solver Onboarding Guide for production deployment.
FreeFlo is designed to support any RTPN with API capability.
-
Implement the Prover — Create a TLSNotary prover in
providers/prover/adapters/<provider>/:// Must generate a Presentation proving: // - Transaction ID // - Amount in cents // - Beneficiary identifier (IBAN, account number, etc.)
-
Register the Server — Add the provider's API domain to
ALLOWED_SERVERSin attestation config -
Implement Solver Integration — Add provider execution logic in
solver/src/providers/<provider>.ts:// Must implement: // - OAuth/auth flow // - Transfer execution // - Prover invocation
-
Register RTPN On-Chain — Solvers declare supported RTPNs via
setSolverRtpn()
See providers/README.md for detailed integration guide.
| Contract | Address |
|---|---|
| OffRampV3 | 0x34249F4AB741F0661A38651A08213DDe1469b60f |
| PaymentVerifier | 0xd72ddbFAfFc390947CB6fE26afCA8b054abF21fe |
| Document | Description |
|---|---|
| Providers | Supported providers |
| Solver Onboarding | Run your own solver |
| Operations Runbook | Production operations |
cd contracts && forge build
cd solver && npm run build
cd attestation && cargo build --release
cd frontend && npm run buildcd contracts && forge test -vvv
cd solver && npm test
cd attestation && cargo testcd contracts && forge fmt --check
cd solver && npm run lintMIT License. See LICENSE.
