EziAgric is a decentralized escrow protocol designed to secure agricultural trade across different regions. By leveraging Soroban Smart Contracts, Amana eliminates the "Trust Gap" between buyers and sellers, ensuring fair trade even when parties are hundreds of miles apart.
This is the main repository containing the smart contracts and orchestration logic. Backend, frontend, and mobile applications are maintained in this monorepo for simpler development and unified deployment.
The root of this repository is intentionally small. Each top-level entry has a single, clear purpose:
frontend/β Next.js app (UI + wallet + Supabase/Pinata client integration)backend/β Node.js/TypeScript API (Supabase + Pinata + integration endpoints)mobile/β React Native Expo app (mobile wallet, notification, and trade UX)contracts/β Rust/Soroban smart contractsdocs/β Current project documentation (ADRs, threat model, policies, guides)docs/archive/β Historical completion summaries and hardening PR notes, kept for reference only. Seedocs/archive/README.md..github/β CI workflows and repository automation
The former top-level
archive/directory has been folded intodocs/anddocs/archive/. Nothing at the repository root is historical β if you are looking for old completion summaries or hardening notes, they now live underdocs/archive/.
To provide a programmable safety net for regional commodity trading. Amana ensures that the risk of "sending first" is eliminated, replaced by a secure, neutral vault that only releases funds when delivery is verified.
- Smart Escrow: Secure funds holding using cNGN/stablecoins on the Stellar network.
- Dynamic Loss Sharing: Negotiable risk-sharing ratios (e.g., 50/50, 70/30) hardcoded into every trade to handle transit accidents or theft.
- Proof-of-Delivery (PoD): An optional video-based verification protocol involving the buyer and the driver to confirm the state of goods. Video evidence can be submitted and stored on IPFS for dispute resolution.
- Volatility Protection: Utilizes Stellar Path Payments to allow users to pay in local currency (NGN) while locking value in cNGN.
- Automated Settlement: A flat 1% platform fee is automatically deducted upon successful trade completion.
- Frontend: Next.js (App Router)
- Smart Contracts: Soroban (Rust)
- Blockchain: Stellar Network
- Wallet Connection: Freighter / Albedo
- Storage: IPFS (via Pinata) for decentralized storage of video evidence.
- Database: Supabase (Off-chain metadata, driver logs, and user profiles).
- Observability: OpenTelemetry distributed tracing with correlation IDs for end-to-end request tracking.
frontend/β Next.js app environment (UI + wallet + Supabase/Pinata client integration)backend/β Node.js/TypeScript API environment (Supabase + Pinata + integration endpoints)mobile/β React Native Expo environment (mobile wallet, notification, and trade UX)contracts/β Rust/Soroban smart contract environment
Run scripts/dev-doctor.sh from the repo root. It checks every toolchain (node, npm, docker,
cargo, wasm32 target, stellar CLI), prints the exact install command for anything missing, and
finishes with one smoke test per stack. Hit something it didn't catch? Open an
Onboarding friction issue.
cd frontendcp .env.example .env.localnpm installnpm run dev
cd backendcp .env.example .envcp .env.tracing.example .env.tracing(for distributed tracing configuration)npm installnpm run dev
cd mobilecp .env.example .env.localnpm installnpm start
- Source of truth:
backend/src/docs/openapi.yaml - Dev Swagger UI:
http://localhost:4000/api/docs - JSON export:
http://localhost:4000/api/docs/openapi.json
The backend writes backend/src/docs/openapi.json from the YAML spec in non-production runs so reviewers can inspect either format.
cd contracts/amana_escrowcargo build
Amana enforces stack-level CI gates on pull requests through .github/workflows/ci.yml.
- Frontend Required Gate:
npm ci,npm run lint,npm run build,npm testinfrontend/ - Backend Required Gate:
npm ci,npm run build,npm testinbackend/ - Mobile Required Gate:
npm ci,npm run type-check,npm run lintinmobile/ - Contracts Required Gate:
cargo testincontracts/amana_escrow/- Also builds the optimized WASM, posts its size and sha256 to the job summary, fails if it exceeds the size budget (
WASM_SIZE_BUDGET_BYTES), and verifies a clean rebuild produces the same sha256
- Also builds the optimized WASM, posts its size and sha256 to the job summary, fails if it exceeds the size budget (
- Conventional PR Title: PR titles must follow Conventional Commits (
.github/workflows/pr-title.yml); see CONTRIBUTING.md
Path-aware execution is enabled to avoid unnecessary runtime. If a stack has no changed files, the gate reports a skip-note and passes.
For the protected branch (main), set these required status checks:
Frontend Required GateBackend Required GateContracts Required Gate
- Initiate: The Seller lists products. The Buyer initiates a trade, depositing funds that are converted to cNGN via a Stellar Path Payment.
- Lock: The Smart Contract locks the funds and stores the agreed-upon
Loss_Ratio. - Dispatch: The Seller provides the driver's name, phone number, and vehicle manifest.
- Verification: - Success: Buyer receives goods and uploads a confirmation video. Funds release to Seller.
- Dispute: Buyer uploads a video of loss/damage with driver affirmation. A mediator reviews the evidence.
- Settlement: Based on the outcome, funds are distributed (either 100% to one party or split via the
Loss_Ratio).
sequenceDiagram
autonumber
actor Buyer
actor Seller
actor Driver
participant Backend
participant Contract as Soroban Contract
participant IPFS
Buyer->>Backend: POST /trades (create trade intent)
Backend->>Contract: invoke create_trade(buyer, seller, amount, ratio)
Contract-->>Backend: TradeCreated event (trade_id)
Backend-->>Buyer: 201 { tradeId }
Buyer->>Contract: invoke fund_trade(trade_id, cNGN amount)
Contract-->>Backend: TradeFunded event
Backend-->>Seller: push notification β trade funded, prepare shipment
Seller->>Driver: hand over goods
Driver->>Backend: POST /trades/:id/delivery (driver confirms delivery)
Driver->>IPFS: upload delivery video evidence
IPFS-->>Driver: CID
Driver->>Backend: POST /trades/:id/evidence { cid }
Backend->>Contract: invoke submit_evidence(trade_id, cid)
Contract-->>Backend: EvidenceSubmitted event
Buyer->>Backend: POST /trades/:id/confirm (buyer confirms receipt)
Backend->>Contract: invoke confirm_delivery(trade_id)
Contract-->>Backend: TradeCompleted event
Contract->>Seller: release funds (minus 1% platform fee)
sequenceDiagram
autonumber
actor Buyer
actor Seller
actor Driver
participant Backend
participant Contract as Soroban Contract
participant IPFS
Note over Buyer,Contract: Trade is in Funded state
Buyer->>Backend: POST /trades/:id/dispute (buyer raises dispute)
Backend->>Contract: invoke raise_dispute(trade_id, reason)
Contract-->>Backend: DisputeRaised event
Seller->>IPFS: upload counter-evidence
IPFS-->>Seller: CID
Seller->>Backend: POST /trades/:id/evidence { cid, party: "seller" }
Driver->>IPFS: upload delivery confirmation
IPFS-->>Driver: CID
Driver->>Backend: POST /trades/:id/evidence { cid, party: "driver" }
Note over Backend,Contract: Mediator quorum reviews evidence
Backend->>Contract: invoke resolve_dispute(trade_id, verdict, loss_ratio)
Contract-->>Backend: DisputeResolved event
alt Verdict: buyer wins
Contract->>Buyer: refund per loss-sharing ratio
Contract->>Seller: partial payment
else Verdict: seller wins
Contract->>Seller: full payment (minus 1% fee)
end
Full diagrams with all events and edge cases are in
docs/event-flow.md.
- Develop core Soroban contract logic (
deposit,release,refund). - Implement basic Next.js UI for trade creation.
- Integrate
Loss_Ratiovariables into the smart contract. - Build the "Mediator" dashboard for dispute resolution.
- IPFS integration for video evidence uploads.
- Driver manifest logging and tracking interface.
- Public pilot program with regional agricultural cooperatives.
- Implementation of a "Trust Score" reputation system.
Amana includes comprehensive distributed tracing with OpenTelemetry for end-to-end request visibility and faster incident triage.
- Correlation IDs: Unique identifiers spanning frontend-backend requests
- Request Tracing: Complete request lifecycle tracking
- Service Integration: Automatic tracing for external services (IPFS, Stellar)
- Observability: Jaeger, Zipkin, and Prometheus integration
- Configure tracing environment variables (see
backend/.env.tracing.example) - Start Jaeger for trace visualization:
docker run -p 16686:16686 jaegertracing/all-in-one - View traces at
http://localhost:16686 - Check metrics at
http://localhost:9464/metrics
See DISTRIBUTED_TRACING_GUIDE.md for detailed setup and usage instructions.
Key architectural decisions are documented as ADRs in docs/adr/. Each ADR follows the standard template and status lifecycle (proposed β accepted β superseded).
- ADR-001: Stellar Path Payment Architecture
- ADR-002: Escrow Loss-Sharing Model
- ADR-003: Off-chain vs. On-chain Data Partitioning
- ADR-004: Idempotency and Retry Strategy
- ADR-005: Frontend State Management
- ADR-006: Mobile Navigation and State Architecture
- ADR-007: Offline Caching and Conflict Resolution
- ADR-008: Notification and Deep-Link Architecture
- Threat Model β reviewed quarterly and on trigger events; see Β§8 for cadence/ownership and
docs/threat-model-review-checklist.mdfor the reviewer checklist. - Secrets Policy & Rotation β secrets inventory (owner, location, max-age), rotation automation, and verification. Rotation reminders are opened automatically by
.github/workflows/secrets-rotation-reminder.yml. - PII Encryption at Rest β classified PII column inventory, app-layer envelope encryption design, blind-index search, key rotation procedure, and decrypt access logging.
- Software Bill of Materials (SBOM) β CycloneDX SBOM generated for every release artifact via
.github/workflows/sbom.yml, attached to GitHub Releases, with a weekly vulnerability diff scan. - Golden Signals Dashboard β Grafana dashboard stored as code (
infra/grafana/) covering API latency/traffic/errors and DB/queue saturation, with deploy annotations wired into staging deploys. - Alert Routing Policy β page-vs-ticket severity rubric, runbook linkage enforced in CI, per-alert dedup windows, and the monthly alert review log.
- Synthetic Probes Policy β hourly staging probe of the core escrow journey (auth β create β deposit β release), with failure alerting and a results dashboard log.
- Preview Environments β per-PR ephemeral backend stack (compose
previewprofile) spun up by thepreviewlabel workflow, smoke-tested and torn down under TTL/concurrency budget caps. - Backup Freshness & Restore Drills β weekly freshness gate that pages
backup_staleon a missing/stale daily backup, plus a quarterly automated restore drill (integrity assertions, app smoke on the restored copy, RTO history underbackup-drills/). - Incident Response β severity levels, incident roles, and channel/ticket conventions; see the postmortem template, the postmortem archive, and a worked tabletop exercise.
EziAgric is an open-source project aimed at improving food security and trade efficiency. We welcome developers, designers, and agricultural experts!
- Fork the Project.
- Create your Feature Branch (
git checkout -b feature/NewFeature). - Commit your Changes (
git commit -m 'Add NewFeature'). - Push to the Branch (
git push origin feature/NewFeature). - Open a Pull Request.
If you are adding or modifying admin routes, see the Admin Route Contribution Guide for architecture details, middleware requirements, testing expectations, and a step-by-step example.
Distributed under the MIT License. See LICENSE for more information.
// setting up and starting out
- #373: [Backend] SMS notification provider abstraction (Termii / Africa's Talking)
- #409: [Frontend] NGN payment quote display in trade funding step
- #410: [Frontend] Trade timeline component on trade detail page
- #408: [Frontend] Loss-ratio negotiation UI with visual risk explainer
- #404: [Frontend] Marketplace page: browse and search listings
- #398: [Backend] OpenAPI coverage check for non-admin routes
- #416: [Frontend] Landing page copy: explain EziAgric flow for farmers and buyers
- #418: [Frontend] Low-bandwidth mode: disable autoplay media and compress images
- #412: [Frontend] Mediator dispute review: side-by-side evidence viewer
- #389: [Backend] Indicative commodity price feed (off-chain) for listings
- #354: [Contract] Milestone-based release for multi-drop deliveries
- #356: [Contract] Per-token minimum trade amount to prevent dust trades
- #357: [Contract] Mediator fee share on resolved disputes
- #358: [Contract] Dispute evidence submission deadline
- #367: [Backend] Product listing model and CRUD API for sellers
- #359: [Contract] Default-resolution fallback when no mediator acts
- #360: [Contract] Rustdoc for every public contract entrypoint
- #363: [Contract] Add
get_trades_by_partyindex with pagination
- #364: [Contract] Reject self-trades where buyer == seller
- #426: [Frontend] Price reference widget on listing form