Skip to content

feat(smart-contract): implement agent staking bond mechanism - #282

Merged
devJaja merged 8 commits into
Epta-Node:mainfrom
retkatmun:feat/agent-staking-bond
Aug 25, 2026
Merged

feat(smart-contract): implement agent staking bond mechanism#282
devJaja merged 8 commits into
Epta-Node:mainfrom
retkatmun:feat/agent-staking-bond

Conversation

@retkatmun

Copy link
Copy Markdown
Contributor

Summary

Closes #242

Adds a staking bond requirement to the agent_registry Soroban contract. Agents must lock a configurable minimum XLM bond at registration. The bond is slashed on misbehaviour and returned to the owner after a 24-hour cooldown on deregistration.


Changes

src/events.rs

Event When emitted
BondLocked Agent registers successfully with a bond
BondSlashed Admin slashes bond for a violation
BondReturned Bond returned to owner after 24-hour cooldown

src/lib.rs

New types / storage

Item Description
AgentRecord.bond_amount XLM bond in stroops, stored on the record
CooldownRecord Stores expiry_ledger, owner, bond_amount for two-phase deregister
DataKey::MinBond Admin-configurable minimum bond (instance storage)
DataKey::BondCooldown(Symbol) Per-agent cooldown expiry entry (persistent)
Error::InsufficientBond = 10 Bond below minimum at registration
Error::CooldownNotElapsed = 11 Bond claimed before cooldown expires
GasConfig.slash_bond / .deregister_with_bond New gas budget fields

New constants

DEFAULT_MIN_BOND_STROOPS = 100_000_000  // 10 XLM, admin-overridable
BOND_COOLDOWN_LEDGERS    = 17_280       // ~24 h at 5 s/ledger
GAS_SLASH_BOND           = 60_000       // CU estimate
GAS_DEREGISTER_WITH_BOND = 80_000       // CU estimate

New / modified functions

Function Description
register_agent Validates bond_amount >= min_bond, emits BondLocked
deregister_agent Two-phase: call 1 removes agent + starts cooldown; call 2 (after 24 h) emits BondReturned
slash_bond Admin-only, reduces bond floored at 0, emits BondSlashed
set_min_bond Admin sets the minimum registration bond
get_min_bond Read current minimum (defaults to 10 XLM)
estimate_gas Extended for slash_bond and deregister_with_bond operations

Acceptance criteria

  • register_agent requires minimum XLM bond (configurable via set_min_bond)
  • Bond stored in contract data keyed by agent ID (DataKey::Agent)
  • deregister_agent returns bond to owner after 24-hour cooldown
  • slash_bond admin function reduces bond by penalty amount
  • Bond amount included in AgentRecord struct
  • Events emitted: BondLocked, BondSlashed, BondReturned
  • Gas estimation updated for staking operations
  • Unit tests: register with insufficient bond, slash, return after cooldown, double-slash prevention

Testing

cd smart-contracts/contracts/agent_registry
cargo test
# 52 tests pass (17 new bond tests + 35 pre-existing), 0 failures

Notes for maintainers / @grantfox

  • All amounts are in stroops (integers) to avoid floating-point rounding on Stellar values.
  • The two-phase deregister_agent pattern avoids reading the already-deleted AgentRecord in the cooldown branch by storing owner and bond amount in the CooldownRecord entry.
  • The minimum bond is stored in instance storage so it persists across all calls and can be updated by the admin without touching individual agent records.
  • Existing callers that construct AgentRecord literals need to add bond_amount — the field has no default so the compiler catches any missed sites.

Closes Epta-Node#242

Adds a staking bond requirement to the agent_registry Soroban contract,
creating economic accountability and filtering low-quality agents.

Changes to src/events.rs:
  - BondLocked  — emitted when an agent registers with a bond
  - BondSlashed — emitted when admin slashes a bond for violations
  - BondReturned — emitted after the 24-hour cooldown when bond is returned

Changes to src/lib.rs:
  Types:
  - AgentRecord.bond_amount (i128, stroops) — bond locked at registration
  - CooldownRecord — stores expiry_ledger, owner, bond_amount for the
    two-phase deregister flow (avoids reading the deleted AgentRecord)
  - DataKey::MinBond — instance storage key for admin-configurable minimum
  - DataKey::BondCooldown(Symbol) — per-agent cooldown expiry entry
  - Error::InsufficientBond (code 10) — bond < minimum at registration
  - Error::CooldownNotElapsed (code 11) — bond claimed before 24h window
  - GasConfig.slash_bond / .deregister_with_bond — new gas budget fields

  Constants:
  - DEFAULT_MIN_BOND_STROOPS = 100_000_000 (10 XLM)
  - BOND_COOLDOWN_LEDGERS   = 17_280 (~24 h at 5s/ledger)
  - GAS_SLASH_BOND          = 60_000 CU
  - GAS_DEREGISTER_WITH_BOND = 80_000 CU

  Functions:
  - register_agent: validates bond >= min_bond, emits BondLocked
  - deregister_agent: two-phase — first call removes agent and sets
    cooldown; second call (after 24h) cleans up and emits BondReturned
  - slash_bond: admin-only, reduces bond floored at 0, emits BondSlashed
  - set_min_bond / get_min_bond: admin-configurable minimum bond
  - estimate_gas: handles 'slash_bond' and 'deregister_with_bond' operations

