This commit introduces the ability for accounts with the necessary privileges to slash other accounts. The amount to be slashed is controlled via the `slashPercentage`. The amount to be slashed will be calculated from the account's current Karma balance, which is the total Karma across all distributors, minus the known `slashAmount` for that account. Under the hood, it calculates the slash amount for each item (distributor or internal balance). This ensure we reduce the slash amount correctly, if a reward distributor is removed. **Example**: For example, if the account has 100 Karma and hasn't been slashed before, the account's balances would look like this: ``` rawBalance: 100 slashAmount: 0 balance: 100 ``` Therefore, `balanceOf(account)` will return `100`. If slashing burns 10% of the account's balance, then, after calling `slash(account)`, the `slashAmount` will be increased accordingly: ``` rawBalance: 100 slashAmount: 10 balance: 90 ``` Notice that `rawBalance` isn't actually a new contract property, but there's a new internal function `_rawBalanceAndSlashAmountOf(account)`, which is used by `balanceOf(account)` to determine the effective balance of an account. **Authorization** In order to slash accounts, the message sender needs to have the newly introduced `SLASHER_ROLE`. Closes #212
Staking Protocol

🧭 Overview
The Staking Reward Streamer Protocol enables secure token staking with dynamic reward calculation on Ethereum. Built with modularity and upgradability in mind, the system includes core components to manage stake deposits, reward calculations, time-based locking, and contract migration through user consent.
🧩 Core Contracts
🛠️ StakeManager
- Handles staking logic, tracks stakes and reward epochs.
- Calculates APY via Multiplier Points, which increase over time.
- Validates vaults using codehash verification for added safety.
- Upgradeable via proxy; users can opt out of migrations.
🔐 StakeVault
- A vault owned by the user, used to store and manage staked tokens.
- Interacts directly with
StakeManagerfor staking and unstaking operations. - Ensures only the owner can execute critical actions.
- Verifies contract code via codehash to ensure safety.
✨ Features
- Secure, user-owned staking vaults
- Dynamic APY via Multiplier Points
- Stake locking to boost rewards
- ERC20-compatible (via OpenZeppelin)
- Proxy upgradeability with opt-in/opt-out support
- Epoch-based reward streaming
🚀 Getting Started
📦 Install Dependencies
pnpm install
⚙️ Usage
📄 Deployment Flow
- Deploy
StakeManager - Deploy a sample
StakeVault(e.g., on a devnet or testnet) - Configure codehash in
StakeManager:
stakeManager.setTrustedCodehash(<vault_codehash>, true);
💰 Staking
- Approve the
StakeVaultto spend your tokens:
erc20.approve(stakeVaultAddress, amount);
- Stake your tokens:
stakeVault.stake(amount, secondsToLock);
⚠️ Do not transfer tokens directly to the
StakeVault. Always useapprove+stake.
Minimum stake amount and lock duration are enforced via contract settings. Epochs are automatically processed on stake actions.
🔓 Unstaking
stakeVault.unstake(amount);
- Only available for unlocked balances.
- Reduces stake proportionally based on amount and duration.
🔁 Migration (Opt-In/Out)
Users may opt-in to a new StakeManager implementation or leave:
stakeVault.acceptMigration(); // opt-in
stakeVault.leave(); // opt-out
Migration triggers automatic reward claiming. Locked balances can still opt out.
📬 Deployed Contracts
These are the official contract deployments on the Sepolia testnet (via Status Network Explorer):
| Contract | Address |
|---|---|
| StakeManagerProxy | 0x2C09141e66970A71862beAcCbDb816ec01D6B676 |
| StakeManager | 0xa2432fB545829f89E172ddE2DeD6D289c7ee125F |
| VaultFactory | 0xA6300Bd8aF26530D399a1b24B703EEf2c48a71Be |
| KarmaProxy | 0x486Ac0F5Eb7079075dE26739E1192D41F278a8db |
| Karma | 0xE9413C84eFF6B08E4F614Efe69EB7eb9a1Ca1180 |
| KarmaNFT | 0xdE5592e1001f52380f9EDE01aa6725F469A8e46F |
🧪 Development
🏗️ Build Contracts
forge build
🧹 Clean Build Artifacts
forge clean
🧪 Run Tests
forge test
🧮 Coverage
forge coverage
🚀 Deploy Locally (Anvil)
forge script script/Deploy.s.sol --broadcast --fork-url http://localhost:8545
Requires
MNEMONICenv variable.
📊 Gas & Linting
Gas Reports
pnpm gas-report
forge snapshot
Linting
pnpm lint
Formatting
forge fmt
Commit preparing command
pnpm adorno