Skip to content

Feature/rebates claimer cap - #141

Merged
avernikoz merged 4 commits into
mainfrom
feature/rebates-claimer-cap
Sep 24, 2026
Merged

avernikoz merged 4 commits into
mainfrom
feature/rebates-claimer-cap

Conversation

@bathord

@bathord bathord commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Problem / goal

Empty FeeManager bag entries (maker user_unsettled_fees and taker protocol_unsettled_fees) can only be cleaned up today by the FeeManager owner or by the admin multisig. At scale, that maintenance is too frequent for multisig and too operationally awkward to leave to individual users. This PR adds a delegated RebatesClaimerCap so a designated operator can claim those storage rebates without being either party.

Approach

Same pattern as LoyaltyAdminCap: a shared object with an owner field, not an owned capability. Unlike loyalty, it is not created in init. Admin + initialized MultisigConfig mint it later via create_rebates_claimer_cap, and can mint more than one to avoid singleton plumbing.

Owner transfer is admin+multisig only (update_rebates_claimer_cap_owner), same as loyalty — the current cap owner cannot reassign it. Claimer methods wrap the existing rebate cores:

  • claim_user_unsettled_fee_storage_rebate_claimer (maker bag)
  • claim_protocol_unsettled_fee_storage_rebate_claimer (taker bag)

Existing user and *_admin paths are unchanged. Auth is sender == cap.owner; rebate SUI still goes to the tx sender.

Why this way: a shared cap with an owner field matches loyalty, is discoverable, and keeps transfer under governance. An owned key + store cap would be simpler to pass around but easier to lose/lock and inconsistent with LoyaltyAdminCap.

Caveats / non-goals: multiple caps can exist by design (no on-chain singleton). Docs (admin.md, unsettled-fees.md) are not updated in this PR. create/update do not check treasury version (loyalty admin ops don’t either); claimer claim paths do, same as the other rebate functions.

Risk & blast radius

Low fund risk: these functions only destroy empty bag entries; non-empty still aborts. Blast radius is fee_manager rebate cleanup only — no trading, settlement, or treasury withdrawal changes. Operational risk is minting a cap to the wrong address; governance can rotate owner. Extra shared caps are consensus objects; minting many is possible but gated by multisig.

How to verify

  • sui move test claimer (with test-fork deps in Move.toml)
  • Create cap as admin multisig; non-multisig and uninitialized config abort
  • Cap owner claims empty maker and taker bag entries; FeeManager owner / random sender cannot use the claimer path
  • Admin can rotate owner; old owner can no longer claim; new owner can
  • Cap owner cannot transfer the cap themselves
  • Non-empty claim still aborts; missing entry is a no-op
  • User and *_admin rebate paths still work

Docs

Not updated. Behavior matches existing rebate-claim docs plus a new delegated actor; docs/admin.md still says the package is managed by two caps. Follow-up should add RebatesClaimerCap there and mention the third claimer in docs/unsettled-fees.md.

@bathord

bathord commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

@claude review

@bathord

bathord commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

/gemini review

@avernikoz
avernikoz changed the base branch from feature/upgrade-pyth to main September 24, 2026 18:17
avernikoz
avernikoz previously approved these changes Sep 24, 2026
Update admin and unsettled-fees docs for the third capability, and add scripts to mint and rotate the claimer cap.

Co-authored-by: Cursor <cursoragent@cursor.com>
@avernikoz
avernikoz merged commit ccef350 into main Sep 24, 2026
3 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.

2 participants