Bound

Reference

Contract reference

Every public method and view of the Bound contract: arguments, caller, deposit and errors. Plus the mock USDC token.

Contract bound-v3.boundproto-nh26.testnet, near-sdk 5.30.1, source near/contracts/bound/src/lib.rs. Amounts are U128 decimal strings in mock USDC base units (6 decimals); times are unix seconds. A panic message starts with the error code, for example FloatExceeded: amount 500000000 > float 9000000 (the 1 yoctoNEAR guard reads Requires attached deposit of exactly 1 yoctoNEAR).

  • Roles are checked with predecessor_account_id, never the signer.
  • Every mutating method needs exactly 1 yoctoNEAR or its storage deposit, except pay, ft_on_transfer and the private callback.
  • No admin setters: the configuration is fixed at init.
  • new(owner, token, treasury, arbiter, config) is the #[init] method, called in the same transaction as the deploy.

Storage and money

storage_deposit

storage_deposit(account_id?: AccountId)

Caller
anyone (registers account_id, default the caller)
Deposit
>= 0.01 NEAR; excess, or all of it if already registered, is refunded
Returns
{ total, available }
Does
Registers an internal balance. Deposits are not refundable in this version.
Errors
StorageDepositTooSmallStorageNotCovered

ft_on_transfer

ft_on_transfer(sender_id, amount: U128, msg: String)

Caller
the configured token only (via ft_transfer_call)
Deposit
none
Returns
unused amount: all of it when the beneficiary is not registered, else 0
Does
Credits the sender, or account_id for a credit msg, then runs the msg action. Any parse error or failed action panics, so the token refunds the whole transfer.
Errors
UnknownTokenInvalidAmountInvalidMsgplus the errors of the msg action

withdraw

withdraw(amount: U128)

Caller
any registered account (its own balance)
Deposit
exactly 1 yoctoNEAR, at least 30 Tgas
Returns
Promise (token ft_transfer, then on_withdraw)
Does
Debits first, then sends the tokens. The private on_withdraw callback re-credits if the transfer fails (for example the caller is not registered on the token).
Errors
NotEnoughGasInvalidAmountNotRegisteredInsufficientBalance

Auditor stake

stake

stake(amount: U128)

Caller
auditor (its own balance)
Deposit
1 yoctoNEAR
Returns
()
Does
Moves internal balance to free stake. The same happens with ft_transfer_call msg {"stake":{}}.
Errors
InvalidAmountNotRegisteredInsufficientBalance

unstake

unstake(amount: U128)

Caller
auditor
Deposit
1 yoctoNEAR
Returns
()
Does
Moves free stake (never allocated stake) back to the internal balance.
Errors
InvalidAmountInsufficientFreeStake

Certificates

publish

publish(agent, bound: U128, reserve: U128, expires_at: u64, auditor, float_cap: U128, allowlist?: AccountId[], premium?: U128)

Caller
operator (registered)
Deposit
>= 0.05 NEAR; excess refunded
Returns
cert_id: u64
Does
Creates a Pending certificate. A premium is escrowed from the operator's balance and paid at attest. expires_at is unix seconds.
Errors
PublishDepositTooSmallNotRegisteredInvalidAgentInvalidAccountInvalidAuditorInvalidAmountInvalidFloatCapInvalidExpiryInvalidAllowlistInsufficientBalanceStorageNotCovered

accept

accept(cert_id: u64)

Caller
the certificate's agent
Deposit
1 yoctoNEAR
Returns
()
Does
Pending to Accepted; makes this the agent's current certificate.
Errors
NoSuchCertNotAgentInvalidStatusCertExpiredAgentHasLiveCert

fund_reserve

fund_reserve(cert_id: u64, amount: U128)

Caller
operator
Deposit
1 yoctoNEAR
Returns
()
Does
Moves internal balance into reserve_locked. Pending, Accepted or Verified. Also via msg {"reserve":{"cert_id":N}}.
Errors
InvalidAmountNoSuchCertNotOperatorInvalidStatusInsufficientBalance

attest

attest(cert_id: u64, allocation: U128)

Caller
the named auditor
Deposit
1 yoctoNEAR
Returns
()
Does
Locks allocation of free stake behind the certificate, pays the escrowed premium (protocol_fee_bps to the treasury, the rest to the auditor), Accepted to Verified.
Errors
NoSuchCertNotAuditorNotAcceptedCertExpiredReserveUnfundedAllocationBelowMinStakeInsufficientFreeStake

