Skip to content

feat(guilds): legion-engage — no-admin refundable engagement stake#5

Open
biwasxyz wants to merge 1 commit into
mainfrom
feat/legion-engage
Open

feat(guilds): legion-engage — no-admin refundable engagement stake#5
biwasxyz wants to merge 1 commit into
mainfrom
feat/legion-engage

Conversation

@biwasxyz

Copy link
Copy Markdown
Contributor

What

New legion-engage contract: providers optionally stake to join a legion and unlock benefits (ranking, treasury share, governance). Staking is never required to earn — earning is free at the gateway.

Why

Moves away from the bond/slash/admin model of legion-providers. There is no admin and no slash — nothing can seize a member's stake. Cheating enforcement lives off-chain at the gateway (de-route), not on-chain confiscation.

Behavior

  • join(ft, amount) — lock sBTC (min 10k sats), become a member.
  • add-stake(ft, amount) — top up.
  • leave(ft) — refund 90% to the member, route the 10% exit fee into the legion treasury (via deposit, so it stays governable).
  • get-stake(who) — read used by the gateway for stake-weighted ranking.

Tests

Clarinet/vitest: 7 new tests, all passing (11/11 with the existing guild suite).

Not deployed

Nothing deployed. For review; deploy awaits sign-off.

Providers optionally stake sBTC to join a legion and unlock benefits
(ranking, treasury share, governance). Staking is NOT required to earn.

- No admin, no slash: nothing can seize a member's stake.
- join / add-stake / leave; leave refunds 90% and routes a 10% exit fee
  into the legion treasury (via deposit, so it stays governable).
- get-stake read used by the gateway for stake-weighted ranking.
- Clarinet tests: 7 new, all passing (11/11 with the existing suite).

Not deployed. For CEO review.

@arc0btc arc0btc left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed legion-engage.clar + engage.test.ts against the existing legion-treasury.clar (not in this diff) to verify the cross-contract fee routing actually works, not just that it type-checks.

What works well:

  • No-admin/no-slash design matches the PR's stated goal — map-delete + balance transfer in leave is the only way a stake moves, and it's always initiated by the member themselves.
  • Checks-effects-interactions is correctly followed in leave: map-delete + var-set TotalStaked happen before either token transfer. Not strictly required here since SBTC is a fixed, trusted trait-checked constant (no reentrancy surface), but it's the right habit anyway.
  • Verified the treasury hop is correct, not just plausible. leave calls (as-contract (contract-call? .legion-treasury deposit ft fee)). Inside legion-treasury.deposit, the transfer is (contract-call? ft transfer amount tx-sender (as-contract tx-sender) none)tx-sender there resolves to legion-engage's principal (because of the outer as-contract wrap), and (as-contract tx-sender) resolves to the treasury's own principal (because that's the contract currently executing). So the fee correctly moves engage → treasury, and it satisfies a typical SIP-010 sender == tx-sender guard. This is a subtle nested-as-contract interaction that's easy to get backwards; it's implemented correctly here.
  • Test coverage is good for the contract's size: min-stake boundary, double-join rejection, add-stake, the 90/10 leave split (both the member's net cost and the treasury's balance delta), quote-exit parity with actual leave output, and not-a-member guards on leave/add-stake.
  • Dust from the integer-division fee rounds in the member's favor (refund = stake - fee), which is the safer direction.

Minor, non-blocking:

  • In leave, fee and refund each independently recompute (get stake m) instead of reusing the already-bound s:
    (s (get stake m))
    (fee (/ (* (get stake m) EXIT_FEE_BPS) u10000))       ;; could be (* s EXIT_FEE_BPS)
    (refund (- (get stake m) (/ (* (get stake m) EXIT_FEE_BPS) u10000)))  ;; could be (- s fee)
    Same result, just three lookups where one would do — worth tightening for readability but not correctness.
  • EXIT_FEE_BPS is a hardcoded constant with no runtime assertion that it's <= u10000; fine today since it's compile-time and set once, but if this ever becomes configurable it'd need a bounds check to avoid refund underflowing.

No blocking issues. Approving.

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.

2 participants