Skip to content

feat(key-staking): epoch reward staking contract for key holders - #1080

Open
solaawojobi00-bit wants to merge 2 commits into
accesslayerorg:mainfrom
solaawojobi00-bit:fix/issue-1069-key-staking-epoch-rewards
Open

solaawojobi00-bit wants to merge 2 commits into
accesslayerorg:mainfrom
solaawojobi00-bit:fix/issue-1069-key-staking-epoch-rewards

Conversation

@solaawojobi00-bit

Copy link
Copy Markdown
Contributor

feat(key-staking): epoch reward staking contract for creator key holders

Problem

Key holders had no way to lock tokens for a fixed number of epochs and earn a share of protocol fee revenue. The existing creator-keys staking is lock-and-claim per creator and has no epoch reward pools, no treasury-gated funding, and no per-epoch distribution event.

Scenario Before After
Holder locks for N epochs Not possible stake(key_id, amount, epochs)
Treasury funds rewards Not possible fund_epoch(epoch_id, amount), treasury-only
Early exit n/a unstake fails with EpochNotComplete
Inspect rewards n/a get_stake_rewards returns accrued and projected
Epoch settled n/a EpochRewardDistributed event

Solution

New key-staking-contract crate, following the bookkeeping-only pattern of lp-reward-contract (no token transfers). Epochs derive from the ledger timestamp. A stake opened in epoch s for k epochs earns from epochs s..s+k and can be unstaked once the current epoch reaches s+k. Each epoch's pool is split pro-rata to stake weight (the staked amount) active in that epoch.

Changes

  • key-staking-contract/src/lib.rs: the contract.
    • initialize(treasury, epoch_duration): one-time setup; stores the treasury address that may fund epochs.
    • stake: validates amount > 0 and 1..=52 epochs (bounds per-stake iteration), adds weight to each epoch in range.
    • unstake: auth by staker, blocked with EpochNotComplete until the lock ends, returns principal plus accrued rewards, closes the position.
    • fund_epoch: require_auth on the stored treasury; rejects ended epochs (EpochEnded) so pools are final once an epoch completes.
    • get_stake_rewards: accrued (completed epochs) and projected (remaining epochs at current pools/weights).
    • distribute_epoch: permissionless, once per completed epoch, emits EpochRewardDistributed { epoch_id, total_distributed }. Added because a contract cannot act by itself at epoch end; rewards do not depend on it being called.
  • key-staking-contract/src/test.rs: 13 tests.
  • Cargo.toml / Cargo.lock: register the new workspace member.

Regression Tests

Acceptance criterion Test
unstake blocked before epoch completion with EpochNotComplete unstake_before_epoch_completion_fails_with_epoch_not_complete
Reward pro-rata to stake weight within epoch rewards_are_split_pro_rata_to_stake_weight_within_epoch, rewards_accumulate_only_over_the_epochs_a_stake_is_active, late_staker_does_not_earn_from_epochs_before_it_joined
fund_epoch restricted to treasury fund_epoch_requires_the_treasury_contract_auth (no auth and a stranger are rejected; treasury succeeds)
get_stake_rewards accurate accrued values get_stake_rewards_reports_accrued_and_projected_values, projected_rewards_shrink_when_another_staker_joins_the_epoch
EpochRewardDistributed with correct epoch and total distribute_epoch_emits_event_with_epoch_and_total_distributed, distribute_epoch_without_stakers_distributes_nothing
Failure paths stake_validates_amount_and_epoch_count, unstake_twice_and_unknown_stake_are_rejected, fund_epoch_rejects_bad_amount_and_ended_epochs_and_accumulates, initialize_is_one_time_and_contract_requires_it

Testing

$ cargo test -p key-staking-contract
running 13 tests
test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
$ cargo fmt --all -- --check          # clean
$ cargo clippy --workspace --all-targets -- -D warnings   # clean
$ cargo test --workspace              # all suites ok (creator-keys: 472 passed; 0 failed)

Notes for Reviewers

  • total_distributed is the epoch's full pool when stakers exist and 0 when none do. Floor-division dust (under one unit per stake) is not paid out, and an epoch with no stakers keeps its funding undistributed.
  • Existing creator-keys staking is untouched; this is a separate contract concern.
  • Persistent entries have no TTL extension, matching lp-reward-contract. Happy to add if preferred.
  • Cargo.lock change is only the new workspace member.

Closes #1069

@drips-wave

drips-wave Bot commented Oct 18, 2026

Copy link
Copy Markdown

@solaawojobi00-bit Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Chucks1093

Copy link
Copy Markdown
Member

Conflict

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.

Implement staking contract for creator key holders with epoch rewards

2 participants