release_reserve

release_reserve(cert_id: u64)

Caller
operator
Deposit
1 yoctoNEAR
Returns
U128 released
Does
Before attestation: at any time, also refunding an escrowed premium. Verified or Invalid: from the settlement deadline on. Returns reserve_locked plus float and marks the certificate Released.
Errors
NoSuchCertNotOperatorCertFrozenLockedNothingToRelease

release_allocation

release_allocation(cert_id: u64)

Caller
the certificate's auditor
Deposit
1 yoctoNEAR
Returns
U128 released
Does
From the settlement deadline on, with no open window: the remaining allocation goes back to free stake.
Errors
NoSuchCertNotAuditorCertFrozenLockedNothingToRelease

Spending

fund_float

fund_float(cert_id: u64, amount: U128)

Caller
operator
Deposit
1 yoctoNEAR
Returns
()
Does
Tops up the float, never above float_cap. Accepted or Verified. Also via msg {"float":{"cert_id":N}}.
Errors
InvalidAmountNotOperatorInvalidStatusFloatCapExceededInsufficientBalance

pay

pay(cert_id: u64, to: AccountId, amount: U128, memo?: String)

Caller
the certificate's agent (pay-only function-call key)
Deposit
none (not payable, so a function-call key can call it)
Returns
()
Does
Moves amount from the float to to's internal balance, meters spent and pay_count, emits a pay event. See How it works for the order of checks.
Errors
NotAgentCertNotVerifiedCertFrozenSettlementDeadlinePassedNotCurrentCertCertHaltedInvalidAmountInvalidRecipientRecipientNotAllowedRecipientNotRegisteredFloatExceededMemoTooLong

halt / resume

halt(cert_id: u64), resume(cert_id: u64)

Caller
operator
Deposit
1 yoctoNEAR
Returns
()
Does
Stops or restarts pay. A settled (Invalid) certificate cannot pay whatever this flag says.
Errors
NoSuchCertNotOperator

clawback

clawback(cert_id: u64)

Caller
operator
Deposit
1 yoctoNEAR
Returns
U128 returned
Does
Takes the unspent float back to the operator's balance.
Errors
NoSuchCertNotOperator

Claims and settlement

challenge

challenge(cert_id: u64, proof: Proof, victim?: AccountId, bond: U128)

Caller
anyone except the operator, agent and auditor
Deposit
>= 0.01 NEAR; excess refunded
Returns
challenge_id: u64
Does
Posts the bond. A false trustless predicate is rejected at once (bond to the treasury); otherwise opens or joins the claim window and freezes the certificate.
Errors
ChallengeDepositTooSmallNoSuchCertNotChallengeableConflictedChallengerSettlementDeadlinePassedBondTooSmallConflictedVictimNotRegisteredInsufficientBalanceInvalidAmountEvidenceUriTooLongWindowFullDuplicateClaimStorageNotCovered

resolve_by_arbiter

resolve_by_arbiter(challenge_id: u64, proven: bool, harm: U128)

Caller
the arbiter
Deposit
1 yoctoNEAR
Returns
()
Does
Decides a Harm claim. Proven: 0 < harm <= claimed, capped at coverage. Rejected: bond to the treasury and the window slot freed.
Errors
NotArbiterNoSuchChallengeNotArbitrableAlreadyResolvedNoOpenWindowInvalidHarm

close_window

close_window(cert_id: u64)

Caller
anyone
Deposit
1 yoctoNEAR
Returns
Settlement | null
Does
Settles every claim in the window atomically: the waterfall (fraud) or the hygiene bounty. Null when nothing was admitted.
Errors
NoSuchCertNotChallengeableNoOpenWindowWindowOpenArbiterGrace

ft_transfer_call msg

Send mock USDC with ft_transfer_call to bound-v3.boundproto-nh26.testnet. An empty msg credits the sender. The sender (or the credited account) must already be registered on Bound.

json
""                                        credit the sender
{"credit":{"account_id":"x.testnet"}}     credit another registered account
{"stake":{}}                              credit, then stake all of it
{"reserve":{"cert_id":0}}                 credit, then fund that reserve
{"float":{"cert_id":0}}                   credit, then fund that float

Views

