Skip to content

feat(creator-keys): add protocol treasury withdrawal function#533

Merged
Chucks1093 merged 3 commits into
accesslayerorg:mainfrom
chonilius:fix/treasury-withdrawal-517
Jun 29, 2026
Merged

feat(creator-keys): add protocol treasury withdrawal function#533
Chucks1093 merged 3 commits into
accesslayerorg:mainfrom
chonilius:fix/treasury-withdrawal-517

Conversation

@chonilius

@chonilius chonilius commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #517

Protocol fees accumulate in the contract but there was no way to move them out. This PR adds a complete treasury accounting layer and an admin-gated withdrawal function so accumulated fees can be extracted to a multisig or treasury address on demand.

Changes

ContractError

  • Append InsufficientTreasuryBalance = 28 (ABI-safe: added at end per existing ordering convention)

DataKey

  • Append TreasuryBalance variant — tracks protocol treasury separately from key-payment escrow and dividend accumulators

Fee accumulation (buy_key / accrue_sell_protocol_fee)

  • Both paths call credit_treasury_balance with the protocol-fee amount alongside the existing ProtocolFeeRecipientBalance credit
  • The two accumulators are independent: one is for the designated fee-recipient address; the other is the admin-controlled treasury pool

get_treasury_balance() -> i128

  • Pure view returning the current withdrawable balance; returns 0 before any fees accrue

withdraw_treasury(admin, amount, recipient) -> Result<i128, ContractError>

  • Requires admin.require_auth() + assert_is_admin guard → Unauthorized on non-admin call
  • Rejects amount <= 0NotPositiveAmount
  • Rejects amount > current_balanceInsufficientTreasuryBalance
  • Decrements treasury balance atomically
  • Partial and full (leaves zero) withdrawals both supported
  • Emits TreasuryWithdrawalEvent { amount, recipient, remaining_balance, ledger }

events.rs

  • TREASURY_WITHDRAWAL_EVENT_NAME = "treas_out"
  • TREASURY_WITHDRAWAL_DATA_FIELDS for indexer stability
  • TreasuryWithdrawalEvent contracttype struct (append-only field ordering)
  • treasury_withdrawal_event_topics(recipient) helper

Test plan

tests/treasury_withdrawal.rs — 13 integration tests, all passing:

  • get_treasury_balance returns zero before any trades
  • get_treasury_balance increases after buy_key
  • get_treasury_balance accumulates across multiple buys
  • Partial withdrawal succeeds and decrements correctly
  • Full withdrawal leaves balance at zero
  • TreasuryWithdrawalEvent fields are correct
  • Non-admin call reverts with Unauthorized
  • Zero amount reverts with NotPositiveAmount
  • Negative amount reverts with NotPositiveAmount
  • Over-withdrawal reverts with InsufficientTreasuryBalance
  • Over-withdrawal on empty balance reverts with InsufficientTreasuryBalance
  • Multiple sequential partial withdrawals track balance correctly
  • Treasury balance is independent of ProtocolFeeRecipientBalance

All 55 existing unit tests and all prior integration tests continue to pass.

…layerorg#517)

**Summary**

Protocol fees accumulate in the contract with no way to extract them.
This commit adds a complete treasury accounting layer and an admin-gated
withdrawal function so accumulated fees can be moved to a multisig or
treasury address on demand.

**Changes**

`ContractError`
- Append `InsufficientTreasuryBalance = 28` at the end of the enum
  (ABI-safe per the existing ordering convention).

`DataKey`
- Append `TreasuryBalance` variant to track the protocol treasury
  separately from key-payment state and dividend accumulators.

`constants::storage`
- Expose `TREASURY_BALANCE` constant for consistent key access.

Fee accumulation (`buy_key` / `accrue_sell_protocol_fee`)
- Both paths now call `credit_treasury_balance` with the computed
  protocol-fee amount alongside the existing `ProtocolFeeRecipientBalance`
  credit. The two accumulators are independent: one is for the fee-recipient
  address to claim; the other is the admin-controlled treasury pool.

`get_treasury_balance() -> i128`
- Pure view that returns the current withdrawable treasury balance.
  Returns 0 before any fees have accrued.

`withdraw_treasury(admin, amount, recipient) -> Result<i128, ContractError>`
- Requires admin auth and `assert_is_admin` guard (Unauthorized).
- Rejects zero/negative amounts (NotPositiveAmount).
- Rejects amounts exceeding the current balance (InsufficientTreasuryBalance).
- Decrements treasury balance atomically.
- Partial and full (leaves 0) withdrawals are both supported.
- Emits `TreasuryWithdrawalEvent { amount, recipient, remaining_balance, ledger }`.

`events.rs`
- `TREASURY_WITHDRAWAL_EVENT_NAME = "treas_out"`
- `TREASURY_WITHDRAWAL_DATA_FIELDS` stability constant.
- `TreasuryWithdrawalEvent` contracttype struct (append-only field order).
- `treasury_withdrawal_event_topics(recipient)` helper.

**Tests** (`tests/treasury_withdrawal.rs` — 13 integration tests)
- `get_treasury_balance_returns_zero_before_any_trades`
- `get_treasury_balance_increases_after_buy_key`
- `get_treasury_balance_accumulates_across_multiple_buys`
- `withdraw_treasury_succeeds_for_admin_partial`
- `withdraw_treasury_succeeds_full_withdrawal_leaves_zero`
- `withdraw_treasury_emits_correct_event`
- `withdraw_treasury_rejects_non_admin`
- `withdraw_treasury_rejects_zero_amount`
- `withdraw_treasury_rejects_negative_amount`
- `withdraw_treasury_rejects_over_withdrawal`
- `withdraw_treasury_rejects_over_withdrawal_on_empty_balance`
- `withdraw_treasury_multiple_partial_withdrawals_track_correctly`
- `treasury_balance_is_independent_of_protocol_fee_recipient_balance`

Closes accesslayerorg#517
@chonilius

Copy link
Copy Markdown
Contributor Author

gm, can you kindly review. all ci s passed

@Chucks1093

Copy link
Copy Markdown
Contributor

Nice implementation

@Chucks1093 Chucks1093 merged commit e5fb45a into accesslayerorg:main Jun 29, 2026
1 check 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.

Add protocol treasury withdrawal function so accumulated fees can be moved to a multisig

2 participants