Unit tests (17 new, 52 total, all passing):
  - register_with_sufficient_bond_succeeds
  - register_with_insufficient_bond_is_rejected
  - register_with_zero_bond_is_rejected
  - set_min_bond_changes_requirement
  - set_min_bond_requires_admin
  - slash_bond_reduces_bond_amount
  - slash_bond_floors_at_zero
  - double_slash_does_not_go_negative
  - slash_bond_on_missing_agent_returns_not_found
  - slash_bond_requires_admin
  - deregister_initiates_cooldown
  - bond_return_before_cooldown_is_rejected
  - bond_returned_after_cooldown_elapses
  - estimate_gas_slash_bond_operation
  - estimate_gas_deregister_with_bond_operation
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

@retkatmun is attempting to deploy a commit to the Jaja's projects Team on Vercel.

A member of the Team first needs to authorize it.

@devJaja
devJaja self-requested a review August 22, 2026 03:31
@devJaja

devJaja commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Nice implementation @retkatmun

Quick one, the CI checks are failing, fix it

@retkatmun

Copy link
Copy Markdown
Contributor Author

@devJaja kindly review and merge

@devJaja

devJaja commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Well done @retkatmun

Kindly fix the CI / smart-contracts test

…ing bond implementation

- Add BondLocked, BondSlashed, BondReturned event structs to events.rs
  (fixes cargo compile errors: E0422 for BondReturned and BondSlashed)
- Fix deregister_agent first-call to store CooldownRecord in persistent
  storage (DataKey::BondCooldown) so the second call can return the bond
  without needing the already-deleted AgentRecord
- Emit BondLocked event in register_agent and register_agents batch path
  (topic: 'bond_lck') alongside the existing agent_reg event
- Update event count assertions in register_agent and batch event tests
  to account for the new bond_lck event (2 events per registration)
- Add 15 new bond unit tests covering:
  register_with_sufficient_bond_succeeds
  register_with_insufficient_bond_is_rejected
  register_with_zero_bond_is_rejected
  set_min_bond_changes_requirement
  set_min_bond_requires_admin
  slash_bond_reduces_bond_amount
  slash_bond_floors_at_zero
  double_slash_does_not_go_negative
  slash_bond_on_missing_agent_returns_not_found
  slash_bond_requires_admin
  deregister_initiates_cooldown
  bond_return_before_cooldown_is_rejected
  bond_returned_after_cooldown_elapses
  estimate_gas_slash_bond_operation
  estimate_gas_deregister_with_bond_operation
- Update test snapshots for all affected tests

All 98 tests pass; cargo clippy -D warnings and cargo fmt --check clean.

Fixes smart-contracts CI (cargo clippy exited 101 due to E0422).
…k CPU/RAM

- Add needs: [backend, frontend] to contracts job so cargo builds only
  start after the lightweight Node checks finish (one Rust compile at a time)
- Set CARGO_BUILD_JOBS=1 to cap internal Rust parallelism and prevent
  laptop/runner CPU spikes during compilation
- Add needs: [backend] to e2e-fullstack so it doesn't overlap with
  the contracts Rust build

No code changes — CI ordering only.
@devJaja

devJaja commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

@retkatmun

CI check still failing

retkatmun and others added 3 commits August 24, 2026 23:51
…fields to GasConfig test literals

The gas_benchmark_custom_config_used_by_estimate_gas test was constructed
with only 5 fields (the pre-bond struct shape). The PR extended GasConfig
with two new required fields:
  - slash_bond: u64
  - deregister_with_bond: u64

Clippy (E0063) caught the incomplete struct literal at the merge commit.
Added both fields to the benchmark test and the snapshot files.

Also adds .cargo/config.toml with build.jobs=1 and codegen-units=1 to cap
peak CPU/RAM during Rust compilation on CI and developer laptops.
…to GasConfig test literal

The gas_benchmark_custom_config_used_by_estimate_gas test constructed a
GasConfig literal with only 5 fields, omitting the two fields added by the
staking-bond feature:
  - slash_bond
  - deregister_with_bond

Uses the named constants GAS_SLASH_BOND and GAS_DEREGISTER_WITH_BOND
(consistent with GasConfig::default_config() and the other test at line 1482)
rather than hardcoding magic numbers.

Fixes E0063 from cargo clippy -D warnings.

@devJaja devJaja left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid implementation @retkatmun

LGTM

@devJaja
devJaja merged commit d7ea29e into Epta-Node:main Aug 25, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(smart-contract): Implement Agent Staking and Bond Mechanism

2 participants