Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
1b5dbd8
feat: add queued stake updates and withdrawals
0xCardiE Apr 13, 2026
8aa960c
decouple main function into separate methods
0xCardiE Apr 13, 2026
1d4543c
fix: block queued withdrawals during freezes and active rounds
0xCardiE Apr 13, 2026
e2f2542
fix: make redistribution payout and linkage atomic
0xCardiE Apr 13, 2026
9f9d1cc
test: harden staking and redistribution coverage
0xCardiE Apr 13, 2026
035ebc8
reconcile slashing
0xCardiE Apr 13, 2026
bf76cb6
fix: align queued stake previews with round-based checks
0xCardiE Apr 13, 2026
10197f9
add comments for staking functions
0xCardiE Apr 13, 2026
f5c74b8
fix: close exit queues and extend withdrawal delay
0xCardiE Apr 13, 2026
01bc6ff
formatting
0xCardiE Apr 14, 2026
e6d7848
Merge remote-tracking branch 'origin/master' into feat/new_staking
0xCardiE Apr 14, 2026
d88be86
refactor: simplify stake initialization state
0xCardiE Apr 15, 2026
9b5d271
fix: execute effective stake withdrawals without redistribution relin…
0xCardiE Apr 15, 2026
ba86d95
loosen checks per SWIP format
0xCardiE Apr 16, 2026
c73b978
refactor: rename queued stake previews to lookahead
0xCardiE Apr 17, 2026
dff2544
fix(staking): harden error paths and visibility
0xCardiE Apr 18, 2026
8bba0ee
refactor(staking): remove dead code and fix privileged caller reverts
0xCardiE Apr 18, 2026
3ece9d1
ci: trigger CI run
0xCardiE Apr 18, 2026
73816e9
style: format Staking.sol with prettier
0xCardiE Apr 18, 2026
36e003e
refactor(staking): remove _revertOnFrozen parameter
0xCardiE Apr 20, 2026
7840796
fix(staking): apply ready updates before freezing
0xCardiE Apr 21, 2026
c0c012c
feat(staking): return effectiveFromRound from enqueue calls
0xCardiE May 6, 2026
285659d
fix(staking): applyUpdates freeze guard and withdrawal fixes
0xCardiE May 6, 2026
27aa102
docs(staking): NatSpec errors and diagnostic revert args
0xCardiE May 6, 2026
e989f4c
refactor(staking): review items—networkId, reconcile, coverage tests
0xCardiE May 6, 2026
e11a04f
test(staking): cover enqueue returns, no-ops, queue limits
0xCardiE May 6, 2026
89d93d5
chore(lint): format Staking.sol and clean tests
0xCardiE May 6, 2026
97c0d7e
refactor(staking): fold full withdraw into BelowMinimumStake
0xCardiE May 7, 2026
9d0a054
test(staking): add FIFO queue and mixed-delay cases
0xCardiE May 7, 2026
9db52e2
test(staking): cover redeposit after exit completes
0xCardiE May 7, 2026
b3562c4
feat(staking): persist protocol freeze per account
0xCardiE May 18, 2026
56e4c04
remove change of network by admin - security
0xCardiE May 18, 2026
ce7e917
fix various staking issues
0xCardiE May 18, 2026
9d9fdb8
code fixes and optimizations
0xCardiE May 18, 2026
261e199
docs(staking): clarify FrozenWithdrawal and satisfy Prettier CI
0xCardiE May 18, 2026
e5ee89b
test(staking): add missing QueueClosed entry to errors map
0xCardiE May 19, 2026
706b292
Migrate freeze test
0xCardiE May 19, 2026
332070d
refactor(staking): move mutators to changing section
0xCardiE May 20, 2026
b984e2a
docs(staking): clarify queue break on frozen withdrawal
0xCardiE May 20, 2026
d8e8091
docs(staking): document _applyStoredUpdate paths
0xCardiE May 20, 2026
2d547a4
Add freeze migration function
0xCardiE May 20, 2026
eb8e635
refactor(staking): rename _applyPreviewUpdate to _simulateUpdate
0xCardiE May 20, 2026
7102938
refactor(staking): consolidate per-address Account
0xCardiE May 21, 2026
5086b5b
style(staking): format Staking.sol with Prettier
0xCardiE May 21, 2026
defe2d8
Merge remote-tracking branch 'origin/master' into feat/new_staking
0xCardiE May 22, 2026
9bebce5
Add Echidna harness for queued-update StakeRegistry
0xCardiE May 22, 2026
401bfd3
style(echidna): format staking harnesses with Prettier
0xCardiE May 22, 2026
1413ca2
refactor(staking): nest UpdateQueue in Account
0xCardiE May 22, 2026
ef2c940
fix description
0xCardiE Jun 1, 2026
68c29df
Introducing constants to use accross the contracts
0xCardiE Jun 1, 2026
62f9737
add other values to constants as well, so we have it all in one place…
0xCardiE Jun 1, 2026
f1a8338
remove network ID change
0xCardiE Jun 1, 2026
71db344
clean echidna and some refactoring
0xCardiE Jun 1, 2026
fc6b4c3
fixed mumbo jumbo :)
0xCardiE Jun 1, 2026
3c670ca
no reuse remove
0xCardiE Jun 1, 2026
04882d0
Add modifier which is reused
0xCardiE Jun 1, 2026
0cb6053
remove duplicate
0xCardiE Jun 1, 2026
c42c12f
remove naming incosistencies
0xCardiE Jun 1, 2026
7181cc9
Prefix function parameters in Redistribution and PriceOracle.
0xCardiE Jun 1, 2026
99241ad
update docs
0xCardiE Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ This project includes the following smart contracts and their metadata:
- HitchensOrderStatisticsTreeLib
- Test Token

