# Staking

## Verified Contracts

All contracts are deployed on Ethereum mainnet and verified on Etherscan.

| Contract   | Address                                      | Etherscan                                                                            |
| ---------- | -------------------------------------------- | ------------------------------------------------------------------------------------ |
| Zentachain | `0x6B58cfBEdCfe7C59d456c864c92257910A61D8FC` | [View](https://etherscan.io/address/0x6B58cfBEdCfe7C59d456c864c92257910A61D8FC#code) |
| StakeChain | `0xC00D35881e66E9eCC116De9696Ae12A18104B106` | [View](https://etherscan.io/address/0xC00D35881e66E9eCC116De9696Ae12A18104B106#code) |
| Zentanode  | `0x5a427e6aA609509C6EE089502a048f17B4895a9b` | [View](https://etherscan.io/address/0x5a427e6aA609509C6EE089502a048f17B4895a9b#code) |

***

## Overview

StakeChain is the staking smart contract that allows CHAIN token holders to stake their tokens in configurable pools and earn rewards. Rewards are minted directly to stakers — there is no pre-funded reward pool.

The contract is built on Solidity ^0.8.28 with OpenZeppelin v5.2.0 libraries and includes reentrancy protection, timestamp manipulation safeguards, and owner-controlled administration.

***

## Live Staking Pools

Three staking pools are currently active on mainnet:

| Pool | APR | Min Stake   | Max Stake    | Pool Size     | Claim Lock | Unstake Lock |
| ---- | --- | ----------- | ------------ | ------------- | ---------- | ------------ |
| 0    | 12% | 1,000 CHAIN | 10,000 CHAIN | 100,000 CHAIN | 1 day      | 7 days       |
| 1    | 16% | 2,000 CHAIN | 20,000 CHAIN | 200,000 CHAIN | 1 day      | 14 days      |
| 2    | 20% | 5,000 CHAIN | 50,000 CHAIN | 500,000 CHAIN | 1 day      | 28 days      |

Higher APR pools require larger minimum stakes and longer lock-up periods.

***

## Pool System

Each staking pool is an independent configuration with its own parameters:

| Parameter              | Description                                         |
| ---------------------- | --------------------------------------------------- |
| APR                    | Annual Percentage Rate in basis points (1600 = 16%) |
| Minimum Stake Amount   | Lowest amount a user can stake                      |
| Maximum Stake Per User | Cap on individual stakes                            |
| Pool Size Limit        | Maximum total tokens in the pool                    |
| Claim Time Lock        | Minimum time between reward claims (1 day)          |
| Unstake Time Lock      | Minimum time before withdrawal allowed              |

***

## Staking

### Standard Staking

Two-transaction process:

1. **Approve** — User approves the StakeChain contract to spend their CHAIN tokens
2. **Stake** — Call `stake(poolId, amount)` to deposit tokens into a pool

### Permit-Based Staking

Single-transaction process using ERC-2612 permit signatures:

* Call `stakeWithPermit(poolId, amount, deadline, v, r, s)`
* Combines approval and staking in one transaction
* Saves gas and improves UX

### Compound Staking

Users can add more tokens to an existing stake at any time:

* Previous rewards are preserved and continue accruing
* The unstake timer resets for the **entire** staked amount
* New rewards begin accruing on the combined amount

***

## Rewards

### Calculation

Rewards are calculated based on time elapsed and the pool's APR:

```
Annual Rewards = (Staked Amount × APR) / 10,000
Rewards Per Second = Annual Rewards / 31,536,000
Earned = Rewards Per Second × Time Elapsed
```

**Key details:**

* APR is stored in basis points (1200 = 12%, 1600 = 16%, 2000 = 20%)
* A 15-minute timestamp buffer is applied to prevent manipulation
* Rewards are **minted** to the user (inflationary), not drawn from a pool

### Claiming

* User calls `claimRewards(poolId)`
* Claim time lock (1 day) must have elapsed since last claim
* Pool must not be paused
* Rewards are minted directly to the user's wallet

***

## Unstaking

### Standard Unstake

* Unstake time lock must have elapsed since staking (7/14/28 days depending on pool)
* Returns full staked amount plus any unclaimed rewards
* Removes user from the pool

### Emergency Unstake

* Only available when emergency mode is enabled for the pool
* Bypasses time lock restrictions
* Returns staked amount plus accumulated rewards
* Emergency mode has a 24-hour toggle cooldown to prevent abuse

***

## Security

| Feature                 | Implementation                                       |
| ----------------------- | ---------------------------------------------------- |
| Reentrancy Protection   | `nonReentrant` modifier on all external calls        |
| No Contract Interaction | `noContracts` modifier blocks smart contract callers |
| APR Change Delay        | 48-hour delay prevents front-running                 |
| Timestamp Buffer        | 15-minute buffer reduces manipulation window         |
| Emergency Cooldown      | 24-hour cooldown on emergency toggle                 |
| Pause Mechanism         | Pools can be paused individually                     |
| Max Stake Limits        | Per-user cap enforcement per pool                    |

***

## Testing & Audit

| Metric               | Value                       |
| -------------------- | --------------------------- |
| Test Cases           | 152 passing (100% coverage) |
| Test Files           | 18 dedicated test files     |
| Security Rating      | 9.2 / 10                    |
| Code Quality         | 9.0 / 10                    |
| Gas Efficiency       | 8.5 / 10                    |
| Solidity Version     | ^0.8.28                     |
| OpenZeppelin Version | 5.2.0                       |
| Framework            | Hardhat + Chai              |

### Security Tests Include

* Access control enforcement
* Staking limits and pool size enforcement
* Timelock mechanism validation
* Emergency function testing
* Reward calculation precision and edge cases
* APR change protection (48-hour delay, front-running prevention)
* Timestamp manipulation protection
* Pool deletion safety (only when empty)
* Emergency toggle rate limiting
* Permit-based staking validation
* Deployment integrity verification

### Security Verification Matrix

| Check                    | Zentachain | StakeChain | Zentanode |
| ------------------------ | ---------- | ---------- | --------- |
| Reentrancy Protection    | Passed     | Passed     | Passed    |
| Access Control           | Passed     | Passed     | Passed    |
| Input Validation         | Passed     | Passed     | Passed    |
| Integer Overflow         | Passed     | Passed     | Passed    |
| Emergency Pause          | Passed     | Passed     | Passed    |
| Time Manipulation        | Passed     | Passed     | Passed    |
| Front-running Protection | Passed     | Passed     | Passed    |
| Rate Limiting            | Passed     | Passed     | Passed    |

***

## APR Changes

To prevent front-running:

1. Owner schedules an APR change via `scheduleAPRChange(poolId, newAPR)`
2. A 48-hour delay period begins
3. After the delay, anyone can call `applyPendingAPRChange(poolId)` to activate it
4. All changes are publicly visible on-chain

***

## View Functions

| Function                                     | Returns                                   |
| -------------------------------------------- | ----------------------------------------- |
| `getPoolInfo(id)`                            | Complete pool configuration               |
| `getUserStakeInfo(address, id)`              | User's staked amount, rewards, timestamps |
| `getUserRewards(address, poolId)`            | Accumulated unclaimed rewards             |
| `getUnstakeUnlockTimestamp(address, poolId)` | When unstake becomes available            |
| `getClaimUnlockTimestamp(address, poolId)`   | When next claim is available              |
| `getPoolStatus(poolId)`                      | Whether pool is paused                    |
| `getPoolEmergencyStatus(poolId)`             | Whether emergency mode is active          |
| `getMaximumStakeStatus(poolId)`              | Whether max stake is enforced             |
| `getPendingAPRChange(poolId)`                | Scheduled APR change details              |

***

## Token Contract

| Property     | Value                                        |
| ------------ | -------------------------------------------- |
| Name         | Zentachain                                   |
| Symbol       | CHAIN                                        |
| Standard     | ERC-20                                       |
| Decimals     | 12                                           |
| Total Supply | 5,000,000                                    |
| Features     | Burnable, Pausable, Permit (EIP-2612)        |
| Address      | `0x6B58cfBEdCfe7C59d456c864c92257910A61D8FC` |

StakeChain and Zentanode are both whitelisted as minters on the CHAIN token contract, allowing them to mint reward tokens directly.

***

## Zentanode Rewards Contract

A separate contract (`Zentanode.sol`) handles node operator rewards independently from StakeChain:

| Parameter         | Value                                        |
| ----------------- | -------------------------------------------- |
| Address           | `0x5a427e6aA609509C6EE089502a048f17B4895a9b` |
| Rewards Per Claim | 25 CHAIN (configurable, max 1,000)           |
| Claim Duration    | 31 days                                      |
| Slash Duration    | 15 days                                      |
| Daily Mint Limit  | 100,000 CHAIN                                |
| Access            | Allowlist-based                              |

Node operators are added to the allowlist and can claim rewards at regular intervals. The contract includes a slashing mechanism for misbehaving nodes and pro-rated partial rewards for operator removal.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.zentachain.io/chain-network/staking.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