ViewReturnsNotes
verify(agent)CertView | nullthe agent's current (last accepted) certificate
get_cert(cert_id)CertView | nullone certificate by id; ids above u32::MAX return null
list_certs(from_index?, limit?)CertView[]a page of certificates; limit at most 50
cert_count()u64number of certificates
challenge_count()u64number of challenges
get_challenge(challenge_id)ChallengeView | nullchallenger, victim, proof, bond, filed_at, adjudicated, proven, harm, verdict
get_window(cert_id)WindowView | nullthe open window: opened_at, closes_at, claims
get_config()Configthe immutable configuration
get_accounts(){ owner, token, treasury, arbiter }the fixed accounts
get_owner()AccountIdowner set at init
balance_of(account_id)U128internal balance (0 if unregistered)
get_stake(auditor){ free, allocated }an auditor's stake
storage_balance_of(account_id){ total, available } | nullregistration check

CertView

cert_id, status, deal, operator, agent, auditor, bound, reserve_promised, reserve_locked, allocation, coverage, premium, float, float_cap, spent, post_expiry_spent, pay_count, allowlist, expires_at, settlement_deadline, halted, window_closes_at, open_claims, settlement, now. See statuses.

Settlement

mode (fraud or hygiene), settled_at, admitted, payable, reserve_draw, victim_paid, fee_paid, slash, slash_to_victims, to_treasury, auditor_returned (the allocation left unslashed, which stays locked until release_allocation; the name predates that rule) and hygiene_bounty_paid.

Challenge verdicts

Pending, Rejected (bond to the treasury), Unadjudicated (half the bond back), Cured (bond back), Upheld (settled, bond back).

Configuration on testnet

FieldValueMeaning
claim_window_secs90length of a claim window (production would be 72 h)
post_expiry_secs300expires_at + this = settlement deadline (production 7 d)
grace_secs60post-expiry spend inside this is not counted (production 24 h)
min_stake10000000minimum allocation (10 mUSDC)
min_challenge_bond10000000minimum bond (10 mUSDC)
challenger_fee_bps1000challenger fee, bps of proven harm
de_minimis_bps10ExpiredSpend threshold, bps of bound
hygiene_bounty10000000flat bounty from the reserve (10 mUSDC)
protocol_fee_bps1000treasury share of the premium

Limits: 16 claims per window, 16 allowlist entries, 64-byte memo, 256-byte evidence URI, 50 certificates per list_certs page. Storage: 0.01 NEAR to register, 0.05 NEAR per publish, 0.01 NEAR per challenge; each method measures the bytes it writes and panics with StorageNotCovered if the deposit does not cover them.

Events

NEP-297 logs with standard bound, version 1.0.0: register, deposit, withdraw, withdraw_failed, stake, unstake, publish, accept, fund_reserve, fund_float, attest, pay, halt, resume, clawback, challenge, challenge_rejected, resolve, window_closed, settle, release_reserve, release_allocation.

pay event
EVENT_JSON:{"standard":"bound","version":"1.0.0","event":"pay","data":[{"cert_id":1,"agent":"bound-agent2-mwfnc6q8.testnet","to":"bound-service-mwfnc6q8.testnet","amount":"1000000","memo":"mcp tool call","spent":"1000000"}]}

Calling it

sh
near contract call-function as-read-only bound-v3.boundproto-nh26.testnet get_config json-args '{}' network-config testnet now
near contract call-function as-read-only bound-v3.boundproto-nh26.testnet verify json-args '{"agent":"bound-agent2-mwfnc6q8.testnet"}' network-config testnet now

Mock USDC token

usdc.boundproto-nh26.testnet, source near/contracts/mock-usdc/src/lib.rs. A plain NEP-141 / NEP-145 / NEP-148 token built on near-contract-standards: name "Bound Mock USDC (testnet play money)", symbol mUSDC, 6 decimals.

  • mint(account_id, amount) is open: anyone can mint up to 1,000,000 USDC per call to any account, and it registers the account on the token if needed. It is play money, not Circle USDC.
  • The standard ft_transfer, ft_transfer_call, ft_balance_of, ft_total_supply, ft_metadata, storage_deposit, storage_withdraw, storage_unregister, storage_balance_bounds and storage_balance_of are the near-contract-standards ones.
  • withdraw on Bound fails and re-credits if the caller is not registered on the token; call the token's storage_deposit and withdraw again.

Unit tests live in src/tests.rs; Security maps the checklist to them.