**`StakeRegistry` / `applyUpdates` (bots & backends):** `applyUpdates(address)` applies the ready prefix of each owner’s update queue. If the next pending item at the queue head is a **due** `WithdrawTokens` or `ExitStake` and payouts are blocked by an active freeze, the call **reverts** with `FrozenWithdrawal()` and the **whole transaction rolls back** (no partial commit from that call). Integrators should treat that as retry-after-unfreeze, or rely on paths that advance the queue internally under different constraints (redistributor-triggered txs, paused migration flows). Inline NatSpec next to `applyUpdates` matches this behaviour.

- Metadata ([Testnet](./testnet_deployed.json),[Mainnet](./mainnet_deployed.json))
- **Chain ID**: Chain ID of the blockchain.
- **Network ID**: Network ID.
Expand Down
12 changes: 9 additions & 3 deletions deploy/local/003_deploy_staking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import { networkConfig } from '../../helper-hardhat-config';
const func: DeployFunction = async function ({ deployments, getNamedAccounts, network }) {
const { deploy, get, log } = deployments;
const { deployer } = await getNamedAccounts();
const swarmNetworkID = networkConfig[network.name]?.swarmNetworkId;
const config = networkConfig[network.name] || {};
const swarmNetworkID = config.swarmNetworkId;

const token = await get('TestToken');
const oracleAddress = (await get('PriceOracle')).address;

const args = [token.address, swarmNetworkID, oracleAddress];
const args = [
token.address,
swarmNetworkID,
config.stakeWaitBase || 2,
config.stakeWaitOverlayChange || 2,
config.stakeWaitWithdrawal || 2,
];
await deploy('StakeRegistry', {
from: deployer,
args: args,
Expand Down
12 changes: 9 additions & 3 deletions deploy/main/003_deploy_staking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import { networkConfig } from '../../helper-hardhat-config';
const func: DeployFunction = async function ({ deployments, getNamedAccounts, network }) {
const { deploy, log, get } = deployments;
const { deployer } = await getNamedAccounts();
const swarmNetworkID = networkConfig[network.name]?.swarmNetworkId;
const config = networkConfig[network.name] || {};
const swarmNetworkID = config.swarmNetworkId;
const token = await get('Token');
const oracleAddress = (await get('PriceOracle')).address;

const args = [token.address, swarmNetworkID, oracleAddress];
const args = [
token.address,
swarmNetworkID,
config.stakeWaitBase || 2,
config.stakeWaitOverlayChange || 2,
config.stakeWaitWithdrawal || 2,
];
await deploy('StakeRegistry', {
from: deployer,
args: args,
Expand Down
14 changes: 11 additions & 3 deletions deploy/main/010_deploy_verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const func: DeployFunction = async function ({ deployments, network }) {
const { log, get } = deployments;

if (network.name == 'mainnet' && process.env.MAINNET_ETHERSCAN_KEY) {
const swarmNetworkID = networkConfig[network.name]?.swarmNetworkId;
const config = networkConfig[network.name] || {};
const swarmNetworkID = config.swarmNetworkId;
const token = await get('Token');

// Verify postageStamp
Expand All @@ -27,14 +28,21 @@ const func: DeployFunction = async function ({ deployments, network }) {

// Verify staking
const staking = await get('StakeRegistry');
const argStaking = [token.address, swarmNetworkID, priceOracle.address];
const redistribution = await get('Redistribution');
const argStaking = [
token.address,
redistribution.address,
swarmNetworkID,
config.stakeWaitBase || 2,
config.stakeWaitOverlayChange || 2,
config.stakeWaitWithdrawal || 2,
];

log('Verifying...');
await verify(staking.address, argStaking);
log('----------------------------------------------------');

// Verify redistribution
const redistribution = await get('Redistribution');
const argRedistribution = [staking.address, postageStamp.address, priceOracle.address];

log('Verifying...');
Expand Down
12 changes: 9 additions & 3 deletions deploy/test/003_deploy_staking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import { networkConfig } from '../../helper-hardhat-config';
const func: DeployFunction = async function ({ deployments, getNamedAccounts, network }) {
const { deploy, log, get } = deployments;
const { deployer } = await getNamedAccounts();
const swarmNetworkID = networkConfig[network.name]?.swarmNetworkId;
const config = networkConfig[network.name] || {};
const swarmNetworkID = config.swarmNetworkId;
const token = await get('TestToken');
const oracleAddress = (await get('PriceOracle')).address;

const args = [token.address, swarmNetworkID, oracleAddress];
const args = [
token.address,
swarmNetworkID,
config.stakeWaitBase || 2,
config.stakeWaitOverlayChange || 2,
config.stakeWaitWithdrawal || 2,
];
await deploy('StakeRegistry', {
from: deployer,
args: args,
Expand Down
14 changes: 11 additions & 3 deletions deploy/test/010_deploy_verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const func: DeployFunction = async function ({ deployments, network }) {
const { log, get } = deployments;

if (process.env.TESTNET_ETHERSCAN_KEY) {
const swarmNetworkID = networkConfig[network.name]?.swarmNetworkId;
const config = networkConfig[network.name] || {};
const swarmNetworkID = config.swarmNetworkId;

// Verify TestNet token
const token = await get('TestToken');
Expand Down Expand Up @@ -34,14 +35,21 @@ const func: DeployFunction = async function ({ deployments, network }) {

// Verify staking
const staking = await get('StakeRegistry');
const argStaking = [token.address, swarmNetworkID, priceOracle.address];
const redistribution = await get('Redistribution');
const argStaking = [
token.address,
redistribution.address,
swarmNetworkID,
config.stakeWaitBase || 2,
config.stakeWaitOverlayChange || 2,
config.stakeWaitWithdrawal || 2,
];

log('Staking');
await verify(staking.address, argStaking);
log('----------------------------------------------------');

// Verify redistribution
const redistribution = await get('Redistribution');
const argRedistribution = [staking.address, postageStamp.address, priceOracle.address];

log('Redistribution');
Expand Down
4 changes: 2 additions & 2 deletions docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The contracts must be deployed in this specific order due to dependencies:
1. Token (external or TestToken for testnets)
2. PostageStamp (depends on Token)
3. PriceOracle (depends on PostageStamp)
4. StakeRegistry (depends on Token and PriceOracle)
4. StakeRegistry (depends on Token)
5. Redistribution (depends on StakeRegistry, PostageStamp, PriceOracle)
6. Role Setup (connects contracts together)
```
Expand Down Expand Up @@ -111,7 +111,7 @@ npx hardhat deploy --network mainnet --tags oracle

**Constructor**:
```typescript
[token.address, swarmNetworkId, priceOracle.address]
[token.address, swarmNetworkId, waitBase, waitOverlayChange, waitWithdrawal]
```

**Network IDs** (from `helper-hardhat-config.ts`):
Expand Down
27 changes: 13 additions & 14 deletions docs/OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ User → PostageStamp.createBatch()

### 2. Staking for Node Operators

Node operators stake tokens to participate in the redistribution game:
Node operators stake BZZ to participate in the redistribution game:

```
Node Operator → StakeRegistry.manageStake()
├─ Calculate overlay from network ID + nonce
├─ Calculate committed stake (in storage units)
├─ Track potential stake (in BZZ tokens)
└─ Allow withdrawal of surplus stake
Node Operator → StakeRegistry (queued updates)
├─ createDeposit / addTokens / increaseHeight / changeOverlay
├─ overlay = keccak256(owner, networkId, nonce)
├─ applyUpdates after round delays
└─ withdraw / exit (with WAIT_WITHDRAWAL)
```

**Key Concept**: Height parameter allows nodes to register additional storage capacity (2^height multiplier).
**Key Concept**: Height sets the minimum stake (`MIN_STAKE * 2^height`). Effective stake for the game is the previewed BZZ balance while not frozen (no oracle in staking).

### 3. Redistribution Game Phases

Expand Down Expand Up @@ -105,14 +105,13 @@ remainingBalance = normalisedBalance - currentTotalOutPayment()

### Staking Economics

- **Committed Stake**: Amount of chunks pledged to store (in oracle price units)
- **Potential Stake**: Actual BZZ tokens staked
- **Effective Stake**: `min(committed_stake * price * 2^height, potential_stake)`
- **Balance**: BZZ locked in `StakeRegistry` for the node
- **Height**: Minimum balance scale (`MIN_STAKE * 2^height`); used with reported depth in redistribution
- **Effective Stake**: Previewed balance when overlay is set and account is not frozen

**Example**:
- Node stakes 1000 BZZ at price 1000 chunks/BZZ with height 2
- Committed stake: 100 chunks
- Effective stake: min(100 * 1000 * 4, 1000 BZZ) = 1000 BZZ
- Node deposits 1 BZZ at height 2 (minimum 0.1 * 4 = 0.4 BZZ)
- `nodeEffectiveStake` returns 1 BZZ (or 0 while frozen)

### Redistribution Economics

Expand All @@ -138,7 +137,7 @@ Each contract defines specific roles:
- `PRICE_UPDATER_ROLE`: Can adjust price based on redundancy (granted to Redistribution)

**StakeRegistry**:
- `DEFAULT_ADMIN_ROLE`: Change network ID, pause
- `DEFAULT_ADMIN_ROLE`: Pause / unpause
- `REDISTRIBUTOR_ROLE`: Freeze and slash deposits (granted to Redistribution)

**Redistribution**:
Expand Down
9 changes: 5 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ Automatically adjusts the price per chunk based on network redundancy.
Manages staking for node operators participating in the redistribution game.

**Key Features:**
- Stake commitment and potential stake
- Overlay management for nodes
- Freeze and slash mechanisms for penalties
- Height-based reserve calculations
- Queued stake updates (deposit, top-up, height, overlay, withdraw, exit)
- Overlay derivation from network ID and nonce
- Effective stake = previewed BZZ balance (gated by freeze)
- Height-based minimum stake (`MIN_STAKE * 2^height`)
- Freeze and slash penalties via `REDISTRIBUTOR_ROLE`

### Redistribution (`Redistribution.sol`)
Implements the Schelling coordination game for reserve commitment consensus.
Expand Down
Loading
Loading