# stkAAVE Staking Flow
End-to-end execution flow for staking AAVE tokens to receive stkAAVE shares.
## Quick Reference
| Aspect | Details |
|--------|---------|
| **Entry Points** | `stake(to, amount)`, `stakeWithPermit(amount, deadline, v, r, s)`, `claimRewardsAndStake(to, amount)`, `claimRewardsAndStakeOnBehalf(from, to, amount)` |
| **Key Transformations** | [Assets → Shares via Exchange Rate](#amount-transformations) |
| **State Changes** | `_balances[to] += sharesToMint`, `_totalSupply += sharesToMint` |
| **Events Emitted** | `Staked`, `RewardsAccrued` (if applicable) |
---
## Flow Diagram
```mermaid
flowchart TD
%% Styling definitions
classDef validation fill:#ffcccc,stroke:#ff0000,stroke-width:2px
classDef transformation fill:#ccffcc,stroke:#00aa00,stroke-width:2px
classDef storage fill:#ccccff,stroke:#0000ff,stroke-width:2px
classDef event fill:#ffffcc,stroke:#aaaa00,stroke-width:2px
classDef error fill:#ff0000,stroke:#000000,color:#fff
%% Entry points
Entry1["stake(to, amount)"] --> CommonStake
Entry2["stakeWithPermit(amount, deadline, v, r, s)"] --> Permit["STAKED_TOKEN.permit
Gasless approval"] --> CommonStake
Entry3["claimRewardsAndStake(to, amount)"] --> ClaimStake["_claimRewardsAndStakeOnBehalf"]
Entry4["claimRewardsAndStakeOnBehalf(from, to, amount)"] --> ClaimStake
%% Main staking flow
CommonStake["_stake(from, to, amount)"] --> Validate1["VALIDATION
!inPostSlashingPeriod
amount != 0"]
class Validate1 validation
ClaimStake --> ValidateClaim["VALIDATION
REWARD_TOKEN == STAKED_TOKEN"]
class ValidateClaim validation
ValidateClaim --> UpdateRewardsClaim["Update unclaimed rewards
_updateCurrentUnclaimedRewards"]
UpdateRewardsClaim --> ClaimRewards["Claim rewards to contract
_claimRewards"]
ClaimRewards --> CommonStake
%% Staking process
Validate1 --> GetBalance["Get recipient balance
balanceOf(to)"]
subgraph RewardsUpdate ["1. Rewards Accrual"]
direction TB
UpdateUserAsset["_updateUserAssetInternal
Calculate accrued rewards"] --> CheckRewards{"Accrued > 0?"}
CheckRewards -->|Yes| StoreRewards["STORAGE UPDATE
stakerRewardsToClaim[to]
+= accrued"]
class StoreRewards storage
CheckRewards -->|No| SkipRewards[Continue]
StoreRewards --> RewardsEvent["EMIT
RewardsAccrued"]
class RewardsEvent event
end
GetBalance --> RewardsUpdate
SkipRewards --> CalcShares
RewardsEvent --> CalcShares
subgraph TokenFlow ["2. Token Flow & Share Minting"]
direction TB
CalcShares["TRANSFORMATION
sharesToMint =
previewStake(amount)"]
class CalcShares transformation
CalcShares --> Transfer["IERC20
safeTransferFrom
from -> contract"]
Transfer --> Mint["_mint(to,
sharesToMint.toUint104)"]
Mint --> UpdateStorage["STORAGE UPDATE
_balances[to] +=
sharesToMint
_totalSupply +=
sharesToMint"]
class UpdateStorage storage
end
%% Final event
UpdateStorage --> FinalEvent["EMIT
Staked"]
class FinalEvent event
%% Error annotations
%% CRITICAL: All validations must pass or transaction reverts
%% CRITICAL: Slashing period blocks new stakes
%% CRITICAL: Permit failures are silently ignored (try/catch)
```
---
## Step-by-Step Execution
### 1. Entry Points
#### 1a. Direct Stake
**File:** `StakedTokenV3.sol`
```solidity
function stake(address to, uint256 amount) external {
_stake(msg.sender, to, amount);
}
```
#### 1b. Stake with Permit (Gasless Approval)
**File:** `StakedTokenV3.sol`
```solidity
function stakeWithPermit(
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
try
IERC20WithPermit(address(STAKED_TOKEN)).permit(
msg.sender,
address(this),
amount,
deadline,
v,
r,
s
)
{
// do nothing
} catch (bytes memory) {
// do nothing - permit failure is silently ignored
}
_stake(msg.sender, msg.sender, amount);
}
```
**Note:** The permit call is wrapped in a try/catch block. If the permit fails (e.g., already used), the stake proceeds assuming existing approval.
#### 1c. Claim Rewards and Stake
**File:** `StakedAaveV3.sol`
```solidity
function claimRewardsAndStake(address to, uint256 amount) external returns (uint256) {
return _claimRewardsAndStakeOnBehalf(msg.sender, to, amount);
}
function claimRewardsAndStakeOnBehalf(
address from,
address to,
uint256 amount
) external onlyClaimHelper returns (uint256) {
return _claimRewardsAndStakeOnBehalf(from, to, amount);
}
```
### 2. Internal Staking Implementation
**File:** `StakedTokenV3.sol`
```solidity
function _stake(address from, address to, uint256 amount) internal {
// [VALIDATION] Prevent staking during slashing period
require(!inPostSlashingPeriod, 'SLASHING_ONGOING');
require(amount != 0, 'INVALID_ZERO_AMOUNT');
uint256 balanceOfTo = balanceOf(to);
// Update rewards for the recipient before stake
uint256 accruedRewards = _updateUserAssetInternal(
to,
address(this),
balanceOfTo,
totalSupply()
);
// Accumulate any accrued rewards to claimable balance
if (accruedRewards != 0) {
stakerRewardsToClaim[to] = stakerRewardsToClaim[to] + accruedRewards;
emit RewardsAccrued(to, accruedRewards);
}
// [TRANSFORMATION] Calculate shares to mint based on exchange rate
uint256 sharesToMint = previewStake(amount);
// Transfer staked tokens from user to contract
STAKED_TOKEN.safeTransferFrom(from, address(this), amount);
// Mint shares to recipient
_mint(to, sharesToMint.toUint104());
// [EVENT] Emit staking event
emit Staked(from, to, amount, sharesToMint);
}
```
### 3. Claim and Stake Implementation
**File:** `StakedAaveV3.sol`
```solidity
function _claimRewardsAndStakeOnBehalf(
address from,
address to,
uint256 amount
) internal returns (uint256) {
// [VALIDATION] Only works when reward token is same as staked token (AAVE)
require(REWARD_TOKEN == STAKED_TOKEN, 'REWARD_TOKEN_IS_NOT_STAKED_TOKEN');
// Update rewards calculation for the user
uint256 userUpdatedRewards = _updateCurrentUnclaimedRewards(
from,
balanceOf(from),
true
);
// Determine amount to claim (capped at available rewards)
uint256 amountToClaim = (amount > userUpdatedRewards)
? userUpdatedRewards
: amount;
if (amountToClaim != 0) {
// Claim rewards to this contract first
_claimRewards(from, address(this), amountToClaim);
// Stake the claimed rewards
_stake(address(this), to, amountToClaim);
}
return amountToClaim;
}
```
### 4. Preview and Exchange Rate Functions
**File:** `StakedTokenV3.sol`
```solidity
function previewStake(uint256 assets) public view returns (uint256) {
return (assets * _currentExchangeRate) / EXCHANGE_RATE_UNIT;
}
function getExchangeRate() public view returns (uint216) {
return _currentExchangeRate;
}
function previewRedeem(uint256 shares) public view returns (uint256) {
return (EXCHANGE_RATE_UNIT * shares) / _currentExchangeRate;
}
```
---
## Amount Transformations
### Assets → Shares Calculation
```
User Input (18 decimals)
↓
amount = 1000 * 10^18 // 1000 AAVE tokens
↓
_currentExchangeRate = 1.1 * 10^18 // Current exchange rate (18 decimals)
↓
sharesToMint = (amount * _currentExchangeRate) / EXCHANGE_RATE_UNIT
= (1000 * 10^18 * 1.1 * 10^18) / 10^18
= 1100 * 10^18
↓
_mint(to, sharesToMint)
```
### Exchange Rate Mechanics
The exchange rate represents the ratio of shares to underlying assets:
**Initial Rate:** 1:1 (1 share = 1 asset)
**After Rewards:** Rate increases as rewards are distributed to stakers
**After Slashing:** Rate decreases to reflect loss of underlying assets
**Exchange Rate Formula:**
```solidity
function _getExchangeRate(uint256 totalAssets, uint256 totalShares)
internal pure returns (uint216)
{
return (((totalShares * EXCHANGE_RATE_UNIT) + totalAssets - 1) / totalAssets);
}
```
**Key Points:**
- `EXCHANGE_RATE_UNIT` = 10^18 (18 decimal precision)
- Rounds **up** to ensure 100% backing of shares (favors the contract)
- Exchange rate increases when rewards are added to the pool
- Exchange rate decreases during slashing events
### Share-to-Asset Conversion
When redeeming, shares convert back to assets:
```
shares = 1100 * 10^18
↓
_currentExchangeRate = 1.1 * 10^18
↓
assets = (EXCHANGE_RATE_UNIT * shares) / _currentExchangeRate
= (10^18 * 1100 * 10^18) / (1.1 * 10^18)
= 1000 * 10^18
```
---
## Event Details
### Staked Event
Emitted whenever AAVE tokens are successfully staked.
```solidity
event Staked(
address indexed from, // Address providing the AAVE tokens
address indexed to, // Address receiving the stkAAVE shares
uint256 assets, // Amount of AAVE tokens staked
uint256 shares // Amount of stkAAVE shares minted
);
```
### RewardsAccrued Event
Emitted when pending rewards are calculated and stored during the staking process.
```solidity
event RewardsAccrued(
address indexed user, // User whose rewards accrued
uint256 amount // Amount of rewards accrued
);
```
### ExchangeRateChanged Event
Emitted when the exchange rate is updated (during slashing or return of funds).
```solidity
event ExchangeRateChanged(
uint216 exchangeRate // New exchange rate (18 decimal precision)
);
```
---
## Error Conditions
| Error | Condition | File |
|-------|-----------|------|
| `SLASHING_ONGOING` | `inPostSlashingPeriod == true` | StakedTokenV3.sol |
| `INVALID_ZERO_AMOUNT` | `amount == 0` | StakedTokenV3.sol |
| `REWARD_TOKEN_IS_NOT_STAKED_TOKEN` | `REWARD_TOKEN != STAKED_TOKEN` | StakedAaveV3.sol |
| `INVALID_BALANCE_ON_COOLDOWN` | User has no balance when calling cooldown | StakedTokenV3.sol |
| `INSUFFICIENT_COOLDOWN` | Cooldown period not yet complete | StakedTokenV3.sol |
| `UNSTAKE_WINDOW_FINISHED` | Redemption window has passed | StakedTokenV3.sol |
| `INVALID_ZERO_MAX_REDEEMABLE` | No redeemable balance available | StakedTokenV3.sol |
---
## Related Flows
- [Redeem Flow](./stk_aave_unstaking.md) - Unstaking stkAAVE to receive AAVE
- [Cooldown Flow](./stk_aave_unstaking.md) - Initiating the unstaking cooldown period
- [Rewards Claim Flow](./rewards_claiming.md) - Claiming staking rewards
- [Slashing Flow](./stk_aave_slashing.md) - Emergency slashing mechanism
---
## Source File Locations
```
contract_reference/aave/stkAAVE/rev_6.sol (StakedAaveV3 - main implementation)
├── stake(to, amount)
├── stakeWithPermit(amount, deadline, v, r, s)
├── claimRewardsAndStake(to, amount)
├── claimRewardsAndStakeOnBehalf(from, to, amount)
└── _claimRewardsAndStakeOnBehalf(from, to, amount)
StakedTokenV3 (inherited)
├── _stake(from, to, amount)
├── previewStake(assets)
├── previewRedeem(shares)
├── getExchangeRate()
└── _getExchangeRate(totalAssets, totalShares)
```