Read a job. The entire read model for the app and the proof page — both render from this and nothing else.
fn job(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>) -> Option
Fund a job. Both parties authorise in one transaction: the client puts up the full amount, the freelancer puts up whatever their reputation charges them. Neither can fund and walk away.
fn open(
env: soroban_sdk::Env,
id: soroban_sdk::BytesN<32>,
freelancer: soroban_sdk::Address,
client: soroban_sdk::Address,
amount: i128,
condition_hash: soroban_sdk::BytesN<32>,
deliver_by: u64,
)
Lifecycle state as a number, so the invariant is checkable in one call. 0 = absent, 1 = open, 2 = released (terminal), 3 = challenged, 4 = settled.
fn state(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>) -> u32
fn token(env: soroban_sdk::Env) -> soroban_sdk::Address
Sweep a job where nothing was ever delivered. The client's money returns and the freelancer's stake is forfeited. Thirty seconds, not fourteen days.
fn expire(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>)
Submit the delivered artifact.
A matching artifact settles inside this same transaction, whether or not a challenge is already open: verification outranks adjudication, because there is nothing left to adjudicate. An unmatched artifact leaves the job open for the client to contest.
There is no window in which approved work can be charged back — that absence is the product.
fn submit(
env: soroban_sdk::Env,
id: soroban_sdk::BytesN<32>,
artifact_hash: soroban_sdk::BytesN<32>,
)
Resolve a challenge. Fully deterministic and fully on-chain: the contract compares the artifact the freelancer submitted against the commitment the client signed at funding. No external input decides this.
fn confirm(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>)
Precision of the settlement asset, as reported by the token itself.
fn decimals(env: soroban_sdk::Env) -> u32
True once the money has moved and can never be moved back.
fn is_final(env: soroban_sdk::Env, id: soroban_sdk::BytesN<32>) -> bool
Contest delivery. The client posts a bond proportional to the amount.
This is where the invariant lives: a job that has already released is not
Open, so it cannot be challenged, and no other entry point moves funds
out of a released job. Money that cleared a verified release is gone.
fn challenge(
env: soroban_sdk::Env,
id: soroban_sdk::BytesN<32>,
reason_hash: soroban_sdk::BytesN<32>,
)
The stake this freelancer must lock to be funded upfront, in basis
points. completed = 0 -> 300. 20 -> 150. 200 -> 27.
fn stake_bps(env: soroban_sdk::Env, freelancer: soroban_sdk::Address) -> i128
The stake, in strops, for a job of amount. Zero when it would fall
below MIN_STAKE.
fn stake_for(
env: soroban_sdk::Env,
freelancer: soroban_sdk::Address,
amount: i128,
) -> i128
Delivery counters: (completed, on_time, disputes_lost, disputes_won).
Counters, not a score. The account carrying them belongs to the
freelancer, which is the whole portability claim.
fn reputation(env: soroban_sdk::Env, who: soroban_sdk::Address) -> (u64, u64, u64, u64)
Portable proof: (freelancer, amount, delivered, state). Derived from
chain state only — no server, no database, no account, no login. This is
what the public proof page renders.
fn attestation(
env: soroban_sdk::Env,
id: soroban_sdk::BytesN<32>,
) -> Option<(soroban_sdk::Address, i128, bool, u32)>
The settlement asset. Immutable — this contract has no admin key and no upgrade path, deliberately. A settlement primitive that can be rewritten after funds are in it is not a settlement primitive.
fn __constructor(env: soroban_sdk::Env, token: soroban_sdk::Address)
The minimum challenge bond for a job of amount, in strops. Proportional
so it scales with the money at stake, floored so griefing costs more than
the gas it burns. This is the number that used to be $337.50 flat.
fn challenge_bond_for(env: soroban_sdk::Env, amount: i128) -> i128