Skip to Content
x207x207 Docs
Smart contracts

Contracts

The x207-circle smart contract on NEAR - what it holds, what it enforces, how to build and test it.

x207-circle is a Rust contract for NEAR. It is the one place where a circle is true. It holds the members and their stakes, records signed vouchers per pair, accepts a clearing proposal from any solver after checking it on chain, applies the best one, covers a default from the debtor's stake, and lets creditors claim USDT.

Everything else in x207 (the SDK, the backend indexer and read API, the solver, the dashboard) reads or calls this contract.

Testnet only. No mainnet deploy has been made. See Deployments and Security and limits.

Interface version

The interface in contracts/abi.json is 0.0.18 (INTERFACE_VERSION in x207-circle/src/lib.rs): 106 public functions (calls and views) and 52 events. Every event log carries this version in its envelope. The ABI file is the frozen reference; names on these pages are copied from it and from the Rust source.

Contract set

PathContents
x207-circle/src/The circle contract. One module per area: member.rs, stake.rs, voucher.rs, clearing.rs, verifier.rs, commit.rs, settlement.rs, bundle.rs, deal.rs, grant.rs, escrow.rs, account_escrow.rs, freeze.rs, params.rs, risk.rs, evm.rs, auto_settle.rs, intents_withdraw.rs, pending.rs, prune.rs, upgrade.rs, events.rs, and others
locker/x207-locker, a small contract put on a NEAR account that is for sale. It releases a full-access key to the buyer only when the circle says so (interface 0.0.18)
dao/Scripts that print Sputnik DAO proposal commands (for set_params and the like)
tests/near-sandbox integration tests and unit tests
probes/Mock token, NFT, multi-token, Intents and gas-probe contracts used by tests and rehearsals
abi.jsonFrozen contract interface
scripts/repro-build.sh, verify-release.sh, fetch-intents-wasm.sh

Roles

The contract has no special roles beyond these. Most steps are open to anyone.

RoleWhoWhat they can do
Ownerowner_id set at new; on x207-v013.testnet a Sputnik DAO, on x207-v018.testnet a team accountset_params, admit_member, freeze and upgrade with timelocks, asset classes, official contract lists
MemberAn account that called join and stakedRegisters vouchers, takes part in clearing, claims
SolverAny accountsubmit_clearing; the best proposal of an epoch is kept
KeeperAny accountclose_epoch, commit_epoch, finalize_epoch, prune_epoch
RelayerAny accountRelays signed actions of EVM members (join_evm, claim_evm, and others)
set_params, admit_membervouchers, claimsubmit_clearingclose_epoch, commit_epoch,finalize_epoch, prune_epochjoin_evm, claim_evmOwnerx207-circleMemberSolverKeeperRelayer

Epoch phases

get_epoch reports one of: open, cutoff, clearing, commit, payment_window, finalized.

open --(epoch_length ends)--> cutoff
cutoff --close_epoch--> clearing        any solver may submit_clearing
clearing --(proposal_window ends)--> commit
commit --commit_epoch--> payment_window  net debtors pay (settle)
payment_window --finalize_epoch--> finalized   defaults covered, creditors claim
epoch_length endsclose_epochproposal_window endscommit_epochfinalize_epochopencutoffclearingcommitpayment_windowfinalized

Member life

get_member reports one of pending, active, suspended, exited.

  • join makes a pending member (with a storage deposit of 0.35 NEAR, JOIN_STORAGE_DEPOSIT).
  • A stake via ft_on_transfer with {"action":"stake"} (and DAO admission when admission is dao) makes it active.
  • A default at finalize_epoch makes it suspended. A later stake top-up reactivates it from a later epoch (member_reactivated).
  • request_exit, then withdraw_stake once the exit epoch is finalized and no debt remains, makes it exited.
joinstake via ft_on_transferdefault at finalize_epochstake top-up from a later epochrequest_exit thenwithdraw_stakependingactivesuspendedexited

Credit limit

The limit is computed by compute_limit in risk.rs:

limit = min(limit_cap, stake * base_limit_ratio / 10000 + settled_history * limit_growth / 10000, stake)

The limit never exceeds the stake. It grows only from residual paid in cash (settled_history); a cycle that clears completely adds nothing. limit_decay lowers the limit of idle members at finalize_epoch.

Fixed limits in code

ConstantValueWhere
MAX_MEMBERS64member.rs
MAX_EDGES_PER_TX300 edges per proposaltypes.rs
JOIN_STORAGE_DEPOSIT0.35 NEARmember.rs
MAX_OPEN_INTENTS_PER_MEMBER64bundle.rs
MAX_BUNDLES_PER_MEMBER_PER_EPOCH16bundle.rs
MAX_BUNDLE_LEGS64bundle.rs
MAX_OPEN_DEALS_PER_MEMBER8deal.rs
MAX_ACTIVE_GRANTS_PER_MEMBER16grant.rs
MAX_PRUNE_ITEMS_PER_CALL200prune.rs
FREEZE_TIMELOCK_NS, UPGRADE_TIMELOCK_NS24 hours eachfreeze.rs, upgrade.rs
RULING_WINDOW_NS7 daysdeal.rs
PENDING_GC_AFTER_NS7 days (owner can change, 1 hour to 30 days)pending.rs

Default parameters

new takes params or null; null gives these defaults (params.rs). set_params stores new values for the next epoch; close_epoch applies them.

FieldDefaultAllowed range
epoch_length (s)3600300 to 604800
payment_window (s)1800900 to 604800
proposal_window (s)60060 to 86400
base_limit_ratio (bps)8000at most 10000
limit_growth (bps)1000at most 10000
limit_cap1,000,000,000 (1,000 credit)1,000,000 to 100,000,000,000
limit_decay (bps)0at most 5000
fees.clearing_fee_bps0at most 100
fees.solver_fee_bps0at most 100
admissionopenopen or dao
max_search_share_bps500at most 10000
bond_threshold100,000,000 (100 credit)10,000,000 to 1,000,000,000,000
bond_bps2000500 to 5000
prune_delay_epochs21 to 30
relayer_fee0at most RELAYER_FEE_MAX (1,000,000 units)

One credit is 1,000,000 units (CREDIT_DECIMALS is 6).

Build and test

Needs Rust 1.99.1 with the wasm32-unknown-unknown target (pinned in rust-toolchain.toml) and cargo-near 0.22.0.

cargo near build non-reproducible-wasm
cargo test

Sandbox tests need the wasm from the build step. A sandbox test that walks a whole epoch takes about one minute because it fast-forwards block time.

Check a release against a deployed account (needs Docker for the reproducible build):

scripts/repro-build.sh <git-ref> <outdir>               # prints the sha256 of x207_circle.wasm
scripts/verify-release.sh v0.0.13 [account] [rpc-url]

The wasm is built in a pinned container, so two builds of one commit give the same bytes. What the check proves and what it does not: contracts/docs/verify-release.md.

Cost

On testnet, a full epoch of 210 edges among 15 members burnt 0.1075 NEAR (about 0.0005 NEAR per edge); commit_epoch of 210 edges burnt 69.4 Tgas. Every number with its transaction hash is in contracts/docs/gas-and-cost.md.

Pages in this section

Source

Edit on GitHub

On this page