Trustless, milestone-based escrow for freelance and remote work — built on Stellar's Soroban smart contract platform.
Remote work and freelance contracting run on trust that often doesn't exist between strangers across borders. Clients hesitate to pay upfront. Workers hesitate to deliver without payment guarantees. The usual fix — a centralized escrow middleman — adds fees, delays, and a single point of failure.
Trellis removes the middleman. Funds are locked on-chain, released milestone by milestone as work is verified, with a built-in dispute process if either party disagrees. No platform holds your money. The contract does.
This matters everywhere, but especially for contributors in emerging markets — where access to reliable, low-fee, borderless payment infrastructure can be the difference between taking on international work or not.
Trellis is deployed and working on Stellar testnet right now.
| Contract ID | CAUAO7CYKULE2K4EJMQ6LLRUHP7Y7JYOH6G2VBXKYG7PTETE3UZ3DU7Q |
| Network | Stellar Testnet |
| Explorer | View on Stellar Lab |
A live test agreement exists on-chain and is queryable right now:
trellis status --agreement-id 0101010101010101010101010101010101010101010101010101010101010101Full deployment details, every verified command, and step-by-step deployment instructions are in DEPLOYMENT.md.
Trellis models a freelance engagement as an agreement made up of one or more milestones, each with its own funding, work submission, and release lifecycle.
Pending ──lock_funds──► Funded ──submit_work──► WorkSubmitted ──approve_and_release──► Completed
│ │ │
│ raise_dispute raise_dispute
│ └──────────────────────────┘
│ │
cancel_unfunded ▼
│ Disputed
│ ┌───────┴────────┐
│ │ │
▼ ▼ ▼
Cancelled resolve_dispute resolve_dispute
(refund payer) (release payee)
│ │
▼ ▼
Refunded Completed
The roles:
- Payer — funds milestones and approves completed work
- Payee — submits proof of completed work and receives payment on approval
- Dispute Resolver — a neutral third party who can rule on disputes, releasing funds to either side
The guarantees:
- Funds are held by the contract, not by either party or a platform
- Either the payer or the payee can raise a dispute — neither party can unilaterally freeze the other's funds by going silent
- Unfunded milestones can be cancelled and walked away from cleanly
- Every state transition emits an on-chain event, so off-chain clients can track progress in real time without polling contract state
Trellis is a monorepo with three layers:
trellis/
├── contracts/trellis_core/ # Soroban smart contract (Rust → WASM)
│ └── src/
│ ├── lib.rs # Contract entrypoints (#[contractimpl])
│ ├── types.rs # Agreement, Milestone, EscrowStatus
│ ├── storage.rs # Ledger read/write helpers (DataKey)
│ ├── errors.rs # TrellisError enum
│ ├── events.rs # Event emitters for off-chain consumers
│ └── test.rs # Integration tests (Soroban sandbox)
│
├── cli/trellis_cli/ # Command-line interface
│ └── src/
│ ├── main.rs # clap entrypoint
│ ├── config.rs # Network config (RPC URL, contract ID, keys)
│ ├── rpc.rs # Soroban RPC / stellar CLI wrapper
│ └── commands/ # Subcommand implementations
│
└── frontend/ # Web dashboard (React + Vite + TypeScript)
└── src/
├── App.tsx # Root component with animated canvas background
├── components/
│ ├── Navbar.tsx # Navigation with wallet connect placeholder
│ └── NetworkBackground.tsx # Animated particle network canvas
└── lib/
└── config.ts # Environment config (contract ID, RPC URL)
| Function | Caller | Effect |
|---|---|---|
init |
Payer | Creates a new agreement with one or more milestones (each amount must be strictly positive) |
lock_funds |
Payer | Deposits funds for a milestone into the contract |
submit_work |
Payee | Submits proof of completed work for a funded milestone |
approve_and_release |
Payer | Approves submitted work, releases funds to payee |
raise_dispute |
Payer or Payee | Flags a milestone for resolver review |
resolve_dispute |
Dispute Resolver | Rules on a dispute — refunds payer or pays payee |
cancel_unfunded_milestone |
Payer | Cancels a milestone that was never funded (fails with InvalidStateTransition once funds are locked) |
get_agreement |
Anyone | Returns the full current state of an agreement (read-only) |
get_total_amount |
Anyone | Returns the agreement's total value — sum of all milestone amounts (read-only) |
extend_agreement_ttl |
Anyone | Renews an agreement's ledger TTL so it is not archived (no state change) |
Soroban archives persistent ledger entries once their TTL expires, so an
agreement that is never touched would eventually be lost. Every state-mutating
entrypoint renews the agreement's TTL to ~30 days automatically. Agreements
that stay idle longer than that — a long delivery window, a stalled dispute —
need extend_agreement_ttl called before the TTL runs out; any address may
call it, and the caller pays the rent.
- Soroban — Stellar's Rust-based smart contract platform (
#![no_std], compiles to WASM) - soroban-sdk 22.x — contract types, storage, auth, and token interfaces
- clap 4 — CLI argument parsing and shell completions
- clap_complete — shell completion generation
- reqwest — Rust HTTP client for Soroban RPC
- serde + serde_json — structured data serialization
- dotenvy — environment variable loading from
.env - React + Vite + TypeScript — frontend dashboard
- Tailwind CSS — frontend styling
- @stellar/stellar-sdk — Soroban contract interaction and transaction building
- @stellar/freighter-api — Freighter wallet integration
- React Router — client-side SPA routing
- Vitest + Testing Library — frontend testing
Why Soroban: contracts are written in real Rust, compiled to WASM, and run on the Stellar network — which has fast finality, low fees, and an established USDC presence via Circle's Stellar Asset Contract, making it well-suited for cross-border payment use cases like this one.
- Rust (stable toolchain)
wasm32-unknown-unknowntarget:rustup target add wasm32-unknown-unknownstellarCLI 26.x+- Node.js 20+ (for the frontend)
cd frontend
cp .env.example .env
npm install
npm run devOpen http://localhost:5173 to see the animated landing page.
cd contracts/trellis_core
cargo testAll 9 integration tests run in the Soroban sandbox — happy path, double-init protection, dispute resolution, milestone cancellation (including the state-transition guard on already-funded milestones), positive-amount validation on init, the pre-computed total_amount, and the get_agreement view function.
make buildRuns the contract WASM build, CLI binary build, and frontend bundle in sequence. See make help for all available targets.
cd contracts/trellis_core
cargo rustc --manifest-path=Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --releaseSee DEPLOYMENT.md for the full deployment walkthrough and why this command is used instead of
stellar contract build.
cd cli/trellis_cli
cargo build --release
export STELLAR_RPC_URL="https://soroban-testnet.stellar.org"
export STELLAR_NETWORK_PASSPHRASE="Test SDF Network ; September 2015"
export TRELLIS_CONTRACT_ID="CAUAO7CYKULE2K4EJMQ6LLRUHP7Y7JYOH6G2VBXKYG7PTETE3UZ3DU7Q"
export TRELLIS_SOURCE_KEY="<your stellar identity name>"
# Create a new agreement
trellis init \
--agreement-id <hex-id> \
--payer <payer-address> \
--payee <payee-address> \
--token <token-contract-address> \
--resolver <resolver-address> \
--milestones "1000,2000"
# Check status
trellis status --agreement-id <hex-id>
# Fund the first milestone
trellis lock-funds --agreement-id <hex-id> --milestone-id 0All 8 CLI commands are implemented — init, lock-funds, submit-work, approve-release, raise-dispute, resolve-dispute, cancel-milestone, and status. See DEPLOYMENT.md for the full command reference.
These flags work with every command and can be combined with .env/env-var configuration:
# Preview the stellar CLI command a state-mutating command would run, without submitting anything
trellis lock-funds --agreement-id <hex-id> --milestone-id 0 --dry-run
# Machine-parseable JSON envelope: {"status", "result", "tx_hash", "events", "error"}
trellis status --agreement-id <hex-id> --json
# Suppress everything except the final JSON result (implies --json)
trellis status --agreement-id <hex-id> --quiet
# Parsed, colorized summary instead of raw stellar CLI JSON (falls back to raw text if parsing fails)
trellis status --agreement-id <hex-id> --human-readable # or -H--json takes priority over --human-readable when both are passed.
# bash
trellis completion bash > /etc/bash_completion.d/trellis
# zsh
trellis completion zsh > "${fpath[1]}/_trellis"
# fish
trellis completion fish > ~/.config/fish/completions/trellis.fishSupported shells: bash, zsh, fish, elvish, powershell.
- Core Soroban escrow contract — all 8 entrypoints implemented and tested
- Full state machine — happy path, dispute resolution, and cancellation paths
- Integration test suite — 5/5 passing in the Soroban sandbox
- Full CLI — all 8 commands wired end-to-end
- Deployed live on Stellar testnet
initandstatusverified against the live contract- Frontend scaffold — animated particle network landing page (React + Vite + TypeScript + Tailwind)
With the core contract, CLI, and frontend scaffold complete, the current focus areas are:
| Area | Description | Difficulty |
|---|---|---|
| Frontend — Wallet connect | Freighter wallet integration in the navbar | Intermediate |
| Frontend — Agreement Status page | Query and display live agreement state | Intermediate |
| Frontend — Create Agreement form | Submit init transaction via Freighter | Intermediate |
| Frontend — Milestone actions | lock, submit, approve, dispute buttons | Intermediate |
| Frontend — Event feed | On-chain event history per agreement | Intermediate |
| Frontend — Router + navigation | React Router page structure | Beginner |
| Frontend — Skeleton loaders | Loading states for async operations | Beginner |
| Frontend — Toast notifications | Transaction feedback system | Beginner |
| Frontend — Mobile responsive | 375px viewport fixes across all pages | Beginner |
| Frontend — Landing page sections | How It Works, Features, State Machine | Beginner |
| Testnet verification | Verify remaining CLI commands on live network | Beginner |
| Native RPC client | Replace stellar CLI shell-out with native Rust HTTP client | Advanced |
| Documentation | CONTRIBUTING.md and contributor onboarding guide | Beginner |
See Issues for the full task list — each issue has exact requirements, acceptance criteria, a suggested branch name, and a timeframe.
Trellis is built in the open and welcomes contributors of all experience levels — from documentation and frontend components to contract enhancements and tooling.
- Fork the repo and clone it locally
- Browse open issues tagged
good first issueorhelp wanted - Comment on the issue to claim it — wait for maintainer confirmation before starting
- Create a branch:
git checkout -b feat/your-feature-name - Run
cargo testincontracts/trellis_coreto confirm your environment works - Open a PR referencing the issue with
Closes #X
If you're new to Soroban, the official Soroban docs are a great starting point. If you're new to React + Stellar, read through frontend/src/lib/config.ts and the Stellar SDK docs.
MIT — see LICENSE for the full text.
Built by Allen · (https://github.com/Trellis-Ecosystem)