> For the complete documentation index, see [llms.txt](https://docs.citrea.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.citrea.xyz/developer-documentation/ctr-token/staking.md).

# xCTR Staking

xCTR is an upgradeable ERC-4626 vault with voting power (ERC20Votes). It is **non-transferable** — `transfer()` and `transferFrom()` revert. Standard ERC-4626 `withdraw()` and `redeem()` also revert; use the exit system instead.

## Specifications

| Property     | Value                               |
| ------------ | ----------------------------------- |
| Name         | Staked CTR                          |
| Symbol       | xCTR                                |
| Decimals     | 18                                  |
| Standard     | ERC-4626 + ERC20Votes (upgradeable) |
| Transferable | No                                  |
| Clock Mode   | Timestamp                           |

## Configuration

| Parameter               | Default    | Description                         |
| ----------------------- | ---------- | ----------------------------------- |
| `epochPeriod`           | 7 days     | Reward streaming period             |
| `minExitDuration`       | 3 days     | Minimum wait before exit completion |
| `maxExitDuration`       | 14 days    | Wait time for zero penalty          |
| `minExitPenaltyBps`     | 0 (0%)     | Penalty at max duration             |
| `maxExitPenaltyBps`     | 5000 (50%) | Penalty at min duration             |
| `instantExitPenaltyBps` | 5000 (50%) | Fixed instant exit penalty          |

All parameters are updatable by the contract owner.

## Staking

Deposit CTR to receive xCTR shares:

```solidity
// Approve first
ctr.approve(xCTR_ADDRESS, amount);

// Deposit CTR, receive xCTR
uint256 shares = xCTR.deposit(amount, receiver);

// Or specify shares
uint256 assets = xCTR.mint(shares, receiver);
```

Shares are calculated at the current exchange rate (`totalAssets / totalSupply`). As rewards accrue, each xCTR becomes redeemable for more CTR.

## Rewards

Rewards are streamed linearly over weekly epochs:

1. Rewards added via `addRewards(amount)` are queued for the **next** epoch
2. When a new epoch starts, queued rewards stream linearly over 7 days
3. Exit penalties are also added as rewards for remaining stakers

```solidity
// Add external rewards (requires approval)
ctr.approve(xCTR_ADDRESS, rewardAmount);
xCTR.addRewards(rewardAmount);
```

## Exit System

### Start Exit

Burns xCTR shares and locks the CTR value at the current exchange rate:

```solidity
uint256 exitId = xCTR.startExit(shares);
```

Each user can have multiple pending exits, each with its own ID and timeline. Exit parameters (penalty rates, durations) are **snapshotted** at start time — subsequent parameter changes don't affect existing exits.

### Complete Exit (after day 3)

Penalty decreases linearly from 50% → 0% over the exit window:

```solidity
xCTR.completeExit(exitId);
```

Penalty formula:

```
penaltyBps = minPenalty + (maxPenalty - minPenalty) × (maxDuration - elapsed) / (maxDuration - minDuration)
```

### Cancel Exit (before day 3)

Returns xCTR at the current exchange rate (may differ from original amount):

```solidity
xCTR.cancelExit(exitId);
```

### Instant Exit

Immediate withdrawal with a fixed 50% penalty:

```solidity
xCTR.instantExit(shares);
```

## Read Functions

```solidity
// xCTR balance
uint256 shares = xCTR.balanceOf(user);

// CTR value of shares
uint256 ctrValue = xCTR.convertToAssets(shares);

// Shares needed for a CTR amount
uint256 sharesNeeded = xCTR.convertToShares(ctrAmount);

// Total assets backing all xCTR
uint256 total = xCTR.totalAssets();

// Preview exit outcome
(uint256 assets, uint256 minDur, uint256 maxDur, uint256 minPen, uint256 maxPen) = xCTR.previewStartExit(shares);

// Preview completion at a given timestamp
(uint256 netAssets, uint256 penalty) = xCTR.previewCompleteExit(user, exitId, timestamp);

// Preview cancellation
(uint256 assets, uint256 sharesRestored) = xCTR.previewCancelExit(user, exitId);

// Penalty calculations
uint256 penalty = xCTR.getExitPenalty(assets, duration);
uint256 instantPenalty = xCTR.getInstantExitPenalty(assets);
```

## Voting

xCTR integrates ERC20Votes for governance. Voting power equals xCTR balance but must be delegated to activate:

```solidity
// Delegate to self
xCTR.delegate(msg.sender);

// Delegate to another address
xCTR.delegate(delegatee);

// Check voting power
uint256 votes = xCTR.getVotes(account);

// Historical voting power
uint256 pastVotes = xCTR.getPastVotes(account, timestamp);
```

## Events

| Event                                                    | Description                   |
| -------------------------------------------------------- | ----------------------------- |
| `RewardsAdded(sender, amount, isPenalty)`                | Rewards queued for next epoch |
| `ExitStarted(user, exitId, shares, assets)`              | Exit initiated                |
| `ExitCompleted(user, exitId, amount, penalty, duration)` | Exit finalized                |
| `ExitCancelled(user, exitId, assets, sharesRestored)`    | Exit cancelled, xCTR restored |
| `InstantExited(user, shares, amount, penalty)`           | Instant exit executed         |
