# Borrow & Repay
Source: https://docs.berachain.com/bend/guides/borrow-repay
Supply collateral, borrow HONEY, monitor LTV/LLTV, and repay to withdraw collateral on Bend.
Follow these steps to supply collateral, borrow \$HONEY, and repay to free your collateral.
## Requirements
* Wallet with collateral (WETH, WBERA, WBTC, etc.) listed on [Bend](https://bend.berachain.com/borrow)
* Native \$BERA for transaction fees
If you don't have collateral, swap on [Berachain Hub](https://hub.berachain.com/swap).
## Supply collateral and borrow
Supply $WBERA as collateral and borrow $HONEY.
Go to [Bend](https://bend.berachain.com/borrow) and connect your wallet.
Choose a market where you can supply collateral. This guide uses \$WBERA.
1. Select **Borrow**
2. Enter the \$WBERA amount to supply as collateral
3. Click **Review** to open the confirmation modal
1. Approve the market to spend your \$WBERA
2. Supply \$WBERA as collateral
You should see your supplied collateral and total value in the market.
1. Enter the \$HONEY amount to borrow
2. Keep borrow below the LTV limit to avoid liquidation
3. Click **Review** to confirm
Approve the borrow in your wallet.
You should see your borrowed \$HONEY and its value in the market.
Monitor your position. If your LTV exceeds the Liquidation LTV (LLTV), your position can be liquidated.
## Repay and withdraw
Repay $HONEY and withdraw your $WBERA collateral.
1. Select **Repay**
2. Enter the \$HONEY amount to repay
3. Enter the \$WBERA amount to withdraw
4. Click **Review** to open the confirmation modal
Confirm the repayment in your wallet.
You should see a zero balance after the repayment.
You have now supplied collateral, borrowed \$HONEY, and repaid to withdraw your collateral on Bend.
# Curator Application
Source: https://docs.berachain.com/bend/guides/curator-application
How curators apply to list a vault on the Bend UI; governance and application form.
A **Curator** sets a vault's strategy and risk (which markets, caps). To have your vault listed on the Bend UI, you apply through the curator application process.
For the Curator role, see [Curator](/bend/learn/curator).
## Process
Curators apply so their vault appears on the Bend UI. The process is run through governance and decided by the guardians. To apply, use the [curator application form](https://ufdx3v8g7qg.typeform.com/to/pg2P0ndW).
# Deposit & Withdraw
Source: https://docs.berachain.com/bend/guides/deposit-withdraw
Step-by-step: supply HONEY to a vault, stake for PoL rewards, unstake, and withdraw from Bend.
Follow these steps to deposit into and withdraw from a Bend vault.
## Requirements
* Wallet with \$HONEY
* Native \$BERA for transaction fees
If you don't have \$HONEY, get it on [Berachain Hub](https://hub.berachain.com/swap) or
[HoneySwap](https://honey.berachain.com).
## Depositing
Deposit \$HONEY to earn native lending yield and PoL reward yield.
Go to [Bend](https://bend.berachain.com/lend) and connect your wallet.
Choose a vault to deposit \$HONEY into.
1. Select **Supply**
2. Enter the \$HONEY amount to deposit
3. Choose **Stake** or **Supply & Stake** (recommended for PoL reward yield)
4. Click **Review** to open the confirmation modal
You can enable **Supply & Stake** later if you skip it now.
Your wallet will prompt you to:
1. Approve the vault to spend your \$HONEY
2. Supply \$HONEY to the vault
3. Receive receipt tokens
4. Stake the receipt tokens in a Berachain PoL Reward Vault for reward yield
You should see your deposited value (supplied and staked) and the receipt token amount.
With your deposit staked, you can claim PoL rewards.
## Withdrawing
Unstake and withdraw your \$HONEY.
In the vault you want to withdraw from:
1. Select **Withdraw**
2. Enter the \$HONEY amount to withdraw
If your balance shows 0, your deposit is staked. Unstake first, then withdraw.
Where your staked balance is shown, click **Unstake**.
Enter the amount to unstake and confirm.
After unstaking, your balance appears under **Deposited**. Withdraw from there.
1. Enter the \$HONEY amount to withdraw
2. Select **Withdraw**
Confirm the withdrawal in your wallet.
When complete, your \$HONEY is back in your wallet.
# Bundlers
Source: https://docs.berachain.com/bend/learn/bundlers
Bundler3: combine supply, borrow, swap, and other Bend actions into a single atomic transaction; gas savings.
Bundlers let you run multiple Bend actions in **one transaction**. You avoid multiple confirmations and pay gas once.
## Bundler implementations
**Bundler3** is Morpho's main bundler and is built into the Morpho UI so you can run complex flows with one action.
## Bundler3 capabilities
With Bundler3 you can:
1. **Combine workflows** — Supply, borrow, swap, and more in a single transaction
2. **Save gas** — One transaction instead of many
3. **Avoid partial failures** — If any step would fail, the whole transaction reverts
4. **Run advanced flows** — Actions that normally need several steps in one go
## Examples
* **One-click leverage**: Convert `$BERA` to `$WETH`, supply as collateral, and borrow `$HONEY` in one transaction
* **Refinancing**: Borrow from one market, repay another, and adjust collateral in one transaction
* **Rebalancing**: Withdraw, swap, and redeposit across markets without waiting for separate confirmations
# Curator
Source: https://docs.berachain.com/bend/learn/curator
Curator role: vault strategy, risk parameterization, market selection, caps, and timelock; distinct from Allocator.
A **Curator** sets a vault's strategy and risk: which markets it can use and exposure caps. Curators do not allocate day-to-day; that is the Allocator's role.
Curators choose which markets a vault can supply to and set supply caps per market. Their choices drive vault performance and risk. Most important Curator actions are **timelocked** so depositors can exit if they disagree.
## Responsibilities
* **Strategy**: Define the vault's investment thesis and which markets (or asset types) are allowed.
* **Risk**: Set and maintain exposure limits (caps) to protect depositors.
Timelocks on major changes give depositors time to react.
## Role system
Vault roles are separated:
* **Owner**: Top-level control; appoints Curator and Allocator; sets vault-wide settings (e.g. fees).
* **Curator**: Decides **what** is allowed—which markets and max exposure. Does not execute allocation.
* **Allocator**: Decides **how** to allocate within those limits (supply/withdraw queues, reallocations). Can be the `PublicAllocator` contract if the owner configures it.
Strategy and risk (Curator) stay separate from day-to-day allocation (Allocator).
# Flash Loans
Source: https://docs.berachain.com/bend/learn/flash-loans
Uncollateralized borrow-and-repay within one transaction; flashLoan flow, callback, and implementation steps.
Flash loans let you **borrow without collateral** as long as you repay in the same transaction.
## Flash loans in Bend
In Bend, flash loans:
* Require no collateral
* Must be repaid in the same transaction
* Execute in one block
* Are aimed at developers and advanced users
## How they work
The Morpho contract exposes `flashLoan` and uses a callback to complete the flow.
### Flow
1. **Initiate**: Your contract calls `morpho.flashLoan(token, amount, data)`.
2. **Receive**: Morpho transfers the requested amount to your contract.
3. **Callback**: Morpho calls `onMorphoFlashLoan(amount, data)` on your contract.
4. **Logic**: Your contract runs its logic (arbitrage, swap, etc.).
5. **Repay**: Your contract approves Morpho to pull back the borrowed amount.
6. **Complete**: Morpho pulls the funds. If repayment fails, the whole transaction reverts.
## Implementation
To use flash loans:
1. Implement a contract that conforms to `IMorphoFlashLoanCallback`.
2. Implement `onMorphoFlashLoan` with your logic.
3. In the callback, approve the Bend (Morpho) contract to pull the borrowed amount before returning.
## Use cases
* **Arbitrage**: Profit from price differences across protocols in one transaction
* **Collateral swap**: Replace one collateral type with another in one transaction
* **Self-liquidation**: Liquidate your own position to avoid third-party liquidation
* **Flash actions**: Combine several Bend operations in one transaction
## Security
* **Atomicity**: If the callback doesn't approve repayment, the transaction reverts.
* **Funds**: Don't leave funds in the flash loan contract after the callback.
* **Reentrancy**: Be careful calling external contracts inside the callback.
* **Gas**: Flash loan flows can use a lot of gas.
## Bend callbacks
Bend supports additional callbacks for richer flows:
* `IMorphoLiquidateCallback`: Liquidations
* `IMorphoRepayCallback`: Repayments
* `IMorphoSupplyCallback`: Supply
* `IMorphoSupplyCollateralCallback`: Supply collateral
You can combine supply, borrow, repay, and withdraw in a single transaction using these callbacks.
# Interest Rates
Source: https://docs.berachain.com/bend/learn/interest-rates
How supply and borrow rates are set by the IRM and utilization; AdaptiveCurveIRM and target utilization.
Interest rates on Bend are set by each market's **Interest Rate Model (IRM)**. If you're building a borrow integration, you need to show how rates work and how they affect positions.
## IRM role
Each Bend market has a single, immutable IRM. That contract computes the borrow rate from market conditions, mainly **utilization** (total borrows / total supply). Only IRMs approved by Bend governance can be used; the main one is **AdaptiveCurveIRM**.
### AdaptiveCurveIRM
AdaptiveCurveIRM targets **90% utilization**.
* **Below 90%**: Borrow rate falls to encourage borrowing.
* **Above 90%**: Borrow rate rises to encourage repayments and more supply.
That keeps markets capital-efficient while keeping enough liquidity for withdrawals. For formulas and behavior, see [Interest Rate Model](/bend/learn/irm).
### Finding the market rate
To get the rate at a given utilization (e.g. 80%) for a market:
* **Morpho Contract** - To retrieve IRM for market
* **IRM Contract** - Query rate at utilization point (e.g. 80%)
With the assumption that you will use one of the following markets:
| Market | MarketId |
| -------------- | -------------------------------------------------------------------- |
| wsRUSD / HONEY | `0x04d3b8b00c6f3b75481492b76139473e2368339ee58587df65684fdb9103984e` |
| WBTC / HONEY | `0x950962c1cf2591f15806e938bfde9b5f9fbbfcc5fb640030952c08b536f1f167` |
| sUSDe / HONEY | `0x1ba7904c73d337c39cb88b00180dffb215fc334a6ff47bbe829cd9ee2af00c97` |
| wgBERA / HONEY | `0x63c2a7c20192095c15d1d668ccce6912999b01ea60eeafcac66eca32015674dd` |
| WETH / HONEY | `0x1f05d324f604bd1654ec040311d2ac4f5820ecfd1801a3d19d2c6f09d4f7a614` |
| WBERA / HONEY | `0x147b032db82d765b9d971eac37c8176260dde0fe91f6a542f20cdd9ede1054df` |
| iBERA / HONEY | `0x594de722a090f8d0df41087c23e2187fb69d9cd6b7b425c6dd56ddc1cff545f0` |
#### Step 1 - Determine IRM contract
Go to [https://berascan.com/address/0x24147243f9c08d835C218Cda1e135f8dFD0517D0#readContract](https://berascan.com/address/0x24147243f9c08d835C218Cda1e135f8dFD0517D0#readContract) and enter one of the **MarketIds** from above in the `idToMarketParams` field.
The returned result should provide something similar to the following:
| Name | Type | Value |
| --------------- | ------- | ------------------------------------------ |
| loanToken | address | 0x0dE23153BC280dD95BE914ddafb5591aE877e067 |
| collateralToken | address | 0xC3aD1095c231bb5D25E7EB1Aa23de7A9439EA12c |
| oracle | address | 0xc76A0E60016dFd4B18Db71b6DaEF769bc8057a3d |
| irm | address | 0x1d5376e532CcF25b740270624111D665830E5dB9 |
| lltv | uint256 | 945000000000000000 |
Take note of the `irm` address and go to that address on [BeraScan](https://berascan.com/).
#### Step 2 - Determine Market Rate Target Utilization
Enter one of the **MarketIds** from above into the `rateAtTarget` field.
## How interest accrues on debt
For a borrower, the most important takeaway is that **interest is constantly accruing**, increasing their total debt over time. This directly impacts their position's health.
The process is as follows:
#### 1. Rate calculation
The IRM calculates the instantaneous `borrowRate` based on the market's current utilization.
#### 2. Interest accrual
This rate is applied to the borrower's debt continuously. The amount of interest accrued increases the `totalBorrowAssets` in the market and, proportionally, the asset value of each borrower's `borrowShares`.
#### 3. Impact on Health Factor
As the debt value increases due to accrued interest, the user's **LTV rises** and their **Health Factor falls**, even if collateral and asset prices remain stable.
Health Factor = (Collateral Value x LLTV) / (Initial Debt + Accrued Interest)
This is a critical concept to communicate to users: their position can become riskier over time simply from interest accrual.
## Onchain state and `accrueInterest`
The Bend contract does not update interest for every block to save gas. Instead, interest is calculated and applied only when a market interaction occurs via the `_accrueInterest` internal function. This function is triggered by actions like `borrow`, `repay`, `supply`, and `withdraw`.
When you fetch a user's position from the contract, the `totalBorrowAssets` value reflects the
state at the last interaction. To get the up-to-the-second debt value, you must account for the
interest accrued since the `lastUpdate` timestamp.
# Interest Rate Model
Source: https://docs.berachain.com/bend/learn/irm
AdaptiveCurveIRM: immutable IRM, target utilization, curve and adaptive mechanisms; supply and borrow rates.
In Bend, the interest rate you pay as a borrower is set by an **Interest Rate Model (IRM)** chosen when the market is created.
The only IRM used for Bend markets is the [AdaptiveCurveIRM](/bend/learn/interest-rates#the-adaptivecurveirm). It differs from typical pool IRMs in two ways:
1. **Immutable**: The model cannot be changed or upgraded. It must respond automatically to market conditions, including rates on other platforms.
2. **Higher target utilization**: Supplied assets in Bend are not used as collateral, so markets don't need to hold large buffers for liquidations. The protocol can target higher utilization and use gentler illiquidity penalties.
The AdaptiveCurveIRM keeps utilization near **90%**. In the short term it avoids utilization drifting too low or too high; over time the rate adapts to market conditions.
Two mechanisms work together:
* [The Curve Mechanism](#the-curve-mechanism)
* [The Adaptive Mechanism](#the-adaptive-mechanism)
## The Curve Mechanism
This mechanism resembles the interest rate curves commonly found in traditional lending protocols.
The curve is defined by the following features:
| Name | Description |
| -------------------------- | ----------------------------------------------------------- |
| Target Rate | $r_{90\%}$ (corresponding to a target utilization of `0.9`) |
| Fixed Steepness Parameters | c = 4 |
> Image provided by [Morpho Docs](https://docs.morpho.org/get-started/resources/contracts/irm#the-curve-mechanism)
Each time you (or any user) interact with the market—e.g. borrow or repay—utilization changes and the rate updates along the curve.
For instance, the following are sample utilization-to-rate relationships:
| Utilization | Rate |
| ----------- | ------------------- |
| 90% | $r_{90\%}$ |
| 100% | $4 \times r_{90\%}$ |
The Curve Mechanism is designed to respond to short-term fluctuations in utilization, helping maintain healthy market liquidity during periods of sudden borrowing or repayment.
## The Adaptive Mechanism
This mechanism continuously shifts the curve to adjust to market conditions over time.
The curve shifts over time so the rate adapts to market conditions even when no one is borrowing or repaying.
> Image provided by [Morpho Docs](https://docs.morpho.org/get-started/resources/contracts/irm#the-adaptive-mechanism)
The adaptive mechanism dynamically shifts the rate curve in response to changing market conditions, even during periods without user interaction.
The key value that moves the curve is $r_{90\%}$—the rate at the target utilization. This value gradually changes over time:
* If utilization rises above the target (90%), $r_{90\%}$ will steadily increase.
* If utilization falls below the target, $r_{90\%}$ will steadily decrease.
The pace at which $r_{90\%}$ moves is recalculated whenever the market is updated (such as through borrowing or repaying). The greater the gap between current and target utilization, the faster $r_{90\%}$ (and thus the whole curve) moves in the appropriate direction.
Example: if utilization stays at 100% for five days, $r_{90\%}$ can roughly double in that period (at maximum speed).
The values of some
[constants](https://github.com/morpho-org/morpho-blue-irm/blob/main/src/adaptive-curve-irm/libraries/ConstantsLib.sol)
are hardcoded into the code deployed on Berachain, such as `TARGET_UTILIZATION`,
`INITIAL_RATE_AT_TARGET`, etc.
## Formula breakdown
| Name | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| $u$ | *utilization* - total assets borrowed divided by total assets supplied |
| $t$ | *time* - the specific moment at which utilization and other parameters are evaluated |
| $u(t)$ | Ratio of total borrow over total supply at time |
| $u_{\text{target}} = 0.9$ | Constant target value for utilization (set to 0.9) that the model aims to maintain. |
| $\forall t$ | For all *time* |
| $e$ | *error* - Difference between the current utilization and the target utilization, divided by a normalization factor |
| $k_{\text{d}}$ | Constant that controls how sharply the interest rate increases when utilization exceeds the target |
| $H$ | The time step (in seconds) between two interest rate updates |
| $last$ | Most recent interaction time before or at a specific time |
| $speed(t)$ | Factor controlling how quickly the interest rate evolves based on utilization changes over time |
| $r$ | Borrow rate |
| $r_{\text{T}}$ | Rate at target - Interest rate corresponding to the target utilization, updated over time using the speed factors |
### Utilization
Utilization ($u(t)$) is the ratio of total borrowed assets to total supplied assets at time ($t$), with a constant utilization target ($u_{\text{target}} = 0.9$).
### Error
Error ($e(u)$) is the normalized difference between the current utilization ($u(t)$) and the target utilization ($u_{\text{target}}$), scaled so that the distance between $u_{\text{target}}$ and $u = 1$ equals the distance between $u_{\text{target}}$ and $u = 0$.
$$
\forall t, \quad
e(u) =
\begin{cases}
\dfrac{u(t) - u_{\text{target}}}{1 - u_{\text{target}}}, & \text{if } u(t) > u_{\text{target}} \\
\dfrac{u(t) - u_{\text{target}}}{u_{\text{target}}}, & \text{if } u(t) \le u_{\text{target}}
\end{cases}
$$
> Image provided by [Morpho Docs](https://docs.morpho.org/get-started/resources/contracts/irm#formal-description)
### Curve
**Curve ($curve(u)$)** determines the shape and sensitivity of the interest rate response to changes in utilization around the target, with different slopes below and above $u_{\text{target}}$ controlled by the constant $k_{\text{d}}$.
$$
\text{curve}(u) =
\begin{cases}
\left(1 - \dfrac{1}{k_d}\right) \cdot e(u) + 1, & \text{if } u \le u_{\text{target}} \\
\left(k_d - 1\right) \cdot e(u) + 1, & \text{if } u > u_{\text{target}}
\end{cases}
$$
with
$$
k_d = 4
$$
### History of interactions
**History of interactions ($H$)** represents the set of all past interaction times up to time ($t$), including the initial time ($0$). Noting that $t_{\text{i}}$ the time at which $i\text{th}$ interaction occurred.
$$
\forall t, \quad H(t) = \{0\} + \{t_i\}_{t_i < t}
$$
### Last interaction
**Last interaction ($last$)** represents the most recent interaction time before or at time ($t$).
$$
\forall t, \quad \text{last}(t) = \max(H(t))
$$
### Speed
**Speed factor ($speed$)** determines how fast the interest rate changes over time based on the error at the last interaction, scaled by ($k_\text{p}$).
$$
\forall t, \quad \text{speed}(t) = \exp\!\left(k_p \cdot e(u(\text{last}(t))) \cdot (t - \text{last}(t))\right), \quad \text{with } k_p = 50
$$
### Rate at target
**Rate at target ($r_{\text{target}}$)** represents the interest rate when utilization equals the target utilization, evolving over time based on the speed factor.
$$
\forall t > 0, \quad r_T(t) = r_T(\text{last}(t)) \cdot \text{speed}(t)
$$
At any time ($t$), the borrow rate ($r$) is given by the formula:
$$
r(t) = r_T(t) \cdot \text{curve}(u(t))
$$
## Calculations
APY is the annualized return for suppliers and cost for borrowers, with compounding. In Bend you use it to compare returns and costs across markets.
### Borrow APY
The Borrow APY is calculated using the following formula:
$$
\text{borrowAPY} = \left( e^{(\text{borrowRate} \times \text{secondsPerYear})} - 1 \right)
$$
Where:
* `borrowRate` is the borrow rate per second, as determined by the Interest Rate Model (IRM).
* `secondsPerYear` represents the total number of seconds in a year (31,536,000).
### Supply APY
The Supply APY is calculated considering the utilization and the fee. The formula is:
$$
\text{supplyAPY} = \text{borrowAPY} \times \text{utilization} \times (1 - \text{fee})
$$
Where:
* `fee` is the fee of the market on a per-market basis and portion of the interest paid by borrowers that is retained by the protocol. See [Yield & Fees](/bend/learn/yield-fees#fee-mechanism) for more details.
* `utilization` is calculated as:
$$
\text{utilization} = \frac{\text{totalBorrowAssets}}{\text{totalSupplyAssets}}
$$
## Constants
The values of the following constants are hardcoded into the [Morpho code deployed on Berachain](https://berascan.com/address/0x24147243f9c08d835C218Cda1e135f8dFD0517D0#code#F7#L1).
* WAD = Wei-based Decimal (WAD = 10^18, meaning 1 WAD = 1.0)
| Parameter | Description | Value |
| ------------------------ | ------------------------------------------------- | -------------------------- |
| `CURVE_STEEPNESS` | Curve steepness (scaled by WAD) | 4 |
| `ADJUSTMENT_SPEED` | Adjustment speed per second (scaled by WAD) | 50/# of seconds per year |
| `TARGET_UTILIZATION` | Target utilization (scaled by WAD) | 90% |
| `INITIAL_RATE_AT_TARGET` | Initial rate at target per second (scaled by WAD) | 4%/# of seconds per year |
| `MIN_RATE_AT_TARGET` | Minimum rate at target per second (scaled by WAD) | 0.1%/# of seconds per year |
| `MAX_RATE_AT_TARGET` | Maximum rate at target per second (scaled by WAD) | 200%/# of seconds per year |
# Liquidation
Source: https://docs.berachain.com/bend/learn/liquidation
When positions are liquidated, health factor and LLTV, liquidation process, and liquidator incentives.
Liquidation protects lenders by closing undercollateralized positions and keeping markets solvent. If you're integrating borrowing, you need to explain when and how liquidation works.
When your position becomes too risky, a **liquidator** can repay your debt and seize your collateral at a discount.
## When liquidation occurs
A position can be liquidated when its **health factor is 1 or below**—that is, when LTV meets or exceeds the market's **LLTV** (Liquidation Loan-to-Value).
**Debt value / Collateral value ≥ LLTV** → position is liquidatable.
Common causes:
* Collateral price falls
* Debt grows from accrued interest
## Liquidation process
Bend liquidations are not auctions. The first liquidator to act repays debt and receives collateral at a discount in one transaction.
1. **Unhealthy position**: A liquidator (often a bot) finds a position with health factor ≤ 1.
2. **Repay debt**: The liquidator calls `liquidate` on the Bend contract and repays some or all of the debt in the loan asset.
3. **Seize collateral**: The liquidator receives collateral worth the repaid amount times the **Liquidation Incentive Factor (LIF)**.
4. **Position updated**: The borrower's debt and collateral are reduced accordingly.
### Liquidation Incentive Factor (LIF)
The **LIF** is the bonus a liquidator earns. It is derived from the market's LLTV; higher LLTV means a smaller bonus to limit cascades.
For **LLTV 86%**, **LIF ≈ 1.05** (about a **5%** bonus on seized collateral). The full incentive goes to the liquidator; Bend does not take a fee.
Formula:
$$
\mathrm{LIF} = \min \left( \mathrm{maxLIF},\ \frac{1}{(1 - \beta) \times (1 - \mathrm{LLTV})} \right)
$$
* **LLTV**: Market liquidation threshold (e.g. 0.86)
* **β** = 0.3 (constant)
* **maxLIF** = 1.15 (15% cap)
### Example (LLTV 86%)
* **Before**: Debt **\$87,000**, collateral **\$100,000** → LTV 87% > 86% → liquidatable.
* **Liquidator** repays **\$87,000** and receives **\$87,000 × 1.05 = \$91,350** of collateral.
* **Borrower**: Debt cleared; **\$4,350** loss (collateral drop).
* **Liquidator**: **\$4,350** profit (minus gas).
### Bad debt
If collateral value falls below the debt (LTV > 100%), a liquidation may not cover the full loan. The shortfall is **bad debt** and is a loss to lenders in that market. Isolated markets and conservative LLTVs are designed to make this rare.
# Collateral, LTV & Health
Source: https://docs.berachain.com/bend/learn/ltv
Collateral and debt relationship, LTV formula, health factor, and liquidation thresholds in Bend markets.
When you borrow on Bend, your position's safety depends on your collateral, your debt, and the market's risk parameters. If you're building a lending integration, calculating and displaying these metrics clearly is critical.
## Collateral
**Collateral** is the asset you supply to a lending market to secure your loan. In a `$wBERA`/`$HONEY` market, `$wBERA` is the collateral. It guarantees that the protocol can recover funds if you default.
Your collateral is per market—not cross-margined. Supplying collateral does not generate yield.
## Loan-to-value (LTV)
LTV measures debt as a share of collateral value. Use it to see how close a position is to liquidation.
### How to calculate LTV
Use this formula for a position on Bend:
**LTV = (BORROWED\_AMOUNT / COLLATERAL\_VALUE\_IN\_LOAN\_TOKEN) x 100%**
Where:
* **BORROWED\_AMOUNT**: Borrowed assets (token base units)
* **COLLATERAL\_VALUE\_IN\_LOAN\_TOKEN**: Collateral value expressed in the loan token
Collateral value in loan token units:
**COLLATERAL\_VALUE\_IN\_LOAN\_TOKEN = (COLLATERAL\_AMOUNT x ORACLE\_PRICE) / ORACLE\_PRICE\_SCALE**
Where:
* **COLLATERAL\_AMOUNT**: Collateral supplied (token base units)
* **ORACLE\_PRICE**: Market oracle price (scaled by ORACLE\_PRICE\_SCALE)
* **ORACLE\_PRICE\_SCALE**: Protocol scaling factor for prices
### Liquidation LTV (LLTV)
**Liquidation Loan-to-Value (LLTV)** is the maximum LTV before a position can be liquidated. It is fixed per market and set at creation from a governance-approved list.
**If LTV ≥ LLTV, the position can be liquidated.**
Example: if a market's LLTV is 86%, your position is at risk once your LTV reaches or exceeds 86%.
### Health factor
Health factor shows how close a position is to liquidation:
**HEALTH\_FACTOR = (COLLATERAL\_VALUE\_IN\_LOAN\_TOKEN x LLTV) / BORROWED\_AMOUNT**
* **LLTV**: Market liquidation threshold (e.g. 0.86 for 86%), in WAD (10^18)
Health factor > 1.0 means the position is healthy. Below 1.0, it is eligible for liquidation.
## Example: LTV and health factor
Example with assumed values:
* Borrowed amount: 150 `$HONEY` (150 × 10^18 base units)
* Collateral amount: 100 \$WBERA (100 × 10^18 base units)
* Oracle price: 3 `$HONEY` per \$WBERA (3 × 10^18)
* Oracle price scale: 10^18
* LLTV: 86% (0.86 × 10^18 WAD)
### Step 1 — Collateral value in loan token
Collateral value = 300 `$HONEY`.
```javascript theme={null}
// All calculations use BigInt for precision
const ORACLE_PRICE_SCALE = 10n ** 18n;
const collateralValueInLoanToken = (collateralAmount * oraclePrice) / ORACLE_PRICE_SCALE;
// = (100n * 10n**18n * 3n * 10n**18n) / 10n**18n
// = 300n * 10n**18n (300 HONEY in base units)
```
### Step 2 — Current LTV
```javascript theme={null}
const WAD = 10n ** 18n;
const borrowedAmount = 150n * 10n ** 18n; // 150 HONEY
const collateralValueInLoanToken = 300n * 10n ** 18n; // 300 HONEY equivalent
const currentLTV = (borrowedAmount * WAD) / collateralValueInLoanToken;
// = (150 * 10^18 * 10^18) / (300 * 10^18)
// = 0.5 * 10^18 (representing 50%)
```
### Step 3 — Health factor
```javascript theme={null}
const lltv = 86n * 10n ** 16n; // 0.86 scaled to WAD
const healthFactor = (collateralValueInLoanToken * lltv) / borrowedAmount;
// = (300 * 10^18 * 0.86 * 10^18) / (150 * 10^18)
// = 1.72 * 10^18 (scaled by WAD)
const healthFactorDisplay = Number(healthFactor) / Number(WAD);
// = 1.72
```
Health factor = 1.72, so the position is healthy with a 72% buffer before liquidation.
### Summary
| Metric | Value |
| ------------------ | ------- |
| Current LTV | 50.00% |
| Max LTV (LLTV) | 86.00% |
| Health Factor | 1.72 |
| Status | Healthy |
| Liquidation buffer | 36.00% |
\$WBERA (18 decimals, 3 `$HONEY` each) collateral against a `$HONEY` (18 decimals) loan, using WAD scaling for on-chain math.
## Oracles
LTV and health factor depend on the **oracle price**.
* **Dynamic pricing**: The oracle gives the current exchange rate between collateral and loan assets.
* **Oracle design**: A market's oracle can combine several feeds or other on-chain data.
* **Risk**: Your LTV and health factor are only as reliable as the oracle. Latency, inaccuracy, or manipulation affect positions.
When you show market data, also show which oracle is used. For details, see [Oracle](/bend/learn/oracle).
# Market
Source: https://docs.berachain.com/bend/learn/market
Lending pool primitive: collateral/loan pairing, supply and borrow positions, risk parameters, and market creation.
A **market** is a lending pool that pairs one collateral asset with one loan asset. Lenders and borrowers interact in that single venue. Bend uses [Morpho Market V1](https://docs.morpho.org/learn/concepts/market/#what-is-a-morpho-market-v1) as its base.
## Overview
In a market you can:
* **Lend assets** and earn interest on deposits
* **Borrow assets** by posting collateral
* **Trade positions** through market mechanisms
Each market has its own parameters and runs independently. Risk stays inside that market and does not spread across the protocol.
Open [Bend markets](https://bend.berachain.com/borrow) to lend or borrow.
## Key characteristics
### Isolated risk
* Each market operates independently
* Risks are contained within individual markets
* No cross-market contamination of losses
### Immutable parameters
* Market rules are set at creation and cannot be changed
* Provides predictable behavior for participants
* Eliminates systematic risks from parameter changes
### Asset pairing
* One collateral asset paired with one loan asset
* Clear separation of lending and borrowing activities
* Simplified risk assessment and management
## Market components
### Core assets
1. **Collateral Asset**: The asset users deposit to borrow against
2. **Loan Asset**: The asset users can borrow from the market
### Market parameters
1. [**Loan-to-Value (LTV) Ratio**](/bend/learn/ltv): Value of loan against collateral
2. **Liquidation Loan-to-Value (LLTV)**: The LTV at which positions become eligible for liquidation
3. [**Interest Rate Model**](/bend/learn/irm): Formula determining borrowing costs and lending yields
4. [**Oracle**](/bend/learn/oracle): Price feed for collateral valuation
5. **Market Cap**: Maximum capacity for lending and borrowing
## Market operations
### For lenders
You cannot supply directly to a market. You supply to a [Vault](/bend/learn/vault) managed by a
[Curator](/bend/learn/curator), which allocates assets across markets.
* **Supply**: Deposit loan assets to earn interest
* **Withdraw**: Remove supplied assets and accrued interest
### For borrowers
* **Borrow**: Take out loans against deposited collateral
* **Repay**: Return borrowed assets to reclaim collateral
* **Manage position**: Monitor health factor and add or remove collateral
### For liquidators
* **Liquidate**: Repay debt in exchange for discounted collateral
* **Maintain market health**: Keep borrowers adequately collateralized
## Market creation
### Permissionless market creation
Bend allows permissionless market creation. You can create isolated markets, each defined by the five key parameters above.
Traditional lending platforms often:
* Require governance approval to list assets or change parameters
* Use a single lending pool, so risk is shared across the protocol
On Bend, each market has immutable parameters set at creation. The loan-to-value (LTV) ratio and interest rate model must come from governance-approved options. This keeps risk isolated, supports new assets quickly, and keeps behavior predictable.
To list a market on the Bend UI, submit a vault whitelist proposal (including allocation) via the
[curator application form](https://ufdx3v8g7qg.typeform.com/to/pg2P0ndW).
# Oracle
Source: https://docs.berachain.com/bend/learn/oracle
Price feeds for Bend markets; oracle-agnostic design, compatible types, and how to query price data.
Oracles supply price data to the chain. In Bend, a market's oracle answers: "How many USD is 1 unit of this collateral worth?" so the protocol can value collateral, set borrowing capacity, and trigger liquidations.
## Oracles in Bend markets
Oracles are used to:
* Value collateral
* Compute borrowing capacity
* Trigger liquidations when positions are undercollateralized
* Support interest rate logic
## Bend implementation
Bend is **oracle-agnostic**: each market specifies its oracle in its parameters. Market creators choose the feed that fits the asset (e.g. Chainlink, Redstone, API3, Pyth, Chronicle, or a custom implementation).
## Compatible oracle types
* **Price feed oracles**: External feeds for exchange rates (Chainlink, Redstone, API3, Pyth, Chronicle).
* **Exchange rate oracles**: For wrapped or rebasing tokens with deterministic rates (e.g. wstETH/stETH).
* **Fixed-price oracles**: For assets with a known peg (e.g. stablecoins).
## Getting price data
Steps to get the current price of a collateral (e.g. \$WBERA).
### 1. Get market ID
Find the Market ID on [Berachain Bend](https://bend.berachain.com/borrow).
### 2. Find oracle address
In [BeraScan](https://berascan.com/) look up the Morpho (Vault) address and open it:
```
# Morpho (Vault)
0x24147243f9c08d835C218Cda1e135f8dFD0517D0
```
### 3. Get base feed address
In the oracle contract, read **BASE\_FEED\_1** and open the linked address.
### 4. Get latest answer
In the base feed contract (or its proxy), call `latestAnswer`.
The value uses **8 decimals**. For example, `202675951` means **2.02675951 USD** (divide by `100000000`).
```
# $BERA/$WBERA price
2.02675951 USD
```
## Oracle security
Oracle choice is critical and **immutable** for a Bend market. Before using a market:
* Verify the oracle implementation
* Understand the price sources
* Consider manipulation or failure modes
# Introduction
Source: https://docs.berachain.com/bend/learn/overview
Decentralized lending on Berachain. Get started with guides and concepts.
Bend is the native lending protocol on Berachain—Morpho-based vaults and markets with Proof-of-Liquidity. Lend, borrow, and earn.
## Get started
How Bend works, key features, use cases, and getting started.
Supply assets to a vault and optionally stake for PoL rewards.
Supply collateral and borrow; monitor LTV to avoid liquidation.
How lending markets and vaults work on Bend.
Developer resources: contracts, markets, and onchain tooling.
# Public Allocator
Source: https://docs.berachain.com/bend/learn/public-allocator
Just-in-time liquidity reallocation between Bend markets; how borrowers get liquidity from multiple vault markets.
Isolated markets in Morpho/Bend contain risk but can **fragment liquidity** across many pools. You might want to borrow from a market that doesn't have enough supply. The **Public Allocator** fixes this: it reallocates a vault's assets between markets on demand so your borrow can succeed.
The Public Allocator is a smart contract that routes liquidity and moves assets between markets when a borrow needs them.
The Vault Owner must whitelist the Public Allocator contract for it to operate.
## Overview
The Public Allocator is callable by anyone. It can move a vault's idle or underused supply into the market where a borrower needs liquidity, at the time of the borrow. For you as a borrower, many small pools behave like one deep pool, with isolated risk preserved.
## Borrower flow
Example: you want to borrow 1,000 WETH from the wstETH/WETH market, but that market only has 200 WETH.
1. **Borrow request**: You (or your app) initiate the borrow. The system sees a 800 WETH shortfall.
2. **Allocator runs**: The Public Allocator is called. It finds 800 WETH in other markets where the same vault has supply (e.g. idle or an underused rETH/WETH market).
3. **Reallocate**: The Allocator runs `reallocate`, moving 800 WETH into the wstETH/WETH market.
4. **Borrow**: The market now has 1,000 WETH; your borrow completes.
With a **Bundler**, reallocation and borrow happen in **one transaction**. You get deep liquidity in a single step.
## Curator controls: flow caps
Curators limit how much the Public Allocator can move:
* **maxIn**: Max assets the Allocator can move **into** a market.
* **maxOut**: Max assets it can move **out of** a market.
So liquidity stays flexible but within the vault's risk parameters.
# Rewards for Lenders
Source: https://docs.berachain.com/bend/learn/rewards-for-lenders
Vault APY, native lending yield, and PoL reward yield from staking vault shares.
Bend offers additional yield on Berachain's native stablecoin \[$HONEY](/general/tokens/honey). Each [Vault](/bend/learn/vault) is managed by [Curators](/bend/learn/curator), who choose which [Markets](/bend/learn/market) supply $HONEY as a loan for specific collateral ($WETH, $WBERA, \$WBTC, etc.).
## Vault APY yields
A vault may offer one or both of:
1. **[Native APY](#native-apy)** — Yield from lending \$HONEY in the vault's markets.
2. **[PoL reward yield](#pol-reward-yield)** — Yield from staking vault shares in an eligible [Proof-of-Liquidity Reward Vault](/general/proof-of-liquidity/reward-vaults).
See the [Deposit & Withdraw Guide](/bend/guides/deposit-withdraw) to deposit.
### Native APY
Native APY comes from lending $HONEY to the collateral types the vault's Curator has enabled. APY depends on borrow rates and utilization—how much of the supplied $HONEY is borrowed.
Each vault shows its allocation and supply yield.
### PoL reward yield
PoL reward yield comes from [validators directing Reward Vault emissions](/general/proof-of-liquidity/block-rewards) to a Bend vault's whitelisted Reward Vault. When you supply \$HONEY to a vault, you receive receipt tokens (shares). Stake those receipt tokens in a whitelisted Reward Vault to become eligible for reward claims.
You must stake the receipt tokens and claim rewards to realize this yield. Rewards are paid as \$WBERA.
# Vaults
Source: https://docs.berachain.com/bend/learn/vault
Yield-generating vault structure, curator and allocator roles, supply/withdraw queues, and risk considerations.
Vaults are yield-generating contracts that pool deposits and allocate them across strategies. On Berachain, they give you a simple way to earn from lending while keeping control of your assets.
## Overview
Vaults pool your deposits and allocate them across lending markets. You receive vault shares (ERC-4626) representing your share of the underlying assets and yield.
## Benefits
### Curated risk profiles
You choose vaults that match your goals and risk tolerance. Unlike a single mixed pool, vaults expose you only to the strategies and markets the Curator has selected.
### Permissionless infrastructure
Anyone (individual, DAO, or protocol) can create and manage vaults with different risk parameters and strategies, giving you many options.
### Non-custodial architecture
You keep full control of your position. Allocations and vault logic are on-chain and transparent.
### Automated yield generation
Vaults reinvest yield and rebalance within their strategy so you don't have to manage positions manually.
## How vaults work
### Curation and market selection
The **Curator** sets risk boundaries: which Morpho V1 markets the vault can use and a supply cap per market. All changes to these settings go through a **timelock**.
### Capital allocation
The **Allocator** manages day-to-day allocation. They maintain:
1. **Supply queue**: Order in which new deposits are allocated to enabled markets.
2. **Withdraw queue**: Order in which capital is pulled from markets to satisfy redemptions.
The Allocator can also **reallocate** between enabled markets to improve utilization and yield.
### Deposits and withdrawals
When you deposit, the vault allocates assets according to the supply queue and market caps. When you withdraw, it sources liquidity from markets in the order of the withdraw queue.
## Roles and responsibilities
* **Owner**: Top authority; appoints Curator and Allocator; sets vault-wide settings (e.g. fees).
* **Curator**: Selects which Morpho V1 markets the vault can use and sets supply caps. Changes are timelocked.
* **Allocator**: Manages supply and withdraw queues and reallocates among approved markets.
* **Guardian**: Can revoke pending timelocked actions from Owner or Curator as a safety check.
## Risk considerations
When integrating vaults, help users understand:
### Smart contract risk
* **Vault contract**: Bugs could cause loss.
* **Underlying protocols**: Morpho markets the vault uses have their own risk.
* **Upgradeability**: Changes to vault or protocol contracts can introduce new risk.
### Market risk
* **Asset price**: Underlying assets can lose value.
* **Interest rates**: Returns vary as market rates change.
* **Liquidity**: Withdrawals may be delayed when demand is high.
### Curator risk
* **Strategy**: Poor Curator choices can hurt performance.
* **Concentration**: Heavy allocation to riskier markets increases loss potential.
* **Governance**: Incompetent or malicious Curators can create operational risk.
### Operational risk
* **Oracle**: Price feed manipulation or outages can affect the vault.
* **Liquidation**: Leveraged strategies may face liquidation in underlying markets.
* **Regulatory**: Regulation may affect DeFi availability or operation.
# What Is Bend?
Source: https://docs.berachain.com/bend/learn/what-is-bend
Morpho-based lending and borrowing with native Proof-of-Liquidity on Berachain; vaults, markets, and key features.
**Bend** is a decentralized lending protocol forked from Morpho. It enables efficient lending and borrowing with native Proof-of-Liquidity (PoL) on Berachain. As a lending primitive, Bend allows permissionless creation of immutable, efficient lending markets.
Bend inherits the core architecture from **Morpho v1** vaults and markets—familiar, battle-tested infrastructure—and adds Berachain's Proof-of-Liquidity consensus.
Use Bend at [bend.berachain.com](https://bend.berachain.com).
## How it works
Bend acts as a lending primitive layer. Smart contracts create immutable, efficient lending markets. The protocol supports:
* **Lending**: Deposit assets to earn interest and PoL rewards
* **Borrowing**: Borrow assets by providing collateral
* **Market creation**: Permissionless creation of new lending markets
* **Interest rates**: Market-driven rates based on supply and demand
## Key features
* **Collateralization**: Provide collateral to borrow assets
* **Risk protection**: Liquidation and loan-to-value ratios protect the protocol
* **Interest accrual**: Dynamic rates based on market conditions
* **Open participation**: Anyone can lend or borrow
* **Non-custodial**: You keep ownership of your assets
* **Vault system**: Morpho v1 vault architecture
* **Native PoL**: Berachain Proof-of-Liquidity rewards lending to borrowers
## Use cases
* **Lending**: Earn interest on deposited assets
* **Borrowing**: Access liquidity while keeping asset exposure
* **DeFi integration**: Build lending into other applications
* **Looping**: Leveraged exposure on yields via looping strategies
## Getting started
Go to [Bend](https://bend.berachain.com), connect your wallet, then choose an action:
* **Lend**: [Deposit & Withdraw](/bend/guides/deposit-withdraw) — supply `$HONEY` to a vault and optionally stake for PoL rewards.
* **Borrow**: [Borrow & Repay](/bend/guides/borrow-repay) — supply collateral and borrow `$HONEY`; monitor LTV to avoid liquidation.
If you use Morpho, Bend will feel familiar and adds Berachain Proof-of-Liquidity and ecosystem benefits. See the **Guides** section and start with [Borrow & Repay](/bend/guides/borrow-repay).
# Yield & Fees
Source: https://docs.berachain.com/bend/learn/yield-fees
How vault yield is generated from lending markets and how fees are applied; share price and depositor interest.
Understanding how vault yield is generated and how fees are applied helps you build accurate earn products and set clear user expectations.
## Yield generation
Bend vaults earn yield by supplying capital to lending markets on Berachain.
### Yield sources
1. **Borrower interest**: Borrowers in underlying markets pay interest.
2. **Market distribution**: Interest is distributed to lenders in each market by supplied share.
3. **Vault collection**: Vaults collect interest as lenders across multiple markets.
4. **Share price increase**: Total vault assets grow, so share price rises.
5. **Depositor interest**: You earn the interest paid by borrowers as your share value increases.
## Fee mechanism
Bend vaults charge **performance fees** and **platform fees**. Both are taken as a share of the native lending yield.
Yield from Proof-of-Liquidity reward emissions is not subject to any fee.
### Platform fee
Charged at the market level and retained by the Berachain Foundation.
### Performance fee
Charged at the vault level and split between the foundation (0%)\_ and the curator (100%)\_.
> \*Values are subject to change.
# Add Token Metadata
Source: https://docs.berachain.com/bex/guides/add-token
Submit token logo, CoinGecko ID, and metadata via the Berachain metadata repo.
Token metadata is maintained on a Berachain token list. This list is used to update information such as:
* Token logo
* CoinGecko ID
* Price feeds
* Tags
You can submit a pull request to the [Metadata](https://github.com/berachain/metadata) repository to update these values. dApps that use the token list, such as BEX, display your token's information and logo.
Review the [Metadata
CONTRIBUTING.md](https://github.com/berachain/metadata/blob/main/CONTRIBUTING.md) for full details
on contributing.
# Fees
Source: https://docs.berachain.com/bex/guides/fees
How trading fees are split between LPs and the protocol and compounded in pools.
Fees are collected on every trade conducted on BEX. A portion of these fees goes to 1) liquidity providers (LPs), and 2) the protocol.
## Fee distribution
Trading fees for LPs are directly compounded inside the pool so that you don't need to perform a separate claim transaction. For example, if token prices within your pool are unchanged between deposit and withdrawal, but there have been trades in the interim, you see a higher balance upon withdrawal due to the accumulation of fees.
The portion of fees going to LPs is set at the time of pool creation, starting from 0.01% for stable pools, and ranging up to 0.3%, 0.5%, and 1% for weighted pools. The lower fee tiers are generally more appropriate for stable pairings (e.g., stablecoins and blue-chip assets), while the higher fee tiers may be more appropriate for exotic assets.
Trading fees in BEX are split 50/50 between LPs and the protocol. Protocol fees are collected in the `ProtocolFeesCollector` contract.
# Liquidity Provision
Source: https://docs.berachain.com/bex/guides/liquidity
Deposit tokens, mint LP positions, and manage or withdraw liquidity.
You can provide liquidity to BEX by committing tokens to a specific pool. *Liquidity providers* (LPs) earn a yield on their capital generated by the swap fees paid into the pool. In return for these fees and other potential benefits, LPs must deal with the fact that the collateral they deposit is not fixed. LP positions continuously rebalance between the two tokens in the pair as swaps occur in the pool.
This rebalancing can impose a cost on LPs in the form of *impermanent loss* (IL), which is the difference between the value of the rebalanced position and an equivalent static portfolio of the two assets. The larger the price ratio of the assets in the pool moves from when you first deposited, the larger the IL cost you experience.
LPs on BEX provide full-range liquidity, meaning that your liquidity is always active at every possible price point in the pool.
## Adding liquidity
*Adding Liquidity* is the process of providing the two constituent tokens of a liquidity pool to either 1) create a new LP position, or 2) commit additional capital to a pre-existing LP position.
When adding liquidity to an LP position, you have the following parameters to choose from:
* The token pair to provide liquidity for
* Deposit quantity, which can be fixed in terms of either side of the pair
* Maximum slippage (only applies when providing unbalanced amounts that require rebalancing) and transaction deadline
## Managing LP positions
View your existing LP positions on **Bera Hub**.
To remove liquidity from a specific position, you can withdraw your LP tokens. This removes the liquidity from the pool and returns the underlying collateral (based on the current token prices) to you.
# Pool Configuration
Source: https://docs.berachain.com/bex/guides/pool-configuration
Stable and weighted pool parameters, fee tiers, and example configurations.
BEX offers two main types of pools, each optimized for different trading scenarios: stable pools and weighted composable pools.
## Stable pool configuration
Stable pools are specifically designed for tokens that maintain similar values, such as stablecoins or wrapped versions of the same asset. They use specialized mathematics to provide optimal pricing and minimal slippage.
### Token selection
* Maximum of 5 tokens per pool
* All tokens must have identical decimals
* Tokens should maintain similar prices (e.g., USDC/USDT/DAI)
### Swap fees
* Range: 0.01% to 0.1%
* Recommended: 0.01% - 0.05% for highly stable pairs
* Higher fees may be suitable for less stable pairs
### Initial liquidity guidelines
* Add similar amounts of each token
* Maintain balanced ratios (e.g., 1:1 for stablecoin pairs)
If the values are significantly different, you may see an error warning about price impact.
## Weighted composable pool configuration
Weighted pools offer more flexibility and are suitable for tokens with different values and volatility profiles. They allow precise control over token weights and support up to 8 tokens.
### Token selection (weighted)
* Maximum of 8 tokens per pool
* No decimal restrictions
* Can mix different types of tokens
### Weight distribution
* Range: 1% to 99% per token
* Total weights must equal 100%
### Swap fees (weighted)
* 0.3%: Standard fee (recommended for most pairs)
* 0.5%: Medium fee (less liquid tokens)
* 1.0%: High fee (exotic or volatile pairs)
### Pool naming convention
Pool names and symbols are automatically generated based on:
* Included tokens
* Weight distribution
* Pool type
Example: `33WBERA-33STGUSDC-33WETH-WEIGHTED`
You can also customize the name and symbol.
## Common configuration scenarios
### Stablecoin pool example
* Pool type: Stable
* Tokens: USDC, USDT, DAI
* Fee: 0.02%
* Initial liquidity: Equal amounts (e.g., \$10,000 each)
### Weighted pool example
* Pool type: Weighted
* Tokens: WETH (40%), WBTC (40%), HONEY (20%)
* Fee: 0.5%
* Initial liquidity: Proportional to weights
# Pool Creation
Source: https://docs.berachain.com/bex/guides/pool-creation
Create a new pool, set initial ratio, and add liquidity; token burn requirement.
On BEX, you can initialize a liquidity pool for an arbitrary pair of tokens. When you initialize the pool, you can select the starting price ratio between the two tokens.
You can create new BEX liquidity pools here: [https://bex.berachain.com/pools/create](https://bex.berachain.com/pools/create)
To prevent spam and ensure the creation of meaningful pools, BEX requires you to permanently burn a small, economically insignificant quantity of both tokens when initializing a new pool.
## Pool initialization steps
Navigate to the "Pool" section of the BEX app and select "Create a Pool".
Select the two tokens you wish to create a pool for.Specify the pool fees, collected on each swap.
Set initial pool token ratios and amount of liquidity to add.
Confirm the pool creation transaction.
## Selecting the ratio
During pool creation, the price/ratio of the tokens is set based on amount of "quote" tokens per "base" token. Using the above scenario as an example, and assuming that the price of `$WETH` is $3000, the initial price of `$HONEY`is set from the initial`3000\` ratio, that is:
* Each `$WETH` is worth `3000` `$HONEY`
* Each `$HONEY` is worth `0.000333` `$WETH`
* The initial price of `$HONEY` is `$3000 * 0.000333 = $1`
If there is a known price to both tokens, you should try to set the initial price ratio to match the known prices. If the price ratio is set too high or too low, arbitrageurs will quickly correct the price to match the market.
# BEX and Proof of Liquidity
Source: https://docs.berachain.com/bex/guides/proof-of-liquidity
Earn PoL rewards by providing liquidity and staking LP tokens in reward vaults.
Proof of liquidity (PoL) rewards liquidity contributions through validator-allocated Reward Vault emissions. As the native dApp powering trading liquidity in the Berachain ecosystem, many BEX pools are eligible for PoL rewards.
## Earning rewards
The process for earning PoL rewards through BEX involves these steps:
Deposit liquidity into your chosen BEX pool.
After providing liquidity, you receive LP tokens. Stake these tokens in the pool's staking
contract.
View and manage your staked positions in reward vaults on [Bera Hub](https://hub.berachain.com/vaults) (Vaults section).
## Whitelisting pools for PoL rewards
If you are a project that uses BEX for your tokens' liquidity, you can apply to have your pool(s) whitelisted for Reward Vault emissions. This process runs through [Berachain governance](/general/governance/reward-vault-governance).
# Swaps
Source: https://docs.berachain.com/bex/guides/swaps
Single-hop and multi-hop swaps, routing, and slippage settings.
BEX lets you exchange one token for another when liquidity exists for the pair. Use the BEX interface on Bera Hub (navbar) for swaps.
## Single-hop and multi-hop swaps
If adequate liquidity exists in a given pair, BEX most likely executes the swap as a *single hop*. Single-hop swaps incur less exchange and gas fees, making them the preferred method.
However, when a trade between the input and output token does not provide the best overall price (or the trading pair does not exist as a single pool), BEX can perform a *multi-hop swap*. Multi-hop swaps involve chaining swaps across multiple pools to find the best overall price. The process of finding a cost-efficient multi-hop path is called *routing*.
### Intermediate tokens
In a multi-hop swap, *intermediate tokens* are the tokens held temporarily when swapping through a sequence of pairs. Intermediate tokens in BEX are never transferred because settlement occurs based on the net debit against the entire BEX protocol.
## Swap parameters
When initiating a swap, you can define the following parameters:
* **Quantity**: The amount of tokens to swap, which can be specified as either a fixed input quantity or a fixed output quantity.
* **Slippage**: The maximum acceptable difference between the expected and actual price of the trade. Slippage is expressed as a percentage and represents the worst-case price impact you are willing to accept. If the actual price impact exceeds this threshold, the swap transaction will revert.
# Decentralized Exchanges
Source: https://docs.berachain.com/bex/learn/dex
How DEXs and AMMs work and how BEX uses Balancer V2-style architecture.
A **decentralized exchange (DEX)** is a cryptocurrency exchange that operates without a central authority or intermediary. Unlike centralized exchanges (CEXs) where a company controls your funds and order matching, DEXs enable peer-to-peer trading directly on the blockchain through smart contracts.
Most DEXs today use an **automated market maker (AMM)** model, where pricing and liquidity are managed algorithmically rather than through traditional order books. While some DEXs may use other models like order book aggregation, AMMs have become the dominant architecture for decentralized exchanges due to their simplicity, efficiency, and continuous liquidity provision.
## Automated market makers (AMMs)
**Automated market makers (AMMs)** are a type of decentralized exchange protocol that uses mathematical formulas to determine asset prices, rather than traditional order books where buyers and sellers place orders.
In an AMM, liquidity is provided by **liquidity providers (LPs)** who deposit pairs of tokens into pools. Swaps occur against these pools using a pricing algorithm, typically based on a constant product formula or similar mathematical model. This removes the need for order matching and enables continuous, automated trading 24/7.
### How AMMs work
Unlike traditional limit order books where traders place specific buy/sell orders, AMMs:
* **Use liquidity pools**: LPs deposit token pairs that collectively form the trading liquidity
* **Price automatically**: Asset prices are determined by the ratio of tokens in the pool using mathematical formulas
* **Enable continuous trading**: Swaps can occur at any time as long as liquidity exists
* **Reward LPs**: Liquidity providers earn fees from trading activity
When you swap tokens, you interact with the pool. The algorithm calculates the output amount based on current pool balances, and the swap executes immediately.
## BEX and Balancer V2 architecture
BEX uses an AMM mechanism inspired by **Balancer V2**, which introduces several innovations to the AMM model:
* **Unified Vault architecture**: All pools share a single [Vault](/bex/learn/vault) contract for token accounting and transfers, while individual pools focus on swap math and liquidity management. This separation of concerns enables gas-efficient swaps through consolidated liquidity, improved security through independent pool balances, and flexibility in pool design.
* **Gas optimization**: Centralized accounting in the Vault reduces gas costs for multi-hop swaps and batch operations.
* **Flexible pool types**: BEX supports multiple pool types optimized for different trading scenarios:
* **[Weighted pools](/bex/learn/weighted-pools)**: Support 2-8 tokens with customizable weights (e.g., 80/20 HONEY/WETH or 33/33/33 HONEY/WETH/WBTC), unlike fixed 50/50 pairs
* **[Stable pools](/bex/learn/stable-pools)**: Optimized for tokens that trade at or near parity (e.g., stablecoins) or with predictable exchange rates
For detailed information about pool mechanics, see the [pools](/bex/learn/pools) overview and individual pool type pages.
# Flash Loans
Source: https://docs.berachain.com/bex/learn/flash-loans
Uncollateralized same-transaction loans; currently disabled via protocol fees.
Flash loans are currently disabled in BEX through protocol fees. This feature may be enabled in
the future through governance.
## How flash loans work
Flash loans are a DeFi primitive that allows you to borrow any amount of assets without providing collateral, as long as the loan is repaid within the same transaction.
## Future availability
While flash loans technically exist in the current implementation, their use is rendered impractical due to a high fee setting. The ability to enable flash loans in the future lies with BEX governance.
# Introduction
Source: https://docs.berachain.com/bex/learn/overview
Native DEX on Berachain. Swap and provide liquidity via weighted and stable pools.
BEX is Berachain's native decentralized exchange. Trade any token pair and provide liquidity through weighted and stable pools.
## Get started
Overview of BEX, pools, and security notice.
Trade tokens on BEX.
Provide liquidity to pools and earn.
How BEX pools and the vault work.
Developer resources: contracts, SDK, and integration guides.
# Pools
Source: https://docs.berachain.com/bex/learn/pools
Weighted and stable pool types, key parameters, and when to use each.
BEX pools contain the logic and math for swapping tokens and for liquidity operations. There are several types of pools, each with its own unique characteristics and use cases.
## Pool types
Custom weights per token; best for uncorrelated assets.
Pegged or correlated tokens (e.g., stablecoins) with a known exchange rate.
## Pool differences
### Pool math
* **Weighted pools** use constant product formulas (similar to Uniswap V2) and allow for custom weight distributions between tokens
* **Stable pools** use specialized stable swap curves optimized for tokens that should trade near parity
### Key parameters
* **Weighted pools** are configured primarily through token weights (e.g., `80/20`, `50/25/25`)
* **Stable pools** use an amplification coefficient to determine how tightly the prices should be bound together
### Use cases
* **Weighted pools**: Best for uncorrelated assets where price divergence is expected (e.g., `HONEY/WETH`)
* **Stable pools**: Ideal for correlated assets like:
* Stablecoins (`USDC/USDT/DAI`)
* Wrapped/underlying tokens (`iBERA/BERA`)
# Stable Pools
Source: https://docs.berachain.com/bex/learn/stable-pools
Pools for stablecoins and correlated assets; composability and amplification.
Stable pools are optimized for assets expected to trade at or near parity, or at a predictable exchange rate. The stable swap algorithm provides swaps with minimal price impact, enhancing capital efficiency for correlated assets.
## Use cases
* **Pegged tokens**: Assets trading near 1:1, such as stablecoins of the same currency (e.g., DAI, USDC, USDT) or synthetic assets (e.g., wrapped versions of BTC)
* **Correlated tokens**: Assets trading at slowly changing ratios, like certain derivatives (e.g., wstETH, WETH)
## Composability
Stable pools on BEX are composable with other pool types. Stable pool LP tokens can be "nested" into other pools, and swaps on that pool route through the underlying stable pool liquidity. This facilitates deeper liquidity and improved pricing.
As an example:
* There is a stable pool with `[HONEY, USDC, and DAI]`
* You create a [weighted pool](/bex/learn/weighted-pools) pairing `WETH` against the `[HONEY, USDC, and DAI]` pool
This results in deeper liquidity and eliminates the need to pair `WETH` against the individual stablecoins in separate pools.
## Technical parameters
### Amplification coefficient
The amplification coefficient controls how tightly the pool maintains price parity between tokens. It has a minimum value of `1` and a maximum value of `5000`.
* **Lower amplification**: More price flexibility, allowing for wider price swings
* **Higher amplification**: More price stability, reducing price swings
### Rate providers
For tokens that should trade at non-1:1 ratios (e.g., `iBERA/BERA` or appreciating ERC4626 tokens), rate providers can supply the expected exchange rate between assets. The inclusion of a rate provider results in a metastable pool.
## Pre-minting
To facilitate gas efficiency, an effectively infinite amount of LP tokens (`2**111`) are minted at the time of pool creation and held in the [Vault](/bex/learn/vault) (and distributed to liquidity providers).
# Vault
Source: https://docs.berachain.com/bex/learn/vault
Unified contract that holds pool tokens and executes swaps, joins, and exits.
The Vault contract is a central component of BEX (not to be confused with Reward Vaults in Proof-of-Liquidity), acting as a unified smart contract that manages all tokens across liquidity pools. It serves as the primary interface for most protocol operations, including swaps, joins, and exits.
## Separating token accounting and pool logic
BEX's Vault and Pool architecture separates the token accounting and management from the pool logic and AMM math. The responsibility for calculating amounts for swaps, joins, and exits is delegated to the pool contracts, while the Vault holds all of the tokens within the various pools (which can even be of different types).
As a simplified example, a swap transaction involves the following steps:
You send a swap request to the Vault, specifying the pool ID to swap through and the amount to
swap.
The Vault looks up the pool contract for the specified pool ID.
The pool contract calculates the balance changes from the swap, returning those amounts to the
Vault.
The Vault updates the token balances based on the pool contract's calculations and sends the
output tokens to you.
The Vault keeps pool balances strictly independent. This is critical for maintaining a permissionless system where tokens and pools can be created freely. Even though the Vault holds consolidated balances, the pool contracts are responsible for managing the balances within their pools. The depth of combined liquidity does not change the price impact of individual pools.
## Gas-efficient swaps
Storing all tokens in the same Vault contract provides greater gas efficiency. For example, in the case of a multi-hop swap (a trade routing through multiple pools), there is no need to transfer tokens at each step. Instead, the Vault keeps track of net balance changes and sends only what is needed at the end, saving on gas costs.
# Weighted Pools
Source: https://docs.berachain.com/bex/learn/weighted-pools
Multi-token pools with configurable weights for uncorrelated assets; exposure and impermanent loss.
Weighted pools are an extension of traditional AMM pools (i.e., Uniswap V2's 50/50 `x = y * k` pools). Weighted pools are suitable for very generalized use cases, particularly for pairs of tokens without price correlation (e.g., `HONEY/WETH`). BEX weighted pools allow you to create pools with multiple tokens and custom weight distributions, such as `80/20` or `60/20/20` ratios.
The following is an example with three tokens:
| Token | Weight |
| ----- | ------ |
| HONEY | 40% |
| WETH | 30% |
| WBTC | 30% |
## Key benefits
### Flexible exposure management
Weighted pools empower pool creators to fine-tune exposure to different assets when providing liquidity. The weighting of a token in a pool influences its susceptibility to impermanent loss during price fluctuations. For instance, in a WBERA/WETH pool, weights control the exposure profile to each asset:
Higher WBERA weight (e.g., `80/20 WBERA:WETH`) means that liquidity providers are more exposed to WBERA price movements, but it would require a larger amount of WBERA to change the relative price of the two assets in the pool. Conversely, equal weights suggest that the assets are expected to maintain relative value long-term.
### Impermanent loss mitigation
Impermanent loss refers to the value difference between holding assets outright and providing liquidity for those assets.
Pools with asymmetric token weights experience reduced impermanent loss, though this comes with a trade-off: swaps between assets in this pool have higher price slippage due to one side having less liquidity.
# What Is BEX?
Source: https://docs.berachain.com/bex/learn/what-is-bex
Trade any token pair and provide liquidity via weighted and stable pools on Berachain.
BEX is the native decentralized exchange (DEX) protocol of Berachain. You can trade any combination of tokens through weighted and stable pools.
Use BEX (swap, pools, and vaults) from **Bera Hub** in the site navbar.
**Security notice**
On January 21st, 2025, Balancer disclosed a vulnerability in their V2 Vault implementation. BEX shares this vulnerability. **Funds currently deposited in BEX are safe and no action is needed.** When creating new pools, exercise caution with untrusted or newly-created tokens. The frontend displays warnings for potentially vulnerable tokens. Future plans include integrating Balancer V3, which mitigates this issue. See the [Balancer disclosure](https://forum.balancer.fi/t/balancer-v2-token-frontrun-vulnerability-disclosure/6309) for details.
# Deployed Contracts
Source: https://docs.berachain.com/build/bend/deployed-contracts
Bend smart contract addresses on Berachain; Morpho, IRM, oracles, URD, and related contracts.
Addresses for reading from or writing to Bend contracts.
Deployed contracts have been audited by multiple parties. Reports are on
[GitHub](https://github.com/berachain/security-audits). To report a security issue, use the
[Berachain bug bounty program on
Immunefi](https://immunefi.com/bug-bounty/berachain/information/).
## Mainnet contracts
### Morpho
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0x24147243f9c08d835C218Cda1e135f8dFD0517D0
```
[Berascan](https://berascan.com/address/0x24147243f9c08d835C218Cda1e135f8dFD0517D0)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xcf247Df3A2322Dea0D408f011c194906E77a6f62
```
[Berascan](https://berascan.com/address/0xcf247Df3A2322Dea0D408f011c194906E77a6f62)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xF920140A65D0f412f2AB3e76C4fEAB5Eef0657ae
```
[Berascan](https://berascan.com/address/0xF920140A65D0f412f2AB3e76C4fEAB5Eef0657ae)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xd2B9667F5214115E27937C410cAeE83E3a901Df7
```
[Berascan](https://berascan.com/address/0xd2B9667F5214115E27937C410cAeE83E3a901Df7)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0x5EDd48C6ACBd565Eeb31702FD9fa9Cbc86fbE616
```
[Berascan](https://berascan.com/address/0x5EDd48C6ACBd565Eeb31702FD9fa9Cbc86fbE616)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0x80108Ee81A92091Db6B8B2326B1875ce9388f461
```
[Berascan](https://berascan.com/address/0x80108Ee81A92091Db6B8B2326B1875ce9388f461)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xB62F34Ab315eaDeAc698e8EaEB6Fc2650951BFe7
```
[Berascan](https://berascan.com/address/0xB62F34Ab315eaDeAc698e8EaEB6Fc2650951BFe7)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xAf2FDC54f7bc9d6e8C2D2760E908f4e1beB04d9E
```
[Berascan](https://berascan.com/address/0xAf2FDC54f7bc9d6e8C2D2760E908f4e1beB04d9E)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0x46fE2bc33b661E01A8946BbC3Bf43F2B8382d802
```
[Berascan](https://berascan.com/address/0x46fE2bc33b661E01A8946BbC3Bf43F2B8382d802)
More vaults may be deployed, please check
[https://bend.berachain.com/lend](https://bend.berachain.com/lend) for the latest deployed vaults.
### Vaults
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0x30BbA9CD9Eb8c95824aa42Faa1Bb397b07545bc1
```
[Berascan](https://berascan.com/address/0x30BbA9CD9Eb8c95824aa42Faa1Bb397b07545bc1)
# Deployed Markets
Source: https://docs.berachain.com/build/bend/deployed-markets
Market IDs and parameters for deployed Bend lending markets on Berachain.
Market IDs deployed on Bend. For the latest whitelisted markets, see [bend.berachain.com/borrow](https://bend.berachain.com/borrow).
## Market IDs
| Market | ID |
| ------------------ | -------------------------------------------------------------------- |
| **WBTC / HONEY** | `0x950962c1cf2591f15806e938bfde9b5f9fbbfcc5fb640030952c08b536f1f167` |
| **sUSDe / HONEY** | `0x1ba7904c73d337c39cb88b00180dffb215fc334a6ff47bbe829cd9ee2af00c97` |
| **wgBERA / HONEY** | `0x63c2a7c20192095c15d1d668ccce6912999b01ea60eeafcac66eca32015674dd` |
| **WETH / HONEY** | `0x1f05d324f604bd1654ec040311d2ac4f5820ecfd1801a3d19d2c6f09d4f7a614` |
| **WBERA / HONEY** | `0x147b032db82d765b9d971eac37c8176260dde0fe91f6a542f20cdd9ede1054df` |
| **iBERA / HONEY** | `0x594de722a090f8d0df41087c23e2187fb69d9cd6b7b425c6dd56ddc1cff545f0` |
To find a market ID, open [bend.berachain.com/borrow](https://bend.berachain.com/borrow) and read the Market ID from the URL.
# Bundlers
Source: https://docs.berachain.com/build/bend/onchain-bundlers
Group multiple Bend onchain operations into one atomic transaction; Bundler3 integration and use cases.
Bundlers let you run multiple onchain operations in **one atomic transaction**—approvals, wrapping, supply, borrow, and more—so users complete flows in a single step and you save gas.
For user-facing apps on Morpho, using a bundler is the recommended approach.
For details, see the Bundler3 contract.
## Why use bundlers
* **Simpler UX**: Users can open leveraged positions or deposit into vaults in one transaction instead of many.
* **Lower gas**: One transaction costs less than several separate ones.
* **Atomicity**: If any step fails, the whole transaction reverts so users never end up in a half-complete state (e.g. tokens approved but not supplied).
## Supported operations
Bundlers support:
* **Tokens**: ERC20 approvals (including permit/permit2), wrap/unwrap native assets, transfers
* **Bend markets**: Supply, supplyCollateral, borrow, repay, withdraw
* **Bend vaults**: Deposit and withdraw (ERC-4626 vaults)
* **Public Allocator**: `reallocateTo` to move liquidity between markets
* **Rewards**: Claim from the Universal Rewards Distributor (URD)
* **Swaps**: DEX aggregator swaps
If you grant Bundler3 token approval, it can execute any transaction on your behalf. Avoid
granting Bundler3 approval (standard approval or permit).
# Public Allocator
Source: https://docs.berachain.com/build/bend/onchain-public-allocator
Programmatically reallocate vault liquidity between Bend markets; just-in-time liquidity for borrowers.
The Public Allocator reallocates vault liquidity between Bend markets so borrows can succeed even when a market has insufficient supply. You call it to move liquidity from other markets into the target market in one transaction.
You get deep, unified liquidity while keeping Bend's isolated market risk. For concepts and flow, see [Public Allocator](/bend/learn/public-allocator).
For the contract interface, see the Public Allocator contract.
Read [Public Allocator](/bend/learn/public-allocator) for an overview of how it works and curator
controls.
## Core integration: `reallocateTo`
Call `reallocateTo` to withdraw from one or more source markets in a vault and supply the total to a single destination market in one atomic transaction.
### Function signature
```solidity theme={null}
function reallocateTo(
address vault,
Withdrawal[] calldata withdrawals,
MarketParams calldata supplyMarketParams
) external payable;
```
### Parameters
```solidity theme={null}
struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
}
```
* **vault**: MetaMorpho vault to withdraw from.
* **withdrawals**: Array of `Withdrawal` (source market + amount). Must be **sorted by market ID ascending**.
* `Withdrawal`: `marketParams` (MarketParams for source market), `amount` (uint128).
* **supplyMarketParams**: Destination market (single market) that receives the total withdrawn amount.
### Requirements
* **Sorted withdrawals**: `withdrawals` must be ordered by market ID ascending.
* **Fee**: Send the required ETH as `msg.value`. Query the fee with `fee(vaultAddress)` on the Public Allocator.
* **Flow caps**: Withdrawals and supply must not exceed the vault curator's `maxOut` and `maxIn` per market.
* **Enabled markets**: All source and destination markets must be enabled for the vault.
* **No self-supply**: The destination market must not appear in `withdrawals`.
### Example
```solidity theme={null}
// Example assumes IPublicAllocator and IMetaMorpho interfaces are imported and available
IPublicAllocator publicAllocator = IPublicAllocator(PA_ADDRESS);
IMetaMorpho vault = IMetaMorpho(VAULT_ADDRESS);
// Step 1: Confirm allocator is registered and get required fee
require(vault.isAllocator(address(publicAllocator)), "PA: Not an allocator");
uint256 requiredFee = publicAllocator.fee(address(vault));
// Step 2: Build withdrawals array (sorted by market ID ascending)
Withdrawal[] memory withdrawals = new Withdrawal[](2);
withdrawals[0] = Withdrawal({ marketParams: marketAParams, amount: 70 * 1e18 });
withdrawals[1] = Withdrawal({ marketParams: marketBParams, amount: 800 * 1e18 }); // Market B ID > Market A ID
// Step 3: Destination market
MarketParams memory supplyMarketParams = wBERAMarketParams;
// Step 4: Call reallocateTo with fee
publicAllocator.reallocateTo{value: requiredFee}(
address(vault),
withdrawals,
supplyMarketParams
);
```
# Overview
Source: https://docs.berachain.com/build/bend/overview
Bend on Berachain: lending protocol built on Morpho, mainnet-only, with deployed contracts, markets, and onchain tooling.
Bend is Berachain’s native lending protocol, built on [Morpho V1](https://docs.morpho.org/build/earn/get-started). Use it to integrate lending, borrowing, oracles, flash loans, and new vaults and markets into your app.
Bend is only available on Berachain Mainnet and has no plans to deploy to Bepolia Testnet.
## Where to go next
* **[Deployed Contracts](/build/bend/deployed-contracts)** — Contract addresses and ABIs for mainnet.
* **[Deployed Markets](/build/bend/deployed-markets)** — Live markets, utilization, liquidity, and metrics.
* **[Onchain tooling](/build/bend/onchain-bundlers)** — Bundlers and the Public Allocator for reading and interacting with Bend programmatically.
# Batch Swap
Source: https://docs.berachain.com/build/bex/concepts/batch-swap
Multi-hop swaps across BEX pools to optimize routes and pricing.
BEX allows multi-hop swaps, or "batch swaps", which can optimize routes across multiple liquidity pools to find the best prices.
The BEX Vault contract exposes the `batchSwap` function to allow multi-hop swaps.
## Functions
### batchSwap
```solidity theme={null}
function batchSwap(SwapKind kind,
BatchSwapStep[] swaps,
IAsset[] assets,
FundManagement funds,
int256[] limits,
uint256 deadline) returns (int256[] assetDeltas)
```
| Parameter | Type | Description |
| --------- | ---------------- | ----------------------------------------------- |
| kind | SwapKind | The type of swap (GIVEN\_IN or GIVEN\_OUT) |
| swaps | BatchSwapStep\[] | Array of steps in the batch swap |
| assets | IAsset\[] | Array of tokens used in the batch swap |
| funds | FundManagement | Specifies token sources and destinations |
| limits | int256\[] | Maximum amounts of each asset to be transferred |
| deadline | uint256 | UNIX timestamp by which the swap must complete |
| Returns | Type | Description |
| ----------- | --------- | ------------------------------------------ |
| assetDeltas | int256\[] | Net token balances resulting from the swap |
### queryBatchSwap
```solidity theme={null}
function queryBatchSwap(SwapKind kind,
BatchSwapStep[] swaps,
IAsset[] assets,
FundManagement funds)
returns (int256[] assetDeltas)
```
| Returns | Type | Description |
| ----------- | --------- | ---------------------------------------------------- |
| assetDeltas | int256\[] | Simulated net token balances resulting from the swap |
Use this function off-chain to estimate swap results and calculate appropriate limits.
## Structs
### BatchSwapStep
```solidity theme={null}
struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
```
| Field | Type | Description |
| ------------- | ------- | ------------------------------------------------------- |
| poolId | bytes32 | The id of the pool to swap with |
| assetInIndex | uint256 | The index of the input token within the `assets` array |
| assetOutIndex | uint256 | The index of the output token within the `assets` array |
| amount | uint256 | The amount to swap, interpretation depends on `kind` |
| userData | bytes | Additional data required by the pool for the swap |
**Important:** When `amount` is set to 0 in a multi-hop swap, BEX will use the full output of the previous step as input.
### FundManagement
```solidity theme={null}
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
```
| Field | Type | Description |
| ------------------- | --------------- | ------------------------------------------- |
| sender | address | The address providing tokens for the swap |
| fromInternalBalance | bool | Whether to use tokens from internal balance |
| recipient | address payable | The address receiving tokens after the swap |
| toInternalBalance | bool | Whether to send tokens to internal balance |
The `SwapKind` parameter in the `batchSwap` function determines how swap amounts are interpreted:
* **GIVEN\_IN**: Specify the exact amount of tokens to swap in. The function calculates and returns the amount of tokens you'll receive.
Example: "Swap exactly 100 USDC for as much ETH as possible."
* **GIVEN\_OUT**: Specify the exact amount of tokens to receive. The function calculates and returns the amount of tokens you need to swap in.
Example: "Receive exactly 1 ETH, how much USDC do I need to swap?"
The choice between GIVEN\_IN and GIVEN\_OUT affects how the `amount` field in the `BatchSwapStep` struct is interpreted and how swap calculations are performed.
## Examples
The following examples demonstrate different ways to use batch swaps in BEX. These patterns can help you optimize trading strategies and reduce gas costs by combining multiple operations into single transactions.
### Multi-hop examples
Multi-hop swaps allow trading between tokens that don't have direct liquidity pools by routing through intermediate tokens. This can often result in better pricing than direct swaps.
#### Example 1: GIVEN\_IN ($USDC -> $HONEY -> \$DAI)
In this example, you want to swap 1000 $USDC through $HONEY to get \$DAI. The swap is executed in two steps:
| Step | Amount | Token In | Token Out | Description |
| ---- | ------ | -------- | --------- | --------------------------------- |
| 0 | 1000 | \$USDC | \$HONEY | Swap exact 1000 $USDC for $HONEY |
| 1 | 0 | \$HONEY | \$DAI | Swap all received $HONEY for $DAI |
By setting the second step's amount to 0, you ensure all \$HONEY received from step 0 is used in step 1.
#### Example 2: GIVEN\_OUT ($USDC -> $HONEY -> \$DAI)
Here you want exactly 500 $DAI, and BEX will calculate backwards how much $USDC you need:
| Step | Amount | Token Out | Token In | Description |
| ---- | ------ | --------- | -------- | ------------------------------- |
| 0 | 500 | \$DAI | \$HONEY | Request exact 500 \$DAI output |
| 1 | 0 | \$HONEY | \$USDC | Calculate required \$USDC input |
The swap calculates first how much $HONEY is needed for 500 $DAI, then how much $USDC is needed for that amount of $HONEY.
### Parallel swap examples
Parallel swaps are batch swaps where multiple unrelated swaps are executed in parallel within a single transaction. This allows for efficient execution of multiple trades by batching them together, reducing overall gas costs compared to executing them separately.
#### Example 3: Parallel GIVEN\_IN swaps
Execute multiple independent swaps in a single transaction:
| Step | Amount | Token In | Token Out | Description |
| ---- | ------ | -------- | --------- | -------------------------- |
| 0 | 1000 | \$USDC | \$HONEY | Swap 1000 $USDC for $HONEY |
| 1 | 0.5 | \$WETH | \$DAI | Swap 0.5 $WETH for $DAI |
| 2 | 0.01 | \$WBTC | \$USDC | Swap 0.01 $WBTC for $USDC |
Each swap is independent and executes in parallel, optimizing gas usage.
#### Example 4: Combined GIVEN\_OUT swaps
This example demonstrates how to combine a multi-hop swap with a single-hop swap in parallel, all using GIVEN\_OUT mode. It performs two independent swaps:
1. A multi-hop swap A->B->C->D to get exactly 100 D
2. A single swap E->F to get exactly 50 F
| Step | Amount | Token Out | Token In | Description |
| ---- | ------ | --------- | -------- | ------------------------------------------- |
| 0 | 100 | D | C | First step: Request exactly 100 D |
| 1 | 0 | C | B | Second step: Use all C from previous step |
| 2 | 0 | B | A | Third step: Calculate required A input |
| 3 | 50 | F | E | Independent parallel swap: Get exactly 50 F |
In this example:
* Steps 0-2 form one multi-hop path (A->B->C->D) where you want exactly 100 D
* Step 3 is a completely independent swap (E->F) where you want exactly 50 F
* BEX will calculate the required amounts of tokens A and E needed as inputs
* The two swaps execute in parallel in a single transaction
This pattern is useful when you need exact output amounts from completely independent swap paths.
# Underlying Tokens
Source: https://docs.berachain.com/build/bex/concepts/lp-underlying
Query underlying tokens for a BEX LP token and your pool share.
## Overview
It's common to want to know what underlying tokens a BEX LP token represents. This documentation explains how to query this information directly from the BEX contract and LP tokens.
## Querying the BEX contract and LP tokens
The BEX contract can provide the exact number of tokens in a pool. By querying this information and calculating your share of the pool, you can determine your underlying token balances.
### Step-by-step process
1. Query the BEX contract for pool tokens and balances
2. Calculate your pool share
3. Calculate your underlying token balances
### Pseudocode
```solidity theme={null}
(tokens, balances, lastChangeBlock) = vault.getPoolTokens(poolId);
yourPoolShare = lpToken.balanceOf(yourAddress) / lpToken.totalSupply();
uint256[] yourUnderlyingBalances = new uint256[](balances.length);
for (i = 0; i < balances.length; i++) {
yourUnderlyingBalances[i] = balances[i] * yourPoolShare;
}
return (tokens, yourUnderlyingBalances);
```
## Special considerations
### Stable pools
For stable pools in BEX:
1. Use `getActualSupply()` instead of `totalSupply()` to get the correct pool supply
2. If you've staked your LP tokens, calculate your total LP token balance as:
```solidity theme={null}
myLpTokens = lpToken.balanceOf(yourAddress) + lpStakingContract.balanceOf(yourAddress);
```
## Example: Querying BERA/HONEY pool
Let's say you want to know your underlying BERA and HONEY balances for a BERA/HONEY pool:
| Step | Action | Result |
| ---- | -------------------- | ------------------------------------------------------------- |
| 1 | Query BEX | tokens = \[BERA, HONEY], balances = \[1000 BERA, 10000 HONEY] |
| 2 | Get LP token balance | yourLpTokens = 100 |
| 3 | Get total LP supply | actualSupply = getActualSupply() = 1000 |
| 4 | Calculate pool share | yourPoolShare = 100 / 1000 = 0.1 (10%) |
| 5 | Calculate underlying | yourBera = 1000 \* 0.1 = 100 BERA |
| 6 | Calculate underlying | yourHoney = 10000 \* 0.1 = 1000 HONEY |
In this example, your 100 LP tokens represent 100 BERA and 1000 HONEY.
# Valuing LP Tokens
Source: https://docs.berachain.com/build/bex/concepts/lp-valuing
Calculate BEX LP token value: informational and on-chain methods.
## Overview
BEX LP tokens represent a share of a liquidity pool. Accurately valuing these tokens is essential for displaying balances, calculating rewards, and for on-chain operations like collateralization and liquidation. There are several methods to value LP tokens, each suited to different use cases.
## Methods of valuation
### Informational (sum of balances)
This calculation is for informational purposes only and should NOT be used on-chain as it can be
easily manipulated.
This method is suitable for off-chain, informational, or UI purposes. It is **not manipulation-resistant** and should not be used for on-chain or critical financial operations.
**Formula:**
$$
Price_{LP\ token} = \frac{\sum_i (Balance_i \times Price_i)}{Supply_{LP\ Tokens}}
$$
**Where:**
* **Balancei**: balance of token $i$ in the pool
* **Pricei**: market price of token $i$
* **SupplyLP\ Tokens**: total supply of LP tokens
**Example:**
Suppose a \$BERA/\$HONEY pool contains the following:
| Token | Balance | Price (USD) | Value (USD) |
| -------------------- | --------- | ----------- | ------------ |
| \$BERA | 1,000 | \$10 | \$10,000 |
| \$HONEY | 10,000 | \$1 | \$10,000 |
| **Total Pool Value** | | | **\$20,000** |
| **LP Token Supply** | **1,000** | | |
* **Pool Value:** 1000 \$BERA $\times$ 10 USD + 10000 \$HONEY $\times$ 1 USD = 20000 USD
* **LP Token Price:** 20000 USD $/$ 1000 \$LPTOKEN = 20 USD
***
### Weighted pools
Weighted pools use a constant product invariant with custom weights. This method is **manipulation-resistant** and is recommended for on-chain or robust off-chain use.
| Calculation Steps | Formula |
| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| **Step 1: Pool Invariant** where $\sum_i w_i = 1$ | $x_1^{w_1} x_2^{w_2} \cdots x_n^{w_n} = k$ |
| **Step 2: Token Weighted Values** | $\frac{p_1 x_1}{w_1} = \cdots = \frac{p_n x_n}{w_n} = \hat{k}$ |
| **Step 3: Calculate $\hat{k}$** | $\hat{k} = k \cdot \prod_i \left( \frac{p_i}{w_i} \right)^{w_i}$ |
| **Step 4: Final LP Token Price** where $s$ is the LP token supply | $p_{LP} = \frac{\hat{k}}{s} = \frac{k}{s} \cdot \prod_i \left( \frac{p_i}{w_i} \right)^{w_i}$ |
**Example**
Imagine an 80 \$BAL/20 \$WETH Pool with the following:
| Calculation Steps | Values |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| **Step 1: Token Weights** | $w_1 = 0.8$ (\$BAL) $w_2 = 0.2$ (\$WETH) |
| **Step 2: Oracle Prices** | $p_1 \approx$ \$4.53 (\$BAL) $p_2 \approx$ \$1090.82 (\$WETH) |
| **Step 3: Pool Parameters** | $k \approx 2,852,257.5$ (from `pool.getInvariant()`) $s \approx 5,628,392.26$ (from `pool.totalSupply()`) |
| **Step 4: Final LP Token Price** | $p_{LP} = \frac{k}{s} \cdot \prod_i \left( \frac{p_i}{w_i} \right)^{w_i} \approx$ \$11.34 |
***
### Stable pools
Stable pools are optimized for assets that trade at or near parity (e.g., stablecoins). They use the StableSwap invariant and often expose a `getRate()` function.
**StableSwap Invariant:**
$$
A \cdot n^n \cdot \sum_i x_i + D = A \cdot D \cdot n^n + \frac{D^{n+1}}{n^n \cdot \prod_i x_i}
$$
Where:
* $A$ is the amplification parameter
* $n$ is the number of tokens
* $x_i$ are token balances
* $D$ is the invariant
**LP Token Price (using getRate):**
Most stable pools expose a `getRate()` function, which returns the value of 1 LP token in terms of the pool's base asset (e.g., USD or a stablecoin):
$$
p_{LP} = \text{getRate()} \times \text{(price of base asset)}
$$
**Manual Calculation (if needed):**
$$
p_{LP} = \frac{\sum_i x_i \cdot p_i}{S}
$$
Where $S$ is the actual supply (use `getActualSupply()` for pre-minted pools).
**Example:**
Suppose a USDC/DAI/USDT pool with $getRate() = 1.01$ and USD as the base asset:
* **LP token price:** $p_{LP} = 1.01 \times 1 = 1.01$ USD per LP token
***
### Linear pools
Linear pools are designed for pairs where one token is a yield-bearing or wrapped version of the other. They use a simple linear invariant and expose a `getRate()` function.
**Linear Pool Invariant:**
$$
x_{main} + r \cdot x_{wrapped} = k
$$
Where $r$ is the rate between the wrapped and main token.
**LP Token Price (using getRate):**
$$
p_{LP} = \text{getRate()} \times \text{(price of main token)}
$$
**Manual Calculation (if needed):**
$$
p_{LP} = \frac{x_{main} \cdot p_{main} + x_{wrapped} \cdot p_{wrapped}}{S}
$$
Where $S$ is the virtual supply (use `getVirtualSupply()`).
**Example:**
Suppose a wstETH/ETH pool with $getRate() = 1.05$ and ETH at $2000$:
* **LP token price:** $p_{LP} = 1.05 \times 2000 = 2100$ USD per LP token
## Special considerations
* **Pre-minted Supply:** Some pools (especially stable pools) pre-mint the maximum possible LP tokens. Always use `getActualSupply()` or `getVirtualSupply()` as appropriate.
* **Protocol Fees:** Some pools accrue protocol fees, which may affect the actual value of LP tokens. Weighted and stable pools account for these in their supply functions.
* **Manipulation Resistance:** For on-chain or critical use, always use invariant-based or rate-based pricing, not simple sum-of-balances.
* **Re-entrancy Protection:** When evaluating on-chain, always check for Vault re-entrancy using `ensureNotInVaultContext(vault)`.
* **Staked LP Tokens:** If LP tokens are staked, include both wallet and staked balances in your calculations.
## References
* [Balancer: Valuing BPT](https://docs-v2.balancer.fi/concepts/advanced/valuing-bpt/valuing-bpt.html#overview)
* [Balancer: Stable Math](https://docs-v2.balancer.fi/concepts/math/stable-math.html)
* BEX Vault Contract
* [BEX Pool Types](/bex/learn/pools)
* [BEX SDK](/build/bex/sdk/overview)
# Pool Exits
Source: https://docs.berachain.com/build/bex/concepts/pool-exits
Remove liquidity from BEX pools via the Vault exitPool function.
Calls to `exitPool()` must be made on the BEX Vault contract. You cannot send this command
directly to a pool.
## exitPool function
```solidity theme={null}
exitPool(
bytes32 poolId,
address sender,
address recipient,
ExitPoolRequest request
)
```
### Arguments explained
| Parameter | Type | Description |
| --------- | --------------- | ------------------------------------------ |
| poolId | bytes32 | ID of the pool you're exiting |
| sender | address | Address sending LP tokens |
| recipient | address | Address receiving tokens |
| request | ExitPoolRequest | Struct containing exit details (see below) |
### ExitPoolRequest struct
```solidity theme={null}
struct ExitPoolRequest {
address[] assets,
uint256[] minAmountsOut,
bytes userData,
bool toInternalBalance
}
```
| Field | Type | Description |
| ----------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| assets | address\[] | Sorted list of all tokens in pool |
| minAmountsOut | uint256\[] | Minimum token receive amounts |
| userData | bytes | Custom bytes field for exit parameters |
| toInternalBalance | bool | True to credit tokens to recipient's internal balance in the Vault (not available for BERA), false to transfer tokens directly to recipient's wallet |
### Token ordering
When providing assets, tokens must be sorted numerically by address. The values in `minAmountsOut` correspond to the same index in `assets`, so these arrays must be created in parallel after sorting.
### Minimum amounts
The `minAmountsOut` parameter sets lower limits for tokens to receive. This protects against price changes between transaction submission and execution.
Best practice:
1. Use `queryExit` to calculate current token amounts you'll receive
2. Apply a slippage tolerance (e.g., multiply by 0.99 for 1% slippage)
3. Use these adjusted amounts as `minAmountsOut`
### userData encoding
The `userData` field encodes the exit type and parameters. Different pool types support different exit kinds.
When encoding `userData` for pools that include their own LP token as part of the pool's tokens,
the LP tokens are not included in the `userData`.
## ExitKind enum
```solidity theme={null}
enum ExitKind {
EXACT_LP_IN_FOR_ONE_TOKEN_OUT,
EXACT_LP_IN_FOR_TOKENS_OUT,
LP_IN_FOR_EXACT_TOKENS_OUT
}
```
### Exit types explained
| ExitKind | Description | Use Case |
| ----------------------------------- | --------------------------------------------------------------------- | ----------------------------------------- |
| EXACT\_LP\_IN\_FOR\_ONE\_TOKEN\_OUT | Send precise LP tokens, receive estimated single token | When you want to exit to a specific token |
| EXACT\_LP\_IN\_FOR\_TOKENS\_OUT | Send precise LP tokens, receive estimated amounts of all tokens | When you want to exit proportionally |
| LP\_IN\_FOR\_EXACT\_TOKENS\_OUT | Send estimated LP tokens, receive precise amounts of specified tokens | When you need exact output amounts |
### userData encoding
| Exit Type | ABI | userData |
| ----------------- | ------------------------------------- | ------------------------------------------------------------- |
| Single Asset Exit | `['uint256', 'uint256', 'uint256']` | `[EXACT_LP_IN_FOR_ONE_TOKEN_OUT, lpAmountIn, exitTokenIndex]` |
| Proportional Exit | `['uint256', 'uint256']` | `[EXACT_LP_IN_FOR_TOKENS_OUT, lpAmountIn]` |
| Custom Exit | `['uint256', 'uint256[]', 'uint256']` | `[LP_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxLPAmountIn]` |
## Examples
### Example 1: Single asset exit (EXACT\_LP\_IN\_FOR\_ONE\_TOKEN\_OUT)
Exit a `$BERA`/`$HONEY` pool to receive only `$BERA`:
| Step | ExitKind | Assets | Parameters | Description |
| ---- | ----------------------------------- | -------------- | ----------------------------------------- | ------------------------------------------------ |
| 1 | EXACT\_LP\_IN\_FOR\_ONE\_TOKEN\_OUT | \[BERA, HONEY] | lpAmountIn: 100 LP exitTokenIndex: 0 | Send 100 LP tokens and receive estimated `$BERA` |
### Example 2: Proportional exit (EXACT\_LP\_IN\_FOR\_TOKENS\_OUT)
Exit a `$BERA`/`$HONEY` pool proportionally:
| Step | ExitKind | Assets | Parameters | Description |
| ---- | ------------------------------- | -------------- | ------------------ | ------------------------------------------------------------------ |
| 1 | EXACT\_LP\_IN\_FOR\_TOKENS\_OUT | \[BERA, HONEY] | lpAmountIn: 100 LP | Send 100 LP tokens and receive proportional amounts of both tokens |
### Example 3: Custom exit (LP\_IN\_FOR\_EXACT\_TOKENS\_OUT)
Exit a `$BERA`/`$HONEY`/`$DAI` pool for specific token amounts:
| Step | ExitKind | Assets | Parameters | Description |
| ---- | ------------------------------- | ------------------- | -------------------------------------------------------------------- | ------------------------------------------------- |
| 1 | LP\_IN\_FOR\_EXACT\_TOKENS\_OUT | \[BERA, HONEY, DAI] | amountsOut: \[10 BERA, 100 HONEY, 50 DAI] maxLPAmountIn: 200 LP | Receive exact token amounts, sending up to 200 LP |
# Pool Interfacing
Source: https://docs.berachain.com/build/bex/concepts/pool-interfacing
Nuances of interacting with different BEX pool types.
Different pool types (e.g. Weighted, Stable) have different interfaces for interacting with them, where the underlying logic can vary widely. This document outlines the common interfaces for interacting with BEX pools.
In general, you will interface with the Vault contract to obtain information on pools. Specific nuances of interacting with different pool types are also detailed below.
## Using `poolId`
To interface with a pool, you will need its `poolId`, which is its unique identifier in BEX (32 bytes in length).
#### Example:
* A pool might have an id `0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014`
* The first 20-bytes of the `poolId` represent the pool's address `0x5c6ee304399dbdb9c8ef030ab642b10820db8f56`
### Obtaining `poolId`
You can get a `poolId` from:
* The pool details page on [Bera Hub](https://hub.berachain.com): `/pools//details/`
* The [SDK](/build/bex/sdk/overview)
* Calling `getPoolId()` on the pool contract itself if you already have it
## Common pool interfaces
### Pool balances (Vault)
The Vault can be queried for a particular pool's token balances using `getPoolTokens`.
For example, calling:
```solidity theme={null}
vault.getPoolTokens(0x8353157092ed8be69a9df8f95af097bbf33cb2af0000000000000000000005d9)
```
returns something resembling:
```
tokens:
[[0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f]
[0x8353157092ED8Be69a9DF8F95af097bbF33Cb2aF]
[0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48]
[0xdAC17F958D2ee523a2206206994597C13D831ec7]]
balances: [794569830111428445878272,2596148429278955607828401901927006,4333829907765,4422662782925]
lastChangeBlock: 20728927
```
### Swap fee
Swap fees are stored at the pool level, which can be obtained using:
```solidity theme={null}
pool.getSwapFeePercentage()
```
This returns an 18 decimal value like:
```
500000000000000 // corresponds to a 0.05% swap fee
```
### Emergency pause state
In the unlikely case that there is an issue with the pools, swaps and pool joins can be paused. Withdrawals are not paused, so you can always exit a pool.
To check if a pool is paused, calling:
```solidity theme={null}
pool.getPausedState()
```
returns something resembling:
```
paused : False
pauseWindowEndTime : 1627668973
bufferPeriodEndTime : 1630260973
```
### Rate providers
Returns the rate providers configured for each token (in the same order as registered). Zero-address entries means there's no rate provider for that token.
```solidity theme={null}
function getRateProviders() external view returns (address[]);
```
### Pool token supply
Because certain pool types (Stable Pools) pre-mint BPT to the Vault contract, the following function should be used to get the total supply of pool tokens:
```solidity theme={null}
function getActualSupply() external view returns (uint256);
```
## Weighted pools
The following section documents functions specific to Weighted Pools.
### getNormalizedWeights
Returns all normalized weights, in the same order as the Pool's tokens.
```solidity theme={null}
function getNormalizedWeights() external view returns (uint256[] memory)
```
## Stable pools
The following section documents functions specific to Stable Pools.
### getRate
This function returns the appreciation of LP tokens relative to the underlying tokens. The rate is a monotonically increasing function *as long as the tokens in the pool do not lose value*.
```solidity theme={null}
function getRate() external view virtual override returns (uint256)
```
# Pool Joins
Source: https://docs.berachain.com/build/bex/concepts/pool-joins
Add liquidity to BEX pools via the Vault joinPool function.
Calls to `joinPool()` must be made on the BEX Vault contract. You cannot send this command
directly to a pool.
To add liquidity to a pool, you must call the `joinPool` function on the BEX Vault.
## joinPool function
```solidity theme={null}
joinPool(
bytes32 poolId,
address sender,
address recipient,
JoinPoolRequest request
)
```
### Arguments explained
| Parameter | Type | Description |
| --------- | --------------- | -------------------------------------------------------- |
| poolId | bytes32 | ID of the pool you're joining |
| sender | address | Address sending tokens to the pool |
| recipient | address | Address receiving LP tokens (usually the same as sender) |
| request | JoinPoolRequest | Struct containing join details (see below) |
### JoinPoolRequest struct
```solidity theme={null}
struct JoinPoolRequest {
address[] assets,
uint256[] maxAmountsIn,
bytes userData,
bool fromInternalBalance
}
```
| Field | Type | Description |
| ------------------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
| assets | address\[] | Sorted list of all tokens in pool |
| maxAmountsIn | uint256\[] | Maximum token amounts to send |
| userData | bytes | Custom bytes field for join parameters |
| fromInternalBalance | bool | If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only |
### Token ordering
When providing assets, tokens must be sorted numerically by address. The values in `maxAmountsIn` correspond to the same index in `assets`, so these arrays must be created in parallel after sorting.
### Maximum amounts
The `maxAmountsIn` parameter sets upper limits for tokens to send. This protects against price changes between transaction submission and execution.
Best practice:
1. Use `queryJoin` to calculate current token amounts needed
2. Add slippage tolerance (e.g., multiply by 1.01 for 1% slippage)
3. Use these adjusted amounts as `maxAmountsIn`
### userData encoding
The `userData` field encodes the join type and parameters. Different pool types support different join kinds.
## JoinKind enum
```solidity theme={null}
enum JoinKind {
INIT,
EXACT_TOKENS_IN_FOR_LP_OUT,
TOKEN_IN_FOR_EXACT_LP_OUT,
ALL_TOKENS_IN_FOR_EXACT_LP_OUT
}
```
| JoinKind | Description |
| ------------------------------------ | ------------------------------------------------------------------------- |
| INIT | Initial join to seed a pool (can be done only once) |
| EXACT\_TOKENS\_IN\_FOR\_LP\_OUT | Send precise token quantities, receive estimated LP tokens |
| TOKEN\_IN\_FOR\_EXACT\_LP\_OUT | Send estimated single token quantity, receive precise LP tokens |
| ALL\_TOKENS\_IN\_FOR\_EXACT\_LP\_OUT | Send estimated token quantities proportionally, receive precise LP tokens |
When encoding `userData` for pools that include their own LP token, the LP tokens are not included
except for `INIT` joins.
## userData encoding
| Join Type | ABI | userData |
| ----------------- | ------------------------------------- | ----------------------------------------------------------- |
| Initial Join | `['uint256', 'uint256[]']` | `[INIT, amountsIn]` |
| Exact Tokens Join | `['uint256', 'uint256[]', 'uint256']` | `[EXACT_TOKENS_IN_FOR_LP_OUT, amountsIn, minimumLP]` |
| Single Token Join | `['uint256', 'uint256', 'uint256']` | `[TOKEN_IN_FOR_EXACT_LP_OUT, lpAmountOut, enterTokenIndex]` |
| Proportional Join | `['uint256', 'uint256']` | `[ALL_TOKENS_IN_FOR_EXACT_LP_OUT, lpAmountOut]` |
## Examples
### Example 1: Initial join (INIT)
Seeding a new pool with `$BERA` and `$HONEY`:
| Step | JoinKind | Assets | AmountsIn |
| ---- | -------- | -------------- | ----------------------------- |
| 1 | INIT | \[BERA, HONEY] | \[100 `$BERA`, 1000 `$HONEY`] |
### Example 2: Exact tokens join (EXACT\_TOKENS\_IN\_FOR\_LP\_OUT)
Joining an existing BERA/HONEY pool:
| Step | JoinKind | Assets | AmountsIn | MinimumLP |
| ---- | ------------------------------- | -------------------- | --------------------- | --------- |
| 1 | EXACT\_TOKENS\_IN\_FOR\_LP\_OUT | \[`$BERA`, `$HONEY`] | \[10 BERA, 100 HONEY] | 95 LP |
### Example 3: Single token join (TOKEN\_IN\_FOR\_EXACT\_LP\_OUT)
Joining a `$BERA`/`$HONEY`/`$DAI` pool with only `$BERA`:
| Step | JoinKind | Assets | LPAmountOut | EnterTokenIndex |
| ---- | ------------------------------ | ---------------------------- | ----------- | --------------- |
| 1 | TOKEN\_IN\_FOR\_EXACT\_LP\_OUT | \[`$BERA`, `$HONEY`, `$DAI`] | 100 LP | 0 |
# Relayers
Source: https://docs.berachain.com/build/bex/concepts/relayers
Opt-in contracts that call the Vault on behalf of users for multi-step operations.
Relayers are opt-in, audited contracts that can make calls to the Vault on your behalf. They can use the sender's ERC20 vault allowance, internal balance, or BPTs after being approved.
## Types of relayers
### BalancerRelayer
The BalancerRelayer allows you to execute multiple vault actions in a single transaction by chaining them together. This improves UX by reducing the number of transactions needed for complex operations.
Example of chaining multiple actions:
```js theme={null}
// Approve relayer
const approval = buildApproval(signature);
// Chain exit pool and swap
const exitPoolCallData = buildExitPool(poolId, bptAmount);
const swapCallData = buildSwap(params);
// Execute all actions in one transaction
const tx = await relayer.multicall([approval, exitPoolCallData, swapCallData]);
```
### PoolCreationHelper
The PoolCreationHelper is a specialized relayer that simplifies pool creation and initial liquidity provision. See the PoolCreationHelper documentation for details.
Key features:
* Creates and joins pools in a single transaction
* Supports joining WBERA pools with either WBERA or BERA
* Handles both weighted and stable pool creation
## Approving a relayer
Before a relayer can act on your behalf, you must approve it using the Vault's `setRelayerApproval` function:
```solidity theme={null}
function setRelayerApproval(
address sender,
address relayer,
bool approved
) external;
```
For example, to approve the PoolCreationHelper:
```js theme={null}
const vault = new ethers.Contract(VAULT_ADDRESS, vaultAbi, wallet);
await vault.setRelayerApproval(
wallet.address, // sender
RELAYER_ADDRESS, // relayer
true // approved
);
```
Only approve relayers that have been audited and are part of the official BEX deployment.
# Single Swap
Source: https://docs.berachain.com/build/bex/concepts/single-swap
Exchange one token for another in a single BEX pool via the Vault.
A single swap in BEX allows you to exchange one token for another within a single liquidity pool by calling the `swap` function on the BEX Vault. This function is implemented in the Vault contract, which serves as the main entry point for all BEX operations.
## Swap
```solidity theme={null}
function swap(
SingleSwap memory singleSwap,
FundManagement memory funds,
uint256 limit,
uint256 deadline
) returns (uint256 amountCalculated)
```
**Parameters**
| Name | Type | Description |
| -------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **singleSwap** | SingleSwap | A definition of the swap to be executed |
| **funds** | FundManagement | A definition of where funds are going to/from |
| **limit** | uint256 | The minimum amount to receive (for GIVEN\_IN swaps) or the maximum amount to send (for GIVEN\_OUT swaps) i.e. slippage tolerance |
| **deadline** | uint256 | The UNIX timestamp by which the swap must be completed |
**Returns**
| Name | Type | Description |
| ---------------- | ------- | ---------------------------------------------------------------------------------- |
| amountCalculated | uint256 | The amount of tokens sent (for GIVEN\_IN swaps) or received (for GIVEN\_OUT swaps) |
## Structs
### SingleSwap
```solidity theme={null}
struct SingleSwap {
bytes32 poolId;
SwapKind kind;
IAsset assetIn;
IAsset assetOut;
uint256 amount;
bytes userData;
}
```
| Field | Type | Description |
| -------- | -------- | ---------------------------------------------------------------------------- |
| poolId | bytes32 | The id of the pool to swap with |
| kind | SwapKind | The type of swap to perform (GIVEN\_IN or GIVEN\_OUT) |
| assetIn | IAsset | The address of the token to swap into the pool |
| assetOut | IAsset | The address of the token to receive in return |
| amount | uint256 | The amount of tokens being sent (for GIVEN\_IN) or received (for GIVEN\_OUT) |
| userData | bytes | Any additional data required by the pool for the swap |
### FundManagement
```solidity theme={null}
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
```
| Field | Type | Description |
| ------------------- | --------------- | --------------------------------------------------------------- |
| sender | address | The address from which tokens will be taken to perform the swap |
| fromInternalBalance | bool | Whether to use tokens stored in the Vault |
| recipient | address payable | The address to which tokens will be sent after the swap |
| toInternalBalance | bool | Whether to store tokens in the recipient's internal balance |
## Example
Here's a small example of how to call the swap function in Solidity:
```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "balancer-v2-monorepo/pkg/interfaces/contracts/vault/IVault.sol";
import "balancer-v2-monorepo/pkg/interfaces/contracts/solidity-utils/openzeppelin/IERC20.sol";
contract BalancerExampleTest is Test {
IVault public vault;
address public user;
address constant VAULT_ADDRESS = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
bytes32 constant WETH_USDC_POOL_ID = 0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019;
function setUp() public {
vm.createSelectFork("rpc-url");
vault = IVault(VAULT_ADDRESS);
user = makeAddr("user");
deal(WETH, user, 1 ether);
vm.startPrank(user);
IERC20(WETH).approve(VAULT_ADDRESS, type(uint256).max);
vm.stopPrank();
}
function testDirectSwap() public {
uint256 amountIn = 1 ether;
uint256 minAmountOut = 1000 * 1e6; // 1000 USDC
uint256 balanceBefore = IERC20(USDC).balanceOf(user);
IVault.SingleSwap memory singleSwap = IVault.SingleSwap({
poolId: WETH_USDC_POOL_ID,
kind: IVault.SwapKind.GIVEN_IN,
assetIn: IAsset(WETH),
assetOut: IAsset(USDC),
amount: amountIn,
userData: ""
});
IVault.FundManagement memory funds = IVault.FundManagement({
sender: user,
fromInternalBalance: false,
recipient: payable(user),
toInternalBalance: false
});
vm.startPrank(user);
vault.swap(singleSwap, funds, minAmountOut, block.timestamp);
vm.stopPrank();
uint256 balanceAfter = IERC20(USDC).balanceOf(user);
uint256 amountReceived = balanceAfter - balanceBefore;
assertGe(amountReceived, minAmountOut, "Received less than minimum amount");
console.log("USDC received:", amountReceived);
}
}
```
# Deployed Contracts
Source: https://docs.berachain.com/build/bex/deployed-contracts
Registry of deployed BEX contract addresses by network.
On January 21st, 2025, Balancer disclosed a long-standing vulnerability in their V2 Vault implementation. BEX incorporates contract logic from Balancer V2 and shares the same vulnerability. Exercise additional caution when creating new pools, particularly when including **untrusted or newly-created tokens**.
**Funds currently deposited in BEX are safe, and no action from LPs is needed.** The issue only potentially affects tokens that are not live on-chain today. Frontend warnings are displayed on BEX for potentially vulnerable tokens.
Future plans include integrating the Balancer V3 codebase, which mitigates this vulnerability and is cross-compatible with current BEX pools.
For more information, see the [Balancer disclosure](https://forum.balancer.fi/t/balancer-v2-token-frontrun-vulnerability-disclosure/6309).
The following is a list of contract addresses for interacting with Berachain BEX.
## Mainnet contracts
```text theme={null}
0x4Be03f781C497A489E3cB0287833452cA9B9E80B
```
[Berascan](https://berascan.com/address/0x4Be03f781C497A489E3cB0287833452cA9B9E80B)
```text theme={null}
0xB8Cf46Cf1b1476E707619913a70B2085d26f1707
```
[Berascan](https://berascan.com/address/0xB8Cf46Cf1b1476E707619913a70B2085d26f1707)
```text theme={null}
0x5083737EC75a728c265BE578C9d0d5333a2c5951
```
[Berascan](https://berascan.com/address/0x5083737EC75a728c265BE578C9d0d5333a2c5951)
```text theme={null}
0x55dccE8165C88aAd4403a15A9cE3A8E244657dD2
```
[Berascan](https://berascan.com/address/0x55dccE8165C88aAd4403a15A9cE3A8E244657dD2)
```text theme={null}
0x1635F0E1B3e8A6713d03aE155ba79458Ba3240C7
```
[Berascan](https://berascan.com/address/0x1635F0E1B3e8A6713d03aE155ba79458Ba3240C7)
```text theme={null}
0xDfA30BDa0375d4763711AB0CC8D91B20bfCC87E1
```
[Berascan](https://berascan.com/address/0xDfA30BDa0375d4763711AB0CC8D91B20bfCC87E1)
```text theme={null}
0xCB4AE3030bA06F7EEE54A7B96AfcA7457f9525cf
```
[Berascan](https://berascan.com/address/0xCB4AE3030bA06F7EEE54A7B96AfcA7457f9525cf)
```text theme={null}
0x4151083172b2CEFB83A33fD7FC9F6cBabb3Fd08d
```
[Berascan](https://berascan.com/address/0x4151083172b2CEFB83A33fD7FC9F6cBabb3Fd08d)
```text theme={null}
0x6044f181aB5E9C05A4ed9Ce295f3B178d2492EE7
```
[Berascan](https://berascan.com/address/0x6044f181aB5E9C05A4ed9Ce295f3B178d2492EE7)
```text theme={null}
0xa966fA8F2d5B087FFFA499C0C1240589371Af409
```
[Berascan](https://berascan.com/address/0xa966fA8F2d5B087FFFA499C0C1240589371Af409)
```text theme={null}
0x33C88ffdEe710ed3908C791137Bd1D4421AabBBf
```
[Berascan](https://berascan.com/address/0x33C88ffdEe710ed3908C791137Bd1D4421AabBBf)
```text theme={null}
0x3C612e132624f4Bd500eE1495F54565F0bcc9b59
```
[Berascan](https://berascan.com/address/0x3C612e132624f4Bd500eE1495F54565F0bcc9b59)
## Bepolia testnet contracts
```text theme={null}
0x708cA656b68A6b7384a488A36aD33505a77241FE
```
[Berascan](https://testnet.berascan.com/address/0x708cA656b68A6b7384a488A36aD33505a77241FE)
```text theme={null}
0x05A607aCf3548E84DD1E44c3706F850c849058Da
```
[Berascan](https://testnet.berascan.com/address/0x05A607aCf3548E84DD1E44c3706F850c849058Da)
```text theme={null}
0xC7c981ADcDC5d48fed0CD52807fb2bAB22676C8f
```
[Berascan](https://testnet.berascan.com/address/0xC7c981ADcDC5d48fed0CD52807fb2bAB22676C8f)
```text theme={null}
0x0dC9964F6CA33d9EF38DEB4925234766127C6B36
```
[Berascan](https://testnet.berascan.com/address/0x0dC9964F6CA33d9EF38DEB4925234766127C6B36)
```text theme={null}
0x8c2D77f0CfcD4Af9cF41494EfE500FE324012c06
```
[Berascan](https://testnet.berascan.com/address/0x8c2D77f0CfcD4Af9cF41494EfE500FE324012c06)
```text theme={null}
0xB60DbBaCEaeC23486a64d12089F467ef949f1bb1
```
[Berascan](https://testnet.berascan.com/address/0xB60DbBaCEaeC23486a64d12089F467ef949f1bb1)
```text theme={null}
0xfD772657FC8c4Ed3884AfF151b680883814052FA
```
[Berascan](https://testnet.berascan.com/address/0xfD772657FC8c4Ed3884AfF151b680883814052FA)
```text theme={null}
0x263a1C5B2c5851beA2177eb8D6caefdfF2A25601
```
[Berascan](https://testnet.berascan.com/address/0x263a1C5B2c5851beA2177eb8D6caefdfF2A25601)
```text theme={null}
0x343215E156Ff586711a5B8C49Fe3099BAF22624C
```
[Berascan](https://testnet.berascan.com/address/0x343215E156Ff586711a5B8C49Fe3099BAF22624C)
```text theme={null}
0xf1d23276C7b271B2aC595C78977b2312E9954D57
```
[Berascan](https://testnet.berascan.com/address/0xf1d23276C7b271B2aC595C78977b2312E9954D57)
```text theme={null}
0x8119E412E00fe3c857739E95dB147817Bf615dB8
```
[Berascan](https://testnet.berascan.com/address/0x8119E412E00fe3c857739E95dB147817Bf615dB8)
```text theme={null}
0xE3723383a0EA73D5c0dE424BAA97F97f86f6cF92
```
[Berascan](https://testnet.berascan.com/address/0xE3723383a0EA73D5c0dE424BAA97F97f86f6cF92)
# Pool Creation Guide
Source: https://docs.berachain.com/build/bex/guides/pool-creation
Create BEX pools programmatically with the SDK.
This guide demonstrates how to create a new BEX pool using the PoolCreationHelper contract. The PoolCreationHelper is a [Relayer](/build/bex/concepts/relayers) allowing pools to be created and joined in a single transaction, simplifying the pool creation process.
For more comprehensive pool creation examples, see the [pool creation
examples](https://github.com/berachain/bex-sdk/tree/main/examples/pool-creation) in the BEX SDK
repository.
## Requirements
Before creating a pool, you'll need the addresses of tokens you want to include in your pool and tokens to join the pool.
## Example: creating a weighted pool
In this example, we'll create a 50/50 HONEY-WBTC weighted pool using [Ethers.js](https://docs.ethers.org/v6/). Following pool creation, the pool will be joined with 1 HONEY and 0.0001 WBTC.
```javascript theme={null}
import { ethers } from "ethers";
// Initialize provider and wallet
const provider = new ethers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Pool parameters
const poolName = "HONEY-WBTC-WEIGHTED";
const poolSymbol = "50HONEY-50WBTC";
const createPoolTokens = [honeyToken.address, wbtcToken.address].sort((a, b) =>
a.toLowerCase() < b.toLowerCase() ? -1 : 1
);
// 50-50 weights (must add up to 1e18)
const normalizedWeights = [ethers.parseUnits("0.5", 18), ethers.parseUnits("0.5", 18)];
// Initial liquidity amounts
const amountsIn = [
ethers.parseUnits("1", 18), // 1 HONEY (18 decimals)
ethers.parseUnits("0.0001", 8), // 0.0001 WBTC (8 decimals)
];
```
### Step 1 - approve the PoolCreationHelper
The PoolCreationHelper must be approved as a relayer in the Vault contract:
```javascript theme={null}
const vault = new ethers.Contract(
VAULT_ADDRESS,
["function setRelayerApproval(address sender, address relayer, bool approved)"],
wallet
);
await vault.setRelayerApproval(
wallet.address, // sender
POOL_CREATION_HELPER_ADDRESS, // relayer
true // approved
);
```
### Step 2 - approve tokens
The Vault contract needs approval to spend your tokens:
```javascript theme={null}
for (const [i, tokenAddress] of createPoolTokens.entries()) {
const tokenContract = new ethers.Contract(
tokenAddress,
["function approve(address spender, uint256 amount)"],
wallet
);
await tokenContract.approve(VAULT_ADDRESS, amountsIn[i]);
}
```
### Step 3 - create and join pool
Finally, create and join the pool in a single transaction. We leverage the function `createAndJoinWeightedPool`.
```javascript theme={null}
const poolCreationHelper = new ethers.Contract(
POOL_CREATION_HELPER_ADDRESS,
POOL_CREATION_HELPER_ABI,
wallet
);
const tx = await poolCreationHelper.createAndJoinWeightedPool(
poolName,
poolSymbol,
createPoolTokens,
createPoolTokens, // joinPoolTokens same as createPoolTokens
normalizedWeights,
createPoolTokens.map(() => ethers.ZeroAddress), // no rate providers
ethers.parseUnits("0.01", 18), // 1% swap fee
amountsIn,
wallet.address, // pool owner
ethers.keccak256(ethers.toUtf8Bytes(`${poolName}-${wallet.address}`)) // salt
);
```
# Error Codes
Source: https://docs.berachain.com/build/bex/help/error-codes
BEX smart contract error codes and meanings.
All error codes for BEX core contracts are defined in `BalancerErrors.sol`. Comments and context for errors are provided below:
## Math
| Code | Error | Comment |
| ---- | ------------------------ | --------------------------------------------------------------- |
| 000 | ADD\_OVERFLOW | |
| 001 | SUB\_OVERFLOW | |
| 002 | SUB\_UNDERFLOW | |
| 003 | MUL\_OVERFLOW | |
| 004 | ZERO\_DIVISION | |
| 005 | DIV\_INTERNAL | Multiplication overflow during FixedPoint Division |
| 006 | X\_OUT\_OF\_BOUNDS | Invalid x in ExpMath.pow(x, y) |
| 007 | Y\_OUT\_OF\_BOUNDS | Invalid y in ExpMath.pow(x, y) |
| 008 | PRODUCT\_OUT\_OF\_BOUNDS | In LogExpMath.pow(x, y), error computing x^y as exp(y \* ln(x)) |
| 009 | INVALID\_EXPONENT | In LogExpMath.exp(x) = e^x; x out of bounds |
## Input
| Code | Error | Comment |
| ---- | ----------------------- | ------------------------------------------------------------------------ |
| 100 | OUT\_OF\_BOUNDS | |
| 101 | UNSORTED\_ARRAY | See UNSORTED\_TOKENS |
| 102 | UNSORTED\_TOKENS | Tokens must be sorted in address order on pool registration |
| 103 | INPUT\_LENGTH\_MISMATCH | Used to ensure array inputs intended to be parallel have the same length |
| 104 | ZERO\_TOKEN | Address to be interpreted as a token cannot be 0 |
| 105 | INSUFFICIENT\_DATA | Used to ensure minimum bytes length |
## Shared pools
| Code | Error | Comment |
| ---- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 200 | MIN\_TOKENS | All pools must contain at least two tokens |
| 201 | MAX\_TOKENS | Token count exceeds the maximum for a given pool type |
| 202 | MAX\_SWAP\_FEE\_PERCENTAGE | |
| 203 | MIN\_SWAP\_FEE\_PERCENTAGE | |
| 204 | MINIMUM\_BPT | On pool initialization, a small amount of BPT is minted to the zero address (keeps math well behaved). If initial balances are too small, initialization can fail |
| 205 | CALLER\_NOT\_VAULT | Certain pool callbacks need to be external, but could be exploited if called by anyone but the Vault |
| 206 | UNINITIALIZED | Pools must be initialized with a special "Init" join, before they can be joined by LPs |
| 207 | BPT\_IN\_MAX\_AMOUNT | Slippage/front-running protection check failed on a pool exit |
| 208 | BPT\_OUT\_MIN\_AMOUNT | Slippage/front-running protection check failed on a pool join |
| 209 | EXPIRED\_PERMIT | |
| 210 | NOT\_TWO\_TOKENS | Pools with oracles are limited to two tokens (e.g., WeightedPool2Tokens and MetastablePools). A pool with the TWO\_TOKEN specialization must have exactly two tokens |
| 211 | DISABLED | Pool factories can be disabled to prevent new pools being created |
## Pools
| Code | Error | Comment |
| ---- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| 300 | MIN\_AMP | Amplification factor out of range (Stable/Metastable pools) |
| 301 | MAX\_AMP | |
| 302 | MIN\_WEIGHT | Weighted Pool minimum weight |
| 303 | MAX\_STABLE\_TOKENS | |
| 304 | MAX\_IN\_RATIO | Token in unbalanced the pool too much on a swap |
| 305 | MAX\_OUT\_RATIO | Token out unbalanced the pool too much on a swap |
| 306 | MIN\_BPT\_IN\_FOR\_TOKEN\_OUT | Disproportionate exit unbalanced the pool too much |
| 307 | MAX\_OUT\_BPT\_FOR\_TOKEN\_IN | Disproportionate join unbalanced the pool too much |
| 308 | NORMALIZED\_WEIGHT\_INVARIANT | Weighted Pool normalized weights must add to 1.0 |
| 309 | INVALID\_TOKEN | |
| 310 | UNHANDLED\_JOIN\_KIND | Some joins are pool type-specific |
| 311 | ZERO\_INVARIANT | Pool balances must be > 0 |
| 312 | ORACLE\_INVALID\_SECONDS\_QUERY | The "ago" timestamp when querying the oracle must be in the past |
| 313 | ORACLE\_NOT\_INITIALIZED | Cannot query an oracle with no data |
| 314 | ORACLE\_QUERY\_TOO\_OLD | Cannot query before the oracle's earliest data sample |
| 315 | ORACLE\_INVALID\_INDEX | Cannot query a sample outside the buffer (1024) |
| 316 | ORACLE\_BAD\_SECS | Oracle query window must have non-zero duration |
| 317 | AMP\_END\_TIME\_TOO\_CLOSE | Amplification parameter change has less than the minimum duration |
| 318 | AMP\_ONGOING\_UPDATE | Cannot start an amplification parameter update if one is already ongoing |
| 319 | AMP\_RATE\_TOO\_HIGH | The requested amplification parameter change is too fast (cannot halve or double over less than a day) |
| 320 | AMP\_NO\_ONGOING\_UPDATE | Cannot cancel an update if there isn't one |
| 321 | STABLE\_INVARIANT\_DIDNT\_CONVERGE | |
| 322 | STABLE\_GET\_BALANCE\_DIDNT\_CONVERGE | |
| 323 | RELAYER\_NOT\_CONTRACT | |
| 324 | BASE\_POOL\_RELAYER\_NOT\_CALLED | |
| 325 | REBALANCING\_RELAYER\_REENTERED | |
| 326 | GRADUAL\_UPDATE\_TIME\_TRAVEL | start > end time in a gradual weights update |
| 327 | SWAPS\_DISABLED | |
| 328 | CALLER\_IS\_NOT\_LBP\_OWNER | |
| 329 | PRICE\_RATE\_OVERFLOW | Rate returned from a rateProvider must fit in 128 bits |
| 330 | INVALID\_JOIN\_EXIT\_KIND\_WHILE\_SWAPS\_DISABLED | Investment pools only allow proportional joins and exits when swaps are disabled (to prevent unbalancing the pool) |
| 331 | WEIGHT\_CHANGE\_TOO\_FAST | Gradual weight update duration too short (minimum 1 day) |
| 332 | LOWER\_GREATER\_THAN\_UPPER\_TARGET | Invalid Linear Pool operating range |
| 333 | UPPER\_TARGET\_TOO\_HIGH | Linear Pool max balance must fit in 112 bits |
| 334 | UNHANDLED\_BY\_LINEAR\_POOL | Some joins/exits are pool type-specific |
| 335 | OUT\_OF\_TARGET\_RANGE | Cannot reset Linear Pool targets if pool is unbalanced |
| 336 | UNHANDLED\_EXIT\_KIND | Some exits are pool type-specific |
| 337 | UNAUTHORIZED\_EXIT | Management fees can only be collected by the pool owner |
| 338 | MAX\_MANAGEMENT\_SWAP\_FEE\_PERCENTAGE | |
| 339 | UNHANDLED\_BY\_MANAGED\_POOL | Some joins/exits are pool type-specific |
| 340 | UNHANDLED\_BY\_PHANTOM\_POOL | Some joins/exits are pool type-specific |
| 341 | TOKEN\_DOES\_NOT\_HAVE\_RATE\_PROVIDER | |
| 342 | INVALID\_INITIALIZATION | |
| 343 | OUT\_OF\_NEW\_TARGET\_RANGE | |
| 344 | FEATURE\_DISABLED | |
| 345 | UNINITIALIZED\_POOL\_CONTROLLER | |
| 346 | SET\_SWAP\_FEE\_DURING\_FEE\_CHANGE | |
| 347 | SET\_SWAP\_FEE\_PENDING\_FEE\_CHANGE | |
| 348 | CHANGE\_TOKENS\_DURING\_WEIGHT\_CHANGE | |
| 349 | CHANGE\_TOKENS\_PENDING\_WEIGHT\_CHANGE | |
| 350 | MAX\_WEIGHT | |
| 351 | UNAUTHORIZED\_JOIN | |
| 352 | MAX\_MANAGEMENT\_AUM\_FEE\_PERCENTAGE | |
| 353 | FRACTIONAL\_TARGET | |
| 354 | ADD\_OR\_REMOVE\_BPT | |
| 355 | INVALID\_CIRCUIT\_BREAKER\_BOUNDS | |
| 356 | CIRCUIT\_BREAKER\_TRIPPED | |
| 357 | MALICIOUS\_QUERY\_REVERT | |
| 358 | JOINS\_EXITS\_DISABLED | |
## Lib
| Code | Error | Comment |
| ---- | ---------------------------------------- | ----------------------------------------------------------------------- |
| 400 | REENTRANCY | |
| 401 | SENDER\_NOT\_ALLOWED | |
| 402 | PAUSED | |
| 403 | PAUSE\_WINDOW\_EXPIRED | |
| 404 | MAX\_PAUSE\_WINDOW\_DURATION | |
| 405 | MAX\_BUFFER\_PERIOD\_DURATION | |
| 406 | INSUFFICIENT\_BALANCE | |
| 407 | INSUFFICIENT\_ALLOWANCE | |
| 408 | ERC20\_TRANSFER\_FROM\_ZERO\_ADDRESS | |
| 409 | ERC20\_TRANSFER\_TO\_ZERO\_ADDRESS | |
| 410 | ERC20\_MINT\_TO\_ZERO\_ADDRESS | |
| 411 | ERC20\_BURN\_FROM\_ZERO\_ADDRESS | |
| 412 | ERC20\_APPROVE\_FROM\_ZERO\_ADDRESS | |
| 413 | ERC20\_APPROVE\_TO\_ZERO\_ADDRESS | |
| 414 | ERC20\_TRANSFER\_EXCEEDS\_ALLOWANCE | |
| 415 | ERC20\_DECREASED\_ALLOWANCE\_BELOW\_ZERO | |
| 416 | ERC20\_TRANSFER\_EXCEEDS\_BALANCE | |
| 417 | ERC20\_BURN\_EXCEEDS\_ALLOWANCE | |
| 418 | SAFE\_ERC20\_CALL\_FAILED | |
| 419 | ADDRESS\_INSUFFICIENT\_BALANCE | |
| 420 | ADDRESS\_CANNOT\_SEND\_VALUE | |
| 421 | SAFE\_CAST\_VALUE\_CANT\_FIT\_INT256 | |
| 422 | GRANT\_SENDER\_NOT\_ADMIN | In AccessControl, the caller of `grantRole` must be an admin |
| 423 | REVOKE\_SENDER\_NOT\_ADMIN | In AccessControl, the caller of `revokeRole` must be an admin |
| 424 | RENOUNCE\_SENDER\_NOT\_ALLOWED | In AccessControl, callers can only `renounceRole` for their own account |
| 425 | BUFFER\_PERIOD\_EXPIRED | |
| 426 | CALLER\_IS\_NOT\_OWNER | |
| 427 | NEW\_OWNER\_IS\_ZERO | |
| 428 | CODE\_DEPLOYMENT\_FAILED | |
| 429 | CALL\_TO\_NON\_CONTRACT | |
| 430 | LOW\_LEVEL\_CALL\_FAILED | |
| 431 | NOT\_PAUSED | |
| 432 | ADDRESS\_ALREADY\_ALLOWLISTED | |
| 433 | ADDRESS\_NOT\_ALLOWLISTED | |
| 434 | ERC20\_BURN\_EXCEEDS\_BALANCE | |
| 435 | INVALID\_OPERATION | |
| 436 | CODEC\_OVERFLOW | |
| 437 | IN\_RECOVERY\_MODE | |
| 438 | NOT\_IN\_RECOVERY\_MODE | |
| 439 | INDUCED\_FAILURE | |
| 440 | EXPIRED\_SIGNATURE | |
| 441 | MALFORMED\_SIGNATURE | |
| 442 | SAFE\_CAST\_VALUE\_CANT\_FIT\_UINT64 | |
| 443 | UNHANDLED\_FEE\_TYPE | |
| 444 | BURN\_FROM\_ZERO | |
## Vault
| Code | Error | Comment |
| ---- | ---------------------------------- | ----------------------------------------------------------------------- |
| 500 | INVALID\_POOL\_ID | |
| 501 | CALLER\_NOT\_POOL | Some Vault hooks can only be called by the pool (e.g., register tokens) |
| 502 | SENDER\_NOT\_ASSET\_MANAGER | |
| 503 | USER\_DOESNT\_ALLOW\_RELAYER | Relayers must be allowed by both governance and the user account |
| 504 | INVALID\_SIGNATURE | |
| 505 | EXIT\_BELOW\_MIN | Exit would yield fewer than the user-supplied minimum tokens out |
| 506 | JOIN\_ABOVE\_MAX | Join would cost more than the user-supplied maximum tokens in |
| 507 | SWAP\_LIMIT | Swap violates user-supplied limits (min out or max in) |
| 508 | SWAP\_DEADLINE | Swap transaction not mined within the specified deadline |
| 509 | CANNOT\_SWAP\_SAME\_TOKEN | |
| 510 | UNKNOWN\_AMOUNT\_IN\_FIRST\_SWAP | A batch swap must start with a non-zero amount in |
| 511 | MALCONSTRUCTED\_MULTIHOP\_SWAP | |
| 512 | INTERNAL\_BALANCE\_OVERFLOW | Unused in current code |
| 513 | INSUFFICIENT\_INTERNAL\_BALANCE | |
| 514 | INVALID\_ETH\_INTERNAL\_BALANCE | Cannot transfer native ETH to/from internal balance |
| 515 | INVALID\_POST\_LOAN\_BALANCE | Flashloan transactions must repay the loan in the same transaction |
| 516 | INSUFFICIENT\_ETH | |
| 517 | UNALLOCATED\_ETH | Unused in current code |
| 518 | ETH\_TRANSFER | Relayers cannot receive ETH directly (only through the Vault) |
| 519 | CANNOT\_USE\_ETH\_SENTINEL | Internal Balance transfers cannot use ETH |
| 520 | TOKENS\_MISMATCH | |
| 521 | TOKEN\_NOT\_REGISTERED | |
| 522 | TOKEN\_ALREADY\_REGISTERED | |
| 523 | TOKENS\_ALREADY\_SET | |
| 524 | TOKENS\_LENGTH\_MUST\_BE\_2 | |
| 525 | NONZERO\_TOKEN\_BALANCE | |
| 526 | BALANCE\_TOTAL\_OVERFLOW | |
| 527 | POOL\_NO\_TOKENS | |
| 528 | INSUFFICIENT\_FLASH\_LOAN\_BALANCE | |
## Fees
| Code | Error | Comment |
| ---- | --------------------------------------- | ------- |
| 600 | SWAP\_FEE\_PERCENTAGE\_TOO\_HIGH | |
| 601 | FLASH\_LOAN\_FEE\_PERCENTAGE\_TOO\_HIGH | |
| 602 | INSUFFICIENT\_FLASH\_LOAN\_FEE\_AMOUNT | |
## FeeSplitter
| Code | Error | Comment |
| ---- | ------------------------------------ | ------- |
| 700 | SPLITTER\_FEE\_PERCENTAGE\_TOO\_HIGH | |
## Misc
| Code | Error | Comment |
| ---- | ------------------- | ------- |
| 998 | UNIMPLEMENTED | |
| 999 | SHOULD\_NOT\_HAPPEN | |
# Overview
Source: https://docs.berachain.com/build/bex/overview
BEX on Berachain: native DEX, deployed contracts, SDK, pool concepts, and guides.
BEX is Berachain’s native decentralized exchange. Use it to integrate swaps, liquidity pools, and LPs into your app—with the same Balancer-style vault and pool patterns you may already know.
## Where to go next
* **[Deployed Contracts](/build/bex/deployed-contracts)** — Contract addresses and ABIs for Mainnet and Bepolia.
* **[Guides](/build/bex/sdk/overview)** — SDK usage (swap, add/remove liquidity, SOR, reference), pool creation, and error codes.
* **[Concepts](/build/bex/concepts/single-swap)** — Single and batch swaps, pool interfacing, joins and exits, LP valuation, and relayers.
# Add Liquidity Guide
Source: https://docs.berachain.com/build/bex/sdk/add-liquidity
Add liquidity to BEX pools with the SDK.
Using the [BEX SDK](https://github.com/berachain/berancer-sdk), you can add liquidity using two primary methods (see [AddLiquidityKind](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L12)):
1. *Unbalanced* - add liquidity with arbitrary amounts of each token
2. *Proportional* - add liquidity with proportional amount of both tokens
For more comprehensive add liquidity examples, see the [add liquidity
examples](https://github.com/berachain/bex-sdk/tree/main/examples/add-liquidity) in the BEX SDK
repository. For price impact calculation examples, see the [price impact
examples](https://github.com/berachain/bex-sdk/tree/main/examples/priceImpact).
## Example: adding unbalanced liquidity
In this example, we use the BEX SDK and [Ethers.js](https://docs.ethers.org/v6/) to add single-token `BERA` liquidity to a given pool.
```javascript theme={null}
import { ethers } from "ethers";
import {
BalancerApi,
AddLiquidity,
AddLiquidityKind,
Slippage,
PriceImpact,
} from "@berachain-foundation/berancer-sdk";
// Initialize provider and wallet
const RPC_URL = "https://rpc.berachain.com/";
const provider = new ethers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const balancerApi = new BalancerApi("https://api.berachain.com/", 80094);
// Get pool data
const poolId = "POOL_ID";
const poolState = await balancerApi.pools.fetchPoolState(poolId);
// Prepare add liquidity input - note we're only adding one token for unbalanced
const addLiquidityInput = {
chainId: CHAIN_ID,
kind: AddLiquidityKind.Unbalanced,
rpcUrl: RPC_URL,
amountsIn: [
{
address: WBERA_TOKEN,
decimals: 18,
rawAmount: ethers.parseUnits("0.1", 18), // 0.1 BERA
},
],
};
const addLiquidity = new AddLiquidity();
// Query expected BPT out and calculate price impact in parallel
const [queryOutput, priceImpact] = await Promise.all([
addLiquidity.query(addLiquidityInput, poolState),
PriceImpact.addLiquidity(addLiquidityInput, poolState),
]);
console.log("Expected BPT Out:", ethers.formatUnits(queryOutput.bptOut.amount, 18));
console.log("Price Impact:", priceImpact.percentage, "%");
// Build transaction with 1% slippage
const slippage = Slippage.fromPercentage("1");
const deadline = BigInt(Math.floor(Date.now() / 1000) + 60);
const callData = addLiquidity.buildCall({
...queryOutput,
chainId: CHAIN_ID,
sender: wallet.address,
poolId: poolState.id,
recipient: wallet.address,
wethIsEth: true,
slippage,
deadline,
});
// Send transaction
const tx = await wallet.sendTransaction({
to: callData.to,
data: callData.callData,
value: callData.value,
});
console.log("Transaction sent:", tx.hash);
const receipt = await tx.wait();
```
Below we breakdown the code example above.
### Helper classes
The main helper classes we use from the SDK are:
* `BalancerApi` - to simplify retrieving pool data from the Pools API
* `AddLiquidity` - to build addLiquidity queries and transactions
* `Slippage` - to simplify creating limits with user defined slippage
* `PriceImpact` - to calculate the price impact of unbalanced liquidity additions
### Fetching pool data
After initializing the `BalancerApi` class, we can fetch current pool data using `fetchPoolState`.
```javascript theme={null}
const balancerApi = new BalancerApi("https://api.berachain.com/", 80094);
// Get pool data
const poolId = "POOL_ID";
const poolState = await balancerApi.pools.fetchPoolState(poolId);
```
### Simulation and price impact
The `AddLiquidity` class has a `query` method that allows us to simulate the add liquidity transaction. This is useful for estimating the expected output and slippage. It takes in the [`addLiquidityInput`](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L42) and `poolState` as arguments.
The `PriceImpact` class calculates the price impact of unbalanced liquidity operations. When adding liquidity with unbalanced amounts (adding only one token or different proportions than the pool), the pool's token ratios change, resulting in price impact. A [PriceImpactAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/priceImpactAmount.ts#L4) is returned, with the price impact expressed in a number of different units.
```javascript theme={null}
// Query expected BPT out and calculate price impact in parallel
const [queryOutput, priceImpact] = await Promise.all([
addLiquidity.query(addLiquidityInput, poolState),
PriceImpact.addLiquidity(addLiquidityInput, poolState),
]);
console.log("Expected BPT Out:", ethers.formatUnits(queryOutput.bptOut.amount, 18));
console.log("Price Impact:", priceImpact.percentage, "%");
// Build transaction with 1% slippage
const slippage = Slippage.fromPercentage("1");
```
The price impact calculation is especially important for unbalanced liquidity additions, as they can significantly alter the pool's token ratios. Use this information to inform your slippage settings and warn about potential price impacts.
### Building the transaction
The `AddLiquidity` class has a `buildCall` method that allows us to build the transaction. This method takes in the `queryOutput` and address parameters.
```javascript theme={null}
const callData = addLiquidity.buildCall({
...queryOutput,
sender: wallet.address,
recipient: wallet.address,
wethIsEth: true,
slippage,
deadline,
});
```
## Adding proportional liquidity
Adding proportional liquidity is similar to adding unbalanced liquidity, but instead of specifying the amount of each token, you specify a `referenceAmount` for one of the tokens. (see [AddLiquidityProportionalInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L37)) The SDK will then calculate the amount of the other token to add based on the current pool composition.
```javascript theme={null}
addLiquidityInput = {
chainId,
kind: AddLiquidityKind.Proportional,
rpcUrl: RPC_URL,
referenceAmount: {
rawAmount: parseUnits(tokenAmount, tokenDecimals),
decimals: tokenDecimals,
address: tokenAddress,
},
};
```
# Berancer SDK
Source: https://docs.berachain.com/build/bex/sdk/overview
BEX SDK introduction, installation, and usage.
The Berancer SDK is a Typescript library for interfacing with the BEX protocol. This includes common contract interactions such as add/remove liquidity and swaps. The SDK is adapted from the [Balancer SDK](https://github.com/balancer/balancer-sdk).
[](https://www.npmjs.com/package/@berachain-foundation/berancer-sdk/v/latest)
## Installation
Install the package with
```bash pnpm theme={null}
pnpm add @berachain-foundation/berancer-sdk
```
```bash yarn theme={null}
yarn add @berachain-foundation/berancer-sdk
```
```bash npm theme={null}
npm install @berachain-foundation/berancer-sdk
```
# SDK API Reference
Source: https://docs.berachain.com/build/bex/sdk/reference
BEX SDK API reference documentation.
The SDK API Reference is a comprehensive guide to the Berancer SDK's API. It includes all the functions and classes available in the SDK, as well as their parameters and return values.
## API initialization
The `BalancerApi` class provides helper functions for interacting with the Berancer API.
### Constructor
```typescript theme={null}
const balancerApi = new BalancerApi(apiUrl: string, chainId: ChainId);
```
| Name | Type | Description | Mainnet Value |
| ------- | ------ | -------------------------- | ---------------------------- |
| apiUrl | string | Url of API | `https://api.berachain.com/` |
| chainId | number | Chain that will be queried | `80094` |
### Properties
The `BalancerApi` class initializes the following properties:
* [Pools](#pools) - Methods for interacting with individual pools
* [NestedPools](#nested-pools) - Methods for working with nested pools
* [SorSwapPaths](#sorswappaths) - Methods for optimizing swap paths
## Pools
### pools.fetchPoolState
Finds state of given pool.
```typescript theme={null}
pools.fetchPoolState(id: string): Promise
```
**Parameters**
| Name | Type | Description |
| ---- | ------ | --------------------------------- |
| id | string | ID of pool, v2=poolId, v3=address |
**Returns**
```typescript theme={null}
Promise;
```
[PoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L5) - State of given pool.
### pools.fetchPoolStateWithBalances
Finds state of given pool including token balances and pool shares.
```typescript theme={null}
fetchPoolStateWithBalances(
id: string,
): Promise
```
**Parameters**
| Name | Type | Description |
| ---- | ------ | ----------- |
| id | string | poolId |
**Returns**
```typescript theme={null}
Promise;
```
[PoolStateWithBalances](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L13) - State of given pool including token balances and pool shares.
## Nested pools
### nestedPools.fetchPoolState
Finds state of a set of nested pools.
```typescript theme={null}
fetchNestedPoolState(id: string): Promise
```
**Parameters**
| Name | Type | Description |
| ---- | ------ | --------------------------------- |
| id | string | ID of pool, v2=poolId, v3=address |
**Returns**
```typescript theme={null}
Promise;
```
[NestedPoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L101) - state of a set of nested pools.
## SorSwapPaths
### sorSwapPaths.fetchSorSwapPaths
Finds optimized swap paths for a given swap config.
```typescript theme={null}
fetchSorSwapPaths(sorInput: SorInput): Promise
```
**Parameters**
| Name | Type | Description |
| -------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| sorInput | [SorInput](https://github.com/berachain/berancer-sdk/blob/main/src/data/providers/balancer-api/modules/sorSwapPaths/index.ts#L9) | Swap configs |
**Returns**
```typescript theme={null}
Promise;
```
[Path\[\]](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/paths/types.ts#L6) - optimized swap paths for the given swap.
## AddLiquidity
This class provides functionality to:
* Perform on-chain queries to see the result of an addLiquidity operation
* Build an addLiquidity transaction, with slippage, for a consumer to submit
* Supported add types: SingleToken, Unbalanced, Proportional
### Constructor
```typescript theme={null}
const addLiquidity = new AddLiquidity();
```
### Methods
### query
Simulate addLiquidity operation by using an onchain call.
```typescript theme={null}
query(
input: AddLiquidityInput,
poolState: PoolState
): Promise
```
**Parameters**
| Name | Type | Description |
| --------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| input | [AddLiquidityInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L42) | User defined inputs |
| poolState | [PoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L5) | Current state of pool that liquidity is being added to |
**Returns**
```typescript theme={null}
Promise;
```
[AddLiquidityQueryOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L59) - Data that can be passed to `buildCall`. Includes updated `bptOut` amount.
***
### buildCall
Builds the addLiquidity transaction using user defined slippage.
```typescript theme={null}
buildCall(input: AddLiquidityBuildCallInput): AddLiquidityBuildCallOutput
```
**Parameters**
| Name | Type | Description |
| ----- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| input | [AddLiquidityBuildCallInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L63) | Parameters required to build the call including user defined slippage |
**Returns**
```typescript theme={null}
AddLiquidityBuildCallOutput;
```
[AddLiquidityBuildCallOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L84) - Encoded call data for addLiquidity that the user can submit.
***
### buildCallWithPermit2
Builds the addLiquidity transaction using user defined slippage and Permit2 signature for token approval.
Check out the [Permit2 Helper section](#permit2-helper) on how to generate a Permit2 signature.
```typescript theme={null}
buildCallWithPermit2(input: AddLiquidityBuildCallInput, permit2: Permit2): AddLiquidityBuildCallOutput
```
**Parameters**
| Name | Type | Description |
| ------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| input | [AddLiquidityBuildCallInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L68) | Parameters required to build the call including user defined slippage |
| permit2 | [Permit2](https://github.com/berachain/berancer-sdk/blob/main/src/entities/permit2Helper/index.ts#L35) | Permit2 object with metadata and encoded signature |
**Returns**
```typescript theme={null}
AddLiquidityBuildCallOutput;
```
[AddLiquidityBuildCallOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L84) - Encoded call data for addLiquidity that the user can submit.
***
## RemoveLiquidity
This class provides functionality to:
* Perform on-chain queries to see the result of a removeLiquidity operation
* Build a removeLiquidity transaction, with slippage, for a consumer to submit
* Supported remove types: Unbalanced, SingleTokenExactOutInput, SingleTokenExactInInput, Proportional
### Constructor
```typescript theme={null}
const removeLiquidity = new RemoveLiquidity();
```
### Methods
### query
Simulate removeLiquidity operation by using an onchain call.
```typescript theme={null}
query(
input: RemoveLiquidityInput,
poolState: PoolState,
): Promise
```
**Parameters**
| Name | Type | Description |
| --------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| input | [RemoveLiquidityInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L58) | User defined inputs |
| poolState | [PoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L5) | Current state of pool that liquidity is being removed from |
**Returns**
```typescript theme={null}
Promise;
```
[RemoveLiquidityQueryOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L78) - Data that can be passed to `buildCall`. Includes updated `amountsOut` amount.
***
### queryRemoveLiquidityRecovery
Calculates proportional exit using pool state. Note - this does not do an onchain query.
```typescript theme={null}
queryRemoveLiquidityRecovery(
input: RemoveLiquidityRecoveryInput,
poolState: PoolState,
): Promise
```
**Parameters**
| Name | Type | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| input | [RemoveLiquidityRecoveryInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L53) | User defined inputs |
| poolState | [PoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L5) | Current state of pool that liquidity is being removed from |
**Returns**
```typescript theme={null}
Promise;
```
[RemoveLiquidityQueryOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L78) - Data that can be passed to `buildCall`. Includes updated `amountsOut` amount.
***
### buildCall
Builds the removeLiquidity transaction using user defined slippage.
```typescript theme={null}
buildCall(
input: RemoveLiquidityBuildCallInput,
): RemoveLiquidityBuildCallOutput
```
**Parameters**
| Name | Type | Description |
| ----- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| input | [RemoveLiquidityBuildCallInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L87) | Input with user defined slippage |
**Returns**
```typescript theme={null}
RemoveLiquidityBuildCallOutput;
```
[RemoveLiquidityBuildCallOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L92) - Encoded call data for removeLiquidity that the user can submit.
***
### buildCallWithPermit
Builds the removeLiquidity transaction using user defined slippage and Permit signature for token approval.
Check out the [Permit Helper section](#permit-helper) on how to generate a Permit signature.
```typescript theme={null}
buildCallWithPermit(
input: RemoveLiquidityBuildCallInput,
permit: Permit,
): RemoveLiquidityBuildCallOutput
```
**Parameters**
| Name | Type | Description |
| ------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------- |
| input | [RemoveLiquidityBuildCallInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L87) | Input with user defined slippage |
| permit | [Permit](https://github.com/berachain/berancer-sdk/blob/main/src/entities/permitHelper/index.ts#L30) | Permit object with metadata and encoded signature |
**Returns**
```typescript theme={null}
RemoveLiquidityBuildCallOutput;
```
[RemoveLiquidityBuildCallOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L92) - Encoded call data for removeLiquidity that the user can submit.
## Swap
This class provides functionality to:
* Perform on-chain queries to see the result of a Swap operation
* Build a Swap transaction, with slippage, for a consumer to submit
### Constructor
```typescript theme={null}
const swap = new Swap(swapInput: SwapInput);
```
| Name | Type | Description |
| --------- | ---------------------------------------------------------------------------------------------- | -------------------------------------- |
| swapInput | [SwapInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L8) | Swap input including path information. |
Note: `SwapInput` data is normally returned from an API SOR query but may be constructed manually.
### Methods
### query
Gets up to date swap result by querying onchain.
```typescript theme={null}
query(
rpcUrl?: string,
block?: bigint,
): Promise
```
**Parameters**
| Name | Type | Description |
| ----------------- | ------ | ----------------------------- |
| rpcUrl (optional) | string | RPC URL, e.g. Infura/Alchemy |
| block (optional) | bigint | Block no to perform the query |
**Returns**
```typescript theme={null}
Promise;
```
[ExactInQueryOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L46) |
[ExactOutQueryOutput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L52)
The updated return for the given swap, either `expectedAmountOut` or `expectedAmountIn` depending on swap kind.
***
### buildCall
Builds the swap transaction using user defined slippage.
```typescript theme={null}
buildCall(
input: SwapBuildCallInput,
): SwapBuildOutputExactIn | SwapBuildOutputExactOut
```
**Parameters**
| Name | Type | Description |
| ----- | -------------------------------------------------------------------------------------------------------- | -------------------------------- |
| input | [SwapBuildCallInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L22) | Input with user defined slippage |
**Returns**
```typescript theme={null}
SwapBuildOutputExactIn | SwapBuildOutputExactOut;
```
[SwapBuildOutputExactIn](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L32) | [SwapBuildOutputExactOut](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L36) - Encoded call data for swap that the user can submit. Includes `minAmountOut` or `maxAmountIn` depending on swap kind.
***
### buildCallWithPermit2
Builds the swap transaction using user defined slippage and Permit2 signature for token approval.
Check out the [Permit2 Helper section](#permit2-helper) on how to generate a Permit2 signature.
```typescript theme={null}
buildCallWithPermit2(
input: SwapBuildCallInput,
permit2: Permit2,
): SwapBuildOutputExactIn | SwapBuildOutputExactOut
```
**Parameters**
| Name | Type | Description |
| ------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| input | [SwapBuildCallInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L22) | Input with user defined slippage |
| permit2 | [Permit2](https://github.com/berachain/berancer-sdk/blob/main/src/entities/permit2Helper/index.ts#L36) | Permit2 object with metadata and encoded signature |
**Returns**
```typescript theme={null}
SwapBuildOutputExactIn | SwapBuildOutputExactOut;
```
[SwapBuildOutputExactIn](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L32) | [SwapBuildOutputExactOut](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L36) - Encoded call data for swap that the user can submit. Includes `minAmountOut` or `maxAmountIn` depending on swap kind.
***
### quote
Gives the combined return amount for all paths. Note - this always uses the original path amounts provided in constructor and does not get updated.
```typescript theme={null}
public get quote(): TokenAmount
```
**Returns**
```typescript theme={null}
TokenAmount;
```
[TokenAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/tokenAmount.ts) - Gives the combined return amount for all paths (output amount for givenIn, input amount for givenOut).
***
### inputAmount
```typescript theme={null}
public get inputAmount(): TokenAmount
```
**Returns**
```typescript theme={null}
TokenAmount;
```
[TokenAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/tokenAmount.ts) - Gives the combined input amount for all paths.
***
### outputAmount
```typescript theme={null}
public get outputAmount(): TokenAmount
```
**Returns**
```typescript theme={null}
TokenAmount;
```
[TokenAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/tokenAmount.ts) - Gives the combined output amount for all paths.
***
### queryCallData
```typescript theme={null}
public queryCallData(): string
```
**Returns**
```typescript theme={null}
string;
```
Encoded query data for swap that you can call to get an updated amount.
***
## PriceImpact
This class provides helper functions to calculate Price Impact for add/remove/swap actions.
### Methods
### addLiquiditySingleToken
Calculate price impact on add liquidity single token operations.
```typescript theme={null}
addLiquiditySingleToken(
input: AddLiquiditySingleTokenInput,
poolState: PoolState,
): Promise
```
**Parameters**
| Name | Type | Description |
| --------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| input | [AddLiquiditySingleTokenInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L31) | Same input used in the corresponding add liquidity operation |
| poolState | [PoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L5) | Current state of pool that liquidity is being added to |
**Returns**
```typescript theme={null}
Promise;
```
[PriceImpactAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/priceImpactAmount.ts) - Price impact for operation.
***
### addLiquidityUnbalanced
Calculate price impact on add liquidity unbalanced operations.
```typescript theme={null}
addLiquidityUnbalanced = async (
input: AddLiquidityUnbalancedInput,
poolState: PoolState,
): Promise
```
**Parameters**
| Name | Type | Description |
| --------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| input | AddLiquidityUnbalancedInput | Same input used in the corresponding add liquidity operation |
| poolState | [PoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L5) | Current state of pool that liquidity is being added to |
**Returns**
```typescript theme={null}
Promise;
```
[PriceImpactAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/priceImpactAmount.ts) - Price impact for operation.
***
### addLiquidityNested
Calculate price impact on add liquidity nested token operations.
```typescript theme={null}
addLiquidityNested = async (
input: AddLiquidityNestedInput,
nestedPoolState: NestedPoolState,
): Promise
```
**Parameters**
| Name | Type | Description |
| --------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| input | [AddLiquidityNestedInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L26) | Same input used in the corresponding add liquidity operation |
| nestedPoolState | [NestedPoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L101) | Current state of nested pools |
**Returns**
```typescript theme={null}
Promise;
```
[PriceImpactAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/priceImpactAmount.ts) - Price impact for operation.
***
### removeLiquidity
Calculate price impact on remove liquidity operations.
```typescript theme={null}
removeLiquidity = async (
input:
| RemoveLiquiditySingleTokenExactInInput
| RemoveLiquidityUnbalancedInput,
poolState: PoolState,
): Promise
```
**Parameters**
| Name | Type | Description |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| input | [RemoveLiquiditySingleTokenExactInInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L35) | Same input used in the corresponding remove liquidity operation |
| input | [RemoveLiquidityUnbalancedInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L30) | Same input used in the corresponding remove liquidity operation |
| poolState | [PoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L5) | Current state of pool that liquidity is being removed from |
**Returns**
```typescript theme={null}
Promise;
```
[PriceImpactAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/priceImpactAmount.ts) - Price impact for operation.
***
### removeLiquidityNested
Calculate price impact on remove liquidity single token nested operations.
```typescript theme={null}
removeLiquidityNested = async (
input: RemoveLiquidityNestedSingleTokenInput,
nestedPoolState: NestedPoolState,
): Promise
```
**Parameters**
| Name | Type | Description |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| input | [RemoveLiquidityNestedSingleTokenInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidityNested/types.ts#L15) | Same input used in the corresponding remove liquidity operation |
| nestedPoolState | [NestedPoolState](https://github.com/berachain/berancer-sdk/blob/main/src/entities/types.ts#L101) | Current state of nested pools |
**Returns**
```typescript theme={null}
Promise;
```
[PriceImpactAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/priceImpactAmount.ts) - Price impact for operation.
***
### swap
Calculate price impact on swap operations.
```typescript theme={null}
swap = async (
swapInput: SwapInput,
rpcUrl?: string,
block?: bigint,
): Promise
```
**Parameters**
| Name | Type | Description |
| ----------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------- |
| swapInput | [SwapInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts#L8) | Swap input including path information. |
| rpcUrl (optional) | string | RPC URL, e.g. Infura/Alchemy |
| block (optional) | bigint | Block no to perform the query |
Note: `SwapInput` data is normally returned from an API SOR query but may be constructed manually.
**Returns**
```typescript theme={null}
Promise;
```
[PriceImpactAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/priceImpactAmount.ts) - Price impact for operation.
## Utils
Helper functions.
### calculateProportionalAmounts
Given pool balances (including BPT) and a reference token amount, it calculates all other amounts proportional to the reference amount.
```typescript theme={null}
calculateProportionalAmounts(
pool: {
address: Address;
totalShares: HumanAmount;
tokens: {
address: Address;
balance: HumanAmount;
decimals: number
}[];
},
referenceAmount: InputAmount,
): {
tokenAmounts: InputAmount[];
bptAmount: InputAmount;
}
```
**Parameters**
| Name | Type | Description |
| --------------- | ----------------------------------------------------------------------------------- | ---------------- |
| pool | See above | Pool state |
| referenceAmount | [InputAmount](https://github.com/berachain/berancer-sdk/blob/main/src/types.ts#L45) | Ref token amount |
**Returns**
```typescript theme={null}
{
tokenAmounts: InputAmount[];
bptAmount: InputAmount;
}
```
Amounts proportional to the reference amount.
***
### Permit2 helper
Balancer v3 handles token approval through Permit2 and this helper facilitates Permit2 signature generation.
Each operation (i.e. `addLiquidity`, `addLiquidityNested`, `addLiquidityBoosted` and `swap`) has its own method that leverages the same input type of the operation itself in order to simplify signature generation.
**Function**
```typescript theme={null}
static async signAddLiquidityApproval(
input: AddLiquidityBaseBuildCallInput & {
client: PublicWalletClient;
owner: Address;
nonces?: number[];
expirations?: number[];
},
): Promise
```
**Parameters**
| Name | Type | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| input | [AddLiquidityBaseBuildCallInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/addLiquidity/types.ts#L63) | Add Liquidity Input |
| client | [PublicWalletClient](https://github.com/berachain/berancer-sdk/blob/main/src/utils/types.ts#L3) | Viem's wallet client with public actions |
| owner | Address | User address |
| nonces (optional) | number\[] | Nonces for each token |
| expirations (optional) | number\[] | Expirations for each token |
**Returns**
```typescript theme={null}
Promise;
```
[Permit2](https://github.com/berachain/berancer-sdk/blob/main/src/entities/permit2Helper/index.ts#L35) - Permit2 object with metadata and encoded signature
***
### Permit helper
Balancer v3 conforms with EIP-2612 and this helper facilitates Permit signature generation.
Each operation (i.e. `removeLiquidity`, `removeLiquidityNested` and `removeLiquidityBoosted`) has its own method that leverages the same input type of the operation itself in order to simplify signature generation.
**Function**
```typescript theme={null}
static signRemoveLiquidityApproval = async (
input: RemoveLiquidityBaseBuildCallInput & {
client: PublicWalletClient;
owner: Hex;
nonce?: bigint;
deadline?: bigint;
},
): Promise
```
**Parameters**
| Name | Type | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| input | [RemoveLiquidityBaseBuildCallInput](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts#L82) | Remove Liquidity Input |
| client | [PublicWalletClient](https://github.com/berachain/berancer-sdk/blob/main/src/utils/types.ts#L3) | Viem's wallet client with public actions |
| owner | Address | User address |
| nonces (optional) | number\[] | Nonces for each token |
| expirations (optional) | number\[] | Expirations for each token |
**Returns**
```typescript theme={null}
Promise;
```
[Permit](https://github.com/berachain/berancer-sdk/blob/main/src/entities/permitHelper/index.ts#L30) - Permit object with metadata and encoded signature
# Remove Liquidity Guide
Source: https://docs.berachain.com/build/bex/sdk/remove-liquidity
Remove liquidity from BEX pools with the SDK.
Using the [Berancer SDK](https://github.com/berachain/berancer-sdk), you can remove liquidity using two primary methods (see [RemoveLiquidityKind](https://github.com/berachain/berancer-sdk/blob/main/src/entities/removeLiquidity/types.ts)):
1. *Proportional* - remove liquidity proportionally across all tokens
2. *SingleTokenExactIn* - remove liquidity and receive a single token
For more comprehensive remove liquidity examples, see the [remove liquidity
examples](https://github.com/berachain/bex-sdk/tree/main/examples/remove-liquidity) in the BEX SDK
repository.
## Example: single token exit
In this example, we'll demonstrate how to remove liquidity and receive a single token (BERA) while calculating the price impact of the transaction.
```javascript theme={null}
import { ethers } from "ethers";
import {
BalancerApi,
RemoveLiquidity,
RemoveLiquidityKind,
Slippage,
PriceImpact,
} from "@berachain-foundation/berancer-sdk";
// Initialize provider and wallet
const RPC_URL = "https://rpc.berachain.com/";
const provider = new ethers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const balancerApi = new BalancerApi("https://api.berachain.com/", 80094);
// Get pool data
const poolId = "POOL_ID";
const poolState = await balancerApi.pools.fetchPoolState(poolId);
// Prepare remove liquidity input for single token exit
const removeLiquidityInput = {
chainId: CHAIN_ID,
kind: RemoveLiquidityKind.SingleTokenExactIn,
rpcUrl: RPC_URL,
bptIn: {
address: poolState.address,
decimals: 18,
rawAmount: ethers.parseUnits("0.1", 18), // 0.1 BPT
},
tokenOut: WBERA_TOKEN,
};
const removeLiquidity = new RemoveLiquidity();
// Query expected outputs and calculate price impact in parallel
const [queryOutput, priceImpact] = await Promise.all([
removeLiquidity.query(removeLiquidityInput, poolState),
PriceImpact.removeLiquidity(removeLiquidityInput, poolState),
]);
console.log(
"Expected Token Out:",
ethers.formatUnits(queryOutput.amountsOut[0].amount, queryOutput.amountsOut[0].token.decimals),
queryOutput.amountsOut[0].token.symbol
);
console.log("Price Impact:", priceImpact.percentage, "%");
// Build transaction with 1% slippage
const slippage = Slippage.fromPercentage("1");
const deadline = BigInt(Math.floor(Date.now() / 1000) + 60);
const callData = removeLiquidity.buildCall({
...queryOutput,
sender: wallet.address,
recipient: wallet.address,
wethIsEth: true,
slippage,
deadline,
});
// Send transaction
const tx = await wallet.sendTransaction({
to: callData.to,
data: callData.callData,
value: callData.value,
});
console.log("Transaction sent:", tx.hash);
const receipt = await tx.wait();
```
Below we breakdown the code example above.
### Helper classes
The main helper classes we use from the SDK are:
* `BalancerApi` - to simplify retrieving pool data from the Pools API
* `RemoveLiquidity` - to build removeLiquidity queries and transactions
* `Slippage` - to simplify creating limits with user defined slippage
* `PriceImpact` - to calculate the price impact of single token exits
### Fetching pool data
After initializing the `BalancerApi` class, we can fetch current pool data using `fetchPoolState`.
```javascript theme={null}
const balancerApi = new BalancerApi("https://api.berachain.com/", 80094);
// Get pool data
const poolId = "POOL_ID";
const poolState = await balancerApi.pools.fetchPoolState(poolId);
```
### Simulation and slippage setting
The `PriceImpact` class calculates the price impact of unbalanced liquidity operations. A [PriceImpactAmount](https://github.com/berachain/berancer-sdk/blob/main/src/entities/priceImpactAmount.ts#L4) is returned, with the price impact expressed in a number of different units.
Its use in the above example is informational, but can be used to provide detailed slippage information.
```javascript theme={null}
const [queryOutput, priceImpact] = await Promise.all([
removeLiquidity.query(removeLiquidityInput, poolState),
PriceImpact.removeLiquidity(removeLiquidityInput, poolState),
]);
const slippage = Slippage.fromPercentage("1");
```
### Building the transaction
The `RemoveLiquidity` class has a `buildCall` method that allows us to build the transaction. This method takes in the `queryOutput` and address parameters.
```javascript theme={null}
const slippage = Slippage.fromPercentage("1");
const callData = removeLiquidity.buildCall({
...queryOutput,
sender: wallet.address,
recipient: wallet.address,
wethIsEth: true,
slippage,
deadline,
});
```
# Smart Order Router (SOR) Guide
Source: https://docs.berachain.com/build/bex/sdk/sor
Find optimal swap routes with the BEX SDK SOR.
The [Berancer SDK](https://github.com/berachain/berancer-sdk) includes a Smart Order Router (SOR) that finds optimal paths for token swaps. This guide demonstrates how to:
1. Use the SOR to find the best swap routes
2. Convert SOR output to batch swap parameters
3. Execute multi-hop swaps using the Vault contract (via [batchSwap](/build/bex/concepts/batch-swap))
For more comprehensive Smart Order Router examples, see the [SOR
examples](https://github.com/berachain/bex-sdk/tree/main/examples/sor) in the BEX SDK repository.
## Example: multi-hop swap using SOR
In this example, we use the Berancer SDK and [Ethers.js](https://docs.ethers.org/v6/) to perform a multi-hop swap from HONEY to WBTC using the optimal path.
Note that the SOR is accessed via `balancerApi.sorSwapPaths.fetchSorSwapPaths`:
```javascript theme={null}
import { ethers } from "ethers";
import {
BalancerApi,
SwapKind,
TokenAmount,
VAULT,
vaultV2Abi,
} from "@berachain-foundation/berancer-sdk";
// Initialize provider and wallet
const RPC_URL = "https://rpc.berachain.com/";
const provider = new ethers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const balancerApi = new BalancerApi("https://api.berachain.com/", 80094);
// Create swap amount (e.g., 1 HONEY)
const tokenAmount = TokenAmount.fromHumanAmount(honeyToken, "1");
// Fetch optimal swap paths
const { paths: sorPaths, routes } = await balancerApi.sorSwapPaths.fetchSorSwapPaths({
chainId: CHAIN_ID,
tokenIn: honeyToken.address,
tokenOut: wbtcToken.address,
swapKind: SwapKind.GivenIn,
swapAmount: tokenAmount,
});
// Convert SOR paths to batchSwap parameters
const batchSwapParams = {
kind: SwapKind.GivenIn,
swaps: routes.flatMap((route) =>
route.hops.map((hop, index) => ({
poolId: hop.poolId,
assetInIndex: assets.indexOf(hop.tokenIn),
assetOutIndex: assets.indexOf(hop.tokenOut),
amount: index === 0 ? tokenAmount.amount : "0",
userData: "0x",
}))
),
assets: Array.from(
new Set(routes.flatMap((route) => route.hops.flatMap((hop) => [hop.tokenIn, hop.tokenOut])))
),
funds: {
sender: wallet.address,
recipient: wallet.address,
fromInternalBalance: false,
toInternalBalance: false,
},
limits: assets.map(
(_, i) =>
i === 0
? tokenAmount.amount.toString() // Exact input amount for first token
: ethers.MaxInt256.toString() // Max int256 for all other tokens
),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
};
// Execute batch swap via Vault contract
const vaultContract = new ethers.Contract(VAULT[CHAIN_ID], vaultV2Abi, wallet);
const tx = await vaultContract.batchSwap(
batchSwapParams.kind,
batchSwapParams.swaps,
batchSwapParams.assets,
batchSwapParams.funds,
batchSwapParams.limits,
batchSwapParams.deadline
);
```
### Understanding SOR output
The SOR returns detailed routing information. For example, a HONEY to WBTC swap might route through multiple pools:
```javascript theme={null}
{
"share": 0.5,
"tokenInAmount": "1",
"tokenOutAmount": "0.00001024",
"hops": [
{
"poolId": "0x3ad1699...",
"pool": { "symbol": "50WBERA-50HONEY-WEIGHTED" },
"tokenIn": "HONEY",
"tokenOut": "WBERA",
"tokenInAmount": "1",
"tokenOutAmount": "0.98"
},
{
"poolId": "0x4a782a6...",
"pool": { "symbol": "50WBERA-50WBTC-WEIGHTED" },
"tokenIn": "WBERA",
"tokenOut": "WBTC",
"tokenInAmount": "0.98",
"tokenOutAmount": "0.00001024"
}
]
}
```
### Converting SOR to batch swap
Other SDK examples have used `buildCall` (see the [Swap Guide](/build/bex/sdk/swap)) for crafting transactions, but SOR routes can also be converted to batch swap parameters for the Vault contract:
1. **Swaps Array**: Each hop of the SOR path (see [BatchSwapStep](/build/bex/concepts/batch-swap#batchswapstep))
2. **Assets Array**: Unique list of all tokens in the path
3. **Limits**: Token spending limits for each asset
4. **Funds**: Sender and recipient information (see [FundManagement](/build/bex/concepts/batch-swap#fundmanagement))
### Executing the swap
The batch swap is executed directly through the Vault contract:
```javascript theme={null}
// First approve token spending
const tokenContract = new ethers.Contract(tokenIn.address, tokenAbi, wallet);
await tokenContract.approve(VAULT[CHAIN_ID], tokenAmount.amount);
// Execute swap
const vaultContract = new ethers.Contract(VAULT[CHAIN_ID], vaultV2Abi, wallet);
const tx = await vaultContract.batchSwap(
batchSwapParams.kind,
batchSwapParams.swaps,
batchSwapParams.assets,
batchSwapParams.funds,
batchSwapParams.limits,
batchSwapParams.deadline
);
```
# Swap Guide
Source: https://docs.berachain.com/build/bex/sdk/swap
Swap tokens with the BEX SDK.
Using the [Berancer SDK](https://github.com/berachain/berancer-sdk), you can execute swaps using the Smart Order Router (SOR) to find optimal swap paths. The SDK supports two types of swaps (see [SwapKind](https://github.com/berachain/berancer-sdk/blob/main/src/entities/swap/types.ts)):
1. *GivenIn* - specify the exact input amount (you know how much you want to send)
2. *GivenOut* - specify the exact output amount (you know how much you want to receive)
For more comprehensive swap examples, see the [swap
examples](https://github.com/berachain/bex-sdk/tree/main/examples/swaps) in the BEX SDK
repository.
## Understanding SwapKind
The `SwapKind` enum determines how your swap amount is interpreted:
* **GivenIn** (`SwapKind.GivenIn`): You specify the exact amount of input tokens to swap. Use this when you know how much you want to spend, and the SDK will calculate how much output tokens you'll receive. Example: "Swap exactly 100 USDC for as much BERA as possible."
* **GivenOut** (`SwapKind.GivenOut`): You specify the exact amount of output tokens you want to receive. Use this when you have a target amount, and the SDK will calculate how much input tokens you need to provide. Example: "Receive exactly 1 BERA, how much USDC do I need to swap?"
## Requirements
Before executing swaps, ensure you have:
* A wallet initialized with [Ethers.js](https://docs.ethers.org/v6/)
* Sufficient token balance for the swap (input token for GivenIn, calculated amount for GivenOut)
* An approved spending allowance for the token (if required)
* Access to an RPC endpoint
## Example: exact input swap (GivenIn)
In this example, we use the Berancer SDK and [Ethers.js](https://docs.ethers.org/v6/) to swap HONEY for USDC using the optimal swap path.
```javascript theme={null}
import { ethers } from "ethers";
import {
BalancerApi,
SwapKind,
Token,
TokenAmount,
Swap,
Slippage,
} from "@berachain-foundation/berancer-sdk";
// Initialize provider and wallet
const RPC_URL = "https://rpc.berachain.com/";
const provider = new ethers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const balancerApi = new BalancerApi("https://api.berachain.com/", 80094);
// Initialize tokens
const honeyToken = new Token(CHAIN_ID, HONEY_TOKEN, 18, "HONEY");
const usdcToken = new Token(CHAIN_ID, USDC_TOKEN, 6, "USDC");
// Create swap amount (e.g., 1 HONEY)
const swapAmount = TokenAmount.fromHumanAmount(honeyToken, "1");
// Fetch optimal swap paths
const { paths: sorPaths } = await balancerApi.sorSwapPaths.fetchSorSwapPaths({
chainId: CHAIN_ID,
tokenIn: honeyToken.address,
tokenOut: usdcToken.address,
swapKind: SwapKind.GivenIn,
swapAmount,
});
const swap = new Swap({
chainId: CHAIN_ID,
paths: sorPaths,
swapKind: SwapKind.GivenIn,
userData: "0x",
});
// Query current rates
const queryOutput = await swap.query(RPC_URL);
// Build transaction with 1% slippage
const slippage = Slippage.fromPercentage("1");
const deadline = BigInt(Math.floor(Date.now() / 1000) + 60);
const callData = swap.buildCall({
slippage,
deadline,
queryOutput,
sender: wallet.address,
recipient: wallet.address,
wethIsEth: false,
});
// Approve token spending
const tokenAbi = ["function approve(address spender, uint256 amount) public returns (bool)"];
const honeyContract = new ethers.Contract(honeyToken.address, tokenAbi, wallet);
await honeyContract.approve(callData.to, swapAmount.amount);
// Send transaction
const tx = await wallet.sendTransaction({
to: callData.to,
data: callData.callData,
value: callData.value,
});
console.log("Transaction sent:", tx.hash);
const receipt = await tx.wait();
```
Below we breakdown the code example above.
### Helper classes
The main helper classes we use from the SDK are:
* `BalancerApi` - to query the Smart Order Router for optimized swap paths
* `Token` and `TokenAmount` - to represent tokens and their amounts
* `Swap` - to build swap queries and transactions
* `Slippage` - to simplify creating limits with user defined slippage
### Finding optimal swap paths
The SDK uses the Smart Order Router (SOR) to find the best swap path for a given token pair:
```javascript theme={null}
const { paths: sorPaths } = await balancerApi.sorSwapPaths.fetchSorSwapPaths({
chainId: CHAIN_ID,
tokenIn: honeyToken.address,
tokenOut: usdcToken.address,
swapKind: SwapKind.GivenIn,
swapAmount,
});
```
The SOR considers all available liquidity to find the path that provides the best execution price.
### Simulation and price quoting
The `Swap` class provides a `query` method to simulate the swap and get current rates:
```javascript theme={null}
const swap = new Swap({
chainId: CHAIN_ID,
paths: sorPaths,
swapKind: SwapKind.GivenIn,
});
const queryOutput = await swap.query(RPC_URL);
const slippage = Slippage.fromPercentage("1");
```
This helps you understand the expected output amount before executing the trade.
### Building the transaction
The `buildCall` method prepares the transaction with the defined slippage protection and deadline:
```javascript theme={null}
const callData = swap.buildCall({
slippage,
deadline,
queryOutput,
sender: wallet.address,
recipient: wallet.address,
wethIsEth: false,
});
```
The `buildCall` method returns an object containing:
* `to`: The contract address to send the transaction to
* `callData`: The encoded transaction data
* `value`: Any native token value (usually 0 for token swaps)
## Example: exact output swap (GivenOut)
When you need to receive an exact amount of output tokens, use `SwapKind.GivenOut`. The SOR will calculate the required input amount:
```javascript theme={null}
import { ethers } from "ethers";
import {
BalancerApi,
SwapKind,
Token,
TokenAmount,
Swap,
Slippage,
} from "@berachain-foundation/berancer-sdk";
// Initialize provider and wallet
const RPC_URL = "https://rpc.berachain.com/";
const provider = new ethers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const balancerApi = new BalancerApi("https://api.berachain.com/", 80094);
// Initialize tokens
const usdcToken = new Token(CHAIN_ID, USDC_TOKEN, 6, "USDC");
const honeyToken = new Token(CHAIN_ID, HONEY_TOKEN, 18, "HONEY");
// Create desired output amount (e.g., want to receive exactly 1000 USDC)
const desiredOutput = TokenAmount.fromHumanAmount(usdcToken, "1000");
// Fetch optimal swap paths for exact output
const { paths: sorPaths } = await balancerApi.sorSwapPaths.fetchSorSwapPaths({
chainId: CHAIN_ID,
tokenIn: honeyToken.address,
tokenOut: usdcToken.address,
swapKind: SwapKind.GivenOut,
swapAmount: desiredOutput, // This is the desired OUTPUT amount
});
const swap = new Swap({
chainId: CHAIN_ID,
paths: sorPaths,
swapKind: SwapKind.GivenOut,
userData: "0x",
});
// Query to get the required input amount
const queryOutput = await swap.query(RPC_URL);
// Build transaction with slippage protection
const slippage = Slippage.fromPercentage("1");
const deadline = BigInt(Math.floor(Date.now() / 1000) + 60);
const callData = swap.buildCall({
slippage,
deadline,
queryOutput,
sender: wallet.address,
recipient: wallet.address,
wethIsEth: false,
});
// Approve the calculated input amount (or maximum expected)
const tokenAbi = ["function approve(address spender, uint256 amount) public returns (bool)"];
const honeyContract = new ethers.Contract(honeyToken.address, tokenAbi, wallet);
// Approve a slightly higher amount to account for slippage
await honeyContract.approve(
callData.to,
(queryOutput.inputAmount.amount * BigInt(110)) / BigInt(100)
);
// Send transaction
const tx = await wallet.sendTransaction({
to: callData.to,
data: callData.callData,
value: callData.value,
});
console.log("Transaction sent:", tx.hash);
const receipt = await tx.wait();
```
### Key differences for GivenOut
* The `swapAmount` parameter represents the desired **output** amount, not input
* The `queryOutput` will contain the calculated **input amount** needed
* You should approve slightly more than the calculated input amount to account for price movements
## Multi-hop swaps
The Smart Order Router automatically finds multi-hop swap paths when direct pools don't offer the best price. The SDK handles routing through intermediate tokens transparently. For advanced multi-hop swap configurations and batch swaps, see the [Smart Order Router Guide](/build/bex/sdk/sor) and [Batch Swap documentation](/build/bex/concepts/batch-swap).
## Best practices
### Slippage protection
Always set appropriate slippage limits based on:
* Token pair volatility
* Current market conditions
* Expected trade size (larger trades may need higher slippage)
```javascript theme={null}
// Conservative slippage for stable pairs
const slippage = Slippage.fromPercentage("0.5");
// Standard slippage for volatile pairs
const slippage = Slippage.fromPercentage("1");
// Higher slippage for large trades
const slippage = Slippage.fromPercentage("3");
```
### Deadline management
Set deadlines that account for network congestion:
```javascript theme={null}
// Short deadline (1 minute) - risky in congested networks
const deadline = BigInt(Math.floor(Date.now() / 1000) + 60);
// Standard deadline (5 minutes) - recommended
const deadline = BigInt(Math.floor(Date.now() / 1000) + 300);
// Extended deadline (20 minutes) - for high-value trades
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1200);
```
### Query before execution
Always query the swap before executing to:
* Validate the swap is possible
* Check expected output amounts
* Estimate price impact
* Identify any errors before sending a transaction
```javascript theme={null}
const queryOutput = await swap.query(RPC_URL);
if (!queryOutput.isValid) {
throw new Error("Swap query failed: " + queryOutput.error);
}
console.log("Expected output:", queryOutput.outputAmount.toHuman());
```
## Error handling
Common errors and how to handle them:
### Insufficient liquidity
```javascript theme={null}
try {
const { paths: sorPaths } = await balancerApi.sorSwapPaths.fetchSorSwapPaths({
// ... swap parameters
});
if (!sorPaths || sorPaths.length === 0) {
throw new Error("No swap path found - insufficient liquidity");
}
} catch (error) {
if (error.message.includes("liquidity") || error.message.includes("path")) {
console.error(
"Insufficient liquidity for this swap. Try a smaller amount or different token pair."
);
}
}
```
### Slippage exceeded
If the transaction reverts due to slippage:
```javascript theme={null}
try {
const receipt = await tx.wait();
} catch (error) {
if (error.message.includes("Slippage") || error.reason?.includes("Slippage")) {
console.error(
"Slippage tolerance exceeded. Consider increasing slippage or reducing trade size."
);
// Retry with higher slippage or smaller amount
}
}
```
### Token approval issues
Ensure sufficient approval before swapping:
```javascript theme={null}
// Check current allowance
const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, wallet);
const currentAllowance = await tokenContract.allowance(wallet.address, callData.to);
if (currentAllowance < swapAmount.amount) {
await tokenContract.approve(callData.to, swapAmount.amount);
// Wait for confirmation before proceeding
}
```
# Common Resources
Source: https://docs.berachain.com/build/getting-started/common-resources
Quick reference for RPC, block explorer, faucet, chain IDs, and key dApp URLs when building on Berachain.
Quick links and reference values you'll need while developing on Berachain.
## Networks at a glance
| Resource | Mainnet | Bepolia testnet |
| ------------------ | ------------------------------------ | ---------------------------------------------------- |
| **Chain ID** | `80094` | `80069` |
| **RPC** | `https://rpc.berachain.com` | `https://bepolia.rpc.berachain.com` |
| **Block explorer** | [berascan.com](https://berascan.com) | [testnet.berascan.com](https://testnet.berascan.com) |
For one-click add network and full connection parameters, see [Connect to Berachain](/general/introduction/connect-to-berachain).
## Key dApp URLs
| App | Mainnet | Bepolia testnet |
| ------------ | -------------------------------------------------- | ------------------------------------------------------------------ |
| **Bera Hub** | [hub.berachain.com](https://hub.berachain.com) | [bepolia.hub.berachain.com](https://bepolia.hub.berachain.com) |
| **Honey** | [honey.berachain.com](https://honey.berachain.com) | [bepolia.honey.berachain.com](https://bepolia.honey.berachain.com) |
| **Faucet** | — | [bepolia.hub.berachain.com](https://bepolia.hub.berachain.com) |
| **Bend** | [bend.berachain.com](https://bend.berachain.com) | Not deployed on Bepolia |
## More tooling
For a full list of RPC providers, oracles, indexers, wallets, and other developer tools, see [Developer tools](/build/getting-started/developer-tools).
# Deployed Contract Addresses
Source: https://docs.berachain.com/build/getting-started/deployed-contracts
Berachain core and staking-pool contract addresses by network.
For **BEX** (DEX) addresses, see [BEX deployed contracts](/build/bex/deployed-contracts). For **Bend** (lending) addresses, see [Bend deployed contracts](/build/bend/deployed-contracts).
All contracts are verified at the [block explorer](https://berascan.com).
* ABI files: [berachain/abis](https://github.com/berachain/abis).
* Core protocol: [berachain/contracts](https://github.com/berachain/contracts).
* Staking pools: [berachain/contracts-staking-pools](https://github.com/berachain/contracts-staking-pools).
All audit reports are publicly available on
[Github](https://github.com/berachain/security-audits). To report a security issue, use the
[Berachain bug bounty program on
Immunefi](https://immunefi.com/bug-bounty/berachain/information/).
## Mainnet contracts
### Proof of Liquidity
| Contract | Resources |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| BeraChef [`0xdf960E8F3F19C481dDE769edEDD439ea1a63426a`](https://berascan.com/address/0xdf960E8F3F19C481dDE769edEDD439ea1a63426a) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/rewards/BeraChef.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/rewards/BeraChef.sol) |
| BlockRewardController [`0x1AE7dD7AE06F6C58B4524d9c1f816094B1bcCD8e`](https://berascan.com/address/0x1AE7dD7AE06F6C58B4524d9c1f816094B1bcCD8e) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/rewards/BlockRewardController.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/rewards/BlockRewardController.sol) |
| Distributor [`0xD2f19a79b026Fb636A7c300bF5947df113940761`](https://berascan.com/address/0xD2f19a79b026Fb636A7c300bF5947df113940761) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/rewards/Distributor.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/rewards/Distributor.sol) |
| DedicatedEmissionStreamManager [`0x813dCdBa9197947792985c866cE98D6739cA821A`](https://berascan.com/address/0x813dCdBa9197947792985c866cE98D6739cA821A) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/rewards/DedicatedEmissionStreamManager.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/rewards/DedicatedEmissionStreamManager.sol) |
| FeeCollector [`0x7Bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650`](https://berascan.com/address/0x7Bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/FeeCollector.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/FeeCollector.sol) |
| IncentivesCollector [`0x1984Baf659607Cc5f206c55BB3B00eb3E180190B`](https://berascan.com/address/0x1984Baf659607Cc5f206c55BB3B00eb3E180190B) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/IncentivesCollector.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/IncentivesCollector.sol) |
| RewardVaultFactory [`0x94Ad6Ac84f6C6FbA8b8CCbD71d9f4f101def52a8`](https://berascan.com/address/0x94Ad6Ac84f6C6FbA8b8CCbD71d9f4f101def52a8) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/rewards/RewardVaultFactory.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/rewards/RewardVaultFactory.sol) |
| RewardVaultHelper [`0xEe233a69A36Db7fC10E03e921D90DEC52Cdce6e2`](https://berascan.com/address/0xEe233a69A36Db7fC10E03e921D90DEC52Cdce6e2) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/rewards/RewardVaultHelper.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/rewards/RewardVaultHelper.sol) |
| LSTStakerVaultFactory [`0xc41bbD6695AB6bdc6D04701b15f4CE5EbA2e2500`](https://berascan.com/address/0xc41bbD6695AB6bdc6D04701b15f4CE5EbA2e2500) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/lst/LSTStakerVaultFactory.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/lst/LSTStakerVaultFactory.sol) |
| HoneyFactory [`0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401`](https://berascan.com/address/0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/honey/HoneyFactory.json) · [Source](https://github.com/berachain/contracts/blob/main/src/honey/HoneyFactory.sol) |
### Tokens
| Contract | Resources |
| -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BGT Token [`0x656b95E550C07a9ffe548bd4085c72418Ceb1dba`](https://berascan.com/address/0x656b95E550C07a9ffe548bd4085c72418Ceb1dba) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/BGT.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/BGT.sol) |
| WBERA [`0x6969696969696969696969696969696969696969`](https://berascan.com/address/0x6969696969696969696969696969696969696969) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/WBERA.json) · [Source](https://github.com/berachain/contracts/blob/main/src/WBERA.sol) |
| HONEY [`0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce`](https://berascan.com/address/0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/honey/Honey.json) · [Source](https://github.com/berachain/contracts/blob/main/src/honey/Honey.sol) |
| WBERA Staker Vault (sWBERA) [`0x118D2cEeE9785eaf70C15Cd74CD84c9f8c3EeC9a`](https://berascan.com/address/0x118D2cEeE9785eaf70C15Cd74CD84c9f8c3EeC9a) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/WBERAStakerVault.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/WBERAStakerVault.sol) |
### Governance
| Contract | Resources |
| --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Governance [`0x4f4A5c2194B8e856b7a05B348F6ba3978FB6f6D5`](https://berascan.com/address/0x4f4A5c2194B8e856b7a05B348F6ba3978FB6f6D5) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/gov/BerachainGovernance.json) · [Source](https://github.com/berachain/contracts/blob/main/src/gov/BerachainGovernance.sol) |
| Timelock [`0xb5f2000b5744f207c931526cAE2134cAa8b6862a`](https://berascan.com/address/0xb5f2000b5744f207c931526cAE2134cAa8b6862a) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/gov/TimeLock.json) · [Source](https://github.com/berachain/contracts/blob/main/src/gov/TimeLock.sol) |
### Staking pools
| Contract | Resources |
| -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| StakingPoolContractsFactory [`0xb79b43dBA821Cb67751276Ce050fF4111445fB99`](https://berascan.com/address/0xb79b43dBA821Cb67751276Ce050fF4111445fB99) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/StakingPoolContractsFactory.json) |
| DelegationHandlerFactory [`0xAd17932a5B1aaeEa73D277a6AE670623F176E0D0`](https://berascan.com/address/0xAd17932a5B1aaeEa73D277a6AE670623F176E0D0) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/delegation/DelegationHandlerFactory.json) |
| WithdrawalVault [`0xE858802Ed532C6DAD2D196AB5B1F2C15F9cb52b4`](https://berascan.com/address/0xE858802Ed532C6DAD2D196AB5B1F2C15F9cb52b4) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/WithdrawalVault.json) |
### Other
| Contract | Resources |
| ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BeaconDeposit [`0x4242424242424242424242424242424242424242`](https://berascan.com/address/0x4242424242424242424242424242424242424242) | [ABI](https://github.com/berachain/abis/blob/main/mainnet/contracts/pol/BeaconDeposit.json) · [Source](https://github.com/berachain/contracts/blob/main/src/pol/BeaconDeposit.sol) |
| Create2 [`0x4e59b44847b379578588920cA78FbF26c0B4956C`](https://berascan.com/address/0x4e59b44847b379578588920cA78FbF26c0B4956C) | [Reference](https://github.com/Arachnid/deterministic-deployment-proxy) |
| Multicall3 [`0xcA11bde05977b3631167028862bE2a173976CA11`](https://berascan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11) | [ABI](https://github.com/mds1/multicall/releases/download/v3.1.0/Multicall3.json) · [Reference](https://github.com/mds1/multicall) |
| Permit2 [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://berascan.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | [ABI](https://github.com/Uniswap/permit2-sdk/blob/main/abis/Permit2.json) · [Reference](https://docs.uniswap.org/contracts/permit2/overview) |
### NFT contracts
Berachain NFT contract addresses on both Ethereum (via LayerZero adapters) and Berachain mainnet.
| Collection | Ethereum Adapter | Berachain Address |
| ---------- | -------------------------------------------- | -------------------------------------------- |
| Bong Bears | `0x1897C001341F81Ca72154b75b882aE708e06bF48` | `0x141De07E5D4C4759EC9301DA106115D4841f66cD` |
| Bond Bears | `0x6b1C374105467d1fC1090C989BcbbCC172c8a89c` | `0xA0CF472E6132F6B822a944f6F31aA7b261c7c375` |
| Boo Bears | `0x7591992F1a98636C6B7207f30382ca4Bec83D9Be` | `0xf49ec5db255854C4a567de5AB3826c9AAbaFc7cF` |
| Baby Bears | `0xc48C54e92d135B356DD0CbF50F803A8c8d38968b` | `0xDDeAf391c4be2d01ca52aBb8C159a06820ef078C` |
| Band Bears | `0x392Faa1b0EF108ded69897Ba5382E909C39Fc09e` | `0x7711B2Eb2451259dbF211e30157ceB7CFeb79a19` |
| Bit Bears | `0x3EB12398753eEd7E8747321c37C85De30d8E2e94` | `0x72D876D9cdf4001b836f8E47254d0551EdA2eebB` |
## Bepolia testnet contracts
### Proof of Liquidity
| Contract | Resources |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BeraChef [`0xdf960E8F3F19C481dDE769edEDD439ea1a63426a`](https://testnet.berascan.com/address/0xdf960E8F3F19C481dDE769edEDD439ea1a63426a) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/rewards/BeraChef.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/rewards/BeraChef.sol) |
| BlockRewardController [`0x1AE7dD7AE06F6C58B4524d9c1f816094B1bcCD8e`](https://testnet.berascan.com/address/0x1AE7dD7AE06F6C58B4524d9c1f816094B1bcCD8e) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/rewards/BlockRewardController.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/rewards/BlockRewardController.sol) |
| Distributor [`0xD2f19a79b026Fb636A7c300bF5947df113940761`](https://testnet.berascan.com/address/0xD2f19a79b026Fb636A7c300bF5947df113940761) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/rewards/Distributor.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/rewards/Distributor.sol) |
| DedicatedEmissionStreamManager [`0xfe83d31669b52B7a619119Bc71805fD29eeEB9Dd`](https://testnet.berascan.com/address/0xfe83d31669b52B7a619119Bc71805fD29eeEB9Dd) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/rewards/DedicatedEmissionStreamManager.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/rewards/DedicatedEmissionStreamManager.sol) |
| FeeCollector [`0x7bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650`](https://testnet.berascan.com/address/0x7bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/FeeCollector.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/FeeCollector.sol) |
| IncentivesCollector [`0x1984Baf659607Cc5f206c55BB3B00eb3E180190B`](https://testnet.berascan.com/address/0x1984Baf659607Cc5f206c55BB3B00eb3E180190B) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/IncentivesCollector.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/IncentivesCollector.sol) |
| RewardVaultFactory [`0x94Ad6Ac84f6C6FbA8b8CCbD71d9f4f101def52a8`](https://testnet.berascan.com/address/0x94Ad6Ac84f6C6FbA8b8CCbD71d9f4f101def52a8) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/rewards/RewardVaultFactory.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/rewards/RewardVaultFactory.sol) |
| RewardVaultHelper [`0xEe233a69A36Db7fC10E03e921D90DEC52Cdce6e2`](https://testnet.berascan.com/address/0xEe233a69A36Db7fC10E03e921D90DEC52Cdce6e2) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/rewards/RewardVaultHelper.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/rewards/RewardVaultHelper.sol) |
| LSTStakerVaultFactory [`0xAf10B532cCC25B26a8e28913D5C4056a77e7a178`](https://testnet.berascan.com/address/0xAf10B532cCC25B26a8e28913D5C4056a77e7a178) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/lst/LSTStakerVaultFactory.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/lst/LSTStakerVaultFactory.sol) |
| HoneyFactory [`0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401`](https://testnet.berascan.com/address/0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/honey/HoneyFactory.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/honey/HoneyFactory.sol) |
### Tokens
| Contract | Resources |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BGT Token [`0x656b95E550C07a9ffe548bd4085c72418Ceb1dba`](https://testnet.berascan.com/address/0x656b95E550C07a9ffe548bd4085c72418Ceb1dba) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/BGT.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/BGT.sol) |
| WBERA [`0x6969696969696969696969696969696969696969`](https://testnet.berascan.com/address/0x6969696969696969696969696969696969696969) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/WBERA.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/WBERA.sol) |
| HONEY [`0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce`](https://testnet.berascan.com/address/0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/honey/Honey.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/honey/Honey.sol) |
| WBERA Staker Vault (sWBERA) [`0x118D2cEeE9785eaf70C15Cd74CD84c9f8c3EeC9a`](https://testnet.berascan.com/address/0x118D2cEeE9785eaf70C15Cd74CD84c9f8c3EeC9a) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/WBERAStakerVault.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/WBERAStakerVault.sol) |
### Governance
| Contract | Resources |
| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Governance [`0x4f4A5c2194B8e856b7a05B348F6ba3978FB6f6D5`](https://testnet.berascan.com/address/0x4f4A5c2194B8e856b7a05B348F6ba3978FB6f6D5) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/gov/BerachainGovernance.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/gov/BerachainGovernance.sol) |
| Timelock [`0xb5f2000b5744f207c931526cAE2134cAa8b6862a`](https://testnet.berascan.com/address/0xb5f2000b5744f207c931526cAE2134cAa8b6862a) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/gov/TimeLock.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/gov/TimeLock.sol) |
### Staking pools
| Contract | Resources |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| StakingPoolContractsFactory [`0x24b8223864d3936F56e5a24C4245ae7620471D4C`](https://testnet.berascan.com/address/0x24b8223864d3936F56e5a24C4245ae7620471D4C) | [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/StakingPoolContractsFactory.json) |
| DelegationHandlerFactory [`0x0aEf09EC97bAc354d31F180b401454cB76abc395`](https://testnet.berascan.com/address/0x0aEf09EC97bAc354d31F180b401454cB76abc395) | [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/delegation/DelegationHandlerFactory.json) |
| WithdrawalVault [`0xBBed2D94338cdE2926A8C0576432De32C05c66e9`](https://testnet.berascan.com/address/0xBBed2D94338cdE2926A8C0576432De32C05c66e9) | [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/WithdrawalVault.json) |
### Other
| Contract | Resources |
| -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BeaconDeposit [`0x4242424242424242424242424242424242424242`](https://testnet.berascan.com/address/0x4242424242424242424242424242424242424242) | [ABI](https://github.com/berachain/abis/blob/main/bepolia/contracts/pol/BeaconDeposit.json) · [Source](https://github.com/berachain/contracts/blob/bepolia/src/pol/BeaconDeposit.sol) |
| Create2 [`0x4e59b44847b379578588920cA78FbF26c0B4956C`](https://testnet.berascan.com/address/0x4e59b44847b379578588920cA78FbF26c0B4956C) | [Reference](https://github.com/Arachnid/deterministic-deployment-proxy) |
| Multicall3 [`0xcA11bde05977b3631167028862bE2a173976CA11`](https://testnet.berascan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11) | [ABI](https://github.com/mds1/multicall/releases/download/v3.1.0/Multicall3.json) · [Reference](https://github.com/mds1/multicall) |
| Permit2 [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://testnet.berascan.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) | [ABI](https://github.com/Uniswap/permit2-sdk/blob/main/abis/Permit2.json) · [Reference](https://docs.uniswap.org/contracts/permit2/overview) |
## Staking pool contracts
Singleton addresses are listed below. See the [Staking pools overview](/nodes/staking-pools/overview) and [staking pool contracts](/nodes/staking-pools/contracts) reference.
#### Mainnet
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xb79b43dBA821Cb67751276Ce050fF4111445fB99
```
[Berascan](https://berascan.com/address/0xb79b43dBA821Cb67751276Ce050fF4111445fB99) · [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/StakingPoolContractsFactory.json)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xAd17932a5B1aaeEa73D277a6AE670623F176E0D0
```
[Berascan](https://berascan.com/address/0xAd17932a5B1aaeEa73D277a6AE670623F176E0D0) · [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/delegation/DelegationHandlerFactory.json)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xE858802Ed532C6DAD2D196AB5B1F2C15F9cb52b4
```
[Berascan](https://berascan.com/address/0xE858802Ed532C6DAD2D196AB5B1F2C15F9cb52b4) · [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/WithdrawalVault.json)
#### Bepolia
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0x24b8223864d3936F56e5a24C4245ae7620471D4C
```
[Berascan](https://testnet.berascan.com/address/0x24b8223864d3936F56e5a24C4245ae7620471D4C) · [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/StakingPoolContractsFactory.json)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0x0aEf09EC97bAc354d31F180b401454cB76abc395
```
[Berascan](https://testnet.berascan.com/address/0x0aEf09EC97bAc354d31F180b401454cB76abc395) · [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/delegation/DelegationHandlerFactory.json)
```text theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
0xBBed2D94338cdE2926A8C0576432De32C05c66e9
```
[Berascan](https://testnet.berascan.com/address/0xBBed2D94338cdE2926A8C0576432De32C05c66e9) · [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/WithdrawalVault.json)
# Developer Tools
Source: https://docs.berachain.com/build/getting-started/developer-tools
IDEs, SDKs, and tooling for building on Berachain.
This section provides an overview of the developer tools that are available on the Berachain network.
Since Berachain is EVM-compatible, if you're familiar with creating dApps on other EVM chains, then you'll feel right at home building on Berachain.
## Development environments
* [Foundry](https://github.com/foundry-rs/foundry)
* [Hardhat](https://hardhat.org/)
* [Remix](https://remix.ethereum.org/)
## Smart contract programming languages
* [Solidity](https://docs.soliditylang.org/en/v0.8.20/)
* [Vyper](https://docs.vyperlang.org/en/stable/)
## Frontend libraries
* [Ethers.js](https://docs.ethers.org/v5/)
* [Viem](https://viem.sh)
* [Web3.js](https://web3js.readthedocs.io/en/v1.10.0/)
## RPC and infrastructure providers
* [Alchemy](https://dashboard.alchemy.com/?utm_source=chain_partner\&utm_medium=referral\&utm_campaign=berachain)
* [BlockPI](https://blockpi.io/)
* [Chainstack](https://chainstack.com/build-better-with-berachain/)
* [dRPC NodeCloud](https://drpc.org/chainlist/berachain)
* [GetBlock](https://getblock.io/nodes/berachain/)
* [Grove](https://grove.city)
* [Envio](https://envio.dev)
* [Enigma](https://enigma-validator.com/services)
* [Nirvana](https://nirvanalabs.io/nodes/berachain)
* [QuickNode RPC](https://quicknode.notion.site/QuickNode-Benefits-for-Berachain-Developers-175d54ec5d644f598fde797633add2c1?pvs=4)
* [RhinoStake](https://rhinostake.com/resources/berachain-apis)
* [RouteMesh](https://routeme.sh)
* [Spectrum](https://spectrumnodes.com)
* [Tatum RPC and Webhooks](https://tatum.io/berachain-and-tatum)
* [Tenderly](https://dashboard.tenderly.co/)
* [Thirdweb](https://thirdweb.com/chainlist)
* [Winnie](https://www.henlo-winnie.dev/)
## Wallets and multisigs
* [Metamask](https://metamask.io/)
* [Frame](https://frame.sh/)
* [Rabby](https://rabby.io/)
* [Binance Web3 Wallet](https://www.binance.com/en/web3wallet)
* [Keplr](https://keplr.app/)
* [Rainbow Wallet](https://rainbow.me/en/)
* [Safe](https://app.safe.global)
## Authentication and account abstraction
* [Dynamic](https://www.dynamic.xyz/)
* [Para](https://getpara.com)
* [Particle](https://particle.network/)
* [Privy](https://www.privy.io/)
* [Thirdweb](https://thirdweb.com/explore/smart-wallet)
* [Turnkey](https://www.turnkey.com/)
* [ZeroDev](https://zerodev.app/)
## Subgraphs and data indexers
* [Codex](https://www.codex.io)
* [Ghost Graph](https://ghostgraph.xyz)
* [GoldRush (powered by Covalent)](https://goldrush.dev/docs/chains/berachain)
* [Envio](https://envio.dev)
* [Tatum Indexed Data API](https://tatum.io/berachain-and-tatum)
* [Thirdweb](https://thirdweb.com/insight?ref=blog.thirdweb.com)
* [SubQuery](https://subquery.network)
## Oracles
* [Api3](https://api3.org)
* [Chronicle](https://chroniclelabs.org/)
* [Pyth](https://pyth.network/)
* [Redstone](https://docs.redstone.finance/docs/introduction)
* [Stork](https://www.stork.network)
* [Supra](https://supra.com)
## Automation
* [Gelato](https://www.gelato.network/web3-functions)
## Verifiable randomness
* [Gelato](https://app.gelato.network/vrf)
* [Pyth](https://docs.pyth.network/entropy)
## Incentives marketplace
These tools provide validator and vault analytics.
* [BGTscan](https://bgtscan.com/bribe-market/bgt-emission-rates) - Onchain analytics tool for Berachain's PoL economics
* [Furthermore](https://furthermore.app/)
* [Nambera](https://nambera.com/)
# Introduction
Source: https://docs.berachain.com/build/getting-started/overview
Get started building on Berachain: networks, tools, BEX, Bend, and common developer resources.
Berachain is EVM-identical, so you can build with the same tools and workflows you use on other EVM chains. Configure your network, pick your stack, and integrate with native protocols like BEX and Bend.
## Getting started
One-click add network, RPC details, chain IDs, and wallet setup for Mainnet and Bepolia.
IDEs, SDKs, RPC providers, oracles, and tooling for building on Berachain.
Quick links: block explorer, faucet, chain IDs, and key dApp URLs.
Core and Proof of Liquidity contract addresses (and staking-pool singletons) by network.
## Build with native protocols
Integrate with the native DEX: deployed contracts, pool concepts, SDK, and guides.
Integrate with the native lending protocol: contracts, markets, and onchain tooling.
Reward Vault setup, incentives, and integration paths for Proof of Liquidity.
# AI Developer
Source: https://docs.berachain.com/build/guides/ai-developer
Use Berachain docs with AI assistants and MCP. Server URL, stack coverage, and how to ask for the right doc.
Use Berachain docs inside **AI assistants** and **MCP-compatible clients** (e.g. Cursor, Claude Code) by adding our MCP server. This page gives the server URL, what the docs cover, and how to ask so the AI uses the right content.
## MCP server
Add the Berachain docs MCP server so the AI can read the latest docs instead of relying on training data:
**URL:** [https://docs.berachain.com/mcp](https://docs.berachain.com/mcp)
Configure this in your MCP client; then the AI can query our pages, guides, and contract references directly.
## What we have examples for
The docs and [Community Developers](/build/guides/community-guides) catalog cover these parts of the stack. Each area has at least one guide with a **Guide** page (requirements, quick start, key files, raw README link for MCP).
| Area | What’s covered | Where |
| ------------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Wallet connections** | Next.js + WalletConnect, ThirdWeb, Particle, RainbowKit, Expo | [Community Developers](/build/guides/community-guides) → Wallet Connections |
| **Bridging** | ERC20 to Berachain via LayerZero V2 OFT | [Community Developers](/build/guides/community-guides) → Bridging |
| **Smart contracts** | Deploy (Ethers, Viem, Hardhat, Foundry), verify on Berascan, ERC20, ERC1155, upgradeable (OpenZeppelin) | [Community Developers](/build/guides/community-guides) → Smart Contract Deployment & Verification; [Verifying smart contracts](/build/guides/verifying-smart-contracts) |
| **Indexing & querying** | Goldsky subgraph, Envio ERC20 indexer (+ The Graph, SubQuery linked) | [Community Developers](/build/guides/community-guides) → Indexing and Querying |
| **Verifiable randomness** | Gelato VRF, Pyth Entropy (e.g. provably fair NFTs) | [Community Developers](/build/guides/community-guides) → Verifiable Randomness |
| **Oracles** | Pyth price feeds (on-demand updates) | [Community Developers](/build/guides/community-guides) → Oracles |
| **Governance** | Reward Vault proposals (BRIP-style) | [Community Developers](/build/guides/community-guides) → Governance |
| **Storage** | Irys uploads paid with \$BERA (Node.js) | [Community Developers](/build/guides/community-guides) → Storage |
Core references (network, RPC, chain IDs, deployed addresses, ABIs): [Developer tools](/build/getting-started/developer-tools), [Deployed contracts](/build/getting-started/deployed-contracts). Protocol-specific: **Build** tab → BEX, Bend; **Nodes** tab for validators and staking pools.
## How to get the right doc
* **Contract addresses / ABIs / network config** → “Use the Berachain **Deployed contracts** page” or “What’s the RPC and chain ID for Berachain mainnet in the docs?”
* **Verify a contract** → “Follow the Berachain **Verifying smart contracts** guide” or “How do I verify with Hardhat/Forge on Berascan?”
* **A specific integration** → Name the stack and, if relevant, the guide: “Using the **Pyth Oracle** guide on Berachain, how do I call `updatePrice`?” or “From the **LayerZero OFT** guide, what’s the exact deploy order?”
* **Chain and network** → Say “Berachain mainnet” or “Berachain testnet (Bepolia)” so the AI picks the right RPC, faucet, and addresses.
The **Guides** dropdown (under Community Developers) lists each example; each guide page includes a raw README link so MCP can fetch the full repo README when needed.
# Berascan Token Update Guide
Source: https://docs.berachain.com/build/guides/berascan-token-update
Learn how to update token information on Berascan, the Berachain block explorer
# Berascan Token Update Guide
This guide explains how to update token information on Berascan, Berachain's official block explorer.
## Overview
Token information on Berascan includes details like:
* Basic project information (name, website, email)
* Social media profiles and links
* Price data sources (CoinMarketCap, CoinGecko)
* Token sale details (ICO/IEO information)
* Project documentation and resources
## How to Update Token Information
### Step 1: Create an Account
If you don't have a Berascan account:
1. **Visit the registration page**: [https://berascan.com/register](https://berascan.com/register)
2. **Sign up** with your email address
3. **Verify your email** to activate your account
### Step 2: Verify Token Ownership
Before you can update token information, you need to verify that you own the token contract:
1. **Access the owner verification tool**: [https://berascan.com/verifyAddress/](https://berascan.com/verifyAddress/)
2. **Follow the verification process** to prove ownership of the token contract
3. **Create the signature** as prompted by the verification tool
4. **Submit the verification** - After creating the signature, you will be prompted to submit the verification
5. **Complete the verification** to gain access to token update features
:::tip Owner Verification Process
The owner verification process involves proving that you control the private key for the token contract address. This ensures that only authorized token creators can update token information on Berascan.
:::
### Step 3: Access the Token Update Page
1. **Log in** to your Berascan account
2. **Navigate to the token update page**: [https://berascan.com/tokenupdate](https://berascan.com/tokenupdate)
:::warning Important Notice
Before submitting your token information, please review Berascan's requirements carefully. Your submission may not receive a reply if any requirements are not met.
:::
**Mandatory Information Required:**
* Website: Official project website
* Email: Contact email matching your website domain (e.g., [contact@yourdomain.com](mailto:contact@yourdomain.com))
* Logo: 32x32 SVG format logo
**Website Requirements:**
Your official website must be accessible and safe to visit, have all working links and updated content, and contain clear information about your project/token.
**Contract Requirements:**
* Must adhere to ERC-20 specifications
* Must be valid and correctly deployed on Berachain
* Must be the correct contract address for your project
**Project Requirements:**
* Name, symbol, and branding must not infringe on existing brands
* Must not be fraudulent or misrepresent other entities
* Team members must be clearly presented with professional profile links
* Must be deployed and operating on the Berachain blockchain
:::warning Submission Policy
These requirements do not guarantee approval. Berascan reserves the right to remove or update information if false details or discrepancies are found.
:::
### Step 4: Update Your Token Information
On the token update page, you can:
* **Request Type**: Choose between new/first-time updates or other categories
* **Basic Information**: Project name, website, email addresses, and logo
* **Social Profiles**: Links to official social media pages (GitHub, Telegram, Discord, etc.)
* **Price Data**: CoinMarketCap and CoinGecko ticker links
* **Token Sale Details**: ICO/IEO information, pricing, allocation, and vesting periods
### Step 5: Submit Changes
1. **Review your changes** to ensure accuracy
2. **Submit the update request**
3. **Wait for approval** from the Berascan team
4. **Changes will be reflected** on the token page once approved
# Governance Proposal for Reward Vaults
Source: https://docs.berachain.com/build/guides/community/berachain-governance-proposal
Create and submit a governance proposal for Reward Vaults on Berachain (BRIP-style).
Use this guide when you want to **create and submit a governance proposal** (e.g. for **Reward Vaults**) on Berachain, following a BRIP-style flow.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/berachain-governance-proposal](https://github.com/berachain/guides/tree/main/apps/berachain-governance-proposal)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/berachain-governance-proposal/README.md)
## Requirements
* Node, npm (or Foundry/Hardhat depending on repo)
* Wallet with governance tokens if required for proposal creation
* Understanding of Berachain governance and Reward Vaults (see [Reward Vault Governance](/general/governance/reward-vault-governance))
## Stack
Typically Foundry or Hardhat, Solidity and/or TypeScript scripts (see repo).
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/berachain-governance-proposal
npm install
```
2. Follow the README for: drafting the proposal payload, targeting the correct governance contract, and submitting (e.g. via script or cast).
For exact steps and contract addresses, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/berachain-governance-proposal/README.md).
# Envio Indexer ERC20
Source: https://docs.berachain.com/build/guides/community/envio-indexer-erc20
Index ERC20 token data on Berachain using Envio's indexer framework. Schema and handlers for ERC20 events.
Use this guide when you want to **index ERC20 token data** (transfers, balances, etc.) on Berachain using **Envio's indexer** (schema + handlers).
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/envio-indexer-erc20](https://github.com/berachain/guides/tree/main/apps/envio-indexer-erc20)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/envio-indexer-erc20/README.md)
## Requirements
* Node (see README), npm
* Envio CLI/config if deploying to Envio
## Stack
Envio, TypeScript/JavaScript, schema + event handlers.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/envio-indexer-erc20
npm install
```
2. Configure Envio (chain: Berachain, contract addresses if fixed). See README for Envio workflow.
3. Build and run/deploy the indexer; query indexed data per Envio docs.
For full schema, handlers, and deploy steps, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/envio-indexer-erc20/README.md).
# HelloWorld with Ethers6 & solc
Source: https://docs.berachain.com/build/guides/community/ethers6-solc-helloworld
Deploy a HelloWorld-style contract to Berachain using Ethers.js v6 and the solc compiler (no Hardhat/Foundry).
Use this guide when you want to **deploy a simple contract** using **Ethers.js v6** and **solc** only (no Hardhat or Foundry).
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/ethers6-solc-helloworld](https://github.com/berachain/guides/tree/main/apps/ethers6-solc-helloworld)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/ethers6-solc-helloworld/README.md)
## Requirements
* Node, npm
* Wallet with testnet \$BERA (e.g. [Bepolia Faucet](https://bepolia.faucet.berachain.com))
## Stack
Node.js, Ethers v6, solc, JavaScript/TypeScript.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/ethers6-solc-helloworld
npm install
```
2. Set private key and RPC in env (see README).
3. Compile and deploy using the repo scripts (Ethers v6 + solc); see README for exact commands.
For full compile/deploy commands, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/ethers6-solc-helloworld/README.md).
# ERC20 with Foundry
Source: https://docs.berachain.com/build/guides/community/foundry-erc20
Create and deploy an ERC20 token on Berachain using Foundry (Forge, Cast).
Use this guide when you want to **create and deploy an ERC20 token** on Berachain using **Foundry** (Forge for build/deploy, Cast for calls).
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/foundry-erc20](https://github.com/berachain/guides/tree/main/apps/foundry-erc20)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/foundry-erc20/README.md)
## Requirements
* [Foundry](https://book.getfoundry.sh/getting-started/installation) (`foundryup`)
* Wallet with testnet \$BERA (e.g. [Bepolia Faucet](https://bepolia.faucet.berachain.com))
## Stack
Foundry, Solidity, ERC20.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/foundry-erc20
forge build
```
2. Set `PRIVATE_KEY` and RPC (e.g. `https://bepolia.rpc.berachain.com/`) in env or `--rpc-url`.
3. Deploy with `forge create` or a script; use `cast` to interact. Exact commands are in the README.
For full deploy and test commands, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/foundry-erc20/README.md).
# Gelato VRF on Berachain
Source: https://docs.berachain.com/build/guides/community/gelato-vrf
Verifiable Random Function (VRF) on Berachain with Gelato. Deploy SimpleVRFContract, create a Gelato VRF task, request and fulfill randomness.
Use this guide when you need **verifiable on-chain randomness** (e.g. for games, NFTs, or randomized logic) on Berachain. Gelato VRF lets your contract request randomness and receive a fulfilled callback.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/gelato-vrf](https://github.com/berachain/guides/tree/main/apps/gelato-vrf)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/gelato-vrf/README.md)
## Official documentation
* [Gelato VRF](https://app.gelato.network/vrf) — deploy and manage VRF tasks.
## Requirements
* Node `v20.0.0` or greater, npm or yarn
* Wallet with \$BERA — [Bepolia Faucet](https://bepolia.faucet.berachain.com)
* Hardhat
## Stack
Solidity, Hardhat, Node.js, Gelato VRF (requester + fulfillment).
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/gelato-vrf
npm install
```
2. **Configure `.env`** from `.env.example`: `PRIVATE_KEY`, `DEDICATED_MSG_SENDER`, and later `SC_ADDRESS` (your deployed contract).
3. **Deploy**
```bash theme={null}
npx hardhat deploy --network berachain
```
Note the deployed address from `deployments/` and set `SC_ADDRESS` in `.env`.
4. **Create a Gelato VRF task** at [app.gelato.network/vrf](https://app.gelato.network/vrf): choose **Berachain Bepolia**, enter your requester contract address, then launch the VRF instance.
5. **Request randomness**
```bash theme={null}
npx hardhat run ./scripts/requestRandomness.ts --network berachain
```
Monitor execution in the Gelato app.
## Key files
| Purpose | Path |
| -------------- | ------------------------------ |
| VRF contract | Contracts in repo (see README) |
| Deploy output | `deployments/` |
| Request script | `scripts/requestRandomness.ts` |
# Index & Query with Goldsky
Source: https://docs.berachain.com/build/guides/community/goldsky-subgraph
Index Berachain data with Goldsky SDK; example subgraph for ERC20 balances. Build, deploy, query via GraphQL.
Use this guide when you want to **index Berachain blockchain data** (e.g. ERC20 balances) and **query it via GraphQL** using the **Goldsky** subgraph SDK.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/goldsky-subgraph](https://github.com/berachain/guides/tree/main/apps/goldsky-subgraph)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/goldsky-subgraph/README.md)
## Official documentation
* [Goldsky](https://goldsky.com/) — [developer docs](https://docs.goldsky.com/introduction)
## Requirements
* Node `v20.11.0` or greater, npm
* [Goldsky account](https://app.goldsky.com) and API key
* Goldsky CLI (install: `curl https://goldsky.com | sh`)
## Stack
Node.js, Goldsky CLI, GraphQL subgraph (schema + mappings).
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/goldsky-subgraph
npm install
```
2. **Goldsky setup:** Create account at [app.goldsky.com](https://app.goldsky.com), create API key, install CLI, run `goldsky login`.
3. **Build subgraph**
```bash theme={null}
npm run codegen
npm run build
```
4. **Deploy**
```bash theme={null}
goldsky subgraph deploy erc20-subgraph/1.0.0 --path .
```
5. **Query** — After indexing, use the public GraphQL endpoint (see Goldsky dashboard). Example query for accounts and balances in README.
## Key files
| Purpose | Path |
| ------------------------ | ------------------------- |
| Subgraph config / schema | Project root (see README) |
| Mappings | See README |
# Hardhat Contract Verification
Source: https://docs.berachain.com/build/guides/community/hardhat-contract-verification
Verify Hardhat-deployed contracts on Berascan using the Ethers v6 verification plugin.
Use this guide when you have **deployed a contract with Hardhat** and want to **verify the source** on Berachain's block explorer (Berascan).
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/hardhat-contract-verification](https://github.com/berachain/guides/tree/main/apps/hardhat-contract-verification)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/hardhat-contract-verification/README.md)
## Requirements
* Node, npm, Hardhat
* Deployed contract address and constructor arguments (if any)
* Same Solidity version and build settings as deployment
## Stack
Hardhat, Ethers v6, Solidity, Hardhat Etherscan/Verification plugin.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/hardhat-contract-verification
npm install
```
2. Configure Hardhat for Berachain (network and verify task); see README.
3. Run the verify task, e.g. `npx hardhat verify --network berachainTestnet `.
For exact verify command and config, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/hardhat-contract-verification/README.md).
# Hardhat Ethers6 ERC1155
Source: https://docs.berachain.com/build/guides/community/hardhat-ethers6-erc1155
Deploy and interact with an ERC1155 multi-token contract on Berachain using Hardhat and Ethers v6.
Use this guide when you want to **deploy and interact with an ERC1155** (multi-token) contract on Berachain using **Hardhat** and **Ethers v6**.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/hardhat-ethers6-erc1155](https://github.com/berachain/guides/tree/main/apps/hardhat-ethers6-erc1155)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/hardhat-ethers6-erc1155/README.md)
## Requirements
* Node, npm, Hardhat
* Wallet with testnet \$BERA
## Stack
Hardhat, Ethers v6, Solidity, ERC1155.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/hardhat-ethers6-erc1155
npm install
```
2. Set `WALLET_PRIVATE_KEY` (and RPC if needed) in `.env`.
3. Compile, deploy, and run any scripts/tests per README.
For deploy and interaction commands, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/hardhat-ethers6-erc1155/README.md).
# HelloWorld with Hardhat & Viem
Source: https://docs.berachain.com/build/guides/community/hardhat-viem-helloworld
Deploy and verify a HelloWorld contract on Berachain testnet with Hardhat and Viem. Includes local node and tests.
Use this guide when you want to **set up Hardhat with Viem** for Berachain, **deploy a simple contract**, **verify it on the block explorer**, and optionally run a **local Hardhat node** and **tests**.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/hardhat-viem-helloworld](https://github.com/berachain/guides/tree/main/apps/hardhat-viem-helloworld)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/hardhat-viem-helloworld/README.md)
## Requirements
* Node `v18.18.2+`, npm
* MetaMask (or other wallet) with \$BERA — use [Berachain testnet faucet](https://bepolia.faucet.berachain.com) or similar
## Stack
Hardhat, Viem, Solidity, TypeScript.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/hardhat-viem-helloworld
npm install
cp .env.example .env
```
Set `WALLET_PRIVATE_KEY` in `.env`.
2. **Compile and deploy to Berachain testnet**
```bash theme={null}
npm run compile
npm run deploy:berachain
```
(Uses `scripts/deploy.ts` and network `berachainTestnet`.)
3. **Verify on block explorer**
```bash theme={null}
npm run verify "Hello From Deployed Contract"
```
4. **Optional — local node:** `npm run node` in one terminal, then `npm run deploy:localhost` in another.
5. **Run tests:** `npm run test`
## Key files
| Purpose | Path |
| ------------- | -------------------------- |
| Contract | `contracts/HelloWorld.sol` |
| Deploy script | `scripts/deploy.ts` |
| Tests | `test/HelloWorld.test.ts` |
# Irys Upload with $BERA (Node.js)
Source: https://docs.berachain.com/build/guides/community/irys-bera-nodejs
Upload files to Irys decentralized storage and pay with Berachain $BERA. Node.js script with Irys SDK.
Use this guide when you want to **upload files** (images, JSON, etc.) to **Irys** (decentralized storage) and **pay with Berachain \$BERA** from a **Node.js** script.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/irys-bera-nodejs](https://github.com/berachain/guides/tree/main/apps/irys-bera-nodejs)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/irys-bera-nodejs/README.md)
## Requirements
* Node `v20.11.0` or greater, npm
* Wallet with **mainnet** \$BERA (this example uses mainnet for payment)
## Stack
Node.js, JavaScript/TypeScript, Irys SDK, Berachain mainnet RPC.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/irys-bera-nodejs
npm install
cp .env.example .env
```
2. **Set `WALLET_PRIVATE_KEY`** in `.env` (wallet must hold mainnet \$BERA).
3. **Run upload**
```bash theme={null}
npm run dev
```
Script reports file size, cost in \$BERA, balance, and returns the Irys receipt and gateway URL (e.g. `https://gateway.irys.xyz/`).
## Key files
| Purpose | Path |
| ------------- | --------------------------------------------------------------- |
| Upload script | See repo (e.g. `app/` or root script; README names exact entry) |
# Bridging ERC20 with LayerZero V2
Source: https://docs.berachain.com/build/guides/community/layerzero-oft
Bridge ERC20 tokens (e.g. $UNI) from Sepolia to Berachain using LayerZero V2 and the Omnichain Fungible Token (OFT) standard.
**Security advisory:** The permissionless WAB deployment configures its own DVN set at deploy
time. Child bridges may have varying security assumptions. Before bridging significant value
through a WAB child, users should verify the DVN configuration via
`endpoint.getConfig(childAddress, sendLibrary, destinationEid, 2)`. Configurations with fewer than
2 required DVN attestations should be treated as single-point-of-failure risk.
Use this guide when you want to **bridge an existing ERC20** from another chain (e.g. Sepolia) to Berachain using **LayerZero V2** and the **OFT** (Omnichain Fungible Token) standard.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/layerzero-oft](https://github.com/berachain/guides/tree/main/apps/layerzero-oft)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/layerzero-oft/README.md)
## Official documentation
* [LayerZero V2](https://docs.layerzero.network/v2) — concepts and OFT.
## Requirements
* Node `v20.11.0` or greater, npm
* Wallet with Berachain testnet \$BERA — [Bepolia Faucet](https://bepolia.faucet.berachain.com)
* Wallet with Sepolia \$UNI (or other source token) — see README for faucet/Uniswap
* [Foundry](https://book.getfoundry.sh/getting-started/installation)
## Stack
Solidity, Foundry (Forge scripts), Node.js, LayerZero V2 OFT.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/layerzero-oft
npm install
```
2. **Create `.env`** with `PRIVATE_KEY`, `SEPOLIA_ADAPTER_ADDRESS`, `BERACHAIN_OFT_ADDRESS` (fill addresses after deploying).
3. **Deploy adapter on Sepolia**
```bash theme={null}
forge script script/MyAdapter.s.sol --rpc-url https://rpc.sepolia.org/ --broadcast
```
Set `SEPOLIA_ADAPTER_ADDRESS` in `.env`.
4. **Deploy OFT on Berachain**
```bash theme={null}
forge script script/MyOFT.s.sol --rpc-url https://bepolia.rpc.berachain.com/ --broadcast
```
Set `BERACHAIN_OFT_ADDRESS` in `.env`.
5. **Bridge tokens**
```bash theme={null}
forge script script/Bridge.s.sol --rpc-url https://rpc.sepolia.org/ --broadcast
```
## Key files
| Purpose | Path |
| -------------------- | ------------------------------------- |
| Source chain adapter | `script/MyAdapter.s.sol` (and source) |
| Berachain OFT | `script/MyOFT.s.sol` (and source) |
| Bridge script | `script/Bridge.s.sol` |
# Upgradeable Contracts (OpenZeppelin)
Source: https://docs.berachain.com/build/guides/community/openzeppelin-upgrades
Deploy upgradeable contracts on Berachain using OpenZeppelin Upgrades (proxy pattern).
Use this guide when you want to **deploy upgradeable contracts** (proxy pattern) on Berachain using **OpenZeppelin's upgradeable contracts** and Hardhat/Foundry plugin.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/openzeppelin-upgrades](https://github.com/berachain/guides/tree/main/apps/openzeppelin-upgrades)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/openzeppelin-upgrades/README.md)
## Requirements
* Node, npm (if using Hardhat) or Foundry
* Wallet with testnet \$BERA
## Stack
Hardhat or Foundry, Solidity, OpenZeppelin Contracts Upgradeable, upgrade plugin.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/openzeppelin-upgrades
npm install
```
2. Configure network (Berachain) and deploy script. Use the plugin to deploy the proxy and implementation.
3. Run deploy; upgrade later with the plugin’s upgrade task. Exact commands in README.
For deploy and upgrade commands, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/openzeppelin-upgrades/README.md).
# Overview
Source: https://docs.berachain.com/build/guides/community/overview
# Community Developer Guides
These are a list of community developer guides for Berachain.
## Wallet Connections
| Project Name | Git Repository |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| NextJS WalletConnect WAGMI + Viem Frontend Contract Deployment | [GitHub](https://github.com/berachain/guides/tree/main/apps/walletconnect-nextjs) |
| ThirdWeb ConnectWallet NextJS | [GitHub](https://github.com/berachain/guides/tree/main/apps/thirdweb-connectwallet-nextjs) |
| Particle Auth Core Vite | [GitHub](https://github.com/berachain/guides/tree/main/apps/particle-auth-core-vite) |
| RainbowKit Vite | [GitHub](https://github.com/berachain/guides/tree/main/apps/rainbowkit-vite) |
| WalletConnect Expo | [GitHub](https://github.com/berachain/guides/tree/main/apps/walletconnect-expo) |
| RPC Provider Guide | [Guide](https://blog.berachain.com/blog/your-berachain-rpc-guide) |
## Bridging
| Project Name | Git Repository |
| ---------------------------------------------------- | -------------------------------------------------------------------------- |
| Bridging ERC20 Tokens to Berachain with LayerZero V2 | [GitHub](https://github.com/berachain/guides/tree/main/apps/layerzero-oft) |
## Smart Contract Deployment & Verification
| Project Name | Git Repository |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Deploy HelloWorld Contract With Ethers6 & solc | [GitHub](https://github.com/berachain/guides/tree/main/apps/ethers6-solc-helloworld) |
| Deploy HelloWorld Contract With Viem & solc | [GitHub](https://github.com/berachain/guides/tree/main/apps/viem-solc-helloworld) |
| Create HelloWorld Contract Using Hardhat & Viem | [GitHub](https://github.com/berachain/guides/tree/main/apps/hardhat-viem-helloworld) |
| Hardhat Ethers6 Contract Verification | [GitHub](https://github.com/berachain/guides/tree/main/apps/hardhat-contract-verification) |
| Hardhat Ethers6 ERC1155 | [GitHub](https://github.com/berachain/guides/tree/main/apps/hardhat-ethers6-erc1155) |
| Create ERC20 Contract Using Foundry | [GitHub](https://github.com/berachain/guides/tree/main/apps/foundry-erc20) |
| Deploy Upgradeable Contracts | [GitHub](https://github.com/berachain/guides/tree/main/apps/openzeppelin-upgrades) |
## Indexing and Querying
| Project Name | Git Repository |
| ------------------------------------------- | -------------------------------------------------------------------------------- |
| Index & Query Berachain Data with Goldsky | [GitHub](https://github.com/berachain/guides/tree/main/apps/goldsky-subgraph) |
| Index & Query Berachain Data with The Graph | [Guide](https://thegraph.com/docs/en/subgraphs/quick-start/) |
| Envio Indexer ERC20 | [GitHub](https://github.com/berachain/guides/tree/main/apps/envio-indexer-erc20) |
| Index & Query Berachain Data with SubQuery | [Guide](https://subquery.network/indexer/berachain-bepolia-testnet) |
## Verifiable Randomness
| Project Name | Git Repository |
| ------------------------------------ | ------------------------------------------------------------------------- |
| Using Gelato VRF | [GitHub](https://github.com/berachain/guides/tree/main/apps/gelato-vrf) |
| Provably Fair NFTs with Pyth Entropy | [GitHub](https://github.com/berachain/guides/tree/main/apps/pyth-entropy) |
## Oracles
| Project Name | Git Repository |
| ------------ | ------------------------------------------------------------------------ |
| Pyth Oracle | [GitHub](https://github.com/berachain/guides/tree/main/apps/pyth-oracle) |
## Governance
| Project Name | Git Repository |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Berachain Governance Proposal For Reward Vaults | [GitHub](https://github.com/berachain/guides/tree/main/apps/berachain-governance-proposal) |
## Storage
| Project Name | Git Repository |
| -------------------------------------------- | ----------------------------------------------------------------------------- |
| Irys NodeJS Upload Script With \$BERA Tokens | [GitHub](https://github.com/berachain/guides/tree/main/apps/irys-bera-nodejs) |
## Relayers/Gasless Transactions
| Project Name | Source |
| ------------------ | ---------------------------------------------------------------------------------------- |
| Using Gelato Relay | [Guide](https://docs.google.com/document/d/1dsSGGYZ4IIE8EAhrMH8SOQFmIygcaibRYHiar2Vj2Kw) |
## Automation
| Project Name | Source |
| --------------------------- | ---------------------------------------------------------------------------------------- |
| Using Gelato Web3 Functions | [Guide](https://docs.google.com/document/d/1kUuvYwUH6tyLM4mNJYNu22jS6lPynvSSF_x8NDAZzRg) |
## Proof of Liquidity Examples
| Project Name | Source |
| -------------------- | --------------------------------------------------------------------------------------- |
| SocialFi | [Guide](https://blog.berachain.com/blog/poltech-proof-of-liquidity-goes-social) |
| Time-limited Rewards | [Guide](https://blog.berachain.com/blog/onlypaws-bearing-it-all-for-proof-of-liquidity) |
# Particle Auth Core Vite
Source: https://docs.berachain.com/build/guides/community/particle-auth-core-vite
Wallet connection and auth on Berachain using Particle Auth in a Vite app.
Use this guide when you want **wallet connection and authentication** on Berachain via **Particle Auth** in a **Vite**-based frontend.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/particle-auth-core-vite](https://github.com/berachain/guides/tree/main/apps/particle-auth-core-vite)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/particle-auth-core-vite/README.md)
## Requirements
* Node (see README), npm
* Particle project/config if required by Particle Auth
## Stack
Vite, TypeScript, Particle Auth.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/particle-auth-core-vite
npm install
```
2. Configure any env or Particle keys (see README).
3. Run the dev server and use the app to connect via Particle and interact with Berachain.
For full setup and Particle configuration, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/particle-auth-core-vite/README.md).
# Provably Fair NFTs with Pyth Entropy
Source: https://docs.berachain.com/build/guides/community/pyth-entropy
Use Pyth Entropy on Berachain for verifiable randomness and provably fair NFT minting. Includes setup, deploy EntropyNFT contract, and request randomness.
Use this guide when you need **on-chain verifiable randomness** or **provably fair NFT minting** on Berachain. Pyth Entropy lets you request secure random numbers and use them in smart contract callbacks (e.g. mint an NFT with a random trait).
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/pyth-entropy](https://github.com/berachain/guides/tree/main/apps/pyth-entropy)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/pyth-entropy/README.md) — an MCP or tool can fetch this for full steps.
## Official documentation
* [Pyth Entropy](https://docs.pyth.network/entropy) — concepts and API.
## Requirements
* Node `v20.11.0` or greater, npm
* Wallet with Berachain testnet \$BERA — [Bepolia Faucet](https://bepolia.faucet.berachain.com)
* [Foundry](https://book.getfoundry.sh/getting-started/installation) (`foundryup`)
## Stack
Solidity, Foundry (Forge), Node.js, `@pythnetwork/entropy-sdk-solidity`, OpenZeppelin contracts.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/pyth-entropy
npm install
```
Add remappings to `foundry.toml` (see README).
2. **Configure `.env`** at project root: `RPC_URL`, `PRIVATE_KEY`, `ENTROPY_ADDRESS`, `PROVIDER_ADDRESS` (values in README).
3. **Deploy the NFT contract**
```bash theme={null}
forge create src/EntropyNFT.sol:EntropyNFT --private-key $PRIVATE_KEY --rpc-url $RPC_URL --constructor-args $ENTROPY_ADDRESS $PROVIDER_ADDRESS
```
Set `ENTROPY_NFT_ADDRESS` in `.env` to the deployed address.
4. **Request a mint** (script requests random number from Pyth and polls for the mint callback)
```bash theme={null}
node app/requestMint.js
```
## Key files
| Purpose | Path |
| ------------------- | -------------------- |
| NFT contract | `src/EntropyNFT.sol` |
| Mint request script | `app/requestMint.js` |
# Pyth Oracle on Berachain
Source: https://docs.berachain.com/build/guides/community/pyth-oracle
Query Pyth Network price data on Berachain with on-demand updates. Deploy consumer contract, fetch price payload, call updatePrice and getPrice.
Use this guide when you need **real-time price data** or **oracle feeds** on Berachain. Pyth uses on-demand updates: your contract pulls the latest price when needed instead of subscribing to a stream.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/pyth-oracle](https://github.com/berachain/guides/tree/main/apps/pyth-oracle)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/pyth-oracle/README.md)
## Official documentation
* [Pyth Network](https://pyth.network/) — oracle overview and on-demand model.
## Requirements
* Node `v20.11.0` or greater, npm
* [jq](https://jqlang.github.io/jq/download/)
* Wallet with testnet \$BERA — [Bepolia Faucet](https://bepolia.faucet.berachain.com)
* [Foundry](https://book.getfoundry.sh/getting-started/installation)
## Stack
Solidity, Foundry (Forge, Cast), Node.js, Pyth Hermes API (price payloads).
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/pyth-oracle
npm install
```
2. **Import wallet** (Foundry keystore): `cast wallet import deployer --interactive`. Set `BERACHAIN_TESTNET_RPC` (e.g. `https://bepolia.rpc.berachain.com`).
3. **Deploy consumer contract**
```bash theme={null}
forge build
forge create ./src/ConsumerContract.sol:ConsumerContract --rpc-url $BERACHAIN_TESTNET_RPC --account deployer
```
4. **Fetch price and update on-chain**
```bash theme={null}
curl -s "https://hermes.pyth.network/v2/updates/price/latest?&ids[]=0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace" | jq -r ".binary.data[0]" > price_update.txt
cast send --rpc-url $BERACHAIN_TESTNET_RPC "updatePrice(bytes[])" "[0x\`cat price_update.txt\`]" --account deployer --value 0.0001ether
```
5. **Read price:** `cast call --rpc-url $BERACHAIN_TESTNET_RPC "getPrice()"`
**Troubleshooting:** `StalePrice` — re-run the update; `InsufficientFee` — increase `--value` (e.g. `0.0005ether`).
## Key files
| Purpose | Path |
| ----------------- | -------------------------- |
| Consumer contract | `src/ConsumerContract.sol` |
# RainbowKit Vite
Source: https://docs.berachain.com/build/guides/community/rainbowkit-vite
Minimal React + Vite app with RainbowKit for connecting wallets to Berachain.
Use this guide when you want a **minimal React + Vite** app with **RainbowKit** to **connect wallets** to Berachain.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/rainbowkit-vite](https://github.com/berachain/guides/tree/main/apps/rainbowkit-vite)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/rainbowkit-vite/README.md)
## Requirements
* Node (see README), npm
* Wallet with testnet \$BERA if sending transactions
## Stack
React, TypeScript, Vite, RainbowKit.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/rainbowkit-vite
npm install
```
2. Run dev server (e.g. `npm run dev`). Use RainbowKit in the UI to connect a wallet and switch to Berachain (add network if needed).
For chain config and full steps, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/rainbowkit-vite/README.md).
# ThirdWeb ConnectWallet NextJS
Source: https://docs.berachain.com/build/guides/community/thirdweb-connectwallet-nextjs
Connect wallet and interact with Berachain using ThirdWeb ConnectWallet and Next.js.
Use this guide when you want to **connect wallets** and **interact with Berachain** using **ThirdWeb's ConnectWallet** SDK in a **Next.js** app.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/thirdweb-connectwallet-nextjs](https://github.com/berachain/guides/tree/main/apps/thirdweb-connectwallet-nextjs)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/thirdweb-connectwallet-nextjs/README.md)
## Requirements
* Node (see README for version), npm
* Wallet with testnet \$BERA if deploying or sending transactions
## Stack
Next.js, TypeScript, ThirdWeb ConnectWallet SDK.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/thirdweb-connectwallet-nextjs
npm install
```
2. Configure environment (see README for `.env` and chain config).
3. Run the dev server and open the app; use ConnectWallet to connect and interact with Berachain.
For full steps and configuration, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/thirdweb-connectwallet-nextjs/README.md).
# HelloWorld with Viem & solc
Source: https://docs.berachain.com/build/guides/community/viem-solc-helloworld
Deploy a HelloWorld-style contract to Berachain using Viem and the solc compiler.
Use this guide when you want to **deploy a simple contract** using **Viem** and **solc** only (no Hardhat or Foundry).
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/viem-solc-helloworld](https://github.com/berachain/guides/tree/main/apps/viem-solc-helloworld)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/viem-solc-helloworld/README.md)
## Requirements
* Node, npm
* Wallet with testnet \$BERA (e.g. [Bepolia Faucet](https://bepolia.faucet.berachain.com))
## Stack
Node.js, Viem, solc, JavaScript/TypeScript.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/viem-solc-helloworld
npm install
```
2. Set private key and RPC in env (see README).
3. Compile and deploy using the repo scripts (Viem + solc); see README for exact commands.
For full compile/deploy commands, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/viem-solc-helloworld/README.md).
# WalletConnect Expo
Source: https://docs.berachain.com/build/guides/community/walletconnect-expo
Mobile app example using Expo and WalletConnect to connect to Berachain.
Use this guide when you want a **mobile app** (Expo / React Native) that **connects to Berachain** via **WalletConnect**.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/walletconnect-expo](https://github.com/berachain/guides/tree/main/apps/walletconnect-expo)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/walletconnect-expo/README.md)
## Requirements
* Node, npm (or yarn), Expo CLI / EAS
* WalletConnect project ID from [WalletConnect Cloud](https://cloud.walletconnect.com) if required
* iOS/Android simulator or device
## Stack
Expo, React Native, TypeScript/JavaScript, WalletConnect.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/walletconnect-expo
npm install
```
2. Configure env (e.g. WalletConnect project ID) per README.
3. Run with `npx expo start` (or EAS) and open on simulator/device; connect wallet and use Berachain.
For full setup and Berachain chain config in the app, fetch the [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/walletconnect-expo/README.md).
# NextJS WalletConnect WAGMI + Viem
Source: https://docs.berachain.com/build/guides/community/walletconnect-nextjs
Deploy a contract from a Next.js frontend with WalletConnect, WAGMI, and Viem. Connect via QR with MetaMask Mobile.
Use this guide when you want a **Next.js dApp** that **connects wallets via WalletConnect** (e.g. MetaMask Mobile QR), uses **WAGMI** and **Viem**, and **deploys a contract** from the browser.
## Repository
* **Code:** [github.com/berachain/guides/tree/main/apps/walletconnect-nextjs](https://github.com/berachain/guides/tree/main/apps/walletconnect-nextjs)
* **README (raw):** [raw README](https://raw.githubusercontent.com/berachain/guides/main/apps/walletconnect-nextjs/README.md)
## Requirements
* Node `v20.11.0`, npm
* [WalletConnect Project ID](https://cloud.walletconnect.com) — set in `.env` as `NEXT_PUBLIC_PROJECT_ID`
* MetaMask Mobile (or other WalletConnect-compatible wallet) for QR flow
## Stack
Next.js 14, TypeScript, WalletConnect, WAGMI, Viem.
## Quick start
1. **Clone and install**
```bash theme={null}
git clone https://github.com/berachain/guides.git && cd guides/apps/walletconnect-nextjs
npm install
copy .env.example .env
```
Add `NEXT_PUBLIC_PROJECT_ID` from [WalletConnect Cloud](https://cloud.walletconnect.com).
2. **Run**
```bash theme={null}
npm run dev
```
3. In the app: **Connect Wallet** → **WalletConnect** → scan QR with MetaMask Mobile. Enter a greeting and click **Deploy** to deploy the contract; confirm in MetaMask.
For RPC and chain config, see [Berachain docs](https://docs.berachain.com).
## Key files
| Purpose | Path |
| ------------ | ------------------------------------- |
| App / config | Next.js app in repo root (see README) |
# Add Incentives to a Reward Vault
Source: https://docs.berachain.com/build/guides/proof-of-liquidity/add-incentives
Add incentive tokens to an existing Reward Vault.
# Add Incentives to a Reward Vault
This guide covers adding incentives to an existing whitelisted Reward Vault using Hub.
## Prerequisites
* Existing whitelisted Reward Vault
* Whitelisted incentive token
* Tokens available in the funding wallet
## Steps
1. Open your target vault/action in [Berachain Hub](https://hub.berachain.com).
2. Click **Add Incentives**.
3. Select token and amount.
4. If you are vault manager, set distribution rate.
5. Approve token transfer and confirm.
## Notes
* Anyone can fund incentives, but manager approval may be required depending on vault configuration.
* Non-manager submissions can remain pending until approved.
## Need multisig execution?
* [Add Incentives with Safe](/build/guides/proof-of-liquidity/add-incentives-safe)
# Add Incentives with Safe
Source: https://docs.berachain.com/build/guides/proof-of-liquidity/add-incentives-safe
Queue and execute Reward Vault incentive transactions through Safe.
# Add Incentives with Safe
Use this flow when the vault manager account is a Safe multisig and you need transaction review/signoff before execution.
## Prerequisites
* Existing whitelisted Reward Vault
* Whitelisted incentive token
* Safe configured as manager or authorized submitter
## Flow
1. Prepare the incentive transaction from Hub or your internal tooling.
2. Send the transaction to Safe for multisig review.
3. Collect required signatures in Safe.
4. Execute the transaction onchain from Safe.
## Operational Tips
* Confirm chain/network in Safe before signing.
* Verify token address, amount, and vault address in the calldata preview.
* Use role-separated signers for stronger operational security.
## Related
* [Add Incentives to a Reward Vault](/build/guides/proof-of-liquidity/add-incentives)
# Setup Reward Vault
Source: https://docs.berachain.com/build/guides/proof-of-liquidity/setup-reward-vault
Create and configure a Reward Vault for PoL integration.
# Setup Reward Vault
Protocols participate in PoL through a Reward Vault. This guide covers the practical setup path for creating a vault and making it ready for users.
## Checklist
* Create a Reward Vault
* Submit/complete your Reward Vault request process
* Configure token and vault metadata for discoverability
* Add adapter support where needed
## 1) Create a Reward Vault
Recommended path:
* Create via [Berachain Hub](https://hub.berachain.com/earn/create)
Alternative paths:
* Create from your deployment scripts
* Call `createRewardVault` on the Reward Vault Factory directly
Contract addresses and references:
* [Deployed Contracts](/build/getting-started/deployed-contracts)
## 2) Complete Reward Vault Request Process
PoL rollout and whitelisting requirements can evolve by governance phase. Use the latest process docs:
* [Reward Vault Governance](/general/governance/reward-vault-governance)
* [Reward Vault Guidelines](/general/help/reward-vault-guidelines)
## 3) Configure Metadata
For a production-quality UX in Hub and explorers:
* Submit token metadata to the Berachain metadata registry
* Submit Reward Vault metadata to the same registry
If you need explorer token metadata updates:
* [Berascan Token Update Guide](/build/guides/berascan-token-update)
## 4) Add Adapter Support (If Needed)
If your staking token is not a plain LP/receipt token, you may need a PoL adapter for yield display and pricing.
* [hub-pol-adapters](https://github.com/berachain/hub-pol-adapters)
## Next Step
* [Add Incentives](/build/guides/proof-of-liquidity/add-incentives)
# Verifying Smart Contracts
Source: https://docs.berachain.com/build/guides/verifying-smart-contracts
Verify smart contracts on Berachain using Berascan, Hardhat, Forge, and Remix.
This guide describes how to verify smart contracts on Berachain. Verification publishes source code to the block explorer so that users can read and audit it.
The following methods are covered:
* **Manual verification (Berascan)** — Verification via the block explorer UI
* **Hardhat** — Verification using Hardhat’s Etherscan plugin
* **Forge** — Verification using Foundry’s `forge verify-contract`
* **Remix** — Verification from the Remix IDE Contract Verification plugin
## Requirements
* A deployed smart contract on Berachain
* The contract’s source code
* The contract address
* Tooling for the chosen method (see sections below)
To deploy a contract first, see [Developer tools](/build/getting-started/developer-tools) for
setup with Hardhat, Foundry, and other tooling.
## Manual Verification (Berascan)
### Step 1: Open the contract on Berascan
Open the block explorer and go to the contract’s page by searching for its address:
* **Mainnet:** [berascan.com](https://berascan.com)
* **Bepolia testnet:** [testnet.berascan.com](https://testnet.berascan.com)
### Step 2: Start verification
On the contract page, open the **Verify and Publish** link.
You are taken to the verification form: [testnet.berascan.com/verifyContract](https://testnet.berascan.com/verifyContract) on Bepolia, or the mainnet equivalent on Berascan.
### Step 3: Enter contract details
Fill in the form:
1. **Contract Address** — The deployed contract address (often pre-filled if opened from the contract page).
2. **Compiler Type** — Choose **Solidity (Single file)** for a flattened contract.
3. **Compiler Version** — The exact Solidity version used to compile the deployed contract (e.g. `v0.8.28+commit.7893614a`).
4. **Open Source License Type** — e.g. MIT License.
5. **Terms of Service** — Accept the terms.
6. Click **Continue**.
For multi-file or dependency-heavy contracts, flatten the source into a single file and use the
single-file option.
### Step 4: Upload source code
On the **Verify & Publish** step:
1. Confirm the shown contract address, compiler type (e.g. SINGLE FILE / CONCATENATED METHOD), and compiler version.
2. Paste the flattened contract source into the source code field.
3. Optionally set optimization, run count, and EVM version under Advanced Configuration.
4. Click **Verify & Publish**.
Contracts that compile in Remix typically compile here as well. Compilation is limited to about 45
seconds per contract. Contracts deployed by another contract (factory pattern) have limited
support.
### Step 5: Confirmation
Berascan verifies the contract, usually within a few seconds. After success, the contract is readable on the explorer and can be interacted with from Berascan.
## Troubleshooting
If verification fails, confirm:
* The compiler version matches the one used at deployment.
* The source code is complete and matches the deployed bytecode (no edits after deployment).
* Constructor arguments are correct and encoded as used at deployment.
* The contract address is correct and deployed on the selected network.
For more help, see Berascan’s verification docs or their support channels.
## Hardhat Verification
Use Hardhat’s Etherscan plugin to verify contracts from the command line or after deployment.
### Requirements
* Hardhat v3.0.0 or later
* An [Etherscan API key](https://etherscan.io/apis) (V2 API; same key works for Berascan)
* The deployed contract and its constructor arguments
### Configuration
Store the API key in Hardhat’s keystore (or use environment variables):
```bash theme={null}
pnpm keystore:set ETHERSCAN_API_KEY
```
In `hardhat.config.ts`, add verification and chain descriptor entries. Merge the following into your existing config:
```ts theme={null}
const config = {
// ... your existing config (networks, solidity, etc.)
verify: {
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY ?? "",
},
},
chainDescriptors: {
80069: {
name: "Berachain Bepolia",
blockExplorers: {
etherscan: {
name: "Berascan Bepolia",
url: "https://testnet.berascan.com",
apiUrl: "https://api.etherscan.io/v2/api",
},
},
},
80094: {
name: "Berachain",
blockExplorers: {
etherscan: {
name: "Berascan",
url: "https://berascan.com",
apiUrl: "https://api.etherscan.io/v2/api",
},
},
},
},
};
```
Ensure your `networks` (or equivalent) use the same chain IDs (80069 for Bepolia, 80094 for mainnet) so the correct explorer is used.
### Verification command
Add a script in `package.json`:
```json theme={null}
{
"scripts": {
"verify:berachain": "hardhat verify --network berachainTestnet --"
}
}
```
Run verification with the contract address and constructor arguments in order:
```bash theme={null}
pnpm verify:berachain 0x2ACD9577B57Ff043F0203730683e8c7C881DcB21 "Hello World"
```
For a contract with no constructor arguments, omit the extra arguments:
```bash theme={null}
pnpm verify:berachain 0x2ACD9577B57Ff043F0203730683e8c7C881DcB21
```
### Example output
```text theme={null}
=== Etherscan ===
Submitted source code for verification on Berascan:
contracts/HelloWorld.sol:HelloWorld
Address: 0x2ACD9577B57Ff043F0203730683e8c7C881DcB21
Waiting for verification result...
Contract verified successfully on Berascan.
contracts/HelloWorld.sol:HelloWorld
Explorer: https://testnet.berascan.com/address/0x2ACD9577B57Ff043F0203730683e8c7C881DcB21#code
```
## Forge Verification
Use Foundry’s `forge verify-contract` to verify contracts from the command line.
### Requirements
* Foundry v1.3.1 or later (Etherscan V2 API support)
* [Etherscan API key](https://etherscan.io/apis)
* The deployed contract and, if applicable, its constructor arguments
### Verification command
Encode constructor arguments with `cast abi-encode` and pass them to `forge verify-contract`. Use the chain name that Forge uses for Berachain (e.g. `Berachain Bepolia` for testnet). Set `ETHERSCAN_API_KEY` in your environment before running.
**Bepolia (testnet):**
```bash theme={null}
forge verify-contract \
--watch \
--chain "Berachain Bepolia" \
0x2ACD9577B57Ff043F0203730683e8c7C881DcB21 \
src/HelloWorld.sol:HelloWorld \
--verifier etherscan \
--etherscan-api-key $ETHERSCAN_API_KEY \
--constructor-args $(cast abi-encode "constructor(string)" "Hello World")
```
**Mainnet:**
```bash theme={null}
forge verify-contract \
--watch \
--chain Berachain \
0x2ACD9577B57Ff043F0203730683e8c7C881DcB21 \
src/HelloWorld.sol:HelloWorld \
--verifier etherscan \
--etherscan-api-key $ETHERSCAN_API_KEY \
--constructor-args $(cast abi-encode "constructor(string)" "Hello World")
```
For contracts with constructor parameters, encode them with `cast abi-encode
"constructor(type1,type2,...)" "arg1" "arg2" ...` and pass the result to `--constructor-args`. For
contracts with no constructor parameters, omit the `--constructor-args` flag.
### Example output
```text theme={null}
Start verifying contract `0x2ACD9577B57Ff043F0203730683e8c7C881DcB21` deployed on Berachain Bepolia
Submitting verification for [src/HelloWorld.sol:HelloWorld] 0x2ACD9577B57Ff043F0203730683e8c7C881DcB21.
Submitted contract for verification:
Response: `OK`
GUID: `xtecz3j...`
URL: https://testnet.berascan.com/address/0x2ACD9577B57Ff043F0203730683e8c7C881DcB21
Contract verification status:
Response: `OK`
Details: `Pass - Verified`
Contract successfully verified
```
## Remix Verification
The Remix IDE Contract Verification plugin supports Berascan. Use it when you develop or deploy from Remix and want to verify in the same environment.
### Requirements
* Contract deployed on a public Berachain network (mainnet or Bepolia)
* Contract compiled in Remix
* Constructor arguments used at deployment (if any)
* Etherscan API key for Berascan verification
### Enabling the plugin
1. Open [remix.ethereum.org](https://remix.ethereum.org).
2. In the Plugin Manager, enable **CONTRACT VERIFICATION**.
3. Open the Contract Verification plugin from the sidebar.
### Supported explorers
* **Berascan** — Etherscan-based; requires an Etherscan API key.
### Verification steps
1. Compile the contract in Remix.
2. In the plugin, select Berascan as the verification service.
3. Enter the deployed contract address.
4. If the contract has constructor parameters, enter the constructor arguments in the format the plugin expects.
5. Submit verification.
### Proxy verification
For a contract behind a proxy:
1. Enable **The deployed contract is behind a proxy**.
2. Enter the proxy contract address.
3. Submit; the plugin verifies both proxy and implementation.
Proxy verification is supported only with Berascan (Etherscan-based), not with Beratrail.
### Settings
In the plugin or Remix settings you can:
* Add and store Etherscan API keys (required for Berascan).
* Adjust API URLs for verification services.
* Manage settings per chain.
The Etherscan V2 API is used, so one API key works for Berascan and many other chains supported by
Remix.
### Verification results
* **Receipts** — Verification status and result for each submission.
* **Lookup** — Check whether a contract is verified and download source.
* **Status indicators** — Hover for details when verification fails.
For full plugin behavior and options, see the [Remix contract verification
documentation](https://remix-ide.readthedocs.io/en/latest/contract_verification.html).
# Advanced PoL Integration
Source: https://docs.berachain.com/build/pol/advanced-pol
Proof of Liquidity Integration Guide for Non-ERC20 Protocols
# Proof of Liquidity Integration Guide for Non-ERC20 Protocols
## Introduction
Users typically engage with Proof of Liquidity by staking ERC-20 receipt tokens into [Reward Vaults](/general/proof-of-liquidity/reward-vaults) to earn PoL rewards. However, this approach may not be suitable for all protocols.
This guide demonstrates how to integrate Berachain's Proof of Liquidity (PoL) system for protocols that do not naturally produce stakeable ERC-20 receipt tokens or that track balances internally. For example, a perpetual futures exchange may wish to reward users who open trading positions, and stop rewards when the position is closed.
By implementing this approach, such protocols can still participate in PoL, benefiting from the improved incentive efficiencies it provides.
:::warning
Note that this article provides only one possible workaround for integrating PoL with non-ERC20 protocols. The solution is not exhaustive and may not be suitable for all use cases.
:::
## Description of Approach
The described approach involves the creation of a dummy `StakingToken` that is staked in a Reward Vault on behalf of users by a protocol. This dummy token is used to track the staked balances of users and is minted and burned by the protocol (operating through `ProtocolContract`) as users provide/withdraw their liquidity from the protocol.
The staked dummy token balance entitles users to earn PoL rewards as if they had staked an ERC-20 receipt token in a PoL vault themselves. This approach is enabled by the `delegateStake` and `delegateWithdraw` methods in the [RewardVault](/general/proof-of-liquidity/reward-vaults) contract.
## Requirements
Before beginning, make sure you have Foundry installed beforehand.
* [Foundry](https://book.getfoundry.sh/getting-started/installation)
## Project Setup
1. Initialize a new Forge project and install dependencies:
```bash theme={null}
forge init pol-smart-stake --no-commit --no-git;
cd pol-smart-stake;
forge install OpenZeppelin/openzeppelin-contracts --no-commit --no-git;
```
2. Create a `remappings.txt` file for OpenZeppelin imports:
```bash theme={null}
# FROM: ./pol-smart-stake
echo "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/" > remappings.txt;
```
## Implement Contracts
#### 1. Create the dummy token contract at `src/StakingToken.sol`:
```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract StakingToken is ERC20, Ownable {
constructor() ERC20("StakingToken", "STK") Ownable(msg.sender) {}
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
function burn(address from, uint256 amount) external onlyOwner {
_burn(from, amount);
}
}
```
This contract creates a dummy ERC20 token that will be used for staking in PoL vaults. Only the owner (`ProtocolContract`) can mint and burn tokens.
#### 2. Create a mock protocol contract at `src/ProtocolContract.sol`:
```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "./StakingToken.sol";
import {IRewardVault, IRewardVaultFactory} from "./interfaces/IRewardVaults.sol";
contract ProtocolContract {
StakingToken public stakingToken;
IRewardVault public rewardVault;
mapping(address => uint256) public userActivity;
constructor(address _vaultFactory) {
// Create new staking token
stakingToken = new StakingToken();
// Create vault for newly created token
address vaultAddress = IRewardVaultFactory(_vaultFactory)
.createRewardVault(address(stakingToken));
rewardVault = IRewardVault(vaultAddress);
}
function addActivity(address user, uint256 amount) external {
// Protocol actions/logic here
userActivity[user] += amount;
// Mint StakingTokens
stakingToken.mint(address(this), amount);
// Stake tokens in RewardVault on behalf of user
stakingToken.approve(address(rewardVault), amount);
rewardVault.delegateStake(user, amount);
}
function removeActivity(address user, uint256 amount) external {
// Protocol actions/logic here
require(userActivity[user] >= amount, "Insufficient user activity");
userActivity[user] -= amount;
// Withdraw tokens from the RewardVault
rewardVault.delegateWithdraw(user, amount);
// Burn the withdrawn StakingTokens
stakingToken.burn(address(this), amount);
}
}
```
This contract is a simple representation of an arbitrary protocol's contract:
* `userActivity` represents the internal accounting and logic specific to that protocol
* The remainder of the `addActivity` and `removeActivity` methods mint and burn `StakingTokens`, and interacts with the relevant RewardVault to stake/unstake on behalf of users
#### 3. Add the PoL Interfaces at `src/interfaces/IRewardVaults.sol`:
```solidity theme={null}
pragma solidity ^0.8.19;
interface IRewardVault {
function delegateStake(address account, uint256 amount) external;
function delegateWithdraw(address account, uint256 amount) external;
function getTotalDelegateStaked(
address account
) external view returns (uint256);
}
interface IRewardVaultFactory {
function createRewardVault(
address stakingToken
) external returns (address);
}
```
These interfaces define the methods for spinning up a new RewardVault from the Factory contract, and for subsequently interacting with it.
## Testing the Integration
Now, we wire everything together with tests to ensure that the integration works as expected. Below is an example test suite for the `ProtocolContract` contract.
Feel free to look at each individual test to get a better idea on how successful scenarios are handled.
```solidity-vue theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "../src/ProtocolContract.sol";
import {IRewardVault, IRewardVaultFactory} from "../src/interfaces/IRewardVaults.sol";
contract ProtocolContractTest is Test {
ProtocolContract public protocol;
IRewardVault public rewardVault;
address public user1 = address(0x1);
address public user2 = address(0x2);
function setUp() public {
IRewardVaultFactory vaultFactory = IRewardVaultFactory(
{{config.contracts.pol.rewardVaultFactory.address.berachainMainnet}}
);
protocol = new ProtocolContract(address(vaultFactory));
rewardVault = protocol.rewardVault();
}
function testAddActivity() public {
protocol.addActivity(user1, 1);
assertEq(protocol.userActivity(user1), 1);
assertEq(rewardVault.getTotalDelegateStaked(user1), 1);
}
function testRemoveActivity() public {
protocol.addActivity(user1, 2);
vm.warp(block.timestamp + 1 days);
protocol.removeActivity(user1, 1);
assertEq(protocol.userActivity(user1), 1);
assertEq(rewardVault.getTotalDelegateStaked(user1), 1);
}
function testMultipleUsers() public {
protocol.addActivity(user1, 1);
protocol.addActivity(user2, 2);
assertEq(rewardVault.getTotalDelegateStaked(user1), 1);
assertEq(rewardVault.getTotalDelegateStaked(user2), 2);
}
}
```
### Run the Test
Finally, we run the test to check that the integration works as expected:
```bash-vue theme={null}
# FROM: ./pol-smart-stake
forge test --rpc-url {{config.mainnet.rpcUrl}};
# [Expected Output]:
# [⠊] Compiling...x
# No files changed, compilation skipped
# Ran 3 tests for test/StakingToken.t.sol:ProtocolContractTest
# [PASS] testAddActivity() (gas: 252067)
# [PASS] testMultipleUsers() (gas: 371503)
# [PASS] testRemoveActivity() (gas: 272693)
# Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 1.73s (1.22ms CPU time)
```
# PoL Integration Overview
Source: https://docs.berachain.com/build/pol/integration-basics
Start here for PoL integration paths, setup flow, and guide map.
# PoL Integration Overview
Proof of Liquidity (PoL) lets protocols reward user behavior through Reward Vault emissions and incentive flows. This page is the entry point for builders and links to the implementation guides for each step.
## Need conceptual grounding first?
If you are looking for PoL fundamentals before implementation details:
* [PoL Overview](/general/proof-of-liquidity/overview)
* [Reward Vaults](/general/proof-of-liquidity/reward-vaults)
## Start Here
Most teams should follow this order:
1. Create and configure a vault: [Setup Reward Vault](/build/guides/proof-of-liquidity/setup-reward-vault)
2. Fund emissions: [Add Incentives](/build/guides/proof-of-liquidity/add-incentives)
3. If manager operations run through multisig: [Add Incentives with Safe](/build/guides/proof-of-liquidity/add-incentives-safe)
## Choose Your Integration Path
### Standard tokenized activity (LP/receipt token style)
Your protocol already emits a stakeable ERC-20 that tracks user participation.
* [Setup Reward Vault](/build/guides/proof-of-liquidity/setup-reward-vault)
* [Add Incentives](/build/guides/proof-of-liquidity/add-incentives)
* [Add Incentives with Safe](/build/guides/proof-of-liquidity/add-incentives-safe)
### Protocol-operated staking patterns
You need to execute staking flows on behalf of users.
* [Staking for Other Accounts](/build/pol/staking-for-other-accounts)
### Custom/non-ERC-20 accounting
Your protocol tracks activity internally and does not naturally emit a stakeable token.
* [Advanced PoL Integration](/build/pol/advanced-pol)
### Advanced reward distribution behavior
You want controlled claiming behavior such as partial claims, streaming, or vesting-style flows.
* [Partial Reward Claims](/build/pol/partial-reward-claims)
* [RewardVaultHelper Claim Flow](/build/pol/reward-vault-helper-claim-flow)
# LST Integration for PoL Auction Yield
Source: https://docs.berachain.com/build/pol/lst-integration
Register an LSTStakerVault so your LST product earns a pro-rata share of WBERA paid through the PoL Incentive Auction.
Liquid Staking Token (LST) issuers can plug their staked-LST product into the Berachain Proof of Liquidity (PoL) Incentive Auction. Once an `LSTStakerVault` and its adapter are registered on `IncentivesCollector`, the vault automatically receives a pro-rata share of every WBERA payment auction buyers make against redirected Reward Vault incentive tokens.
This page is the integrator-facing guide. For the auction mechanics from the protocol side, see [Incentive Marketplace — Incentive fee settlement](/general/proof-of-liquidity/incentives#incentive-fee-settlement).
## What registration enables
When a buyer settles redirected incentive tokens through `IncentivesCollector`, the WBERA they pay is split **pro-rata, by WBERA-denominated total assets**, across:
* The shared `WBERAStakerVault` (sWBERA stakers).
* Every registered `LSTStakerVault` whose LST has an adapter that converts WBERA into the underlying LST.
A registered LST vault therefore accrues two sources of return for its stakers: the LST's own underlying yield, plus a continuous share of PoL incentive-auction WBERA proportional to its total stake value.
Stakers of a registered LST do not need to interact with PoL directly. As long as the vault and adapter are registered, the WBERA flows in automatically on every auction settlement.
## How auction yield reaches an LST vault
1. Reward Vaults transfer redirected incentive tokens (incentives not paid as validator commission) into `IncentivesCollector`.
2. An auction buyer calls `claim(recipient, incentiveTokens)` and pays WBERA into the collector.
3. The collector splits the paid WBERA pro-rata across the sWBERA Staking Vault and registered `LSTStakerVault`s, sized by each vault's WBERA-denominated total assets.
4. For each registered LST vault, the collector calls the vault's registered **adapter** to convert that WBERA share into the vault's underlying LST, then credits the LST to the vault through `receiveRewards`.
5. The vault's per-share value rises; its stakers earn the increment without taking any action.
The split is recomputed on every settlement, so vaults that grow attract a larger share of subsequent WBERA flows.
## Currently registered LSTs
The first registered LST is **iBERA** from Infrared. Its staked version, **siBERA**, accrues both the underlying iBERA yield and its pro-rata share of PoL incentive-auction WBERA via the registered LST adapter.
Additional LSTs can be registered through the steps below.
## Integration steps for LST issuers
The integration is two contract deployments and one registry write. Coordinate with governance for the registry call.
### 1. Deploy your LST vault system
Deploy the `LSTStakerVault` system via [`LSTStakerVaultFactory`](/build/getting-started/deployed-contracts). The factory produces:
* An `LSTStakerVault` ERC-4626-style vault that accepts your LST as the asset.
* The supporting contracts for withdrawal accounting and reward receipt.
The vault must implement `receiveRewards` so that the collector can credit converted LST balances during auction settlement.
### 2. Deploy an LST adapter
The adapter is the contract that converts WBERA into your underlying LST during auction settlement. It must:
* Accept WBERA from `IncentivesCollector`.
* Convert that WBERA into the LST (through your protocol's mint, swap, or wrap path).
* Hand the resulting LST to the registered `LSTStakerVault` via `receiveRewards`.
Adapter design is LST-specific. Refer to the iBERA adapter for a working pattern.
### 3. Register the vault and adapter with governance
Submit a governance proposal to call `addLstStakerVault` on `IncentivesCollector`, supplying:
* The deployed `LSTStakerVault` address.
* The deployed adapter address.
Once the registry write lands, the collector includes the new vault in every subsequent pro-rata split. The vault begins accruing WBERA-sourced LST yield on the next auction settlement.
See [Deployed contract addresses](/build/getting-started/deployed-contracts) for the live `IncentivesCollector` and `LSTStakerVaultFactory` addresses on each network.
## Operational notes
* **Vault total assets must be denominated comparably to WBERA.** The collector sizes each vault's share by its WBERA-denominated total assets; an adapter that misreports the underlying value will distort the split for every other registered vault.
* **No staking-pool LSTs.** Staking pool shares (stBERA) are not LSTs and cannot be registered through this path; they have their own yield mechanics. See [Staking Pools Overview](/nodes/staking-pools/overview).
## See also
* [Incentive Marketplace — Incentive fee settlement](/general/proof-of-liquidity/incentives#incentive-fee-settlement) — the auction mechanics from the protocol side.
* [\$sWBERA Token](/general/tokens/swbera) — the sister product that shares the pro-rata WBERA split.
* [Deployed contract addresses](/build/getting-started/deployed-contracts) — current `IncentivesCollector` and `LSTStakerVaultFactory` addresses.
* [Proof of Liquidity Overview](/general/proof-of-liquidity/overview) — the broader PoL picture.
# Partial Reward Claims
Source: https://docs.berachain.com/build/pol/partial-reward-claims
Guide to partial reward claiming in Reward Vaults with WBERA emissions.
# Partial Reward Claims
`getPartialReward` lets integrators claim a selected portion of accrued Reward Vault rewards instead of claiming the entire balance in one call.
Reward Vault emissions are distributed in **\$WBERA**.
## `getReward` vs `getPartialReward`
| Method | Amount claimed | Remaining rewards | Common use case |
| ------------------ | ---------------------- | ----------------- | ---------------------------- |
| `getReward` | Full accrued amount | 0 | Full claim |
| `getPartialReward` | Caller-selected amount | Non-zero | Streaming or periodic claims |
A `getPartialReward` call settles the requested amount in \$WBERA. `rewardToken()` reads **\$WBERA**.
## Solidity example
```solidity theme={null}
pragma solidity ^0.8.26;
interface IRewardVault {
function earned(address account) external view returns (uint256);
function getPartialReward(address account, address recipient, uint256 amount) external;
}
contract PartialClaimManager {
IRewardVault public immutable rewardVault;
constructor(address vault) {
rewardVault = IRewardVault(vault);
}
function claimPartial(uint256 amount, address recipient) external {
uint256 available = rewardVault.earned(msg.sender);
require(amount <= available, "amount exceeds rewards");
rewardVault.getPartialReward(msg.sender, recipient, amount);
}
}
```
## Integration guidance
* Read `earned(account)` before submitting a claim.
* Keep claim sizes below available rewards to avoid `AmountGreaterThanReward`.
* For delegated claim flows, enforce explicit operator authorization.
## Related pages
* [Reward Vaults](/general/proof-of-liquidity/reward-vaults)
* [Block production and rewards](/general/proof-of-liquidity/block-rewards)
* [RewardVaultHelper output-token claims](/build/pol/reward-vault-helper-claim-flow)
# RewardVaultHelper Claim Flow
Source: https://docs.berachain.com/build/pol/reward-vault-helper-claim-flow
Claim Reward Vault rewards as sWBERA or native BERA.
# RewardVaultHelper Claim Flow
`RewardVaultHelper` lets a staker claim rewards from one or many vaults and choose the output token format.
Supported output tokens:
* `$sWBERA` (configured by admin)
* Native `$BERA` (`address(0)` selector)
The two-argument overload `claimAllRewards(vaults, receiver)` transfers accrued **\$WBERA** (the emission ERC-20) to `receiver` without wrapping or unwrapping.
## Contract behavior
* `claimAllRewards(vaults, receiver)` credits **\$WBERA** to `receiver`.
* `claimAllRewards(vaults, receiver, outputToken)` routes output based on selector (`sWBERA` or native BERA).
* For `$sWBERA`, helper approves WBERA to the sWBERA vault and calls `IERC4626.deposit`.
* For native BERA, helper unwraps WBERA with `IWBERA.withdraw` and forwards ETH.
## Permissions
* `setSWBERA(address)` is gated by `DEFAULT_ADMIN_ROLE`.
* Claim methods are permissionless for stakers.
## Worked example (sWBERA and native BERA)
```solidity theme={null}
pragma solidity ^0.8.26;
interface IRewardVaultHelper {
function claimAllRewards(address[] memory vaults, address receiver) external;
function claimAllRewards(address[] memory vaults, address receiver, address outputToken) external;
function WBERA_ADDRESS() external view returns (address);
function sWBERA() external view returns (address);
}
contract HelperClaimExample {
IRewardVaultHelper public immutable helper;
constructor(address helper_) {
helper = IRewardVaultHelper(helper_);
}
function claimAsSWBERA(address[] memory vaults) external {
address sWbera = helper.sWBERA();
helper.claimAllRewards(vaults, msg.sender, sWbera);
}
function claimAsNativeBERA(address[] memory vaults) external {
helper.claimAllRewards(vaults, msg.sender, address(0));
}
}
```
## Integration notes
* The helper validates output selectors; unsupported tokens revert.
* Prefer exposing **`$sWBERA`** and **native `$BERA`** as the staker-facing claim outputs.
* Use [Partial Reward Claims](/build/pol/partial-reward-claims) for claim-size control.
# Staking for Other Accounts
Source: https://docs.berachain.com/build/pol/staking-for-other-accounts
Guide to using stakeOnBehalf functionality in Berachain Reward Vaults
# Staking for Other Accounts
## Introduction
The `stakeOnBehalf` function in [Reward Vaults](/general/proof-of-liquidity/reward-vaults) allows any account to stake tokens directly for another account without requiring delegation permissions. This feature enables new integration patterns and simplifies certain protocol workflows.
## Key Differences from Existing Staking Methods
### `stake()` vs `delegateStake()` vs `stakeOnBehalf()`
| Method | Who Stakes | Who Owns Tokens | Who Controls Staked Balance | Use Case |
| ----------------- | ----------------- | ----------------- | --------------------------- | -------------------------------- |
| `stake()` | Account holder | Account holder | Account holder | Direct user staking |
| `delegateStake()` | Delegate/Protocol | Delegate/Protocol | Delegate can withdraw | Protocol staking with control |
| `stakeOnBehalf()` | Any account | Staker | Account holder | Protocol staking without control |
**Key Distinction**: With `stakeOnBehalf`, the staking account provides the tokens, but the beneficiary account gains full control over the staked balance and can withdraw at any time.
## Use Cases
### 1. Protocol-to-Protocol Integrations
Protocols can stake tokens for users as part of their core functionality:
```solidity theme={null}
// A lending protocol stakes collateral for borrowers
function depositCollateral(uint256 amount) external {
collateralToken.transferFrom(msg.sender, address(this), amount);
// Stake on behalf of the user in the reward vault
collateralToken.approve(address(rewardVault), amount);
rewardVault.stakeOnBehalf(msg.sender, amount);
// Update internal accounting
userCollateral[msg.sender] += amount;
}
```
### 2. Automated Staking Services
Services can automatically stake newly acquired tokens for users:
```solidity theme={null}
// An automated DCA service that stakes purchased tokens
function executeDCA(address user, address token, uint256 amount) external {
// Purchase tokens through DCA logic
uint256 purchased = _executeDCAPurchase(user, token, amount);
// Automatically stake for the user
IERC20(token).approve(address(rewardVault), purchased);
rewardVault.stakeOnBehalf(user, purchased);
}
```
### 3. Custodial Solutions
Custodial services can stake on behalf of their customers while maintaining user ownership:
```solidity theme={null}
// A custodial service staking for customers
function stakeForCustomer(address customer, uint256 amount) external onlyAuthorized {
require(customerBalances[customer] >= amount, "Insufficient balance");
stakingToken.approve(address(rewardVault), amount);
rewardVault.stakeOnBehalf(customer, amount);
customerBalances[customer] -= amount;
}
```
## Implementation Examples
### Basic Integration
```solidity theme={null}
pragma solidity ^0.8.26;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IRewardVault} from "./interfaces/IRewardVault.sol";
contract StakingService {
IRewardVault public immutable rewardVault;
IERC20 public immutable stakingToken;
constructor(address _rewardVault, address _stakingToken) {
rewardVault = IRewardVault(_rewardVault);
stakingToken = IERC20(_stakingToken);
}
/// @notice Stake tokens on behalf of a user
/// @param beneficiary The account that will own the staked tokens
/// @param amount The amount to stake
function stakeForUser(address beneficiary, uint256 amount) external {
// Transfer tokens from caller
stakingToken.transferFrom(msg.sender, address(this), amount);
// Approve and stake on behalf of beneficiary
stakingToken.approve(address(rewardVault), amount);
rewardVault.stakeOnBehalf(beneficiary, amount);
}
}
```
### Advanced Integration with Access Control
```solidity theme={null}
pragma solidity ^0.8.26;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IRewardVault} from "./interfaces/IRewardVault.sol";
contract ManagedStakingService is AccessControl {
bytes32 public constant STAKER_ROLE = keccak256("STAKER_ROLE");
IRewardVault public immutable rewardVault;
IERC20 public immutable stakingToken;
mapping(address => uint256) public stakedForUser;
event StakedForUser(address indexed beneficiary, address indexed staker, uint256 amount);
constructor(address _rewardVault, address _stakingToken) {
rewardVault = IRewardVault(_rewardVault);
stakingToken = IERC20(_stakingToken);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/// @notice Stake tokens on behalf of a user (only authorized stakers)
function stakeOnBehalfOf(
address beneficiary,
uint256 amount
) external onlyRole(STAKER_ROLE) {
stakingToken.transferFrom(msg.sender, address(this), amount);
stakingToken.approve(address(rewardVault), amount);
rewardVault.stakeOnBehalf(beneficiary, amount);
stakedForUser[beneficiary] += amount;
emit StakedForUser(beneficiary, msg.sender, amount);
}
/// @notice Batch stake for multiple users
function batchStakeOnBehalf(
address[] calldata beneficiaries,
uint256[] calldata amounts
) external onlyRole(STAKER_ROLE) {
require(beneficiaries.length == amounts.length, "Length mismatch");
uint256 totalAmount = 0;
for (uint256 i = 0; i < amounts.length; i++) {
totalAmount += amounts[i];
}
stakingToken.transferFrom(msg.sender, address(this), totalAmount);
for (uint256 i = 0; i < beneficiaries.length; i++) {
stakingToken.approve(address(rewardVault), amounts[i]);
rewardVault.stakeOnBehalf(beneficiaries[i], amounts[i]);
stakedForUser[beneficiaries[i]] += amounts[i];
emit StakedForUser(beneficiaries[i], msg.sender, amounts[i]);
}
}
}
```
## Security Considerations
### 1. Token Approval Management
Always ensure proper token approvals and consider using safe approval patterns:
```solidity theme={null}
// Safe approval pattern
function safeStakeOnBehalf(address beneficiary, uint256 amount) external {
stakingToken.transferFrom(msg.sender, address(this), amount);
// Reset allowance first (for tokens like USDT)
stakingToken.approve(address(rewardVault), 0);
stakingToken.approve(address(rewardVault), amount);
rewardVault.stakeOnBehalf(beneficiary, amount);
}
```
### 2. Access Control
Implement appropriate access controls for who can stake on behalf of others:
```solidity theme={null}
modifier onlyAuthorizedStaker() {
require(authorizedStakers[msg.sender], "Not authorized to stake");
_;
}
```
### 3. Front-Running Protection
Consider implementing protection against front-running in sensitive operations:
```solidity theme={null}
// Use commit-reveal or similar patterns for sensitive staking operations
mapping(bytes32 => StakeCommit) public stakeCommits;
struct StakeCommit {
address staker;
uint256 timestamp;
bool executed;
}
```
## Gas Optimization Tips
### 1. Batch Operations
When staking for multiple users, batch operations to save gas:
```solidity theme={null}
function batchStakeOnBehalf(
address[] calldata beneficiaries,
uint256[] calldata amounts
) external {
// Single transferFrom for total amount
uint256 total = 0;
for (uint256 i = 0; i < amounts.length; i++) {
total += amounts[i];
}
stakingToken.transferFrom(msg.sender, address(this), total);
// Individual stakes
for (uint256 i = 0; i < beneficiaries.length; i++) {
stakingToken.approve(address(rewardVault), amounts[i]);
rewardVault.stakeOnBehalf(beneficiaries[i], amounts[i]);
}
}
```
### 2. Pre-approved Contracts
For frequent operations, consider pre-approving maximum amounts:
```solidity theme={null}
constructor(address _rewardVault, address _stakingToken) {
rewardVault = IRewardVault(_rewardVault);
stakingToken = IERC20(_stakingToken);
// Pre-approve maximum amount to save gas on each stake
stakingToken.approve(_rewardVault, type(uint256).max);
}
```
## Interface Definition
```solidity theme={null}
interface IRewardVault {
/// @notice Stake tokens on behalf of another account
/// @param account The account to stake for
/// @param amount The amount of tokens to stake
function stakeOnBehalf(address account, uint256 amount) external;
/// @notice Check staked balance for an account
/// @param account The account to check
/// @return The staked balance
function balanceOf(address account) external view returns (uint256);
}
```
## Integration Examples
### Viem Integration
```typescript theme={null}
import { createPublicClient, createWalletClient, http, parseEther } from "viem";
import { berachain } from "viem/chains";
const publicClient = createPublicClient({
chain: berachain,
transport: http(),
});
const walletClient = createWalletClient({
chain: berachain,
transport: http(),
});
// Stake on behalf of a user
async function stakeOnBehalf(beneficiaryAddress: string, amount: bigint) {
// First approve the staking token
const approveRequest = await publicClient.simulateContract({
address: "0x...", // Staking token address
abi: erc20Abi,
functionName: "approve",
args: ["0x...", amount], // Reward Vault address, amount
});
await walletClient.writeContract(approveRequest.request);
// Then stake on behalf
const stakeRequest = await publicClient.simulateContract({
address: "0x...", // Reward Vault address
abi: rewardVaultAbi,
functionName: "stakeOnBehalf",
args: [beneficiaryAddress, amount],
});
const hash = await walletClient.writeContract(stakeRequest.request);
return hash;
}
```
### EthersJS Integration
```javascript theme={null}
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://rpc.berachain.com");
const signer = new ethers.Wallet(privateKey, provider);
async function stakeOnBehalf(beneficiaryAddress, amount) {
const stakingToken = new ethers.Contract(tokenAddress, stakingTokenAbi, signer);
const rewardVault = new ethers.Contract(vaultAddress, rewardVaultAbi, signer);
// Approve staking token
const approveTx = await stakingToken.approve(vaultAddress, amount);
await approveTx.wait();
// Stake on behalf of beneficiary
const stakeTx = await rewardVault.stakeOnBehalf(beneficiaryAddress, amount);
await stakeTx.wait();
console.log(`Staked ${amount} for ${beneficiaryAddress}`);
return stakeTx.hash;
}
```
### Foundry Testing Example
```solidity theme={null}
pragma solidity ^0.8.26;
import "forge-std/Test.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IRewardVault} from "../src/interfaces/IRewardVault.sol";
contract StakeOnBehalfTest is Test {
IRewardVault rewardVault;
IERC20 stakingToken;
address user1 = makeAddr("user1");
address user2 = makeAddr("user2");
address staker = makeAddr("staker");
function setUp() public {
// Setup contracts (implementation specific)
rewardVault = IRewardVault(/* deployed address */);
stakingToken = IERC20(/* token address */);
}
function testStakeOnBehalf() public {
uint256 stakeAmount = 100e18;
// Mint tokens to staker
deal(address(stakingToken), staker, stakeAmount);
// Staker stakes on behalf of user1
vm.startPrank(staker);
stakingToken.approve(address(rewardVault), stakeAmount);
rewardVault.stakeOnBehalf(user1, stakeAmount);
vm.stopPrank();
// Verify user1 has the staked balance
assertEq(rewardVault.balanceOf(user1), stakeAmount);
// Verify user1 can withdraw (they control the balance)
vm.prank(user1);
rewardVault.withdraw(stakeAmount);
assertEq(rewardVault.balanceOf(user1), 0);
}
function testBatchStakeOnBehalf() public {
address[] memory beneficiaries = new address[](3);
uint256[] memory amounts = new uint256[](3);
beneficiaries[0] = user1;
beneficiaries[1] = user2;
beneficiaries[2] = makeAddr("user3");
amounts[0] = 100e18;
amounts[1] = 200e18;
amounts[2] = 150e18;
uint256 totalAmount = amounts[0] + amounts[1] + amounts[2];
// Mint tokens to staker
deal(address(stakingToken), staker, totalAmount);
vm.startPrank(staker);
stakingToken.approve(address(rewardVault), totalAmount);
// Stake for each beneficiary
for (uint256 i = 0; i < beneficiaries.length; i++) {
rewardVault.stakeOnBehalf(beneficiaries[i], amounts[i]);
assertEq(rewardVault.balanceOf(beneficiaries[i]), amounts[i]);
}
vm.stopPrank();
}
}
```
### Deployment Script
```solidity theme={null}
// Foundry deployment script
pragma solidity ^0.8.26;
import "forge-std/Script.sol";
import "./interfaces/IRewardVault.sol";
contract StakeOnBehalfScript is Script {
function run() external {
vm.startBroadcast();
IRewardVault rewardVault = IRewardVault(0x...);
IERC20 stakingToken = IERC20(0x...);
address beneficiary = 0x...; // Address to stake for
uint256 amount = 100e18; // Amount to stake
// Approve and stake
stakingToken.approve(address(rewardVault), amount);
rewardVault.stakeOnBehalf(beneficiary, amount);
console.log("Staked", amount, "for", beneficiary);
vm.stopBroadcast();
}
}
```
## Best Practices
1. **Clear Permissions**: Ensure users understand who can stake on their behalf
2. **Event Logging**: Emit comprehensive events for transparency
3. **Gas Efficiency**: Use batch operations when possible
4. **Error Handling**: Implement proper error handling and revert messages
5. **Documentation**: Clearly document the trust model and permissions
## Related Documentation
**Contract References:**
* [Reward Vaults](/general/proof-of-liquidity/reward-vaults) - Reward Vault overview and reference
**Integration Guides:**
* [Advanced PoL Integration](/build/pol/advanced-pol) - Non-ERC20 protocols
* [PoL Integration Quickstart](/build/pol/integration-basics) - Getting started
* [Partial Reward Claims](/build/pol/partial-reward-claims) - Complementary feature
# EIP-5792 Introduction
Source: https://docs.berachain.com/build/protocol/eip5792-overview
Learn about EIP-5792 and how it enables wallet batching capabilities for improved user experience
# EIP-5792: Wallet Batching Capabilities
EIP-5792 is a proposed Ethereum Improvement Proposal that enables applications to request wallets to process batches of on-chain write calls and to check their status. This standard introduces a new wallet method, `wallet_sendCalls`, along with supporting methods for status checking and capability discovery.
EIP-5792 represents a significant step forward in improving the user experience of Ethereum applications. By providing a standardized way for applications to request batch operations from wallets, it eliminates the need for complex multi-transaction flows and enables more intuitive user interfaces.
The combination of EIP-5792 (application interface) and EIP-7702 (on-chain execution) creates a powerful foundation for the future of Ethereum transaction batching, making the ecosystem more accessible to both developers and end users.
**Practical Implementation**: Use the official [MetaMask batch transactions guide](https://docs.metamask.io/wallet/how-to/send-transactions/send-batch-transactions) and the [MetaMask `wallet_sendCalls` reference](https://docs.metamask.io/wallet/reference/json-rpc-methods/wallet_sendcalls/) when implementing EIP-5792 flows.
**Working Example**: For a complete, working implementation with React and TypeScript, see our [Berachain EIP-5792 Implementation Guide](https://github.com/berachain/guides/tree/main/apps/eip-5792).
**Status**: EIP-5792 is currently in "Last Call" with a deadline of May 5, 2025. There is
significant support from wallets and tools.
**Get Involved**: Review the EIP, test implementations, and join the conversation on Ethereum
Magicians to help shape the future of wallet batching capabilities.
## What is EIP-5792?
EIP-5792 enables applications to request that a wallet process a batch of on-chain write calls and check the status of those calls. These calls can be enhanced by `capabilities` (such as atomic execution or paymasters) if supported by the wallet.
### Key Components
* **`wallet_sendCalls`**: The main method for submitting batch transactions.
* **`wallet_getCallsStatus`**: Checks the status of submitted calls.
* **`wallet_showCallsStatus`**: Displays call information to users.
* **`wallet_getCapabilities`**: Discovers wallet capabilities.
* **Capabilities**: Features such as atomic execution, paymaster support, flow control, and auxiliary funds.
## Relationship with EIP-7702
EIP-5792 and EIP-7702 work together to provide a complete batching solution:
* **EIP-7702**: Enables EOAs to temporarily upgrade to smart contract functionality for batch execution.
* **EIP-5792**: Provides the wallet interface and capability discovery for batch operations.
The Bectra upgrade includes EIP-7702, which enables Externally Owned Accounts (EOAs) to have their address represented by the code of an existing smart contract. This means any account on Berachain will be able to make batched calls, assuming the EOA's designated smart contract supports it.
EIP-5792 provides the application layer interface to access these new capabilities.
## User Benefits
End users will no longer need to manually execute multiple transactions one by one. The canonical example is the "approve and transfer" flow for ERC-20 tokens, which currently requires two transactions and results in a disorienting UI, especially for users who are new to the space.
### Improved User Experience
* **Simplified Interfaces**: User interfaces can be simpler and more intuitive.
* **Reduced Transaction Count**: Multiple operations can be performed in a single transaction.
* **Better Context**: Wallets can provide richer confirmation dialogs with full batch context.
* **Atomic Execution**: All operations either succeed or fail together.
## Developer Benefits
### For Application Developers
* **Simplified Integration**: No need to "guess" what a given wallet is capable of.
* **Capability Discovery**: Clear indication of supported features.
* **Reduced Complexity**: No need to create complex multi-transaction interfaces.
* **Future-Proof**: Provides a foundation for iterative functionality improvements.
### For Wallet Developers
* **Richer Context**: Provides more information about application intent.
* **Better UX**: Allows the display of comprehensive batch information instead of individual transactions.
* **Capability Signaling**: Indicates support for advanced features.
* **Extensible**: Serves as a framework for adding new capabilities over time.
## Atomic Execution Capabilities
Wallets can indicate their support for atomic execution of batches through the `atomic` capability, which can have three possible states:
* **`supported`**: The wallet executes all calls atomically and contiguously.
* **`ready`**: The wallet can be upgraded to `supported`, pending user approval (e.g., via EIP-7702).
* **`unsupported`**: The wallet does not provide any atomicity or contiguity guarantees.
## Core Methods
The following are core methods that can be called by libraries for wallets.
### wallet\_getCapabilities
Applications call this method to discover which capabilities the wallet supports:
```typescript theme={null}
// Example capability discovery
const capabilities = await wallet.request({
method: "wallet_getCapabilities",
});
console.log(capabilities);
// {
// atomic: 'supported',
// paymasterService: 'supported',
// flowControl: 'supported',
// auxiliaryFunds: 'unsupported'
// }
```
### wallet\_sendCalls
Submit a batch of calls for execution:
```typescript theme={null}
// Example batch submission
const result = await wallet.request({
method: "wallet_sendCalls",
params: {
calls: [
// Notice the multiple requests are an array
{
to: "0x...",
data: "0x...",
value: "0x0",
},
{
to: "0x...",
data: "0x...",
value: "0x0",
},
],
capabilities: {
atomic: true,
},
},
});
```
### wallet\_getCallsStatus
Check the status of submitted calls:
```typescript theme={null}
// Example status check
const status = await wallet.request({
method: "wallet_getCallsStatus",
params: {
callId: "0x...",
},
});
```
## Use Cases
### 1 - Approve and Transfer
The most common use case is to approve a token and then transfer it in a single operation:
```typescript theme={null}
const calls = [
{
to: tokenAddress,
data: encodeFunctionData({
abi: erc20ABI,
functionName: "approve",
args: [spender, amount],
}),
value: "0x0",
},
{
to: spenderAddress,
data: encodeFunctionData({
abi: contractABI,
functionName: "transfer",
args: [recipient, amount],
}),
value: "0x0",
},
];
```
### 2 - Complex DeFi Operations
Multi-step DeFi operations, such as swapping tokens and then staking:
```typescript theme={null}
const calls = [
// Swap tokens
{
to: dexAddress,
data: encodeSwapData(tokenA, tokenB, amount),
value: "0x0",
},
// Approve staking contract
{
to: tokenBAddress,
data: encodeFunctionData({
abi: erc20ABI,
functionName: "approve",
args: [stakingAddress, swappedAmount],
}),
value: "0x0",
},
// Stake tokens
{
to: stakingAddress,
data: encodeFunctionData({
abi: stakingABI,
functionName: "stake",
args: [swappedAmount],
}),
value: "0x0",
},
];
```
### 3 - NFT Operations
Batch NFT operations, such as minting and setting metadata:
```typescript theme={null}
const calls = [
// Mint NFT
{
to: nftContractAddress,
data: encodeFunctionData({
abi: nftABI,
functionName: "mint",
args: [recipient],
}),
value: mintPrice,
},
// Set metadata
{
to: nftContractAddress,
data: encodeFunctionData({
abi: nftABI,
functionName: "setTokenURI",
args: [tokenId, metadataURI],
}),
value: "0x0",
},
];
```
## Comparison with Other Solutions
| Feature | EIP-5792 | Multicall3 | Permit2 | Meta-tx + Forwarder | EIP-4337 + Bundlers |
| ------------------------ | ------------ | ---------- | ---------- | ------------------- | ------------------- |
| **Wallet Integration** | ✅ Native | ❌ Manual | ✅ Native | ❌ Manual | ❌ Complex |
| **Capability Discovery** | ✅ Built-in | ❌ None | ⚠️ Limited | ❌ None | ❌ None |
| **Atomic Execution** | ✅ Guaranteed | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| **Gas Optimization** | ✅ High | ⚠️ Medium | ✅ High | ⚠️ Medium | ⚠️ Medium |
| **User Experience** | ✅ Excellent | ⚠️ Manual | ✅ Good | ⚠️ Complex | ⚠️ Complex |
| **Implementation** | ✅ Simple | ✅ Simple | ⚠️ Medium | ❌ Complex | ❌ Complex |
## Implementation Example
Here's a complete example demonstrating how to implement EIP-5792 in a web application:
```typescript theme={null}
// EIP-5792 implementation example
class EIP5792Client {
private wallet: any;
constructor(wallet: any) {
this.wallet = wallet;
}
async getCapabilities() {
return await this.wallet.request({
method: "wallet_getCapabilities",
});
}
async sendCalls(calls: any[], capabilities?: any) {
const params: any = { calls };
if (capabilities) {
params.capabilities = capabilities;
}
return await this.wallet.request({
method: "wallet_sendCalls",
params,
});
}
async getCallsStatus(callId: string) {
return await this.wallet.request({
method: "wallet_getCallsStatus",
params: { callId },
});
}
async showCallsStatus(callId: string) {
return await this.wallet.request({
method: "wallet_showCallsStatus",
params: { callId },
});
}
}
// Usage example
const client = new EIP5792Client(window.ethereum);
// Check capabilities
const capabilities = await client.getCapabilities();
console.log("Wallet capabilities:", capabilities);
// Prepare batch calls
const calls = [
{
to: "0x...",
data: "0x...",
value: "0x0",
},
];
// Send batch with atomic execution if supported
const result = await client.sendCalls(calls, {
atomic: capabilities.atomic === "supported",
});
// Check status
const status = await client.getCallsStatus(result.callId);
console.log("Batch status:", status);
```
## Migration Guide
### From Multicall3
If you are currently using Multicall3, migrating to EIP-5792 is straightforward:
```typescript theme={null}
// Before: Multicall3
const multicall = new Multicall3(provider);
const results = await multicall.call([
{ target: contract1, callData: data1 },
{ target: contract2, callData: data2 },
]);
// After: EIP-5792
const calls = [
{ to: contract1, data: data1, value: "0x0" },
{ to: contract2, data: data2, value: "0x0" },
];
const result = await wallet.request({
method: "wallet_sendCalls",
params: { calls },
});
```
### From Manual Batching
If you are manually handling multiple transactions:
```typescript theme={null}
// Before: Manual batching
const tx1 = await contract1.approve(spender, amount);
await tx1.wait();
const tx2 = await contract2.transfer(recipient, amount);
await tx2.wait();
// After: EIP-5792
const calls = [
{
to: contract1.address,
data: contract1.interface.encodeFunctionData("approve", [spender, amount]),
value: "0x0",
},
{
to: contract2.address,
data: contract2.interface.encodeFunctionData("transfer", [recipient, amount]),
value: "0x0",
},
];
await wallet.request({
method: "wallet_sendCalls",
params: { calls, capabilities: { atomic: true } },
});
```
## Best Practices
### 1 - Always Check Capabilities
Before sending calls, always check which capabilities the wallet supports:
```typescript theme={null}
const capabilities = await wallet.request({
method: "wallet_getCapabilities",
});
// Only request atomic execution if supported
const useAtomic = capabilities.atomic === "supported";
```
### 2 - Handle Errors Gracefully
Implement proper error handling for unsupported capabilities:
```typescript theme={null}
try {
const result = await wallet.request({
method: "wallet_sendCalls",
params: { calls, capabilities: { atomic: true } },
});
} catch (error) {
if (error.code === 4201) {
// User rejected the request
console.log("User rejected batch transaction");
} else if (error.code === 4001) {
// Wallet doesn't support the requested capability
console.log("Wallet doesn't support atomic execution");
// Fall back to individual transactions
}
}
```
### 3 - Provide Fallbacks
Always provide fallback mechanisms for wallets that do not support EIP-5792:
```typescript theme={null}
async function executeBatch(calls: any[]) {
try {
// Try EIP-5792 first
return await wallet.request({
method: "wallet_sendCalls",
params: { calls },
});
} catch (error) {
// Fall back to individual transactions
console.log("EIP-5792 not supported, using individual transactions");
return await executeIndividualTransactions(calls);
}
}
```
### 4 - Monitor Status
Use the status-checking methods to provide better user feedback:
```typescript theme={null}
const result = await wallet.request({
method: "wallet_sendCalls",
params: { calls },
});
// Monitor status
const checkStatus = async () => {
const status = await wallet.request({
method: "wallet_getCallsStatus",
params: { callId: result.callId },
});
if (status.status === "pending") {
setTimeout(checkStatus, 1000);
} else {
console.log("Batch completed:", status);
}
};
checkStatus();
```
## Future Considerations
### Upcoming Features
* **Paymaster Integration**: Native support for gasless transactions.
* **Flow Control**: Advanced transaction ordering and dependencies.
* **Auxiliary Funds**: Support for complex funding scenarios.
* **Cross-Chain Batching**: Extensions for multi-chain operations.
### Ecosystem Adoption
As EIP-5792 moves toward finalization, you can expect to see:
* **Wallet Integration**: Major wallets adding native support.
* **Framework Updates**: Libraries like ethers.js and viem adding built-in support.
* **Tooling**: Development tools and debugging utilities.
* **Standards**: Additional EIPs building on this foundation.
## Resources
* [EIP-5792 Specification](https://eips.ethereum.org/EIPS/eip-5792)
* [MetaMask: Send batch transactions](https://docs.metamask.io/wallet/how-to/send-transactions/send-batch-transactions)
* [MetaMask: `wallet_sendCalls` reference](https://docs.metamask.io/wallet/reference/json-rpc-methods/wallet_sendcalls/)
* [Official EIP-5792 Documentation](https://www.eip5792.xyz/)
* [GitHub Repository](https://github.com/ethereum/EIPs/pull/5792)
* [Ethereum Magicians Discussion](https://ethereum-magicians.org/t/eip-5792-wallet-send-calls/12345)
* [Berachain EIP-5792 Implementation Guide](https://github.com/berachain/guides/tree/main/apps/eip-5792)
# Eip7702 basics
Source: https://docs.berachain.com/build/protocol/eip7702-basics
# EIP-7702 Basics For Setting Code For An EOA
The goal of this tutorial is to understand the new functionality of [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) and its limitations using Foundry.
In this guide, we will deploy a basic HelloWorld contract, a Counter contract, a SimpleDelegate contract, and perform cleanup to understand the nuances of setting code for an EOA.
:::danger
PLEASE AVOID PRODUCTION: These contracts have not been audited and deploying them would be at your own risk.
:::
## Requirements
Make sure you have the following installed on your computer before we begin:
* Foundry Forge & Cast v1.0.0 or greater
## Setting HelloWorld Code For EOA
In this first step, we will set specific code for an EOA to a `HelloWorld.sol` contract to demonstrate what is expected to function and what is not expected to work.
### Step 1 - New Forge Project
```bash theme={null}
mkdir eip7702-basics;
cd eip7702-basics;
forge init --force;
```
### Step 2 - Create HelloWorld & Deployment Contract
```bash theme={null}
# FROM: /eip7702-basics
cat > src/HelloWorld.sol << EOF
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract HelloWorld {
string public message = "Default message";
address public owner;
bool public init;
constructor() {
message = "Hello World";
owner = msg.sender;
}
function initialize() public {
require(!init, "Contract already initialized");
owner = msg.sender;
message = "Something Else!";
init = true;
}
function setMessage(string memory newMessage) public {
require(msg.sender == owner, "Only owner can set message");
message = newMessage;
}
function getMessage() public view returns (string memory) {
return message;
}
}
EOF
cat > script/HelloWorld.s.sol << EOF
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {HelloWorld} from "../src/HelloWorld.sol";
contract HelloWorldScript is Script {
function run() public {
vm.startBroadcast();
new HelloWorld();
vm.stopBroadcast();
}
}
EOF
```
### Step 3 - Deploy HelloWorld Contract
```bash-vue theme={null}
# FROM: /eip7702-basics
# EOA we're setting code for
EOA_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
EOA_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
OTHER_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8;
OTHER_PK=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;
RPC_URL={{config.bepolia.rpcUrl}}
forge script script/HelloWorld.s.sol --rpc-url $RPC_URL --private-key $EOA_PK --broadcast -vvvv;
# [Expected Similar Output]:
# ...
# Contract Address: 0x5FbDB2315678afecb367f032d93F642f64180aa3
# ...
```
Export the contract address as an environment variable:
```bash theme={null}
# FROM: /eip7702-basics
CONTRACT_ADDRESS=
```
### Step 4 - Verify Deployed Contract Message
```bash theme={null}
# FROM: /eip7702-basics
cast call $CONTRACT_ADDRESS "getMessage()(string)" --rpc-url $RPC_URL;
# [Expected Output]:
# "Hello World"
```
### Step 5 - Set HelloWorld Code For EOA
You'll notice that the transaction submitted in this next step sets the code for the EOA, but the transaction must come from another account.
This is because if the transaction comes from the original EOA attempting to set its own code, clients like `cast` will treat it as a normal EOA transaction and never load the bytecode or apply the signed authorization.
```bash theme={null}
# FROM: /eip7702-basics
SIGNED_AUTH=$(cast wallet sign-auth $CONTRACT_ADDRESS --private-key $EOA_PK --rpc-url $RPC_URL);
# Must not come from the original EOA_PK
cast send $(cast az) --private-key $OTHER_PK --auth $SIGNED_AUTH --rpc-url $RPC_URL;
# [Expected Similar Output]:
# ...
# transactionHash 0x0aa309f974118982c198ef24860c2be98921e871ed262f0cb92473944d6a0107
# ...
```
Export the transaction hash:
```bash theme={null}
# FROM: /eip7702-basics
TXN=
```
### Step 6 - Verify Authorization List
```bash theme={null}
# FROM: /eip7702-basics
cast tx $TXN --rpc-url $RPC_URL;
# [Expected Similar Output]:
# authorizationList [
# {recoveredAuthority: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, signedAuthority: {"chainId":"0x7a69","address":"0x5fbdb2315678afecb367f032d93f642f64180aa3","nonce":"0x1","yParity":"0x1","r":"0xb479f2c7d70a98008f27bbf4fb5e67daa0764459ed4c3337cdd906e53ac8b427","s":"0x34d77ad2562e633a90f2a3c9d84098b4b6e6a9e8ad04d8484fe7d8d205f41483"}}
# ]
```
### Step 7 - Verify EOA Code Set
```bash theme={null}
# FROM: /eip7702-basics
cast code $EOA_ADDRESS --rpc-url $RPC_URL;
# [Expected Similar Output]:
# 0xef01005fbdb2315678afecb367f032d93f642f64180aa3
```
Notice the prefix `0xef0100` and the contract address `5FbDB2315678afecb367f032d93F642f64180aa3`.
### Step 8 - Verify EOA Code Message
```bash theme={null}
# FROM: /eip7702-basics
cast call $EOA_ADDRESS "getMessage()(string)" --rpc-url $RPC_URL;
# [Expected Output]:
# ""
```
Why is it blank, and why didn't the constructor initialize the message variable?
This is because storage initialization normally gets applied during contract deployment. When setting code for an EOA, you're effectively not deploying or executing a constructor, and no storage allocations have been initialized or set.
If you compare the `$EOA_ADDRESS` to the `$CONTRACT_ADDRESS` slots, you can see the difference:
```bash theme={null}
# FROM: /eip7702-basics
cast storage $CONTRACT_ADDRESS 1;
# [Expected Output]:
# 0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266
cast storage $EOA_ADDRESS 1;
# [Expected Output]:
# 0x0000000000000000000000000000000000000000000000000000000000000000
```
### Step 9 - Initialize HelloWorld EOA Code
When calling functions on the `EOA_ADDRESS`, they can be called by any wallet, including the originating `EOA_PK`.
The main consideration is that the `owner` of the contract is set by whoever calls `initialize`.
```bash theme={null}
# FROM: /eip7702-basics
cast send $EOA_ADDRESS "initialize()" \
--private-key $OTHER_PK \
--rpc-url $RPC_URL;
# [Expected Similar Output]:
# blockHash 0x0a4faff75f3ff5a4fef264ee0587be0ee833d74586ed64ae7d5f84f7c6ba5db4
# ...
```
### Step 10 - Verify New EOA Message
We can verify that the `message` has been updated and also verify the storage slot change.
```bash theme={null}
# FROM: /eip7702-basics
cast call $EOA_ADDRESS "getMessage()(string)" --rpc-url $RPC_URL;
# [Expected Output]:
# "Something Else!"
cast storage $EOA_ADDRESS 1;
# [Expected Output]:
# 0x00000000000000000000000170997970c51812dc3a010c7d01b50e0d17dc79c8
```
### Step 11 - Set New EOA Message
Additional functions can also be triggered, but since the `OTHER_ADDRESS` initialized the contract, it has been set as the `owner` and is the only address that can set messages.
```bash theme={null}
# FROM: /eip7702-basics
cast send $EOA_ADDRESS "setMessage(string)" "Goodbye World" --private-key $OTHER_PK --rpc-url $RPC_URL;
cast call $EOA_ADDRESS "getMessage()(string)" --rpc-url $RPC_URL;
# [Expected Output]:
# "Goodbye World"
```
## Setting Counter Code For EOA
Next, we'll walk through deploying a simple Counter contract to set as code for an EOA and examine how that affects its storage slots.
### Step 1 - Create Counter Contract Code
```bash theme={null}
# FROM: /eip7702-basics
cat > src/Counter.sol << EOF
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
uint256 public number;
function setNumber(uint256 newNumber) public {
number = newNumber;
}
function increment() public {
number++;
}
}
EOF
cat > script/Counter.s.sol << EOF
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";
contract CounterScript is Script {
Counter public counter;
function setUp() public {}
function run() public {
vm.startBroadcast();
counter = new Counter();
vm.stopBroadcast();
}
}
EOF
```
### Step 2 - Deploy Counter Contract
```bash-vue theme={null}
# FROM: /eip7702-basics
# EOA we're setting code for
EOA_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
EOA_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
OTHER_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8;
OTHER_PK=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;
RPC_URL={{config.bepolia.rpcUrl}}
forge script script/Counter.s.sol --rpc-url $RPC_URL --private-key $EOA_PK --broadcast -vvvv;
# [Expected Similar Output]:
# ...
# Contract Address: 0x5FbDB2315678afecb367f032d93F642f64180aa3
# ...
```
Export the contract address as an environment variable:
```bash theme={null}
# FROM: /eip7702-basics
CONTRACT_ADDRESS=
```
### Step 3 - Set Counter Code For EOA
```bash theme={null}
# FROM: /eip7702-basics
SIGNED_AUTH=$(cast wallet sign-auth $CONTRACT_ADDRESS --private-key $EOA_PK --rpc-url $RPC_URL);
# Must not come from the original EOA_PK
cast send $(cast az) --private-key $OTHER_PK --auth $SIGNED_AUTH --rpc-url $RPC_URL;
# [Expected Similar Output]:
# ...
# transactionHash 0x305b3a0337ed695e84fcaeda8a4490eb502db8e8165cfea0daa9bdc6d0e33e29
# ...
```
Export the transaction hash:
```bash theme={null}
# FROM: /eip7702-basics
TXN=
```
### Step 4 - Verify Previous Storage Slots Conflict
Now that we have set our EOA code to another contract code, we will see that the storage slot value has carried over from our [initialized HelloWorld contract](#step-10-verify-new-eoa-message).
```bash theme={null}
# FROM: /eip7702-basics
cast storage $EOA_ADDRESS 1;
# [Expected Output]:
# 0x00000000000000000000000170997970c51812dc3a010c7d01b50e0d17dc79c8
```
This can also pose a problem, as we noted before that storage slots aren't initialized and simply carry forward what was there before.
:::warning
NOTE: When setting code for an EOA, consider examining its storage slots and creating an initialize function that resets these values.
:::
```bash theme={null}
# FROM: /eip7702-basics
cast call $CONTRACT_ADDRESS "number()(uint256)" --rpc-url $RPC_URL;
# [Expected Output]:
# 0
cast call $EOA_ADDRESS "number()(uint256)" --rpc-url $RPC_URL;
# [Expected Incorrect Output]:
# 37738841482167102822784567685588449352038201195630954555629069933914850590750
```
## Setting SimpleDelegate Code For EOA
As another example, we'll demonstrate how you can specify any call data for the EOA to execute.
:::danger
USE ONLY FOR TESTING PURPOSES. This contract essentially provides an open door for anyone to make you process transactions.
:::
### Step 1 - Create SimpleDelegate Contract Code
```bash theme={null}
# FROM: /eip7702-basics
cat > src/SimpleDelegate.sol << EOF
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleDelegate {
struct Call {
bytes data;
address to;
uint256 value;
}
error ExternalCallFailed();
function execute(Call[] memory calls) external payable { // lack of access control
for (uint256 i = 0; i < calls.length; i++) {
Call memory call = calls[i];
(bool success,) = call.to.call{value: call.value}(call.data);
require(success, ExternalCallFailed());
}
}
}
EOF
cat > script/SimpleDelegate.s.sol << EOF
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {SimpleDelegate} from "../src/SimpleDelegate.sol";
contract SimpleDelegateScript is Script {
SimpleDelegate public simpleDelegate;
function setUp() public {}
function run() public {
vm.startBroadcast();
simpleDelegate = new SimpleDelegate();
vm.stopBroadcast();
}
}
EOF
```
### Step 2 - Create ERC20Token Contract Code
```bash theme={null}
# FROM: /eip7702-basics
cat > src/ERC20Token.sol << EOF
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract ERC20Token is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
_mint(msg.sender, 1000000 * 10 ** decimals());
}
}
EOF
cat > script/ERC20Token.s.sol << EOF
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {ERC20Token} from "../src/ERC20Token.sol";
contract ERC20TokenScript is Script {
ERC20Token public token;
function setUp() public {}
function run() public {
vm.startBroadcast();
token = new ERC20Token("MyToken", "MTK");
vm.stopBroadcast();
}
}
EOF
```
### Step 3 - Deploy SimpleDelegate ERC20Token Contract
```bash-vue theme={null}
# FROM: /eip7702-basics
# EOA we're setting code for
EOA_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
EOA_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
OTHER_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8;
OTHER_PK=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;
RPC_URL={{config.bepolia.rpcUrl}}
forge install OpenZeppelin/openzeppelin-contracts;
forge script script/SimpleDelegate.s.sol --rpc-url $RPC_URL --private-key $EOA_PK --broadcast -vvvv --via-ir;
forge script script/ERC20Token.s.sol --rpc-url $RPC_URL --private-key $EOA_PK --broadcast -vvvv --via-ir;
# [Expected Similar Output]:
# ...
# Contract Address: 0x0165878A594ca255338adfa4d48449f69242Eb8F
# ...
```
Export the contract addresses as an environment variables:
```bash theme={null}
# FROM: /eip7702-basics
CONTRACT_ADDRESS=
ERC20_ADDRESS=
```
### Step 4 - Set SimpleDelegate Code For EOA
```bash theme={null}
# FROM: /eip7702-basics
SIGNED_AUTH=$(cast wallet sign-auth $CONTRACT_ADDRESS --private-key $EOA_PK --rpc-url $RPC_URL);
# Must not come from the original EOA_PK
cast send $(cast az) --private-key $OTHER_PK --auth $SIGNED_AUTH --rpc-url $RPC_URL;
# [Expected Similar Output]:
# ...
# transactionHash 0xc1316e76e9a4cc43d646cebef47e5cae7207a84e3eef73d2b5ec45ae4785a193
# ...
```
### Step 5 - Burn BERA
Next, we'll introduce a transaction to burn some of the balance from the `EOA_ADDRESS`.
```bash theme={null}
# FROM: /eip7702-basics
# Confirm existing balance
cast balance $EOA_ADDRESS;
# [Expected Similar Output]:
# 9999998774554943145151
# Burn 5 $BERA
cast send $EOA_ADDRESS "execute((bytes,address,uint256)[])" \
'[(0x,0x0000000000000000000000000000000000000000,5000000000000000000)]' \
--private-key $OTHER_PK \
--rpc-url $RPC_URL;
# Recheck balance
cast balance $EOA_ADDRESS;
# [Expected Similar Output]:
# 9994998774554943145151
```
### Step 6 - Transfer ERC20 Tokens
Finally, we'll introduce a transaction to transfer ERC20 tokens to `OTHER_ADDRESS`.
```bash theme={null}
# FROM: /eip7702-basics
# Check erc20 balances
cast call $ERC20_ADDRESS "balanceOf(address)(uint256)" $OTHER_ADDRESS --rpc-url $RPC_URL;
# [Expected Output]:
# 0
cast call $ERC20_ADDRESS "balanceOf(address)(uint256)" $EOA_ADDRESS --rpc-url $RPC_URL;
# [Expected Output]:
# 1000000000000000000000000
# Transfer erc20
CALL_DATA=$(cast calldata "transfer(address,uint256)" $OTHER_ADDRESS 700000000000000000000000);
cast send $EOA_ADDRESS "execute((bytes,address,uint256)[])" \
"[($CALL_DATA,$ERC20_ADDRESS,0)]" \
--private-key $OTHER_PK \
--rpc-url $RPC_URL;
# Recheck erc20 balances
cast call $ERC20_ADDRESS "balanceOf(address)(uint256)" $OTHER_ADDRESS --rpc-url $RPC_URL;
# [Expected Output]:
# 700000000000000000000000
cast call $ERC20_ADDRESS "balanceOf(address)(uint256)" $EOA_ADDRESS --rpc-url $RPC_URL;
# [Expected Output]:
# 300000000000000000000000
```
## Clean Up Code For EOA
The last remaining step is to remove any code associated with the `EOA_ADDRESS`.
```bash-vue theme={null}
# FROM: /eip7702-basics
EOA_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
EOA_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
OTHER_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8;
OTHER_PK=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;
RPC_URL={{config.bepolia.rpcUrl}}
# Verify existing code
cast code $EOA_ADDRESS;
# [Expected Similar Output]:
# 0xef01000165878a594ca255338adfa4d48449f69242eb8f
# Set new code to 0x0
SIGNED_AUTH=$(cast wallet sign-auth 0x0000000000000000000000000000000000000000 --private-key $EOA_PK --rpc-url $RPC_URL);
# Must not come from the original EOA_PK
cast send $(cast az) --private-key $OTHER_PK --auth $SIGNED_AUTH --rpc-url $RPC_URL;
# Recheck existing code
cast code $EOA_ADDRESS;
# [Expected Similar Output]:
# 0x
```
# EIP-7702 Batch Transactions
Source: https://docs.berachain.com/build/protocol/eip7702-batch-transactions
Guide to batch transactions using EIP-7702 on Berachain.
# EIP-7702 Batch Transactions
This guide covers how to perform batch transactions using EIP-7702 on Berachain, focusing on direct user-submitted transactions. Sponsorship (where a third party submits the transaction) is not implemented here, but is possible with EIP-7702.
> **Note:** The main guide and code examples will be maintained at [https://github.com/berachain/guides/tree/main/apps/batch-transactions](https://github.com/berachain/guides/tree/main/apps/batch-transactions).
>
> For a deeper understanding of EIP-7702 and temporary smart account upgrades, see [EIP-7702 Basics](/build/protocol/eip7702-basics).
## Overview
*Batch transactions* allow you to execute multiple operations in a single EIP-7702 session, improving efficiency and reducing gas costs. This guide walks through the concepts, use cases, and implementation patterns for batch transactions with EIP-7702.
## Key Benefits
* Significantly reduced gas costs
* Atomic transaction execution
* Improved user experience
* Reduced network load
## What is EIP-7702?
EIP-7702 is a proposed Ethereum Improvement Proposal that introduces a new transaction type for batching multiple operations. It enables users to combine multiple transactions into a single transaction, reducing gas costs and improving efficiency. The proposal is designed to be backward compatible with existing Ethereum infrastructure while providing enhanced functionality for batch operations.
## Limitations
1. **Wallet Support for EIP-7702 Batch Contract Deployment**
* Native EIP-7702 batch transactions require the wallet provider (e.g., MetaMask, Rabby, etc.) to support deploying and interacting with batch contracts directly from the frontend. As of now, most wallets do not natively support this, so users may need to rely on custom integrations or scripts.
2. **Batch Transaction Dependency**
* The batch transactions are executed sequentially within the contract. This means that later transactions in the batch can depend on the successful execution of earlier ones (e.g., using an approval in the first call and a transfer in the second). However, if any transaction in the batch fails, the entire batch reverts, ensuring atomicity. Parallel execution is not supported; all calls are processed in order.
## Advanced Batch Logic
In a custom implementation, it is feasible to utilize the results of earlier calls in the batch as inputs for later calls. This requires additional logic in the batch contract to capture and pass the return values between calls. However, our current example does not support this functionality. If you need to implement such advanced batch logic, you would need to modify the contract to store intermediate results and use them in subsequent calls.
## Comparison Table
| Feature | EIP-7702 + EIP-5792 | Multicall3 | Permit2 | Meta-tx + Forwarder | EIP-4337 + Bundlers |
| ------------------------- | ------------------- | ---------- | ------------ | ------------------- | ------------------- |
| Transaction Batching | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes |
| Gas Optimization | ✅ High | ⚠️ Medium | ✅ High | ⚠️ Medium | ⚠️ Medium |
| Atomic Execution | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Backward Compatibility | ✅ Yes | ✅ Yes | ⚠️ Limited | ⚠️ Limited | ❌ No |
| Implementation Complexity | ⚠️ Medium | ✅ Low | ⚠️ Medium | ⚠️ High | ⚠️ High |
| Gas Cost per Operation | ✅ Lowest | ⚠️ Medium | ✅ Low | ⚠️ Medium | ⚠️ Medium |
| Trust Assumptions | ✅ None | ⚠️ Low | ✅ None | ❌ High | ⚠️ Medium |
| Frontend Integration | ✅ Native | ⚠️ Manual | ✅ Native | ⚠️ Manual | ⚠️ Complex |
| Token Support | ✅ All | ✅ All | ❌ ERC20 Only | ✅ All | ✅ All |
| Setup Requirements | ✅ Minimal | ✅ Minimal | ⚠️ Medium | ⚠️ High | ❌ Complex |
## Why These Ratings?
* **Transaction Batching:** EIP-7702 + EIP-5792 provides native batching with frontend support. Multicall3 and Meta-tx + Forwarder support batching but with different limitations. Permit2 doesn't support batching but enables gasless approvals. EIP-4337 + Bundlers supports batching but requires complex setup.
* **Gas Optimization:** EIP-7702 + EIP-5792 and Permit2 offer high gas optimization through native batching and gasless approvals respectively. Multicall3, Meta-tx + Forwarder, and EIP-4337 + Bundlers provide medium optimization due to additional overhead or complexity.
* **Atomic Execution:** EIP-7702 + EIP-5792, Multicall3, Permit2, and EIP-4337 + Bundlers all support atomic execution. Meta-tx + Forwarder doesn't guarantee atomicity due to its relay-based nature.
* **Backward Compatibility:** EIP-7702 + EIP-5792 and Multicall3 maintain high compatibility with existing infrastructure. Permit2 and Meta-tx + Forwarder have limited compatibility due to specific token standards or relay requirements. EIP-4337 + Bundlers requires significant infrastructure changes.
* **Implementation Complexity:** Multicall3 is simplest to implement. EIP-7702 + EIP-5792 and Permit2 require medium complexity. Meta-tx + Forwarder and EIP-4337 + Bundlers require high complexity due to relay infrastructure or account abstraction.
* **Gas Cost per Operation:** EIP-7702 + EIP-5792 offers lowest per-operation cost through native batching. Permit2 provides low costs through gasless approvals. Other solutions have medium costs due to additional overhead.
* **Trust Assumptions:** EIP-7702 + EIP-5792 and Permit2 require no trust assumptions. Multicall3 has low trust requirements. Meta-tx + Forwarder requires high trust in relayers. EIP-4337 + Bundlers requires medium trust in bundlers.
* **Frontend Integration:** EIP-7702 + EIP-5792 and Permit2 offer native frontend integration. Multicall3 and Meta-tx + Forwarder require manual integration. EIP-4337 + Bundlers requires complex integration.
* **Token Support:** EIP-7702 + EIP-5792, Multicall3, Meta-tx + Forwarder, and EIP-4337 + Bundlers support all token types. Permit2 is limited to ERC20 tokens.
* **Setup Requirements:** EIP-7702 + EIP-5792 and Multicall3 require minimal setup. Permit2 requires medium setup for token integration. Meta-tx + Forwarder requires high setup for relay infrastructure. EIP-4337 + Bundlers requires complex setup for account abstraction.
## Architecture
The architecture diagram below shows the high-level flow of batch transactions. Each component plays a crucial role in ensuring atomic execution and proper error handling.
```mermaid actions={false} theme={null}
flowchart TD
A[User] -->|Submit Batch Transaction| B[EOA EIP-7702 Contract Code]
B -->|Process Batch| C[Transaction Executor]
C -->|Execute| D[Transaction 1]
C -->|Execute| E[Transaction 2]
C -->|Execute| F[Transaction N]
D -->|Result| G[Batch Result]
E -->|Result| G
F -->|Result| G
G -->|Return| A
```
## Transaction Flow
The sequence diagram below illustrates how batch transactions are processed, from submission to final result. Each step is crucial for maintaining atomicity and proper error handling. **Sponsorship is not implemented in this guide, but is possible with EIP-7702.**
```mermaid actions={false} theme={null}
sequenceDiagram
actor User
participant BatchContract
participant Executor
participant Blockchain
User->>BatchContract: Submit Batch Transaction
BatchContract->>Executor: Process Batch
loop Each Transaction
Executor->>Blockchain: Execute Transaction
Blockchain-->>Executor: Transaction Result
end
Executor-->>BatchContract: Batch Results
BatchContract-->>User: Final Result
```
## Implementation
```solidity [BatchTransaction.sol] theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @notice VULNERABLE, UNAUDITED CODE. DO NOT USE IN PRODUCTION.
*/
contract BatchTransaction {
struct Call {
address to;
uint256 value;
bytes data;
}
function execute(Call[] calldata calls) external payable {
require(msg.sender == address(this), "EIP-7702: only self-call allowed");
for (uint256 i = 0; i < calls.length; i++) {
(bool success, ) = calls[i].to.call{value: calls[i].value}(calls[i].data);
require(success, "Call failed");
}
}
}
// See the full contract at: https://github.com/berachain/guides/blob/main/apps/batch-transactions/src/BatchTransaction.sol
```
```typescript [usage.ts] theme={null}
// Example usage with viem and EIP-7702 signing
import { createWalletClient, http, parseEther, encodeFunctionData } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { eip7702Actions } from "viem/experimental";
import { batchABI, batchContractAddress } from "./abi";
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const client = createWalletClient({
account,
chain: {
id: 1337,
name: "Local",
rpcUrls: { default: { http: ["http://localhost:8545"] } },
},
transport: http(),
}).extend(eip7702Actions());
// Prepare your batch of calls
const calls = [
{
to: "0xContract1",
value: parseEther("0"),
data: "0x...", // encoded function data
},
// ... more calls
];
// Sign EIP-7702 authorization
const authorization = await client.signAuthorization({
account,
contractAddress: batchContractAddress,
executor: "self", // for direct execution
});
// Encode the batch call
const data = encodeFunctionData({
abi: batchABI,
functionName: "execute",
args: [calls],
});
// Send the EIP-7702 transaction to the EOA address
await client.sendTransaction({
authorizationList: [authorization],
data,
to: account.address,
});
// For a full working script, see:
// https://github.com/berachain/guides/blob/main/apps/batch-transactions/script/deploy-and-execute.js
```
## Benefits
1. **Gas Efficiency**: Significantly reduces gas costs by combining multiple transactions into one
2. **Atomic Execution**: All transactions in a batch either succeed or fail together
3. **Improved User Experience**: Users can perform multiple operations in a single transaction
4. **Reduced Network Load**: Fewer transactions on the network means better overall performance
## Use Cases
* DeFi operations requiring multiple steps
* NFT batch minting and transfers
* Complex smart contract interactions
* Multi-step protocol operations
## Security Considerations
1. **Gas Limits**: Ensure batch size doesn't exceed block gas limits
2. **Transaction Ordering**: Maintain proper transaction ordering for dependent operations
3. **Error Handling**: Implement proper error handling for failed transactions
4. **Access Control**: Implement appropriate access control mechanisms
## Best Practices
1. Validate all transaction parameters before execution
2. Implement proper error handling and rollback mechanisms
3. Consider gas costs when determining batch size
4. Test thoroughly with different batch sizes and transaction types
5. Monitor and optimize gas usage
## Implementation Checklist
To get started with EIP-7702 batch transactions:
1. Review the implementation example above
2. Test the batch transaction functionality in a development environment
3. Consider the security implications and best practices
4. Integrate batch transactions into your application where appropriate
## Related Resources
* [EIP-7702 Specification](https://eips.ethereum.org/EIPS/eip-7702)
* [EIP-5792 (Account Abstraction)](https://eips.ethereum.org/EIPS/eip-5792)
* [EIP-7702 Basics Guide](/build/protocol/eip7702-basics)
* [BatchTransaction.sol on GitHub](https://github.com/berachain/guides/blob/main/apps/batch-transactions/src/BatchTransaction.sol)
* [deploy-and-execute.js on GitHub](https://github.com/berachain/guides/blob/main/apps/batch-transactions/script/deploy-and-execute.js)
# Eip7702 gas sponsorship
Source: https://docs.berachain.com/build/protocol/eip7702-gas-sponsorship
# EIP-7702 Gas Sponsorship with Anvil
In this guide, we will walk you through a demo of gas sponsorship accomplished with EIP-7702, all on a local anvil fork. EIP-7702 is one of the improvement proposals implemented in the Bectra upgrade, on Bepolia, mirroring the changes made within Ethereum Mainnet with Pectra.
Gas Sponsorship simply entails:
* An EOA authorizing an implementation contract at its own address
* Use of EIP-7702 transaction type, 0x04, to carry out transactions with this implementation logic
* The EOA signing an authorized transaction offchain and passing the details to a Sponsor to broadcast it to the chain, where the transaction `to` variable is actually the EOA itself
## Gas Sponsorship with EIP 7702 vs EIP 4337 and Pre-Signed Transactions
Before getting into the guide, it is important to highlight key distinctions between the alternative methods for gas sponsorship. The three main options today include:
* Pre-signed transactions with relayers
* EIP-4337 with account abstraction and a complex system
* EIP-7702 where an EOA acts like a smart account offering gas sponsorship methods
Pre-signed transactions with relayers are the simplest way to do gas sponsorship. The process simply includes the user signing something offchain, and a relayer sending the actual transaction onchain. It works, but it’s all custom, where the designer has to handle replay protection, validation, and signatures manually. Since there is no standard format or infrastructure, implementation can get complicated.
On the other hand, EIP-4337 takes things further with a full account abstraction system. There are aspects such as an EntryPoint contract, UserOperations, bundlers, paymasters, and more. The standard unlocks powerful features like sponsor-paid gas, batching, and smart account logic, but the tradeoff is high complexity and more infrastructure that doesn’t exist natively on Ethereum.
EIP-7702 is a newer option that offers more simplicity holistically. It lets an EOA act like a smart account. That means you get gas sponsorship and smart account behavior without needing complex infrastructure such as bundlers and more. The standard is protocol-native, easy to integrate, and flexible enough to cover most real-world use cases without the overhead of EIP-4337.
In comparison to the other two, EIP-7702 gives the designer and user the benefits of account abstraction; ie. smart logic, delegated signing, sponsor support, without reinventing the transaction process. It offers the benefits of EIP-4337 without custom mempools, complex validation flows, etc., whilst also offering simplicity like pre-signed transactions. In essence, it really is just a EVM-native, standardized method to delegate behavior in a transaction. It's an exciting design space that will open up with Pectra on the EVM, and Bectra on Bepolia.
## Gas Sponsorship Flow Diagram with EIP-7702
Gas Sponsorship with EIP-7702 is an interesting design space. We have gone ahead and made this simple "Part 1" where the main flow of carrying out a gas sponsorship is highlighted. We will publish more parts outlining:
* Gas Sponsorship with ERC20s, where the EOA authorizes a transaction that transfers ERC20 to the Sponsor as a payment for the Sponsor to cover the gas required for the transaction.
* Using on-chain checks to ensure that the signer of the contract is the EOA itself. This will use solidity scripts leveraging Foundry.
Today's main flow consists of the following architecture and flow shown in the below diagram. The steps highlighted in the schematic will be walked through in the code walk through.
:::tip
For further information on Bectra, make sure to follow our [berachaindevs twitter](https://x.com/berachaindevs) as we publish more content!
:::
## Requirements
Before starting, ensure that you have carried out the following:
* Install [Foundry](https://book.getfoundry.sh/getting-started/installation) `v1.0.0` or greater
* Clone the guides [repo](https://github.com/berachain/guides/tree/main) and make your way to the `apps/eip-7702-gas-sponsorship` directory to find the Gas Sponsorship Guide and associated code we'll walk through today.
# Guide Walk Through
Run all the commands within this guide while at the file path: `./apps/eip-7702-gas-sponsorship`.
## Step 1 - Setup
Install dependencies for the project:
```bash theme={null}
pnpm install
```
Set up environment variables:
```bash theme={null}
cp .env.example .env
```
There are already `anvil` test addresses and private keys there. For this guide, keep it as is until you have walked through everything. Afterwards, feel free to use your own of course for your own testing.
Start `anvil` fork at hard fork prague:
```bash theme={null}
anvil --hardfork prague --chain-id 80069 --port 8545
```
## Step 2 - Deploy the `SimpleDelegate.sol` Contract
Deploy the `SimpleDelegate.sol` contract and populate `.env` with contract address. This contract logic will be what is assigned to the `EOA` to leverage EIP-7702. The below bash code will automatically update the `.env` with the new `SimpleDelegate.sol` `CONTRACT_ADRESS`.
```bash theme={null}
source .env && forge script script/Implementation.s.sol:SimpleDelegateScript \
--rpc-url $TEST_RPC_URL \
--private-key $EOA_PRIVATE_KEY \
--broadcast -vvvv \
| tee deployment.log && \
CONTRACT_ADDRESS=$(grep -Eo '0x[a-fA-F0-9]{40}' deployment.log | tail -n1) && \
sed -i '' "/^CONTRACT_ADDRESS=/d" .env && echo "CONTRACT_ADDRESS=$CONTRACT_ADDRESS" >> .env
```
## Step 3 - Get the Nonce to Use for the Authorized Transaction
As mentioned, gas sponsorship with EIP-7702 requires:
* The `SPONSOR` to actually send enough gas to cover the transaction in the broadcasted call.
* Onchain checks for replay attacks, including across different chains, is recommended as well.
The latter concern is covered in the next part of the Gas Sponsorship guide.
The implementation contract provided has a getter called `getNonceToUse()`. It is a simple function where you just pass the actual current EOA nonce to it as a param, and it will return a nonce that is ahead of the EOA nonce. The hope is to provide a nonce that is a good deal larger than the current EOA nonce so the "Service" can order transactions as needed without concern that the EOA will have done numerous transactions causing a pre-authorized transaction to become stale.
Get current EOA nonce, `EOA_NONCE`, and query contract for `getNonceToUse()`. The `.env` is updated with the value `EOA_NONCE`.
```bash theme={null}
source .env && \
EOA_NONCE=$(cast nonce $EOA_ADDRESS --rpc-url $TEST_RPC_URL) && \
NONCE_TO_USE=$(cast call $CONTRACT_ADDRESS "getNonceToUse(uint256)(uint256)" $EOA_NONCE --rpc-url $TEST_RPC_URL) && \
sed -i '' "/^NONCE_TO_USE=/d" .env && echo "NONCE_TO_USE=$NONCE_TO_USE" >> .env
```
Double check that this nonce makes sense with what the current `EOA_NONCE`, it should be 10 less than `NONCE_TO_USE` that you'll see in your `.env`. You can do this with this cast call:
```bash theme={null}
source .env && cast nonce $EOA_ADDRESS --rpc-url $TEST_RPC_URL
```
## Step 4 - Prepare the Offchain Transaction Details to be Broadcast
Now that you have the correct nonce to use in your `.env`, the next step is to use `sign-auth` to have the `EOA` authorize the transaction to be broadcast by the `SPONSOR`.
The bash command provided does the following:
Obtains the output from the `cast wallet sign-auth` call, `AUTH_SIG`. This variable will be used when the `SPONSOR` broadcasts the `EOA`'s transaction as it specifies the details of the actual transaction, and has implicit proof within it that the transaction is signed by the EOA.
Then, it prepares callData that directly invokes the `execute()` function in the smart account logic, passing no inner call for now. This is enough to test sponsorship, nonce, and signature checks implicitly using `cast` and `-auth`.
Then it carries out the transaction using `cast send`, where the `to` is the `$EOA_ADDRESS`, and the `$AUTH_SIG` has been signed offchain by the `EOA_PK`. The call is being carried out by the `SPONSOR` though to pay the gas!
Logs are output afterwards showcasing the results, which will be expanding on more in the next step!
```bash theme={null}
source .env && \
# ⛽ Capture balances before
EOA_BAL_BEFORE=$(cast balance $EOA_ADDRESS --rpc-url $TEST_RPC_URL) && \
SPONSOR_BAL_BEFORE=$(cast balance $SPONSOR_ADDRESS --rpc-url $TEST_RPC_URL) && \
echo "💰 EOA Balance Before: $EOA_BAL_BEFORE wei" && \
echo "💸 Sponsor Balance Before: $SPONSOR_BAL_BEFORE wei" && \
# ✍️ Sign EOA authorization
AUTH_SIG=$(cast wallet sign-auth $CONTRACT_ADDRESS \
--private-key $EOA_PRIVATE_KEY \
--nonce $NONCE_TO_USE \
--rpc-url $TEST_RPC_URL) && \
# 📦 Prepare calldata for `execute(...)`
CALLDATA=$(cast calldata "execute((bytes,address,uint256),address,uint256)" \
"(0x,$CONTRACT_ADDRESS,0)" $SPONSOR_ADDRESS $NONCE_TO_USE) && \
# 🚀 Send the sponsored transaction
TX_HASH=$(cast send $EOA_ADDRESS "$CALLDATA" \
--private-key $SPONSOR_PRIVATE_KEY \
--auth "$AUTH_SIG" \
--rpc-url $TEST_RPC_URL | grep -i 'transactionHash' | awk '{print $2}') && \
# 🧾 Retrieve gas used and cost
RECEIPT=$(cast receipt $TX_HASH --rpc-url $TEST_RPC_URL) && \
GAS_USED=$(echo "$RECEIPT" | grep gasUsed | awk '{print $2}') && \
GAS_PRICE=$(echo "$RECEIPT" | grep effectiveGasPrice | awk '{print $2}') && \
GAS_COST_WEI=$(echo "$GAS_USED * $GAS_PRICE" | bc) && \
GAS_COST_GWEI=$(echo "scale=9; $GAS_COST_WEI / 1000000000" | bc) && \
# 💰 Capture balances after
EOA_BAL_AFTER=$(cast balance $EOA_ADDRESS --rpc-url $TEST_RPC_URL) && \
SPONSOR_BAL_AFTER=$(cast balance $SPONSOR_ADDRESS --rpc-url $TEST_RPC_URL) && \
# 📉 Calculate deltas
EOA_DELTA=$(echo "$EOA_BAL_BEFORE - $EOA_BAL_AFTER" | bc) && \
SPONSOR_DELTA=$(echo "$SPONSOR_BAL_BEFORE - $SPONSOR_BAL_AFTER" | bc) && \
# 🧾 Output Results
echo "📦 Transaction Hash: $TX_HASH" && \
echo "🔍 To view the auth list, run:" && \
echo "source .env && cast tx $TX_HASH --rpc-url $TEST_RPC_URL" && \
echo "To view the receipt and ensure that the transaction was successful or not, run: " && \
echo "source .env && cast receipt $TX_HASH --rpc-url $TEST_RPC_URL" && \
echo "📜 Gas Used: $GAS_USED gas units" && \
echo "💸 Gas Cost: $GAS_COST_WEI wei (~$GAS_COST_GWEI gwei)" && \
echo "💰 EOA Balance After: $EOA_BAL_AFTER wei" && \
echo "💸 Sponsor Balance After: $SPONSOR_BAL_AFTER wei" && \
echo "📉 EOA Δ: $(echo "$EOA_DELTA / 10^9" | bc) Gwei" && \
echo "📉 Sponsor Δ (gas): $(echo "$SPONSOR_DELTA / 10^9" | bc) Gwei" && \
# 🔬 Gas sanity check
cast receipt $TX_HASH --rpc-url $TEST_RPC_URL | grep -E 'gasUsed|effectiveGasPrice' && \
echo "✅ If sponsor delta roughly equals gasUsed * effectiveGasPrice → gas was paid by SPONSOR."
```
The output should look like this:
```bash theme={null}
SPONSOR." sponsor delta roughly equals gasUsed * effectiveGasPrice → gas was paid b
💰 EOA Balance Before: 9999999555915999555916 wei
💸 Sponsor Balance Before: 10000000000000000000000 wei
�� Transaction Hash: 0xd8c7c699172330bc68cceff113a188dd146a1460a59aad50ff85dfbd52f91e41
🔍 To view the auth list, run:
source .env && cast tx 0xd8c7c699172330bc68cceff113a188dd146a1460a59aad50ff85dfbd52f91e41 --rpc-url http://localhost:8545
To view the receipt and ensure that the transaction was successful or not, run:
source .env && cast receipt 0xd8c7c699172330bc68cceff113a188dd146a1460a59aad50ff85dfbd52f91e41 --rpc-url http://localhost:8545
📜 Gas Used: 47476 gas units
💸 Gas Cost: 41717194480676 wei (~41717.194480676 gwei)
💰 EOA Balance After: 9999999555915999555916 wei
💸 Sponsor Balance After: 9999999958282805519324 wei
📉 EOA Δ: 0 Gwei
📉 Sponsor Δ (gas): 41717 Gwei
effectiveGasPrice 878700701
gasUsed 47476
✅ If sponsor delta roughly equals gasUsed * effectiveGasPrice → gas was paid by SPONSOR.
```
## Step 5 - Assessing the Results
The output from running the last command will provide two `cast` commands to assess the results. If you prefer, just run the following commands though and copy and paste the transaction hash in accordingly.
1. To see the Authorization List and other details signifying that the EIP-7702 transaction was successful, run:
```bash theme={null}
source .env && cast tx $TX_HASH --rpc-url $TEST_RPC_URL
```
Here you'll see the following:
*Using our example contract address to illustrate, you'll have a different one. Our contract address as seen in the previous screenshot is: 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82*
Under authorization list, you should see the contract address:
```bash theme={null}
authorizationList [{"chainId":"0x138c5","address":"0x0dcd1bf9a1b36ce34237eeafef220932846bcd82","nonce":"0x18","yParity":"0x1","r":"0x5b9ac56625105f2b627f344470290bfa3e5c5b19075ee741f5eedeb3e7288db2","s":"0xaa0bf8139cd82e5de12d33b13c1199444c9bca7f60fa7d577fafc7ddd455511"}]
```
and the `to` specified should be the EOA address, and the `from` address should be the SPONSOR address. These will be the same for you too assuming you followed the guide and are using the anvil test wallets 1 and 2:
```bash theme={null}
to 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
from 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
```
2. To see the transaction receipt, run:
```bash theme={null}
source .env && cast receipt $TX_HASH --rpc-url $TEST_RPC_URL
```
Here you can see the gasUsed, as well as that the transaction has successfully passed.
```bash theme={null}
blockHash 0x4c20dd22bfec22f1a1e7e647a38d2af17087e8a02b39adcd4f77ab96cb985558
blockNumber 2
contractAddress
cumulativeGasUsed 47476
effectiveGasPrice 878700701
from 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
gasUsed 47476
logs []
logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
root
status 1 (success)
transactionHash 0xd8c7c699172330bc68cceff113a188dd146a1460a59aad50ff85dfbd52f91e41
transactionIndex 0
type 4
blobGasPrice 1
blobGasUsed
to 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
```
Regarding the gas, we can do a check on how much gas was taken from the Sponsor, and how much was reimbursed. We do just that with the previous command you sent where gas logs were output, but of course take what we need and carry out comparisons.
```bash theme={null}
SPONSOR." sponsor delta roughly equals gasUsed * effectiveGasPrice → gas was paid b
💰 EOA Balance Before: 9999999555915999555916 wei
💸 Sponsor Balance Before: 10000000000000000000000 wei
�� Transaction Hash: 0xd8c7c699172330bc68cceff113a188dd146a1460a59aad50ff85dfbd52f91e41
🔍 To view the auth list, run:
source .env && cast tx 0xd8c7c699172330bc68cceff113a188dd146a1460a59aad50ff85dfbd52f91e41 --rpc-url http://localhost:8545
To view the receipt and ensure that the transaction was successful or not, run:
source .env && cast receipt 0xd8c7c699172330bc68cceff113a188dd146a1460a59aad50ff85dfbd52f91e41 --rpc-url http://localhost:8545
📜 Gas Used: 47476 gas units
💸 Gas Cost: 41717194480676 wei (~41717.194480676 gwei)
💰 EOA Balance After: 9999999555915999555916 wei
💸 Sponsor Balance After: 9999999958282805519324 wei
📉 EOA Δ: 0 Gwei
📉 Sponsor Δ (gas): 41717 Gwei
effectiveGasPrice 878700701
gasUsed 47476
✅ If sponsor delta roughly equals gasUsed * effectiveGasPrice → gas was paid by SPONSOR.
```
The rough gas used matches the delta (gas spent) from the `SPONSOR` address, whereas the `EOA` has not spent any gas at all.
That's it! Congrats you've walked through a high level example of gas sponsorship using EIP-7702 and Foundry Cast. Feel free to add comments or suggestions on our `guides` repo or reach out via Discord.
# Part 2 - Using Foundry Solidity Scripts and Implementing EOA Signer Checks
The guide so far walked through the high level transaction flow when working with EIP-7702 to leverage gas sponsorship for an EOA. In this second part of the guide, we will cover the usage of Foundry Solidity Scripts and changes within the authorized implementation logic, `SimpleDelegatePart2.sol`. The below steps walk through each core lesson, and the commands to run the entire guide is in [step #5](#step-5---understanding-and-running-the-solidity-script).
The files used include:
* `SimpleDelegatePart2.sol` - An expansion on `SimpleDelegate.sol` contract where it showcases signer recovery, nonce management, and use of inner calls with calldata.
* `SimpleDelegatePart2.s.sol` - A solidity script that is used to enact deployment of the implementation contract, authorization of the implementation logic for the EOA as per EIP-7702, and the broadcast of the sponsored transaction from the `SPONSOR` on behalf of the `EOA`.
The files are ready to test and work with. Let's briefly walk through what is going on in each of them to help solidify key lessons.
## Step 1 - Signer Checks with EIP-7702
It is important to check that the authorized transaction is originally signed by the EOA. Changes have been made to the Part 1 `SimpleDelegate.sol` contract where:
* The address of the signer is recovered using the `call` information and the provided `signature`.
The SimpleDelegatePart2.s.sol script is very different from the script used in part 1. Instead of using `cast` calls to deploy and interact with the contract and accounts, the solidity script carries out all the steps within the code.
> `vm.signDelegation` and `vm.attachDelegation` are cheatcodes from Foundry that allow EOAs and Sponsors to carry out EIP-7702 transactions when broadcast to networks. Please note that within simulations, such as with `forge test` or where solidity scripts are ran but not broadcast, Foundry is still working to support EIP-7702 transactions in full. For this tutorial, we will simply test against a local anvil forked network or the network directly.
The main gotcha with the script code and the changed implementation code is that the implementation code checks if the recovered address, and thus the signer, is the EOA itself. Only then will it pass and allow the transaction to carry out.
```solidity theme={null}
...
bytes32 digest = keccak256(
abi.encodePacked(userCall.to, userCall.value, keccak256(userCall.data), sponsor, nonce)
);
address recovered = MessageHashUtils.toEthSignedMessageHash(digest).recover(signature);
require(recovered == address(this), "Invalid signer");
```
Above is the core check that exposes a couple of things about EIP-7702. The `address(this)` value is actually the `EOA` in the context of the EIP-7702 transaction. Therefore, any call and transaction enacted through it, must be signed by the `EOA_PK` itself.
This check ensures that no malicious transactions are carried through.
## Step 2 - Replay Attacks Prevention via Nonce Management
Replay attacks can be prevented with EIP-7702 transactions by having proper checks within the authorized implementation logic. Essentially if there is no check for the nonce being used in the authorized transaction, it could be re-used, which of course is a huge attack vector.
Expanding on part 1, where the high level transaction flow was shown for Gas Sponsorship with EIP-7702, we now use the persistent storage of the EOA to our advantage and keep track of the arbitrary nonces used from the "service" preparing transactions signed by the EOA.
Recall that the EOA nonce is not the same as the nonce used in these checks. The nonce used in these checks correspond to the arbitrary nonce that the offchain service uses to prepare transactions signed by the EOA ultimately. One reason for these arbitrary nonces is to prevent replay attacks by marking the nonce as used within a mapping in the implementation logic itself.
> A key lesson here is that the storage of the EOA persists when it comes to using authorized implementation logic via EIP-7702. It can be transient as well.
The changes made with respect to nonce management include:
* A mapping of nonces to bool values, signifying when a nonce has been used.
* Conditional logic that reverts if the mapping of the proposed transaction nonce shows that it is a used nonce already.
```solidity theme={null}
...
mapping(uint256 => bool) public nonceUsed; // Whether EOA nonce has been used or not
error NonceAlreadyUsed();
...
function execute(
Call memory userCall,
address sponsor,
uint256 nonce,
bytes calldata signature
) external payable {
...
if (nonceUsed[nonce]) revert NonceAlreadyUsed();
nonceUsed[nonce] = true;
...
}
```
## Step 3 - Crosschain Replay Attack Prevention via ChainID Management
Crosschain replay attacks occur when an authorized, signed, transaction is replayed on a separate network then from where it originated. This exposes risks to funds on different networks, and more.
ChainIDs are used in the implementation logic as a way to prevent these crosschain attacks. Combined with the nonce mapping mentioned before, chainIDs can be used to ensure the transaction can only occur once on the specified network, and no where else.
```solidity theme={null}
...
function execute(
Call memory userCall,
address sponsor,
uint256 nonce,
bytes calldata signature
) external payable {
...
bytes32 digest = keccak256(
abi.encodePacked(block.chainid, userCall.to, userCall.value, keccak256(userCall.data), sponsor, nonce)
);
address recovered = MessageHashUtils.toEthSignedMessageHash(digest).recover(signature);
require(recovered == address(this), "Invalid signer");
...
}
```
## Step 4 - Using Actual calldata via `burnNative()` with EIP-7702 Calls
Within the first part of this guide, we sent empty `calldata` to focus in on the use of EIP-7702 for gas sponsorship. Typically, transactions will not have empty calldata. An additional expansion for part 2 of this guide is to include a `burnNative()` function to showcase the execution of an internal call within the authorized transaction.
With EIP-7702, it's especially important to validate that inner calldata works as expected because the EOA is executing the smart logic directly at its own address. This means:
* The logic inside the calldata is executed in the context of the EOA.
* The storage, balance, and even msg.sender will reflect this.
* Verifying that a function like burnNative() executes properly ensures the EIP-7702 delegation, signature recovery, and calldata routing are working together correctly.
The burnNative() function used in this step serves as a representative real-world inner call that modifies the EOA’s native balance. It’s a good example because:
* It visibly changes state (burns funds by sending them to address(0) or similar),
* It involves value transfer and storage updates,
* It lets us confirm that calldata isn’t just routed, but actually executed properly within the EOA’s own contract context.
```bash theme={null}
SimpleDelegatePart2.Call memory call = SimpleDelegatePart2.Call({
to: EOA,
value: burnAmount,
data: abi.encodeWithSelector(simpleDelegate.burnNative.selector)
});
bytes32 digest = keccak256(
abi.encodePacked(block.chainid, call.to, call.value, keccak256(call.data), SPONSOR, nonce)
);
bytes32 ethSigned = MessageHashUtils.toEthSignedMessageHash(digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(EOA_PK, ethSigned);
bytes memory signature = abi.encodePacked(r, s, v);
uint256 sponsorBefore = SPONSOR.balance;
uint256 eoaBefore = EOA.balance;
vm.startBroadcast(SPONSOR_PK);
vm.attachDelegation(signedDelegation);
bytes memory code = address(EOA).code;
require(code.length > 0, "no code written to EOA");
(bool success, ) = EOA.call{value: transferAmount}(
abi.encodeWithSelector(
SimpleDelegatePart2.execute.selector,
call,
SPONSOR,
nonce,
signature
)
);
require(success, "Call to EOA smart account failed");
vm.stopBroadcast();
```
### Step 5 - Understanding and Running the Solidity Script
We have discussed the changes made to the implementation logic in `SimpleDelegatePart2.sol`. Within this section, we will walk through the main components within the Solidity Script. First we must expand our `.env`, and stop the anvil network that you started in Part 1, and create another network that forks off of Bepolia, to ensure we get the full features offered by BECTRA.
You have to update your `.env` so you can broadcast properly to Bepolia. You will need to have \$tBERA within your wallets that you are using for both the EOA and the SPONSOR.
> If you need \$tBERA, you can get some from our [faucet](#step-5---understanding-and-running-the-solidity-script), or contact us directly.
```
# YOUR OWN WALLET DETAILS FOR DEPLOYING TO ACTUAL NETWORKS
EOA_WALLET1_ADDRESS=
EOA_WALLET1_PK=
SPONSOR_WALLET2_ADDRESS=
SPONSOR_WALLET2_PK=
```
Next, run the following commands:
```bash theme={null}
lsof -i :8545
```
You will see an output listing the PID for the network you launched for Part 1. Now you must kill it so you can relaunch another network at port 8545. Run the following command:
```bash theme={null}
kill -9
```
Now we can start a new `anvil` fork with Bepolia at "hardfork prague."
```bash theme={null}
source .env && anvil --fork-url $BEPOLIA_RPC_URL --chain-id 80069 --hardfork prague --port 8545
```
Instead of piecing things together with `cast`, we use a full Foundry Solidity script to handle everything: deployment, delegation, authorization, broadcasting, and even checking for replay and signature mismatches. This is a great way to simulate what a service or wallet might actually do when working with EIP-7702.
Using a Foundry Solidity script gives you a lot:
* You get full control over both the EOA and the sponsor inside one flow
* Cheatcodes like `vm.sign`, `vm.envAddress`, `vm.startBroadcast` make this super clean
* You can easily simulate replay, forged signatures, chain ID mismatches
* You can inspect balances, gas deltas, and storage at the EOA address directly
The file is `SimpleDelegatePart2.s.sol`, and the main entry point is the `SimpleDelegate2Script` contract. You can run it using:
```bash theme={null}
source .env && forge script script/SimpleDelegatePart2.s.sol:SimpleDelegate2Script \
--rpc-url $TEST_RPC_URL \
--broadcast -vvvv
```
This script works on a local anvil fork or Bepolia. As mentioned before, just make sure your `.env` has the right test keys and enough \$tBERA.
#### What the Script Does
The Solidity script does the entire 7702 lifecycle in one flow:
1. Deploys the `SimpleDelegatePart2` contract
2. Signs a delegation from the EOA to itself
3. Constructs a call to `burnNative()` using the implementation logic
4. Signs the transaction offchain using the EOA private key
5. Has the sponsor broadcast the transaction using `execute(...)`
6. Logs balances, costs, and verifies the result
This is a pretty accurate simulation of how things would work in practice with a wallet frontend, sponsor backend, and protocol logic.
We also run two important tests mentioned before:
* **Replay attack**: Try sending the same tx again → should revert due to `nonceUsed[nonce]` being true.
* **Cross-chain replay**: Try a forged signature using the wrong chain ID → should fail signature recovery.
### Core Lessons in the Script
There are a few key things this script shows:
* `address(this)` inside the implementation contract equals the EOA, since EIP-7702 executes the logic at the EOA's address
* Storage writes like `nonceUsed[nonce] = true` persist at the EOA
* Including `block.chainid` in the digest ensures signatures only work on the intended chain
All of these are things are highly recommended to get right in a production deployment of EIP-7702 sponsorship flows.
### Step 6 - Assessing the New Final Results
The contract has been successfully interacted with at the EOA's address by observing the following:
Below you can see the `to` address is the EOA, the `from` address is the SPONSOR, and the implementation address is seen under the `authorizationList`.
```json theme={null}
{
"hash": "0x131051742f94c2fb10422d53d134a78deb41404dd5b47e99cff721dc4eb70b02",
"transactionType": "CALL",
"contractName": null,
"contractAddress": "0x63e6ab65010c695805a3049546ef71e4a242eb6c",
"function": "execute((bytes,address,uint256),address,uint256,bytes)",
"arguments": [
"(0xfbc7c433, 0x63E6ab65010C695805a3049546EF71e4A242EB6C, 10000000000000000)",
"0x00195EFB66D39809EcE9AaBDa38172A5e603C0dE",
"17",
"0xbb7f5ba622d818bb258263974aaa2131dd1dda771c78f88b89152d6b432cfbfe3d02237565c005bb0e6811de03311210e04ec7224d262b2bf7c764b66ce4aff41b"
],
"transaction": {
"from": "0x00195efb66d39809ece9aabda38172a5e603c0de",
"to": "0x63e6ab65010c695805a3049546ef71e4a242eb6c",
"gas": "0x2a7d1",
"value": "0x6a94d74f430000",
"input": "0xd65fcb6c000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000195efb66d39809ece9aabda38172a5e603c0de00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000006000000000000000000000000063e6ab65010c695805a3049546ef71e4a242eb6c000000000000000000000000000000000000000000000000002386f26fc100000000000000000000000000000000000000000000000000000000000000000004fbc7c433000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041bb7f5ba622d818bb258263974aaa2131dd1dda771c78f88b89152d6b432cfbfe3d02237565c005bb0e6811de03311210e04ec7224d262b2bf7c764b66ce4aff41b00000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x2",
"chainId": "0x138c5",
"authorizationList": [
{
"chainId": "0x138c5",
"address": "0xddb11edb9498e778d783e1514519631db978cefe",
"nonce": "0x7",
"yParity": "0x1",
"r": "0xd304ce7ae24007d5f367dd397030c92686c7a10180c8094825736854a96be633",
"s": "0x1c304881690f634b1a2f9192137097d0e7e37d27538353f26446368925e0c2e5"
}
]
},
"additionalContracts": [],
"isFixedGasLimit": false
}
```
The output will showcase a successful transaction and a reversion for both `NonceAlreadyUsed()` and `Invalid Signer`.
```bash theme={null}
[⠊] Compiling...
No files changed, compilation skipped
Warning: Detected artifacts built from source files that no longer exist. Run `forge clean` to make sure builds are in sync w
ith project files. - /Users/ichiraku/Documents/1-CODE/2-Guides/May-22/guides/apps/eip-7702-gas-sponsorship/script/SimpleDelegatePart2RealDeploy
ment.s.sol Warning: EIP-3855 is not supported in one or more of the RPCs used.
Unsupported Chain IDs: 80069.
Contracts deployed with a Solidity version equal or higher than 0.8.20 might not work properly.
For more information, please see https://eips.ethereum.org/EIPS/eip-3855
Script ran successfully.
== Logs ==
Sponsor balance (wei): 19992485519997369932
---- Execution Summary ----
Sponsor Gas Spent (wei): 996420000348747
EOA Delta (wei): 0
Amount reimbursed to Sponsor (wei): 29003579999651253
---- Test Case 1: Replay with Same Nonce ----
Replay failed as expected (nonce already used).
---- Test Case 2: Replay with Wrong ChainID ----
Cross-chain replay failed as expected (invalid chainId in signature).
## Setting up 1 EVM.
[12323] 0x63E6ab65010C695805a3049546EF71e4A242EB6C::execute{value: 30000000000000000}(Call({ data: 0xfbc7c433, to: 0x63E6ab
65010C695805a3049546EF71e4A242EB6C, value: 10000000000000000 [1e16] }), 0x00195EFB66D39809EcE9AaBDa38172A5e603C0dE, 19, 0x91f3ff0d4999ce2d97ad98d9c78c967e30a17e313941763f726f943d122d6f6d24d01b00050c9cf12c2a060a22c012d70787d07a174b87187b6cd083cc24b59e1b) ├─ [3000] PRECOMPILES::ecrecover(0xbaf222251f89c11d41a500b7a405321c85d41d2cbc0a926cdb39d802150a979c, 27, 6601646718486358
1129492900441135972292030061417493464943343716828623890378605, 16650953086154607435429330488942928347914477230191894192328024569607478359454) [staticcall] │ └─ ← [Return] 0x00000000000000000000000063e6ab65010c695805a3049546ef71e4a242eb6c
└─ ← [Revert] NonceAlreadyUsed()
[10049] 0x63E6ab65010C695805a3049546EF71e4A242EB6C::execute{value: 30000000000000000}(Call({ data: 0xfbc7c433, to: 0x63E6ab
65010C695805a3049546EF71e4A242EB6C, value: 10000000000000000 [1e16] }), 0x00195EFB66D39809EcE9AaBDa38172A5e603C0dE, 19, 0x90574dcdf09f26952729cdbede854d50a10d377fb224970f2eb38654c8627f34293347fdd28a3d4396dbd9f7f80f157cc930a6c5b221efd916fb7441614e294d1c) ├─ [3000] PRECOMPILES::ecrecover(0xbaf222251f89c11d41a500b7a405321c85d41d2cbc0a926cdb39d802150a979c, 28, 6528730287722426
9808226580710698351715294006742266363989773667897382331907892, 18635432859247198048365968425627914393284181918472378048396876387465699404109) [staticcall] │ └─ ← [Return] 0x0000000000000000000000001191ad406538b598920074e2197cbe682dc0f449
└─ ← [Revert] Invalid signer
Error: Simulated execution failed.
```
The below snippit from the output shows chat the EOA successfully burns a small amount of \$tBERA, thus showcasing the authorized transaction with an inner call broadcast by the SPONSOR.
```bash theme={null}
├─ [36698] 0x63E6ab65010C695805a3049546EF71e4A242EB6C::burnNative{value: 10000000000000000}()
│ ├─ [0] 0x000000000000000000000000000000000000dEaD::fallback{value: 10000000000000000}()
│ │ └─ ← [Stop]
│ ├─ emit Burned(from: 0x63E6ab..., amount: 10000000000000000 [1e16])
│ └─ ← [Return]
```
Finally, we have walked through the second part of gas sponsorship. Congrats! In the next gas-sponsorship guide expansion, we will walk through support for ERC20 payment flows.
# Protocol Features
Source: https://docs.berachain.com/build/protocol/overview
Berachain-supported EIPs and chain-level capabilities for developers.
Berachain supports core Ethereum upgrades plus newer transaction UX standards. This section is a high-level map of what is live on-chain today, with detailed guides in the sidebar.
## Upgrade Timeline
| Upgrade | Mainnet activation (YYYY-MM-DD) | Bepolia activation (YYYY-MM-DD) | Status |
| -------------------------------------------------- | ------------------------------------ | ------------------------------------ | --------------------------------- |
| Pre-merge hardforks (Frontier through GrayGlacier) | Genesis | Genesis | Live |
| Merge (Paris) | Genesis | Genesis | Live |
| Shanghai | Genesis | Genesis | Live |
| Cancun | Genesis | Genesis | Live |
| Prague / Pectra | 2025-06-04 | 2025-05-07 | Live |
| Berachain Prague1 | 2025-09-03 | 2025-08-06 | Live |
| Berachain Prague2 | 2025-09-30 | 2025-09-17 | Live |
| Fulu + Osaka = Fusaka | 2026-06-24 | 2026-05-27 | Live |
| Osaka1 | 2026-08-05 | 2026-07-08 | Bepolia Live Mainnet Pending |
## Fusaka Upgrade Features
The Fusaka upgrade activated several execution-layer features on the Berachain EVM:
* **EIP-6110 (in-payload deposits):** Deposit requests are included in execution payloads and consumed by consensus **in the same block**, replacing the eth1 follow-distance deposit queue. Berachain processes deposits **per block** and emits a chain-specific deposit event for tooling that monitors stake flows.
* **EIP-7951 (P-256 precompile):** Native verification of passkey and hardware-key signatures, usable for smart-account flows backed by WebAuthn, Apple Secure Enclave, Android Keystore, and HSMs.
* **EIP-7939 (CLZ opcode):** A faster bit-math primitive that returns the position of the leading set bit in a single opcode.
* **EIP-7823 & EIP-7883 (MODEXP repricing and bounds):** Repriced modular exponentiation, with an input-size bound (capped at 8,192 bits) that rejects oversized arguments outright.
* **Code-size limits (EIP-7954 aligned):** Expanded contract code and initcode size limits (32 KB and 64 KB, respectively), raising the ceiling on what a single contract can deploy.
* **EIP-7934 & EIP-7825 (Size caps):** Defense-in-depth caps on block size (10 MiB) and per-transaction gas (16.7M gas), sized well above current usage.
## EIP-7702 — Native Account Abstraction
EIP-7702 allows externally owned accounts to temporarily behave like smart contracts within a single transaction. This unlocks batched calls, gas sponsorship, and other account-abstraction patterns without deploying a separate contract wallet.
* [Basics](/build/protocol/eip7702-basics) — understand the fundamentals
* [Batch Transactions](/build/protocol/eip7702-batch-transactions) — combine multiple calls into one transaction
* [Gas Sponsorship](/build/protocol/eip7702-gas-sponsorship) — let a third party pay for gas
## EIP-5792 — Wallet Batching Capabilities
EIP-5792 introduces `wallet_sendCalls`, enabling applications to request wallets to process batches of on-chain write calls and check their status.
* [Overview](/build/protocol/eip5792-overview) — how wallet batching works
* [MetaMask reference docs](https://docs.metamask.io/wallet/how-to/send-transactions/send-batch-transactions) — implementation details and wallet behavior
# PoL Governance
Source: https://docs.berachain.com/general/governance/reward-vault-governance
Governance controls for Proof of Liquidity: vault whitelisting, emission parameters, incentive fee routing, and DES.
Proof of Liquidity has several governance-controlled surfaces. Creating a [Reward Vault](/general/proof-of-liquidity/reward-vaults) is permissionless, but governance must whitelist vaults before validators can route emissions to them. See the [RFRV process](#request-for-reward-vault-rfrv) below for whitelisting requirements.
## Governance-controlled surfaces
### Incentive fee destination
Governance sets the shared collector that receives redirected incentive tokens from vaults created by a Reward Vault factory. The collector is the settlement point that turns those incentive tokens into WBERA-denominated yield for liquid staking.
See [Incentive Marketplace — Incentive fee settlement](/general/proof-of-liquidity/incentives#incentive-fee-settlement) and [Deployed contract addresses](/build/getting-started/deployed-contracts).
### Dedicated Emission Stream controls
Governance controls how much of the Reward Vault emission can be carved out for Dedicated Emission Stream allocations, and where those capped allocations go.
See [Block rewards — Dedicated Emission Stream](/general/proof-of-liquidity/block-rewards#dedicated-emission-stream-des) for how DES carve-out and per-vault caps interact with validator reward allocation.
## Request for Reward Vault (RFRV)
The Reward Vault whitelisting process requires both: 1) a submitted **Request for Reward Vault**
(RFRV) form, and 2) a proposal thread on the [Governance
Forum](https://forum.berachain.com/c/proposals/).
### Application form
[RFRV Application Form](https://dub.sh/berarfrv)
### BEX pool RFRVs
* Pool must be live on BEX.
* Incentive tokens must be active on that pool.
* Pairing with major assets is preferred (BERA, HONEY, BYUSD, USDC, wETH, wBTC).
* Proposal should show meaningful ecosystem impact.
### General (non-BEX) RFRVs
* Contracts must be deployed and live.
* Incentive tokens must be live.
* Proposal should show measurable ecosystem value and risk controls.
Proposals are reviewed weekly by governance stewards.
For contract-level references, see [Deployed contract addresses](/build/getting-started/deployed-contracts).
# Frequently Asked Questions
Source: https://docs.berachain.com/general/help/faqs
Common questions about Berachain.
## General & Network
Berachain has the following properties:
* Block time: \~2 seconds.
* Transactions per Second (TPS): This can vary but the following should help with the number of possible transactions (Block gas limit (30m) / Average gas limit per txn) / Block time (2s) = TPS.
* Finality: single slot finality
* 100% EVM compatibility
Governance is the process by which the community decides what changes to make to the Berachain protocol. This includes how to upgrade the node and what parameters to set for various components on the chain.
## Tokens
\$BERA is the single staking and emission token:
* **Validator staking**: Node operators stake \$BERA to run validators, produce blocks, and secure the chain. Additional stakers can deposit \$BERA to increase a validator's block-production probability.
* **PoL emissions**: Block rewards are emitted as \$WBERA (wrapped BERA). Validators receive a base rate; the remainder is routed through BeraChef reward allocation to Reward Vaults.
* **\$sWBERA**: Users deposit \$BERA or \$WBERA into the Staking Vault to receive \$sWBERA, which earns yield from the Incentive Auction.
`$HONEY` is the native stablecoin of the Berachain ecosystem. It is a multicollateral backed
stablecoin, and is used throughout the Berachain ecosystem.
It was previously used in early versions of Proof of Liquidity for governance and reward routing.
Users who still hold \$BGT should unstake it, redeem it 1:1 for native \$BERA, and stake their
\$BERA for \$sWBERA to participate in the current Proof of Liquidity system. See the [Berachain
Hub](https://hub.berachain.com/) which will walk you through the process.
To ensure stability, there is a small fee on every mint and burn of `$HONEY`. Additionally, because minting & burning requires a transaction, there will be a small gas fee in \$BERA.
## Proof of Liquidity & Validators
A validator can refer to three things:
1. A blockchain node that validates transactions, produces blocks, and comes to consensus with other validators in the network
2. The entity that owns and operates the validator node
3. The blend of points #1 and #2 that manages a portion of Proof of Liquidity & Governance votes
\$sWBERA earns yield from the Incentive Auction. Protocols fund incentive tokens to attract
validator reward allocation; after validator commission, the remaining incentive tokens are
redirected to `IncentivesCollector`. Auction buyers pay WBERA to claim those tokens, and that
WBERA is split pro-rata between the \$sWBERA Staking Vault and registered LST staker vaults.
Holding \$sWBERA gives you a share of that yield without needing to manage vault positions or
validator selection.
Yes, validators only need to stake \$BERA within the designated min and max range of **250,000** and **10,000,000**, and once in the active set they will propose blocks. Base rate reward is distributed in \$WBERA.
## BEX & Liquidity
DEX stands for Decentralized Exchange. It is a place where you can buy and sell tokens directly on the chain instead of through any one centralized service. This means that all liquidity can be seen directly on-chain, and the smart contracts themselves verifiably own it. A DEX enables you to swap tokens directly from your wallet, as well as allowing anyone to launch their own tokens and provide liquidity.
A swap is the process of exchanging one token for another. This can be thought of as a buy or a
sell, depending on which token you're looking at. For example, if you're looking to buy \$BERA
with `$ETH`, you would be swapping `$ETH` for \$BERA. This is essentially "selling" `$ETH` and
"buying" \$BERA.
Each swap has a fee that varies depending on the fee set when the pool was created. Common fees
are 0.05%, 0.1%, 0.3% or 1% but you should always check when performing a swap to ensure you are
okay with the fee on that pool.
Liquidity is the term for the amount of a token available to swap. The more liquidity a token has,
the easier it is to swap that token.
Liquidity pools are pairings of 2 or more tokens that liquidity providers deposit tokens into.
This enables DEX users to swap between any of the tokens in the pool.
Liquidity providers deposit tokens into a liquidity pool. They earn a portion of the fees
generated from swaps in the pool.
APY stands for annual percentage yield. In the context of BEX pools, this refers to the current
APY for a given pool. APY yield comes from fees collected on every swap made using that pool.
When you deposit liquidity into a BEX pool, you receive an LP token representing your share of the pool. To earn Proof of Liquidity emissions, you must take that LP token and stake it into its corresponding Reward Vault. As validators direct emissions to that vault, you will accumulate claimable rewards denominated in \$WBERA. You can claim \$WBERA directly from the vault or through the Reward Vault Helper as \$sWBERA or native BERA.
# Glossary
Source: https://docs.berachain.com/general/help/glossary
Technical terms used across Berachain docs.
## BeaconKit
BeaconKit is a modular and customizable consensus layer framework that leverages the CometBFT consensus algorithm for building Ethereum-based blockchains. Read more in [BeaconKit overview](/nodes/beaconkit/overview).
## BERA token
\$BERA is the native gas token of Berachain's L1 and serves multiple purposes:
* Used for paying transaction fees
* Validator staking token to secure the network — more BERA staked means more blocks proposed
* PoL emission token (as WBERA) for Reward Vault rewards
Read more in [Tokens - \$BERA](/general/tokens/bera).
## BEX
Berachain's native decentralized exchange. Read more in [BEX Overview](/bex/learn/overview).
## BGT (Bera governance token)
BGT was the Berachain Governance Token. It was previously used for governance and reward routing. It can be burned and converted 1:1 for BERA.
Read more in [Tokens - \$BGT](/general/tokens/bgt).
## Block
A data unit containing a list of transactions that is permanently added to the blockchain in a sequential manner.
## Block time
The time it takes to create a new block on the blockchain. Berachain has an average block time of \< 3 seconds. Note that block time can increase depending on network congestion.
## CometBFT
A general-purpose blockchain consensus engine used by Berachain to achieve high throughput and fast finality in transactions. Read more at [Cometbft.com](https://cometbft.com).
## Consensus client
The consensus client is a piece of software responsible for achieving agreement among network nodes about the current state of the blockchain. It handles the process of validating transactions and blocks, ensuring they adhere to network rules, and deciding which blocks get added to the blockchain. On Berachain, the consensus client is BeaconKit; see [BeaconKit consensus](/nodes/architecture/beaconkit-consensus).
## Consensus mechanism
The protocol by which nodes in the Berachain network agree on the state of the blockchain. Berachain uses Proof-of-Liquidity to select validators based on their provided liquidity. See [Proof of Liquidity overview](/general/proof-of-liquidity/overview).
## Delegation
The process by which a token holder grants voting or validation power to another participant in the network. For Reward Vault delegation, see [Reward Vaults — Delegation](/general/proof-of-liquidity/reward-vaults#delegation).
## DEX (decentralized exchange)
A platform that enables the buying and selling of tokens directly on the blockchain without a centralized intermediary. All liquidity is verifiably owned by smart contracts.
## Dedicated Emission Stream (DES)
Governance-selected carve-out of Reward Vault emission allocated before validator-specific allocation, with per-vault cumulative caps. See [Block rewards — Dedicated Emission Stream](/general/proof-of-liquidity/block-rewards#dedicated-emission-stream-des).
## Effective balance
A validator's stake rounded down to a 10,000 BERA increment. Voting power and reward weight are computed from effective balance, not actual balance. Effective balance updates lazily — it only moves when actual balance crosses the [hysteresis](#hysteresis) buffer above or below the next increment, which keeps voting power stable under small balance fluctuations.
## EIP (Ethereum Improvement Proposal)
A standards document that specifies a change or addition to Ethereum. EIPs cover protocol features (new opcodes, precompiles, gas repricings), wallet behaviors, and ecosystem conventions, and are the unit Berachain references when adopting upstream changes (for example, the Fusaka EVM features). Each EIP has a number; the [Protocol Features](/build/protocol/overview) page lists which EIPs are live on Berachain and links to the upstream specification.
## Engine API
The Engine API is the interface that allows communication between the execution and consensus layers of an EVM node. BeaconKit, as a consensus layer, leverages this to be easily paired with any execution client. See [BeaconKit consensus](/nodes/architecture/beaconkit-consensus).
## Execution client
An EVM (Ethereum Virtual Machine) execution client is a software application responsible for the actual computation of transactions within blocks. It interprets and executes the code of smart contracts using the EVM, manages state changes, and executes the transaction logic. See [EVM execution](/nodes/architecture/evm-execution).
Supported EVM Execution Client:
* **Bera-Reth:** Berachain's fork of the Reth execution client, a Rust-based client focusing on performance and reliability
## Finality
The assurance that once a transaction is confirmed on the blockchain, it cannot be altered or reversed. Berachain provides instant finality for transactions; see [single slot finality](#single-slot-finality).
## Governance
The system by which decisions are made within the Berachain ecosystem. Governance involves proposals, voting, and the implementation of changes for PoL & Berachain's native dapps (BEX, HoneySwap). For PoL governance specifics, see [Reward vault governance](/general/governance/reward-vault-governance).
## Hysteresis
The buffer that decouples a validator's [effective balance](#effective-balance) from small movements in actual balance. Effective balance only steps up or down when actual balance crosses an asymmetric threshold around an increment boundary: actual balance must rise more than 1,000 BERA above the next 10,000 BERA increment to trigger an upward step (\~110% of the increment), and must fall more than 100 BERA below an increment to trigger a downward step. See [Validator lifecycle](/nodes/architecture/validator-lifecycle#effective-balance-and-hysteresis) for the worked example.
## HONEY
`$HONEY` is the native stablecoin of the Berachain ecosystem, soft-pegged to 1 USDC. It is used throughout the Berachain ecosystem and involves minting and burning fees. Read more in [Tokens - `$HONEY`](/general/tokens/honey).
## Liquidity
The availability of liquid assets to facilitate trading on the Berachain network. Liquidity is often provided by users through liquidity pools.
## Liquidity pool
A collection of funds locked in smart contracts, used to facilitate trading on decentralized exchanges and other DeFi services.
## Liquidity provider
A user who deposits tokens into a liquidity pool, earning a portion of fees generated from swaps in the pool, as well as other potential rewards (from PoL or otherwise).
## Mainnet
The primary network where transactions comprising real value occur on the Berachain blockchain, as opposed to test networks used for development.
## Proof-of-Liquidity
A consensus mechanism that aligns economic incentives between validators, applications, and users. Validators are selected based on staked \$BERA; Reward Vault emissions are distributed in \$WBERA. Read more in [Proof of Liquidity overview](/general/proof-of-liquidity/overview).
## Single slot finality
A process where finality is achieved in the same block proposed. Sometimes also referred to as *Instant Finality*.
## Staking
The process of locking up tokens to support the operations of a blockchain network. In Berachain, staking is used to secure the network and participate in governance.
## Swap
The process of exchanging one token for another on a decentralized exchange. Swaps involve a fee, which varies depending on the pool's settings.
## WBERA staking
By staking BERA or WBERA into the Staking Vault, holders receive \$sWBERA and earn yield from the Incentive Auction. After validator commission (default 5%, max 20%), redirected incentive tokens are settled for WBERA via `IncentivesCollector`; auction WBERA is split pro-rata between the \$sWBERA vault and registered LST staker vaults. Read more in [`\$sWBERA`](/general/tokens/swbera).
# Honeypaper
Source: https://docs.berachain.com/general/help/honeypaper
Berachain Proof of Liquidity honeypaper.
February 3, 2025
Download as PDF
## 1. Modern Problems, Modern Solutions
Over the past few years, new infrastructure has trended toward solving a single problem: improving the throughput and transaction costs of chains [\[1\]](#ref-1). This has given rise to a number of new virtual machines and consensus engines, all of which seek to make blockspace cheaper and more plentiful than ever before. While these products solve important bottlenecks for scaling, they fail to deliver any novel economic solutions to filling this blockspace.
While the last few years have been dominated by the production of novel infrastructure layers, few to none of these solutions have gained meaningful adoption among developers, or even more importantly, among consumers. The blockchain industry, and crypto users as a whole, have come to see infrastructure layers as extractive, as opposed to value additive [\[2\]](#ref-2).
It has also become increasingly obvious that a chain's utility (and blockspace occupancy) is directly correlated to the quality and quantity of applications that are built on top of it [\[3\]](#ref-3). The Berachain Protocol (Berachain) is the first L1 that has been purpose-built to drive value to the applications built on top of it, making it easier for applications to bootstrap their own liquidity, increase their capital efficiency, and reach escape velocity from a user adoption point of view.
With this backdrop in mind, Berachain seeks to solve two crucial issues that nearly every chain faces.
First, Berachain seeks to better align the value proposition between participants on the network, namely users, validators, and applications.
Second, Berachain seeks to create a chain that mechanically derives its value from the applications built on it, rather than vice-versa.
Proof of Stake L1s face a major headwind due to the lack of value normalization across the execution and consensus layers. Rewards for validators are largely dictated by a preset curve, where only staking rate meaningfully affects the outcome. Execution-side variables like MEV can impact returns, but only impact rates to the upside [\[4\]](#ref-4).
Ideally, a chain should mechanically adjust the reward rate that validators receive for providing economic security based on demand for economic security. If demand for economic security among the execution layer - namely applications and users - is low relative to its supply, then the reward rate for validators is higher than it needs to be. This is a cost that is then borne by the chain in aggregate, as validators are effectively overpaid for their increasingly commoditized services.
A simple example of this (that is far from uncommon among new L1 launches) is a new chain that launches with a \$5 billion FDV, \$250 million of TVL, and a 10% staking rate. The chain is issuing \$500 million a year in inflation to underwrite the security of \$250 million of assets, effectively paying out \$2 to secure \$1 worth of assets. Noting that chains require both security and liquidity to effectively scale, this imbalance speaks to the lack of effective alignment between these two factors at the network level.
While there may be an argument that overpaying for economic security in the early days helps solve cold start problems, a better solution would allow validator rewards to be closely tied to demand for their services.
Additionally, rewards spent on economic security necessarily become rewards that aren't used for bolstering growth of the network. This structure decreases the impetus for activity on the network, as the elevated rewards rate for stakers offers a low-risk alternative for users and applications on the network. This causes difficulty in creating the network effects needed for activity on a chain, forcing many chains to resort to long-running and inefficient grant programs.
A better solution would seek to generate activity among users and applications as the primary target for chain inflation, and create a systematic way to reward validators for their economic security as demand for it naturally rises.
## 2. Overview of Berachain
To address the issues presented above, Berachain utilizes a novel solution: Proof of Liquidity (PoL).
### 2.1 Two Token Model
Proof of Stake blockchains often have a governance token that is used to secure the network through staking with validators. Oftentimes, this is the main network token and is used for gas, staking, governance, and economic incentives. To facilitate PoL, Berachain utilizes a two-token model, featuring BERA (the gas and staking token) and BGT (the governance and rewards token).
BERA forms the basis for economic security on Berachain. It is used to pay for transactions on the network. It is also used to activate validator nodes. The economic value of all BERA tokens staked adds up to form the base layer of the security of the chain with BGT building on top of it for enhanced security.
BGT is the non-transferable governance and rewards token of the network. All emissions on Berachain and block rewards are in the form of BGT. It may only be earned via staking BERA as a validator, or by staking a PoL eligible receipt token into a reward vault.
1. Governance - BGT is used to vote on governance proposals for Berachain. Users can either vote directly on proposals, or delegate their voting power.
2. Burn - BGT can be instantly redeemed 1:1 for BERA. However, BERA cannot be converted back to BGT. The only way to earn BGT is through staking in reward vaults.
3. Economic Incentives - Users that boost a validator with their BGT are eligible to receive incentives from that validator, based on the validator's commission rate and ability to convert their BGT block rewards to application incentives.
PoL radically changes the way L1 economics are structured, prioritizing users and applications over validator rewards at baseline.
Berachain runs a Proof of Stake (PoS) model that features two components: Stake (in BERA) and Boost (in BGT). The Berachain active set is determined by a validator's stake; the top 69 validators by stake become part of the active set. Within the active set, each validator has a chance of winning a block proportional to their BERA stake, and the size of their block reward is determined by their BGT boost.
Validators receive a small commission for proposing a successful new block, which is designed to cover the operational overhead of running infrastructure. This base block reward is present for any active validator, even those with zero boost.
Validators will have a minimum and maximum BERA stake, with the minimum serving as the base requirement for activation, and participation within the active set dictated by the validators with the largest stake. Emissions scale concavely with validators' BGT boost, in order to promote decentralization - as a validator's boost increases, the marginal benefit of increasing boost further decreases.
The remainder, and the majority, of the block reward instead must go to application reward vaults by default. Reward vaults, which are similar to Curve's gauges [\[5\]](#ref-5), are a key piece of infrastructure that allows applications to leverage PoL, enabling teams to incentivize users' actions in exchange for block rewards. A protocol can have multiple reward vaults, each with its own PoL-eligible asset to be staked. For example, a decentralized digital asset exchange (DEX) could have multiple pools earning block rewards, each with its own reward vault and respective PoL-eligible asset. By staking that PoL-eligible ERC20 LP token in BGT Station, a user may start earning BGT rewards from the validator set, as a reward for their liquidity provision.
Applications with reward vaults are able to incentivize specific actions, through the use of stakeable ERC20 receipt tokens. These incentives can come in the form of the application's native token, or in any arbitrary ERC20, so long as they are governance approved. Applications also set the exchange rate of their incentive asset, allowing them to dictate the terms of their exchange. An exchange rate at or above the current market price of the incentive asset would generate positive ROI on spend relative to standard emissions. An exchange rate below the current market price of the incentive asset would allow an application to trade off efficiency of spend for improved distribution, as liquidity providers will generally prefer a chain asset relative to an application asset. Validators also have the ability to partially or fully fill any offered incentive in the marketplace.
Validators are then largely rewarded through this mechanism, by which they release their block rewards to an application in exchange for an incentive from that project for doing so. Successful validators will be those that are able to:
* Most effectively secure BGT boost, as this increases the value of the block rewards that they win, and
* Most efficiently exchange their BGT block rewards for application incentives, as this dictates their return on the blocks they win.
Finally, validators then take a commission on the incentives they receive in return for the block reward exchange. Taking into account the small commission designed to offset operational cost of validation, this effectively makes validator profitability a function of (1) size of BGT boost, (2) efficiency of exchange, and (3) validator commission rate on incentives.
This model creates meaningful economic alignment between among previously isolated groups, relative to a traditional PoS system, as the validators that are most actively interfacing with users and applications will have a much better chance of securing BGT boost and efficient incentive exchanges.
The amount that validators can earn in application incentives is determined by their BGT boost. Thus, validators that return the maximum value to their BGT boosters are likely to receive the most boost. This is most easily accomplished in the form of minimizing their commission rate and maximizing their distribution of block rewards towards highly utilized pools and pools with large incentives.
As a result of this model, the majority of block rewards in a PoL-enabled chain go toward incentivizing user activity on the chain, whether through providing liquidity, or simply participating in an action that an application values.
## 3. BERA & BGT Dynamics and Market Equilibrium
The BERA and BGT relationship creates interesting dynamics around new issuance that accelerate adoption of the application layer from users. In periods where the value of application incentives exchanged for BGT block rewards is higher than the rate of issuance, economic incentives for BGT boosters will remain high. This increases the opportunity cost of redeeming BGT for BERA, and likely creates more liquidity in the system as users seek to participate in reward vaults to acquire new BGT. In turn, increases in liquidity drive system productivity and allow applications to scale.
In periods where the incentives for BGT boosters grow at a rate below new issuance, or contract, users will likely choose to burn BGT for BERA. As redemption occurs, it also decreases the amount of BGT in circulation, which in turn lowers the number of claimants on the economic incentives generated. This allows the incentive rate for BGT holders to normalize, returning the system to equilibrium and creating new demand for BGT.
An important distinction between Berachain and other loosely comparable systems like Curve or Solidly-style DEX is the source of inflation. Inflation in a DEX is exogenous to the operation of the application itself. Emissions in PoL are endogenous to the chain itself; a PoS chain needs some level of inflation to operate. The goal of PoL is to minimize that level of inflation unless the validator is successful in contributing to the network.
Additionally, Berachain doesn't introduce new inflation to the system. The entirety of the BGT issued loosely follows a PoS emission schedule (with some variation based on the boost distribution). As a result, even if the entirety of BGT emitted were to burn to BERA, the system would effectively just end up back at PoS, with extra steps.
## 4. Extensions of PoL
The following are a few practical examples of how PoL works.
### 4.1 AMM Decentralized Exchange
Take, for instance, a theoretical enshrined DEX on Berachain. This DEX would have a permissionsless ability to spin up new Liquidity Pools (LPs), which could then go through governance to enable native-chain rewards on the user-generated pools with BGT incentives. This would allow a new application to create a liquidity pool, enabling onboarding and usage of the application's token. This project could then go through governance to instantiate a PoL-powered reward vault on top of the LP, incentivizing users to supply liquidity in exchange for their native asset, or any other governance-approved ERC20 for the vault.
Applications would then incentivize validators in one of the reward vault accepting ERC20 tokens, which, if filled, would result in BGT emissions on the Liquidity Pool in BeraSwap. Users interested in receiving BGT would then supply liquidity on this exchange, and receive a pro-rata rate of BGT emissions based on their share of the pool.
This helps applications solve cold start problems, or improve efficiency of spend, depending on where they are in their life cycle. A new application might choose to exchange at a rate below the fair market value of their incentive asset, as liquidity providers might prefer to earn BGT over a newer application asset. This would help increase the visibility of the liquidity in the new pair, potentially onboard more liquidity, and allow teams to focus on product cycles quickly. A well-established blue chip application might choose to incentivize at an exchange rate above the fair market value, as validators might be willing to accept slightly worse returns for higher-quality and/or more liquid assets. This allows applications valued by the network to scale effectively, improving distribution without having to increase spend, all while bringing more liquidity to Berachain and the PoL network.
### 4.2 Real World Assets
PoL isn't solely limited to DeFi use-cases. Any arbitrary action with an ERC20 receipt token can be effectively incentivized at the chain level. Take, for example, an asset issuer that takes offchain assets such as T-Bills or real estate and issues tokenized receipts for them on Berachain. This opens up the opportunity for native yield farming, fractionalized ownership of otherwise hard-to-acquire assets. The asset issuer could utilize PoL in a number of ways.
For example, the asset issuer could utilize a reward vault to decentralize and scale their off-chain asset's liquidity profile. If a primary bottleneck for scaling the real-world asset (RWA) platform is finding quality teams to originate, generating a receipt token for issuers with a reward vault would allow the protocol to reward those teams mechanically at the chain level, increasing the value proposition for high-quality originators to onboard and tokenize their assets.
If the primary bottleneck for the platform is integration and distribution of the RWA, they could issue receipt tokens for holding or utilization of the RWA. This would allow them to more effectively create secondary market liquidity for the RWA, increasing surface area for integrations and use-cases. Alternatively, simply incentivizing holders is also feasible, and can even be done with a portion of the tokenization platform's margin on yield of the asset. Given that these margins are generally in high-quality assets, like fiat, they would likely get positive ROI on validator incentives for the asset, meaning it's actually more efficient than simply passing through the full yield to users while also allowing them to maintain a margin on issuance.
### 4.3 L2s
While the network itself acts as a general purpose solution for a wide variety of smart contract applications, there may be developers looking to leverage Berachain and PoL in their own isolated setting. On the Ethereum network, these are commonly referred to as L2s. While Berachain does not necessarily need the side-chain ecosystem to achieve scale, L2s can be useful for many different applications including:
* KYC enforced applications revolving around identity, traditional finance, and data
* Privacy-first applications that need to encrypt user data to achieve their use-case
* High throughput environments using novel scaling tools to allow for web2-like user experiences.
Berachain offers an advantage over other networks for these types of applications through PoL, where as a developer, they can solve the common cold start bootstrapping problem by immediately tapping into the liquidity and security that Berachain offers. Indeed, L2s inherit the economic security of Berachain, based on the aggregate BERA value staked by validators.
For example, a L2 network might launch on Berachain that enables native rewards for its users by taking advantage of the yield opportunities on Berachain mainnet. Users on Berachain can bridge to the L2, receive a synthetic token representing their deposit in the bridge, and have that token be yielding by default due to the L2's opt-in model to stake the underlying bridge deposits into PoL vaults. Alternatively, these bridge-generated rewards could also be utilized to incentivize a reward vault on Berachain mainnet for the application's native asset, improving ease of onboarding new users and use-cases for the section of the network with the highest density of applications.
Alternatively, developers could build out a side-chain network to enable one specific use-case, but whitelist relevant assets and/or pools on Berachain Mainnet to receive BGT emissions. This would allow the network to not have to launch their own incentive token to get initial deposits, as they're able to solve cold start problems via the native bridge rewards. This solution is especially interesting, as it would allow for more accessible assets like USDC, wBERA, or wBTC to be utilized as the primary asset on the network. This allows users to onboard with assets they likely already have in their wallet, while also generating meaningful native rewards through the bridge contract as those assets also have a plethora of yield sources available on Berachain.
## 5. Staking/Boost Logic & Implications
Across PoL, the different parameters and incentives a user, application, or validator is optimizing for varies considerably. Users, like with any other PoS system, are often optimizing for the highest yield and highest trust rate when staking with a validator. Because of the ability to control inflation with validators, the way staking works must be modified at the beacon chain level with the validators themselves.
Validators are searching for an industry-standard base bond of the native gas token to get their operation going, as with any other chain. In order to attract BGT boost to increase their weight over the staking pool, they will have to offer the market-rate or lower commission rates, offer the best uptimes, and create the strongest voting strategy in the market to beat out competition. Some things the validator optimizes for are:
* Dollar sum of incentives available to claim
* Liquidity of those incentives once claimed
* Closing arbitrage rates between the average market return per vote and their average market return per vote
In order to play this second side of PoL staking logic, the validators are not rent-seeking BERA (the token they bonded) but rather BGT, the governance token of the network. The more BGT the validator controls, the more BGT inflation they will be able to dictate, which means a higher chance of attracting boost.
## 6. BERA/BGT Math
The PoL model defines the rules of block production and emissions on Berachain. Its main objectives are promoting chain security and decentralization while capping inflation.
### 6.1 Block Production
The active validator set is a set of *N* validators who are able to produce blocks. Only the top *N* validators ordered by the number of BERA staked by each validator stay within the active set. The probability for a validator within the active set to be chosen to propose a block is proportional to its staked BERA. There is both a floor and a cap to a validator's stake.
### 6.2 Emissions
Every time a validator is chosen to propose a block, they emit a quantity of BGT. Emissions have two components:
* *Base emission:* this goes to the validator proposing the block. This is a fixed amount equal to the base rate parameter *B*.
* *Reward emission:* this is emitted towards vaults selected by the validator in their reward allocation, proportional to their weights. This is a variable amount that depends on the validator's boost *x*.
This is the emission formula that represents how many BGT are created each block as a function of a validator's boost *x*, which is a number in the range \[0,1] that indicates the percentage of total BGT directed to that validator out of the total number of BGT directed to validators.
#### Parameters
* *B (base rate):* this is the BGT amount a validator gets when producing a block.
* *R (reward rate):* this is the BGT amount a validator issues towards reward vaults before applying the boost multiplier.
* *a (boost multiplier):* this determines the impact of boost on the emissions towards reward vaults. High boost multiplier = boost is very important.
* *b (convexity parameter):* this determines how quickly boost impacts emissions towards reward vaults. High convexity = validators with low boost are penalized.
* *m (minimum reward):* this is the floor to emissions towards reward vaults. High minimum reward = more emissions for validators with low boost.
Figure 1: Sample parameters *B* = 0.5, *R* = 1.5, *a* = 3.5, *b* = 0.4, *m* = 0
### 6.3 Inflation
In the proposed model, emissions grow with the amount of boost *x* a validator has, up to a cap. The theoretical cap of BGT emitted in a block happens if a validator has 100% of the boost and is equal to:
See Appendix A for the proof. The amount of BERA staked does not influence how many BGT are created every block, but just the frequency of a validator being chosen to propose a block.
## 7. Incentives Marketplace
In the proposed model, emissions grow with the amount of boost x a validator has, up to a cap. The theoretical cap of BGT emitted in a block happens if a validator has 100% of the boost and is equal to:
### 7.1 Introduction
Berachain introduces an incentives marketplace where protocols can bid for validators' emissions using any whitelisted token. Validators can choose to direct emissions towards the highest bidding protocol (or any arbitrary one) by including the protocol's reward vault into its own reward allocation. In order for a validator to select a reward vault, the vault needs to be whitelisted. Part or all incentives validators receive from protocols can be redistributed to BGT holders who boost that specific validator. Incentives distribution is handled off-chain.
### 7.2 Whitelisting Vaults
To whitelist a vault, a protocol/user must:
* Base emission: this goes to the validator proposing the block. This is a fixed amount equal to the base rate parameter B.
* Specify which token is accepted as the "staking token" for that vault. Only one vault can exist per staking token and cannot be changed.
* Create a governance proposal to whitelist the vault. Once the proposal is accepted, the vault gets whitelisted and becomes eligible to receive emissions.
### 7.3 Whitelisting Incentive Token
To whitelist an incentive token, a protocol/user must create a governance proposal including:
* The token to be whitelisted (address).
* The "minIncentiveRate".
* A manager for that specific incentive token.
Each vault has its own whitelisted tokens, which can be removed from the whitelist through governance proposals. Moreover, it is possible to update the manager for a specific token via a governance proposal.
### 7.4 Marketplace Functionality
A vault's incentives manager is allowed to specify an incentive rate p and add an amount of tokens to sustain that rate for a certain period. For example, he can specify he's willing to pay 10 protocol tokens PT in exchange for 1 BGT (p = 10), then he adds 1000 PT tokens to the vault. The incentive manager can deposit multiple incentive tokens whitelisted by governance (vault-wide approval). Every time a validator emits an amount x of BGT towards the protocol's vault, a corresponding p · x amount of PT tokens will be transferred to the validator's operator. In the example above, if a validator emits 1 BGT towards the vault, he will receive 10 PT tokens in exchange. The manager can later bring additional tokens to continue paying the specified rate p or he can change the rate upon the following conditions:
* If there is no remaining amount of incentive tokens, the rate can be updated to any value greater or equal to a minimum rate decided at incentive token whitelisting.
* If there is a residual amount of incentive tokens, only a higher incentive rate p' > p can be set if enough incentive liquidity is provided along with the setter to support the new rate. It is not possible to reduce the incentive rate while residual tokens are still left in the vault.
Validators are expected to distribute a portion of tokens from these incentives towards its boosters; rewarding them for their contributions to his BGT weight, and completing the alignment between validators, protocols, and users.
## 8. Impacts on Decentralization & Risks
L1 blockchains usually face centralization risks due to economic pressures. As Vitalik Buterin highlights in a blog post [\[6\]](#ref-6), this is due to economies-of-scale in participating in core proof-of-stake mechanisms, which naturally lead to large stakers dominating, and small stakers dropping out to join large pools [\[7\]](#ref-7). This leads to higher risk of 51% attacks, transaction censorship, and other crises. In addition to the centralization risk, there are also risks of value extraction: a small group capturing value that would otherwise go to L1 users.
The PoL model is a variation of the PoS model and it therefore shares many of such risks. Moreover, PoL introduces many novelties such as different governance and gas tokens, emissions towards reward vaults and an incentives marketplace. These mechanisms can impact existing L1 risks as well as introduce new ones. PoL presents the first opportunity for validators on an L1 to express a variety of economic opinions at the network level, beyond their chosen commission rates. Each validator on Berachain has the opportunity to have its own distinct distribution of block rewards and incentives to applications on the chain, and its boosters, incentivizing increased stake decentralization as users and protocols choose their validator based on risk profiles and expected returns, both of which are downstream of their reward distribution choices. However, even in this system, PoL could lead to the emergence of economic risks for the blockchain. We address 2 key risks in PoL: (1) The end of PoL, (2) Centralization. There are also other risks such as Inefficient Liquidity, Low BERA staked, Parameters Changes, Low BGT Governance attacks.
### 8.1 The End of PoL
As long as protocols actively compete for emissions, PoL should work as intended; however, in certain conditions it is possible that different actors stop playing the PoL game.
For example, validators could collude and set commissions on incentives to 100%, effectively making the BGT boosters APY zero. This would make many BGT boosters burn to BERA and would reduce protocols competition for BGT emissions, possibly causing the end of proof of liquidity and a migration to Proof of Stake. This scenario demonstrates a possible way for BERA stakers to 'censor' BGT holders and force them to burn to BERA. However, there is a strong incentive for a new validator to come in, set lower commissions and quickly take all the boost. The PoL model has been specifically calibrated to penalize validators with low boost, effectively invalidating all emissions towards pools. This should incentivize validators to actively fight for BGT boost by distributing a significant amount of incentives back to boosters.
Another scenario is one where a strong demand to short BERA for hedging or a large anticipated airdrop causes a big jump in BERA lending rates. This would make validators unstake BERA and lend it, causing a drop in BERA staked. Also BGT boosters at some point might burn BGT for BERA in order to lend it at such high rates. As more BGT is burnt for BERA, remaining BGT holders will receive more incentives for each BGT used to boost. In addition, more BERA lent into the market would most likely lead to interest rate normalization over time, so the system would likely reach a new equilibrium where PoL goes back to working as expected.
### 8.2 Centralization
Similar to Lido on Ethereum [\[8\]](#ref-8), we could see the emergence of a large Liquid Staking provider on Berachain who may take control of a large percentage of the network by leveraging economies of scale [\[9\]](#ref-9). A LST protocol could create a xBERA/BERA pool and direct 100% of emissions towards it, enabling xBERA holders to receive emissions in addition to the staking rewards. As this LST protocol gains more and more BERA staked, incentives distributed to its BGT boosters keep growing, therefore attracting more boost. This process does not grow indefinitely since at some point, the LST protocol would hit the BERA staked cap and would need to spin up a new node, starting with zero BERA staked and boost, limiting the economies of scale. Even in the event of a large boost concentration within one or few validators, the boostMultiplier parameter determines the emissions cap towards protocols, further reducing economies of scale and keeping inflation under control even in high concentration scenarios. Moreover, using a concave function to determine the impact of BGT boost on emissions makes sure the marginal benefit of increasing boost decreases with each additional BGT boosted.
### 8.3 Other Risks
Other economic risks not exclusive to PoL may arise from insufficient staked value to secure the chain's Total Value Locked (TVL), PoL parameters changes enacted by the blockchain's governance and other governance attacks. The impact of these risks should be similar to other L1 blockchains.
## 9. PoL Overview
Figure 2: High level overview of the PoL life cycle
PoL is an extension of the PoS system, and replaces the whole incentive mechanism. Therefore, there needs to be a link from the consensus layer to the execution layer where the rewards are being minted and distributed. The Prover is how Berachain achieves this, using the EIP-4788 specification [\[10\]](#ref-10). The execution layer has access to the Beacon block roots that the consensus layer posts to the execution layer each block. Since the block has access to the public keys of the validator and at which block that they proposed, the PoL system can credit them for being able to mine BGT rewards and distribute them.
The life cycle above showcases the runtime of each block that block rewards are minted, the steps:
1. Prove that the validator in question proposed a block using the beacon block root.
2. The block rewards controller controls the inflation per block and matches with the BGT boost how much BGT to mint for the current validator.
3. The validators have a choice of the set of reward vaults and their weighting.
4. The weight-reward is then forwarded to the vault in question.
5. Validators are now compensated with incentives that the ecosystem can set on reward vaults.
## 10. Governance Overview
Berachain governance is controlled by BGT. From genesis, the chain has on-chain token governance. The full scope of this governance are:
* The token to be whitelisted (address).
* Parameters on the PoL smart contracts.
* Full governance over the application that makes Berachain.
Figure 3: Proposal lifecycle
The proposal lifecycle is shown as above, the main stages are the governance state where proposals are proposed and voted on and the timelock period. This ensures that there is time for users to view, vote, and censor malicious governance proposals/attacks.
Figure 4: Governance flow
The diagram above walks through the safety mechanisms and processes that the governance system enforces. Guardians are an important mechanism to protect against governance attacks that could arise at the dApp level; these are chosen and implemented by overall chain governance. The main roles therefore are: **Holders**, **Proposers**, **Delegates**, and **Guardians**. The Governor is responsible for gating transactions to delegates/holders, and the timelock executor leaves time for the chain to censor transactions that are attacks on the target contract.
## 11. BeaconKit Overview
BeaconKit is a modular framework designed to streamline the development of Ethereum Virtual Machine (EVM) consensus clients. It enables developers to launch both L1 and L2 EVM-identical blockchains with full Ethereum Improvement Proposal (EIP) compatibility, single-slot finality, and enhanced performance. By utilizing the EngineAPI [\[11\]](#ref-11) to facilitate communication between the consensus and execution layers, BeaconKit decouples the EVM execution environment from consensus mechanisms like CometBFT [\[12\]](#ref-12), allowing for greater flexibility and modularity in blockchain design. This approach mirrors Ethereum's own consensus layer, providing a familiar environment for developers. BeaconKit supports integration with standard, unmodified execution clients, achieving 100% EVM identicality with the Ethereum mainnet. It has been tested with clients such as Geth, Erigon, Nethermind, Besu, Reth, and EthereumJS, ensuring broad compatibility and leveraging the robust tooling and community support these clients offer.
## 12. BeaconKit for EVM Identicality
One of the critical advantages of BeaconKit is its ability to deliver EVM identicality rather than mere compatibility. By allowing operators to run standard execution clients without modifications, developers can utilize the mature ecosystem of tools, libraries, and testing frameworks available for Ethereum. This eliminates the need for maintaining custom forks of execution clients, which can become unsustainable due to rapid updates and the complexity of supporting multiple programming languages. BeaconKit leverages a custom BeaconBlock on top of the standard CometBFT block to support immediate execution and optimistic payload building. Validators can sign over the proposed state root before accepting a block, significantly speeding up block verification and reducing block times by up to 40%. Immediate execution also simplifies the integration of EIP-4788, enabling permissionless verification and proof of consensus layer data on the execution layer.
## 13. Implications of BeaconKit
The introduction of BeaconKit has several significant implications for the blockchain landscape:
1. **Improved Developer Experience:** Achieving EVM identicality allows developers to leverage existing Ethereum tools without modification, reducing the learning curve and accelerating development.
2. **Enhanced Performance:** Immediate execution and optimistic payload building reduce block times and improve transaction throughput, addressing scalability challenges faced by previous implementations like Polaris.
3. **Modularity and Flexibility:** BeaconKit's design eliminates reliance on standard Cosmos modules and Protobuf encoding, allowing developers to inject custom logic and implement custom block validity rules. This opens possibilities for innovative blockchain designs tailored to specific use cases.
4. **Support for Advanced Features:** Inclusion of EIPs like EIP-4844 enables better support for rollups and L2 solutions, facilitating the creation of scalable and efficient blockchain networks.
5. **Sustainability:** By decoupling from specific execution clients and eliminating the need for maintaining forks, BeaconKit offers a sustainable path forward for blockchain projects, reducing engineering overhead and improving client diversity [\[13\]](#ref-13).
Overall, BeaconKit represents a significant advancement in blockchain development, providing the tools necessary to build high-performance, EVM-identical blockchains with greater ease and flexibility. Its introduction heralds a new era where developers can focus on innovation without being hindered by underlying technical complexities or the maintenance of cumbersome execution client forks.
## 14. Conclusion
1. Berachain is a novel EVM-identical L1 blockchain built using BeaconKit and powered by Proof of Liquidity, aligning liquidity and security at the network level.
2. Berachain is secured by BERA, the gas and staking token of the network, with rewards and governance administered through BGT, the non-transferrable governance token of the network, obtained via providing liquidity or staking PoL-eligible receipt tokens in reward vaults.
3. Users who provide liquidity or engage with applications on Berachain can participate in the network's Proof of Liquidity system by earning and delegating BGT to validators.
4. Any variety of applications and scaling solutions may make use of Proof of Liquidity to increase their capital efficiency and distribution while contributing towards network security.
5. Berachain aims to ultimately align incentives between validators, dApps, and users interacting in a network to serve as an accelerant for the application layer, and unlock the next generation of 0 to 1 blockchain primitives.
## References
* \[1] Dinesh Kumar, Duraimutharasan, Shanthi, Vennila, Prabu Shankar and
Senthil. *Comparative Analysis of Transaction Speed and Throughput in Blockchain and Hashgraph: A
Performance Study for Distributed Ledger Technologies.* Journal of Machine and Computing, 3(4),
2023\.
[https://pdfs.semanticscholar.org/2e97/1b66175ee27ed558fdcfafb3c645ddd3d68d.pdf](https://pdfs.semanticscholar.org/2e97/1b66175ee27ed558fdcfafb3c645ddd3d68d.pdf)
* \[2]
[https://blog.berachain.com/blog/the-pol-post](https://blog.berachain.com/blog/the-pol-post)
* \[3]
[https://blog.berachain.com/blog/the-fat-bera-thesis](https://blog.berachain.com/blog/the-fat-bera-thesis)
* \[4] Urban J. Jermann. *A Macro Finance Model for Proof-of-Stake
Ethereum.* National Bureau of Economic Research (NBER), 2023.
[https://papers.ssrn.com/sol3/papers.cfm?abstract\_id=4335835](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4335835)
* \[5]
[https://docs.curve.fi/assets/pdf/CurveDAO.pdf](https://docs.curve.fi/assets/pdf/CurveDAO.pdf)
* \[6] Vitalik Buterin. *Possible futures of the Ethereum protocol, part
3: The Scourge.* 2024.
[https://vitalik.eth.limo/general/2024/10/20/futures3.html](https://vitalik.eth.limo/general/2024/10/20/futures3.html)
* \[7] Li Li. *Mitigating Challenges in Ethereum's Proof-of-Stake
Consensus: Evaluating the Impact of EigenLayer and Lido.* 2024
[https://arxiv.org/abs/2410.23422](https://arxiv.org/abs/2410.23422)
* \[8]
[https://research.lido.fi/t/is-lido-good-for-ethereum/5520](https://research.lido.fi/t/is-lido-good-for-ethereum/5520)
* \[9]
[https://notes.ethereum.org/@djrtwo/risks-of-lsd](https://notes.ethereum.org/@djrtwo/risks-of-lsd)
[https://www.mdpi.com/1099-4300/25/9/1320](https://www.mdpi.com/1099-4300/25/9/1320)
* \[10] Alex Stokes, Ansgar Dietrichs, Danny Ryan, Martin Holst Swende,
light-client. *EIP-4788: Beacon block root in the EVM.* 2022
[https://eips.ethereum.org/EIPS/eip-4788](https://eips.ethereum.org/EIPS/eip-4788)
* \[11]
[https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md](https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md)
* \[12]
[https://docs.cometbft.com/v0.37/spec/abci/](https://docs.cometbft.com/v0.37/spec/abci/)
* \[13]
[https://ethresear.ch/t/considering-client-diversity-through-the-lens-of-network-performance/18885](https://ethresear.ch/t/considering-client-diversity-through-the-lens-of-network-performance/18885)
## Appendix
The block emission formula is given by the following equation:
Where:
* **x**: validator boost
* **a**: boost multiplier
* **b**: convexity parameter
* **B**: base rate
* **R**: reward rate
* **m**: minimum boosted reward rate
It is straightforward to show that the theoretical maximum is given by the following expression:
### Proof
Let us first introduce the notation:
* **V**: validator set
* **vᵢ ∈ V**: i-th validator, with i ∈ \{0, 1, ..., |V|}
* **xᵢ ∈ \[0,1]**: boost of the i-th validator s.t. ∑ᵢ xᵢ = 1
* **pᵢ ∈ \[0,1]**: proposal probability for the i-th validator s.t. ∑ᵢ pᵢ = 1
* **eᵢ**: BGT emission per block for the i-th validator
Let e be the number of BGTs emitted for the next block proposal, then we have:
# Reward Vault Requirements and Guidelines
Source: https://docs.berachain.com/general/help/reward-vault-guidelines
Ecosystem value, technical requirements, and governance rules for Reward Vault whitelisting and WBERA emissions.
Reward Vaults drive meaningful economic activity on Berachain. To qualify for validator-allocated **WBERA emissions**, your vault must show that it supports ecosystem goals and creates real user value.
This guide captures all essential requirements. For submission forms and the request process, see [Reward Vault Governance](/general/governance/reward-vault-governance).
## Ecosystem value requirements
### Adds ecosystem utility
* The vault must boost use or value of BERA or other major assets.
* For example: enhancing yield across "Major" assets (as defined below) to encourage looping activity, or utilization of yield-bearing collateral across more exotic trading pairs, or other revenue-generating use cases.
* This can happen through:
* Liquidity pools (e.g., paired with BERA or majors)
* Vault structures that carry out more complex multi-step operations
* Proposals should clearly explain how the Reward Vault will help scale your existing product. How does this vault become more than a simple farm?
* The best barometer for ecosystem utility is often correlation to some tangible, empirical metric of interest. PoL does not work if it solely subsidizes idle capital. PoL works when it encourages economic activity, which often shows up as application revenue, volume, fee generation, or user growth.
* The above guidelines require some nuance. There is value in experiments, whether in memecoins, token launchers, RWAs, consumer applications, and more. For established DeFi applications or adjacent vault products, there should be a path toward sustainable economic activity and growth.
### Reasonable emission fees
* Fees taken on yield **must not be excessive** and must not be used mainly to recycle incentives.
### Yield stacking restrictions
* If a vault (e.g., ERC-4626) generates yield from PoL reward farming, the majority of that yield must be passed through to stakers, not recycled.
* Yield from non-PoL sources can be recycled into incentives.
* This prevents circular reward extraction without adding utility.
## Technical requirements
All Reward Vaults must meet these expectations:
### Open access
* The staking token must be **accessible to everyone**, with **no special permissions or barriers.**
* Special exceptions may apply for certain real-world asset (RWA) protocols that require permissioned or compliance-specific applications.
### Standard ERC20 token
* The staking token must be a **standard ERC20 token**.
### Rebasing tokens
* Rebasing staking tokens **must use a wrapper**.
* This does **not apply** if the rebasing rate is **0%** (e.g., OHM).
### Verified contract
* The token's smart contract must be **verified on-chain** and publicly viewable on tools such as **Berascan**.
### Audits
* The protocol must have **at least one audit** and **no recent exploits**.
* If an exploit occurred, the proposer must provide **clear evidence that the issue has been resolved.**
### Staking and incentive token liquidity requirements
The tokens must have sufficient liquidity to support sustainable participation:
* The token should have at least **\$100,000 in Total Value Locked (TVL).**
* If the token represents a **decentralized exchange (DEX) liquidity pool**, it must meet the following criteria:
* At least **\$50,000 in TVL paired with a "Major" asset**, defined as:
* BERA, HONEY, BYUSD, USDC, wETH, wBTC, stablecoins
* Or other widely recognized, large-cap tokens (e.g., SOL, or comparable assets reasonably within the top 10 by market capitalization; only established tokens are eligible—purely speculative or memecoin assets do not qualify)
* An asset that is a **wrapper of a major asset** listed above
* An asset that is **minted using a major asset as collateral**
* The token must be **deployed and verified on-chain.**
Why focus on majors?
* Boosts fee generation: major-token pairs attract higher trading volumes, increasing fees for liquidity providers and making Berachain a home for desirable assets.
* Reduces volatility risk: major tokens are more stable, lowering impermanent loss for liquidity providers.
* Democratized access: requiring a major token in every liquidity pair lowers barriers to entry so new users can participate without acquiring obscure or illiquid tokens.
Why enforce a minimum TVL requirement?
* Serves as a filter for teams that might look to extract from the system, and demonstrates some degree of capital at risk and ideally multiple participants from the community, indicating broader interest and trading potential for the pair.
### Contracts must be live
* All contracts being incentivized must be **already deployed and fully live on-chain.**
* No rewards for theoretical, unlaunched, or coming-soon contracts.
### Protocol must be live
* The protocol itself must be **live on-chain and actively operating** before applying for a Reward Vault.
### Gas efficiency
* Transfers must **not exceed 500,000 gas units per transaction** to keep fees reasonable.
## Minimum program viability
### Sufficient duration
* Incentives must **run at least 2 months** to sustain engagement.
### Sustainable incentives
* Must target **\$10,000+ per month in incentives.**
* Incentive levels must be **reasonably priced** relative to market conditions. Resources such as [Furthermore](https://furthermore.app/) are recommended for understanding current market incentive rates.
## Governance and compliance
### Proposal requirements
* **Submission:** Complete the relevant form and post on the [BeraHub governance forum](https://hub.forum.berachain.com) for community awareness and discussion. See [Reward Vault Governance](/general/governance/reward-vault-governance) for the current BEX Pool and General RFRV forms and submission steps.
### Three-week maximum rollover policy
* All unapproved applications will **automatically roll over and remain in the review pipeline for up to three weeks.**
* **After the three-week period**, your team must **comment under your forum post or submit a new application** if you want the proposal reactivated and put up for consideration again.
### PoL use clarity
* When applying, be specific about exactly how PoL emissions and incentive flows will be used.
### Deployment
* Reward Vaults must be **deployed at time of submission.**
* Any changes require a **new proposal and vote.**
To protect the system from value-extractive incentives, systemic risk, and abuse, the following measures may be exercised at any time:
#### Time-limited whitelisting
* All whitelisted vaults are subject to periodic reevaluation and must continue to meet eligibility criteria to maintain active status.
#### Emergency veto
* In the event of security vulnerabilities, critical protocol risks, or evidence of abuse, an emergency veto may be activated to suspend or revoke Reward Vault emissions immediately.
#### Post-approval disqualification
* Any vault that no longer meets the required standards may be disqualified or have emissions removed, even after prior approval.
Teams at risk of being delisted will be directly contacted.
## Submission deadlines
Proposals for each new batch must be submitted by **Wednesday 11:59 PM EST every week.** Proposals received after this deadline will be reviewed in the next batch.
## References
* [Governance Phase One Details](https://hub.forum.berachain.com/t/governance-phase-one-is-here/30)
* [Reward Vault Governance](/general/governance/reward-vault-governance) (forms and request process)
# Connect to Berachain
Source: https://docs.berachain.com/general/introduction/connect-to-berachain
Add Berachain to your wallet and switch networks.
Blockchain wallets let you access your assets on Berachain. They create and store your private keys so you can prove ownership when you trade tokens, buy NFTs, play games, and more.
## Berachain Mainnet RPC
Use these values in any wallet that supports custom RPCs. Add the network in one click, or copy from the table:
| Key | Value |
| ------------------ | ---------------------------- |
| Network | `Berachain` |
| RPC URL | `https://rpc.berachain.com/` |
| Chain ID | `80094` |
| Currency symbol | `BERA` |
| Block explorer URL | `https://berascan.com/` |
## Berachain Bepolia Testnet RPC
Use these values for the testnet. Add in one click, or copy from the table:
| Key | Value |
| ------------------ | ------------------------------------ |
| Network | `Berachain Bepolia` |
| RPC URL | `https://bepolia.rpc.berachain.com/` |
| Chain ID | `80069` |
| Currency symbol | `BERA` |
| Block explorer URL | `https://testnet.berascan.com/` |
### Bepolia testnet dApps
| App | URL |
| -------- | -------------------------------------------------------------------------- |
| Hub | [https://bepolia.hub.berachain.com](https://bepolia.hub.berachain.com) |
| BEX | Not publicly available on Bepolia |
| Berascan | [https://testnet.berascan.com](https://testnet.berascan.com) |
| Honey | [https://bepolia.honey.berachain.com](https://bepolia.honey.berachain.com) |
## Supported Wallets
Any [EVM-based wallet](https://ethereum.org/en/wallets/find-wallet/) that supports custom RPCs works with Berachain. Examples:
* [MetaMask](https://metamask.io)
* [Rabby](https://rabby.io)
* [Coinbase Wallet](https://www.coinbase.com/wallet)
* [Brave Wallet](https://brave.com/wallet/)
* [Frame](https://frame.sh)
* [Gem Wallet](https://gemwallet.com/)
* [Zerion](https://zerion.io/download?utm_source=berachain)
## Set Up MetaMask With Berachain
Chrome is recommended.
On [metamask.io](https://metamask.io/), download the browser extension. Confirm it is from `metamask.io`.
Click `Create a new wallet`. Set a password, then follow the steps to back up your recovery phrase. The phrase proves you own the wallet—store it safely.
## Add Berachain Network Manually
If you prefer not to use the one-click buttons above, add the network in MetaMask manually:
1. **Open the network list** — Click the drop-down in the top-left of MetaMask.
2. **Add network** — Click `Add network` in the modal.
3. **Add network manually** — At the bottom of the list, click `Add network manually`.
4. **Enter details** — Use the values from the [Mainnet](#berachain-mainnet-rpc) or [Bepolia Testnet](#berachain-bepolia-testnet-rpc) RPC tables above.
5. **Save** — Click `Save`.
You should now be connected to Berachain or Berachain Bepolia.
# How to Get $BERA
Source: https://docs.berachain.com/general/introduction/how-to-get-bera
Bridge, buy, or earn $BERA for gas and staking.
\$BERA is the network token used to pay for transactions on Berachain. This article describes several ways to obtain \$BERA to participate in the network.
Learn more about the [BERA Token](/general/tokens/bera).
## Bridging
Bridging services enable you to transfer tokens from one blockchain to another. The canonical bridge to Berachain is the [Berachain Bridge](https://bridge.berachain.com/), powered by LayerZero. Several source chains support bridging to Berachain.
When using the bridge, you have the option to exchange tokens for a small amount of \$BERA at their destination.
## Exchanges
Several centralized exchanges have listed \$BERA. You can trade other assets for \$BERA on these platforms and then bridge them to Berachain.
* [Binance](https://www.binance.com/en/trade/BERA_USDT?type=spot)
* [Bitget](https://www.bitget.com/spot/BERAUSDT)
* [Bithumb BERA/KRW](https://www.bithumb.com/trade/order/BERA_KRW)
* [Bybit](https://www.bybit.com/en-US/trade/spot/BERA/USDT)
* [Coinbase](https://exchange.coinbase.com/trade/BERA-USD)
* [Gate](https://www.gate.com/trade/BERA_USDT)
* [HTX](https://www.htx.com/trade/bera_usdt/)
* [Kraken BERA/USD](https://pro.kraken.com/app/trade/bera-usd)
* [Kraken BERA/EUR](https://pro.kraken.com/app/trade/bera-eur)
* [KuCoin](https://www.kucoin.com/trade/BERA-USDT)
* [MEXC](https://www.mexc.com/exchange/BERA_USDT)
* [NDAX BERA/CAD](https://ndax.io/markets/beracad)
* [OKX](https://www.okx.com/trade-spot/bera-usdt) and [OKJ](https://www.okcoin.jp/)
* [BingX](https://bingx.com/en-us/spot/BERAUSDT?ref=KUGDGF)
A complete list of CEX and DEX markets where \$BERA trades [is on Coinmarketcap](https://coinmarketcap.com/currencies/berachain/).
# What is Berachain?
Source: https://docs.berachain.com/general/introduction/what-is-berachain
EVM-identical L1, Proof-of-Liquidity consensus, and BeaconKit.
Berachain is an EVM-identical Layer 1 built around Proof of Liquidity, an incentive system designed to align the chain, applications, validators, and users around productive on-chain growth. Instead of treating emissions as a one-way cost to secure the network or subsidise activity, Berachain routes incentives toward liquidity, applications, and businesses that generate value back into the ecosystem.
Under the hood, Berachain remains fully compatible with Ethereum tooling and upgrades, while BeaconKit provides the modular consensus framework that powers the network.
## EVM Identical
Berachain's execution environment is identical to the Ethereum Virtual Machine as used on Ethereum Mainnet. Developers can deploy existing Solidity contracts, use familiar Ethereum tooling, and interact with the chain through standard EVM infrastructure. Berachain uses Bera-Reth, a lightly modified fork of Reth, to execute smart contracts while preserving compatibility with the EVM developer stack. This includes compatibility with all RPC namespaces and endpoints, and any improvements made to execution clients can be applied immediately to Berachain.
## Proof-of-Liquidity
Most chains spend emissions like a faucet: tokens flow out to secure the network, subsidise activity, and attract applications, but little value flows back.
Berachain flips this model with Proof of Liquidity. Instead of treating emissions as a pure cost, Berachain turns them into growth capital for businesses building on the chain.
These emissions help teams bootstrap liquidity, grow usage, and generate value back into the ecosystem. The loop is simple: emissions to businesses → businesses grow and earn more → revenue is shared with the chain → stronger \$BERA → more businesses funded → repeat.
The objective is to make every emitted token work harder by supporting productive businesses, deepening liquidity, and strengthening the Berachain economy over time.
Native applications such as BEX, Bend, and HONEY demonstrate how Proof of Liquidity can support core DeFi markets and create the foundation for broader business growth on Berachain.
Read more in [What Is Proof-of-Liquidity](/general/introduction/what-is-proof-of-liquidity).
## BeaconKit
BeaconKit is Berachain's modular framework for building EVM consensus clients. It combines EVM execution with CometBFT-based consensus, giving Berachain a flexible foundation for performance, finality, and future network upgrades. While Proof of Liquidity defines Berachain's economic model, BeaconKit provides the consensus architecture that allows the network to operate and evolve.
Read more in [What Is BeaconKit](/nodes/beaconkit/overview).
# What is Proof of Liquidity?
Source: https://docs.berachain.com/general/introduction/what-is-proof-of-liquidity
Berachain’s economic coordination system for turning network emissions into productive liquidity, application growth, and ecosystem value.
Proof of Liquidity (PoL) is Berachain’s answer to the emissions problem. Most chains spend emissions like a faucet: tokens flow out to secure the network, subsidise activity, and attract applications, but little value flows back.
Berachain flips this model with Proof of Liquidity. Instead of treating emissions as a pure cost, PoL turns them into growth capital for applications building on the chain. Emissions are directed toward useful liquidity, application activity, and businesses that can generate value back into the ecosystem.
The loop is simple: emissions to businesses → businesses grow and earn more → revenue is shared with the chain directly or through the incentive market → \$BERA strengthen → more businesses funded with emissions → repeat.
*The PoL value cycle: emissions fund businesses, businesses return revenue, revenue strengthens BERA, and a stronger BERA economy underwrites the next emissions.*
## How PoL works
PoL starts when validators stake \$BERA to secure the chain and produce blocks. When blocks are produced, the network emits \$WBERA.
A portion of those emissions goes to validator operators. The rest flows through Berachain’s reward allocation system and is directed toward Reward Vaults, where applications connect useful liquidity and activity to network incentives.
Applications can fund incentives to attract emissions. Users can earn rewards by providing liquidity or activity through eligible vaults, or by participating through simpler staking paths such as \$sWBERA.
Incentives earned through the system are converted into \$BERA and accrued into \$sWBERA, creating a cleaner value path for users and keeping rewards aligned with the broader \$BERA economy.
This creates a market for useful activity. Validators compete for stake, applications compete for emissions, and users choose where to supply liquidity or participate in staking.
In addition to market-based allocation, selected businesses can receive [**Dedicated Emission Streams**](/general/proof-of-liquidity/dedicated-emission-stream): predictable emissions they can plan around and use for long-term growth. These businesses use emissions to grow, then return value to Berachain through premium repayments and long-term revenue sharing.
For a comprehensive breakdown of all PoL components, roles, and pathways, continue to [Proof of Liquidity Overview](/general/proof-of-liquidity/overview). If you’re building on Berachain, start your integration journey with [Integration Basics](/build/pol/integration-basics).
# Block Production & Rewards
Source: https://docs.berachain.com/general/proof-of-liquidity/block-rewards
Block production, fixed WBERA reward rate, and reward distribution.
Proof of Liquidity pays Reward Vault emissions in \$WBERA. Validators produce blocks according to \$BERA stake, then allocate a fixed WBERA emission to governance-whitelisted Reward Vaults.
## Validator selection
The network maintains an active set of **69 validators** eligible for block production.
* The top **69 validators** by \$BERA stake enter the active set.
* Block proposal probability is proportional to staked \$BERA.
* Stake range per validator:
* Minimum: 250,000 \$BERA
* Maximum: 10,000,000 \$BERA
## Block reward model
Each block has two reward paths:
1. **Base reward**: a fixed amount paid to the block-producing validator.
2. **Reward Vault emission**: a fixed `rewardRate` emitted in \$WBERA and allocated to Reward Vaults.
## Fixed emission rates
`BlockRewardController` encodes **base** and **reward** emission **constants** in contract bytecode:
* **`baseRate()` — validator:** **0.4 WBERA per block** paid to the block-producing **validator’s operator**.
* **`rewardRate()` — Reward Vault path:** **1.305 WBERA per block** sent to the **distributor** for BeraChef → Reward Vault allocation.
**`getMaxEmissionPerBlock()`** returns **`baseRate() + rewardRate()`** — **1.705 WBERA per block** total from this controller when both legs apply.
For the end-to-end flow from block emission through Reward Vaults to user yield, see the diagram on [Proof of Liquidity Overview](/general/proof-of-liquidity/overview). This page focuses on the rate model and BeraChef allocation mechanics.
## BeraChef reward allocation
BeraChef manages how each validator allocates the Reward Vault emission:
1. Reward allocation weights across whitelisted vaults.
2. Validator incentive commission settings.
3. Vault whitelisting and baseline allocation behavior.
Validators can queue allocation updates and activate them after the delay window.
## Dedicated Emission Stream (DES)
Before validator-specific allocation is applied, governance can carve out a portion of the Reward Vault emission and route it to strategically important vaults. DES guarantees minimum emission without relying on individual validator allocation decisions.
See [Dedicated Emission Stream](/general/proof-of-liquidity/dedicated-emission-stream) for the full business rationale, governance parameters, and per-vault cap mechanics.
## Reward distribution timing
Distributor pushes emissions per block. Reward Vaults then stream claimable rewards across their configured duration window, often three days.
This behaves like a rolling stream: each new block updates the amount entering the unlock schedule.
# What's New
Source: https://docs.berachain.com/general/proof-of-liquidity/changelog
Proof of Liquidity changes over time.
## May 2026 - PoL Next
The May 2026 Proof of Liquidity Next upgrade deeply simplifies Berachain token model.
BGT added friction. Two tokens, boost mechanics, and a governance layer that confused traditional allocators more than it helped.
PoL Next consolidates everything into \$sWBERA. All emissions become BERA. Boost and BGT are phased out. All incentives accrue into \$sWBERA. One token, one yield path, one value sink.
Businesses integrate once. Users don’t need to learn PoL-specific concepts. Value flows through a single, predictable rail.
**LST Staker Vault owners gain new powers from this upgrade.** They now have a direct, pro-rata, on-chain claim on the entire incentive-auction WBERA flow via the collector split. With an approved LSTStakerVault registered with the fee collector, WBERA paid during incentive auctions is automatically split pro-rata to their vault.
**High-growth teams** receive guaranteed, multi-month dedicated emission streams sized to their stage and plan. For a validator operator who is also a business owner or partner in an ERA cohort, this changes the model from “bid every epoch for yield” to “underwrite and finance a company with protocol capital." It is closer to a growth-equity financing round than pure validator operations.
This upgrade deployed to the network approximately 24 hours before the [Fusaka upgrade](/build/protocol/overview#fusaka-upgrade-features).
### BGT is deprecated
BGT no longer has a user-facing role in Proof of Liquidity. It no longer influences validator reward allocation weight, block rewards, or governance. The Hub UI helps users to redeem their BGT.
**Reward emissions are paid in WBERA.** WBERA is the per-block emission token. Stakers see accrued rewards on the vault read in WBERA terms.
**Stakers in reward vaults can claim rewards as \$sWBERA or native BERA.** The default reward token is \$WBERA, but a permissionless helper converts it atomically on claim so stakers receive \$sWBERA or native \$BERA without extra steps.
**Incentives, net of validator commission, are auctioned for BERA and accrue into \$sWBERA.**
**The boost curve is gone.** Per-block emission splits into fixed **`baseRate`** (**0.4 WBERA**) to the validator’s operator and **`rewardRate`** (**1.305 WBERA**) to the distributor for Reward Vaults. Per-block emission no longer scales with boosted BGT.
**Residual BGT on existing vaults is settled automatically.** Any BGT allowance left on a vault from before the upgrade is converted to WBERA on the next claim from that vault, transparently to the staker. No vault-owner action is required, no claim is missed, no balance disappears.
**Staking pools:** adjusted to correspond to the BGT deprecation; see [Staking pools — Post-BGT Deprecation](/nodes/staking-pools/overview#post-bgt-deprecation).
References: [BGT](/general/tokens/bgt), [Reward vaults](/general/proof-of-liquidity/reward-vaults), [BERA and WBERA](/general/tokens/bera), [Partial reward claims](/build/pol/partial-reward-claims), [RewardVaultHelper claim flow](/build/pol/reward-vault-helper-claim-flow), [\$sWBERA](/general/tokens/swbera), [Block rewards](/general/proof-of-liquidity/block-rewards).
### Deployment timeline
**Timeline**: These Proof of Liquidity changes deployed 24 hours before the [Fusaka hardfork](/build/protocol/overview#fusaka-upgrade-features).
| User / Actor | Expected Action |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BGT holders | Redeem BGT directly or migrate through the [Hub](https://hub.berachain.com) UI, following Fusaka activation ([timeline](/build/protocol/overview#fusaka-upgrade-features)) |
| Reward vault stakers | No action needed. Continue using eligible reward vaults. Rewards will shift to BERA and be claimable as either \$BERA or \$sWBERA |
| Vault owners | No action needed. Existing vaults do not require owner-side migration |
| Validators | Per-block emissions will become fixed, and boost mechanics will be removed. |
| Integrators | Update integrations to remove dependencies on BGT LSTs, boost dynamics, and BGT-based reward assumptions |
### Source code & audits
* [**PoL Next ABIs**](https://github.com/berachain/abis/tree/main/bepolia/)
* [**PoL Next contract source**](https://github.com/berachain/contracts/tree/bepolia)
* [**PoL Next Audit by Zenith**](https://github.com/berachain/security-audits/blob/main/20260508-PoLNext-Zenith.pdf)
* [**PoL Next Audit by Cantina**](https://github.com/berachain/security-audits/blob/main/20260425-PoLNext-Spearbit.pdf)
### PoL Contract changes
#### Contracts & Interfaces Renamed
| Renamed From | Renamed To |
| --------------------------- | ----------------------------- |
| `BGTIncentiveFeeCollector` | `IncentivesCollector` |
| `IBGTIncentiveFeeCollector` | `IIncentivesCollector` |
| `BGTIncentiveFeeDeployer` | `IncentivesCollectorDeployer` |
#### Functions
| Interface added | What |
| --------------- | --------------------------------------------------- |
| `IWBERA` | New interface for WBERA (`deposit()`, `withdraw()`) |
| Functions Added | Location |
| ---------------------------------------------- | --------------------------------------------------------- |
| `getMaxEmissionPerBlock()` | `IBlockRewardController`, `BlockRewardController` |
| `burnExceedingBalance()` | `IBlockRewardController`, `BlockRewardController` |
| `baseRate()` | `BlockRewardController` (was a setter; now a pure getter) |
| `rewardRate()` | `BlockRewardController` (was a setter; now a pure getter) |
| `setEmissionToken()` | `IDistributor`, `Distributor` |
| `emissionToken()` | `IDistributor` |
| `setIncentiveTokensCollector(address)` | `IRewardVaultFactory`, `RewardVaultFactory` |
| `incentiveTokensCollector()` | `IRewardVaultFactory` |
| `setSWBERA(address)` | `Distributor` |
| `claimAllRewards(address[], address, address)` | `IBGTIncentiveDistributor`, `Distributor` |
| `deposit()` | `Distributor` |
| `withdraw(uint256)` | `Distributor` |
| `initialize()` (reinitializer) | `Distributor` |
| `rewardToken()` | `StakingRewards`, `RewardVault` |
| `claim(address, address[])` | `IIncentivesCollector`, `IncentivesCollector` |
`claimFees(address, address[])` remains exposed on `IIncentivesCollector` and `IncentivesCollector`; it delegates to the same settlement flow as `claim(address, address[])`.
| Functions Removed | Location |
| -------------------------------------------------- | ------------------------------------------------- |
| `setBaseRate(uint256)` | `IBlockRewardController`, `BlockRewardController` |
| `setRewardRate(uint256)` | `IBlockRewardController`, `BlockRewardController` |
| `setMinBoostedRewardRate(uint256)` | `IBlockRewardController`, `BlockRewardController` |
| `setBoostMultiplier(uint256)` | `IBlockRewardController`, `BlockRewardController` |
| `setRewardConvexity(int256)` | `IBlockRewardController`, `BlockRewardController` |
| `computeReward(uint256, uint256, uint256, int256)` | `IBlockRewardController`, `BlockRewardController` |
| `minBoostedRewardRate()` | `IBlockRewardController` |
| `boostMultiplier()` | `IBlockRewardController` |
| `rewardConvexity()` | `IBlockRewardController` |
| `setBGTIncentiveDistributor(address)` | `IRewardVaultFactory`, `RewardVaultFactory` |
| `setBGTIncentiveFeeRate(uint256)` | `IRewardVaultFactory`, `RewardVaultFactory` |
| `setBGTIncentiveFeeCollector(address)` | `IRewardVaultFactory`, `RewardVaultFactory` |
| `bgtIncentiveDistributor()` | `IRewardVaultFactory` |
| `bgtIncentiveFeeRate()` | `IRewardVaultFactory` |
| `bgtIncentiveFeeCollector()` | `IRewardVaultFactory` |
| `getIncentiveFeeAmount(uint256)` | `IRewardVaultFactory`, `RewardVaultFactory` |
| Function renamed from | Renamed to | Location |
| ------------------------------ | ------------------------------- | --------------------------------------------------------- |
| `getBGTIncentiveDistributor()` | `getIncentiveTokensCollector()` | `FactoryOwnable` |
| `getMaxBGTPerBlock()` | `getMaxEmissionPerBlock()` | `BlockRewardController` (both exist; old name deprecated) |
#### Events
| Events Added | Location |
| ------------------------------------------------------------------------------------------ | -------------------------- |
| `ExceedingBalanceBurnt(uint256 amount)` | `IBlockRewardController` |
| `EmissionTokenSet(address indexed emissionToken)` | `IDistributor` |
| `IncentivesCollected(bytes pubkey, address token, uint256 rewardsEmitted, uint256 amount)` | `IRewardVault` |
| `IncentivesCollectionFailed(...)` | `IRewardVault` |
| `RewardTokenMigrated(address oldToken, address newToken)` | `IRewardVault` |
| `IncentiveTokensCollectorUpdated(address newAddress, address oldAddress)` | `IRewardVaultFactory` |
| `SWBERASet(address sWBERA)` | `Distributor` |
| `RewardsClaimed(uint256 amount, address receiver, address outputToken)` | `IBGTIncentiveDistributor` |
| Events Removed | Location |
| ------------------------------------------------ | ------------------------ |
| `MinBoostedRewardRateChanged(uint256, uint256)` | `IBlockRewardController` |
| `BoostMultiplierChanged(uint256, uint256)` | `IBlockRewardController` |
| `RewardConvexityChanged(uint256, uint256)` | `IBlockRewardController` |
| `BGTBoosterIncentivesProcessed(...)` | `IRewardVault` |
| `BGTBoosterIncentivesProcessFailed(...)` | `IRewardVault` |
| `BGTIncentiveDistributorSet(...)` | `IRewardVaultFactory` |
| `IncentiveFeeRateUpdated(uint256, uint256)` | `IRewardVaultFactory` |
| `IncentiveFeeCollectorUpdated(address, address)` | `IRewardVaultFactory` |
| Events Renamed | Change |
| ------------------------------------------------------------------ | ------------------------------- |
| `IncentivesProcessed` | `bgtEmitted` → `rewardsEmitted` |
| `IncentivesProcessFailed` | `bgtEmitted` → `rewardsEmitted` |
| `IncentiveFeeCollected` → `IncentivesCollected` | Full rename + parameter rename |
| `IncentiveFeeCollectionFailed` → `IncentivesCollectionFailed` | Full rename + parameter rename |
| `IncentiveFeeCollectorUpdated` → `IncentiveTokensCollectorUpdated` | Full rename |
| `IncentiveFeesClaimed` → `IncentivesClaimed` | Full rename |
| `IncentiveFeeTokenClaimed` → `IncentiveTokenClaimed` | Full rename |
#### Errors
**Removed** (all from `IPOLErrors`):
* `ZeroPercentageWeight`
* `InvalidIncentiveFeeRate`
* `InvalidBaseRate`
* `InvalidRewardRate`
* `InvalidMinBoostedRewardRate`
* `InvalidBoostMultiplier`
* `InvalidRewardConvexity`
* `NotRewardDurationManager`
* `RewardDurationCoolDownPeriodNotPassed`
### [GraphQL API](https://api.berachain.com) changes
**Reward vault dynamic data**
* **Deprecated:** `apy`, `projectedApy`, `lastDayReceivedBGTAmount`, `allTimeReceivedBGTAmount`, `bgtCapturePercentage`, `bgtCapturePerBlock`.
* **Added:** `apr` (`Float`), `projectedApr` (`Float`), `lastDayRewards`, `allTimeRewards`, `rewardCapturePercentage` (`Float!`), `rewardCapturePerBlock` (`Float!`). `apr` and `projectedApr` return the same values as the deprecated `apy` / `projectedApy`; the rename is cosmetic.
**Reward vault ordering (`GqlRewardVaultOrderBy`)**
* **Deprecated:** `last24hBGTReceived`, `bgtCapturePercentage`.
* **Added:** `apr`, `projectedApr`, `rewardCapturePercentage`, `activeIncentivesValueUsd`, `activeIncentivesRateUsd`.
**Validator dynamic data**
* **Deprecated:** `allTimeDistributedBGTAmount`, `bgtCapturePercentage`, `bgtCapturePerBlock`, `boostApr` (prior POL boost APR model).
* **Added:** `allTimeDistributedRewards`, `allTimeEarnedRewards`, `lastDayDistributedRewards`, `lastDayEarnedRewards`, `rewardCapturePercentage` (`Float!`), `rewardCapturePerBlock` (`Float!`), `rewardRate` (`String!`, per-validator reward rate), `commissionOnIncentives` (`Int!`). Existing `allTimeEarnedBGTAmount`, `lastDayDistributedBGTAmount`, and `lastDayEarnedBGTAmount` remain without deprecation in this schema revision; prefer the new `…Rewards` fields for updated integrations.
**Validator (`GqlValidator`)**
* **Deprecated:** `rewardAllocationStartBlock`.
* **Added:** `incentives: [GqlValidatorIncentive!]!` (aggregated, per-validator), `valStats: GqlValidatorStats` (active-boost and staked-BERA percentages of the total). `valStats` is computed against a network-wide total, so select it only when you need the share-of-total figures.
**Validator allocation weights (`GqlValidatorRewardAllocationWeight`)**
* **Deprecated:** `percentageNumerator` (use `percentage`), `receivingVault`.
* **Added:** `percentage` (`Float!`, equal to `percentageNumerator / 1e4`).
**Validator ordering (`GqlValidatorOrderBy`)**
* **Added:** `rewardRate`, `commissionOnIncentives`.
**Global info**
* **Deprecated:** `totalDistributedBGTAmount`, `annualizedBGTEmission`, `annualizedBGTInflation`.
* **Added:** `totalDistributedRewards`, `annualizedPoLEmissions` (`Float!`), `annualizedInflation` (`Float!`).
**Reward vault snapshots**
* **Deprecated:** `bgtCapturePercentage` on snapshot rows.
* **Added:** `rewardCapturePercentage` (`Float!`).
**Pool sorting (`GqlPoolOrderBy`)**
* **Deprecated:** `bgtApr`.
* **Added:** `polApr`.
**New queries and types**
* **Queries:** `polGetTopVaultDeposits(chain, vaultAddress, top)`; `polGetSWberaVaultSnapshots(chain, range)`; `polGetSWberaVaultMetadata(chain, resolution)`.
* **Types:** `GqlValidatorIncentive` (per-validator aggregated incentive, `incentiveRate`/`remainingAmount` plus USD-denominated variants), `GqlValidatorStats`, `GqlValidatorCommissionHistory`, `GqlValidatorInList`, `GqlSWberaVaultSnapshot`, `GqlSWberaVaultMetadata`.
**Removed**
* The **`GqlUserBGTBalance`** type (chain, user address, BGT balance breakdown) is no longer exposed.
## February 2026
**[Staking pools released](/nodes/staking-pools/overview)** — Validator-operated liquid staking went live. Validators can deploy a pool, offer **stBERA** liquid shares to their community, and earn commission on Proof of Liquidity incentives that flow through their validator. Stakers deposit any amount of BERA, receive auto-compounding stBERA, and withdraw via a shared `WithdrawalVault` with on-chain finalization delay.
Alongside the contracts, Berachain ships an example operator stack in the [`berachain/guides`](https://github.com/berachain/guides/tree/main/apps/staking-pools) repo: a [React frontend template](https://github.com/berachain/guides/tree/main/apps/staking-pools/frontend) you can fork as a staking UI, and [install helpers](https://github.com/berachain/guides/tree/main/apps/staking-pools/install-helpers) — bash scripts and a Python `smart-operator-manager` CLI — that generate the `cast` commands, configuration, and frontend env you need to bring a pool online and operate it day-to-day.
See [Staking Pools Overview](/nodes/staking-pools/overview), [Operator Guide](/nodes/staking-pools/operators), and [Smart Contract Reference](/nodes/staking-pools/contracts).
## December 2025
**[Reward allocation documentation updates](/nodes/guides/manage-reward-allocations)** — Introduced automated BeraChef reward allocations for validators
## October 2025
**[Safe integration for reward vault incentives](/build/guides/proof-of-liquidity/add-incentives-safe)** — Added integration documentation for adding incentives to a reward vault from a Safe (formerly Gnosis Safe) multisig.
**[\$sWBERA token documentation](/general/tokens/swbera)** — Added documentation, including 7-day unstaking period details and integration patterns.
**[PoL integration updates](/build/pol/integration-basics)** — Updated with Incentivize Anything playground examples.
## August 2025
**[Reward vault enhanced functionality](/general/proof-of-liquidity/reward-vaults)** — Two staker-facing additions:
* **Staking on behalf of another account** — Any account can stake tokens directly for another address without that address granting delegation permission. See [Staking for other accounts](/build/pol/staking-for-other-accounts).
* **Partial reward claims** — Stakers can claim a specific amount of accumulated rewards instead of the full balance. See [Partial reward claims](/build/pol/partial-reward-claims).
**[BRIP-0004](https://github.com/berachain/brips/blob/main/meta/BRIP-0004.md) — Enshrine PoL** — Each block automatically includes the reward distribution transaction for the previous block, removing the dependency on external bots to trigger payout. Shipped as part of the August 2025 hardfork.
## July 2025
**BERA staking** — Launched. Earn yield on BERA via [the Hub](https://hub.berachain.com/stake/). The WBERA staker vault and incentive fee collector contracts shipped alongside; PoL incentive fees flow through the collector to BERA stakers.
**[Reward vault rate-based emissions](/general/proof-of-liquidity/reward-vaults#emission-modes)** — Reward vault managers can configure emissions to flow at a target per-second rate; the distribution period is computed automatically from the reward amount and target rate.
**[Validator commission cap](/nodes/guides/manage-incentives-commission)** — Validator commission on incentive tokens is capped at **20%**. Existing validators with rates above 20% are automatically capped.
## June 2025
**[Reward allocation delay](/nodes/guides/manage-reward-allocations#understanding-reward-allocations)** — Reduced from 8,191 blocks to 500.
## April 2025
**[PoL updates](/general/proof-of-liquidity/overview):**
1. New maximum of 3 incentives per reward vault. See [Incentives](/general/proof-of-liquidity/incentives).
2. Block reward emissions modified in line with the targeted inflation rate of 10%. See [Block rewards](/general/proof-of-liquidity/block-rewards).
3. Auto-Incentivizer: fees from default cutting board BEX reward vaults automatically offer incentives.
4. Reward allocations limited to 30% share of emissions per reward vault. See [Manage reward allocations](/nodes/guides/manage-reward-allocations).
## January 2025
**[Proof of Liquidity launch](/general/proof-of-liquidity/overview)** — Public release of the [Honey Paper](https://honeypaper.berachain.com/) and Berachain mainnet.
**BGT minting unlocked** — Beacon Kit v1.1.0 unlocked minting of tokens towards the BGT contract.
# Dedicated Emission Stream (DES)
Source: https://docs.berachain.com/general/proof-of-liquidity/dedicated-emission-stream
Governance-guaranteed WBERA emission for strategically important vaults.
## Why DES exists
Validator reward allocation is market-driven: validators route WBERA emissions toward vaults that offer the best incentive return. This works well for established vaults but can under-serve strategically important liquidity that the network needs regardless of short-term incentive rates.
DES lets governance guarantee a minimum emission flow to specific vaults without relying on individual validator decisions. Use cases include:
* **Bootstrapping new vaults** — seeding emission before incentive markets develop.
* **Sustaining protocol-critical liquidity** — ensuring core trading pairs or lending markets receive steady emission even when competing incentives shift.
* **Targeted growth programs** — time-limited emission to vaults aligned with ecosystem priorities, with per-vault caps that automatically sunset the stream.
## How DES works
Before each block's validator-specific allocation is applied, the Distributor carves out a governance-configured percentage of the WBERA Reward Vault emission and allocates it to DES-selected vaults.
*How the WBERA reward-rate splits between DES vaults and the validator-allocation path. When a DES vault hits its `targetEmission` cap, the excess returns to the remainder pool for validator allocation.*
When DES is active, each block:
1. The Distributor computes `emissionPerc` of the Reward Vault emission as the DES share.
2. The DES share is split across configured vaults according to their weight (must sum to 100%).
3. Each vault tracks cumulative DES emission (`debt`). Once a vault's debt reaches its `targetEmission` cap, it stops receiving DES — excess flows back to the validator allocation path.
4. The remainder (plus any returned excess) enters normal BeraChef validator allocation.
## Governance parameters
`DedicatedEmissionStreamManager` is controlled by two roles:
| Role | Controls |
| --------------------------------- | -------------------------------------------------------------- |
| `DEFAULT_ADMIN_ROLE` (governance) | Distributor address, BeraChef reference, upgrades, role grants |
| `ALLOCATION_MANAGER_ROLE` | `emissionPerc`, vault weights, per-vault target caps |
### Parameters
* **`emissionPerc`** — carve-out percentage in basis points (0–10,000). A value of 500 means 5% of the Reward Vault emission goes to DES before validator allocation.
* **Reward allocation weights** — a `Weight[]` array specifying whitelisted vaults and their share of the DES carve-out. All receivers must be BeraChef-whitelisted. Weights must sum to 10,000 (100%). Updating this replaces the entire allocation atomically.
* **`targetEmission[vault]`** — per-vault cumulative emission cap. Once reached, the vault's DES stream stops. Raising the target restarts the stream.
DES emissions are denominated in \$WBERA (inherited from the Distributor's emission token).
## Impact on validators
The DES carve-out reduces the emission available for validator-directed allocation. If `emissionPerc` is 5%, validators control 95% of the Reward Vault emission per block. The base reward (0.4 WBERA to the validator operator) is unaffected.
Validators can read the current DES parameters from the [`DedicatedEmissionStreamManager`](/build/getting-started/deployed-contracts) contract on-chain.
## See also
* [Block rewards](/general/proof-of-liquidity/block-rewards) — how base rate and reward rate are sized.
* [PoL Governance](/general/governance/reward-vault-governance) — DES controls as a governance surface.
# Incentive Marketplace
Source: https://docs.berachain.com/general/proof-of-liquidity/incentives
How businesses and protocols bid for validator-allocated WBERA emissions.
Proof of Liquidity lets businesses and protocols bid for validator reward allocation by attaching incentive tokens to whitelisted Reward Vaults. Validators allocate \$WBERA emissions to those vaults and earn incentive commission when their allocation captures funded incentives.
This page covers the market-based path for routing emissions through validator allocation. Dedicated Emission Streams are a separate routing path for selected businesses that receive predictable emissions outside the standard validator-allocation market.
## How incentives work
1. Validators stake \$BERA to enter the active set; the more BERA staked, the greater probability to produce a block
2. When validators produce blocks, BeraChef applies their reward allocation to route \$WBERA to whitelisted vaults.
3. Businesses and protocols fund Reward Vault incentives to attract validator allocation..
4. Incentives split into two paths: validator commission and incentives redirected to \$sWBERA. Validator commission is paid to the validator operator. Redirected incentives are settled through the Incentive Auction, converted into \$BERA / \$WBERA yield, and accrued into \$sWBERA.
## Incentive distribution roles
| Participant | Role |
| --------------- | ------------------------------------------------------------------------- |
| Protocol | Funds incentive tokens to attract validator reward allocation. |
| Validator | Allocates Reward Vault emission and earns commission on incentive tokens. |
| BERA staker | Stakes \$BERA with validators, increasing block-production probability. |
| \$sWBERA staker | Earns WBERA yield from the Incentive Auction through \$sWBERA. |
| LST staker | Earns WBERA yield when their LST has a registered staker vault. |
| Vault staker | Stakes receipt tokens and earns allocated WBERA emissions from the vault. |
## Incentive token lifecycle
### Whitelisting
Reward Vaults can whitelist up to two incentive tokens through governance.
### Token managers
Each whitelisted incentive token has a token manager that funds incentives and manages rates under governance constraints.
### Incentive rate
Incentives use an exchange rate per unit of allocated emission:
`incentiveTokens = emissionAllocatedToVault * incentiveRate`
Rates can increase when the token manager deposits more inventory. Decreasing rates is restricted until existing inventory is exhausted.
## Commission and payouts
Validator commission is capped at **20%**. Reward Vault incentive processing sends the validator commission to the validator operator and sends the remaining incentive tokens to `IncentivesCollector`.
```mermaid actions={false} theme={null}
flowchart LR
Incentives["🎁 Incentive tokens"] --> Commission["🖥️ Validator commission"]
Incentives --> Collector["♻️ Redirected incentives"]
Collector --> Auction["🔁 Incentive Auction"]
Auction --> \$sWBERA["🍯 \$sWBERA yield"]
Auction --> LSTYield["🪙 LST staker yield"]
```
## Incentive fee settlement
The redirected incentive share collects in `IncentivesCollector`, the shared settlement path for Reward Vault incentives. Incentive tokens accumulate in the collector,
and WBERA paid into settlement becomes yield for \$sWBERA stakers and registered LST stakers.
See [Deployed contract addresses](/build/getting-started/deployed-contracts) for the current **IncentivesCollector** address.
### Settlement behavior
Settlement is a fixed-price exchange against the collector: a buyer pays WBERA into the incentive collector, receives the selected incentive tokens held by the collector, and the paid WBERA is split **pro-rata** (by WBERA-denominated total assets) between the \$sWBERA staking vault and registered `LSTStakerVault`s. Registered LST vaults receive yield through their LST adapter (`receiveRewards`).
**LST integration:** issuers deploy an `LSTStakerVault` system via [`LSTStakerVaultFactory`](/build/getting-started/deployed-contracts), then register the vault and adapter on the collector's LST registry to join the auction split. See [LST Integration](/build/pol/lst-integration) for the full integrator workflow.
## Incentive redirection
After validator commission, redirected incentives move through the auction path:
* **Validator commission**: paid directly to the validator operator.
* **Incentive Auction**: redirected incentive tokens are settled for \$WBERA.
* **Yield receivers**: auction WBERA becomes yield for \$sWBERA stakers and registered LST stakers.
See [\$sWBERA Token](/general/tokens/swbera) for the staking-vault side of this flow.
# Proof-of-Liquidity Overview
Source: https://docs.berachain.com/general/proof-of-liquidity/overview
How BERA stake, WBERA emissions, Reward Vaults, Dedicated Emission Streams, incentives, and sWBERA fit together.
Proof of Liquidity (PoL) is Berachain’s system for directing network emissions toward useful economic activity. It rewards the participants who secure the chain, allocate emissions, provide liquidity, and build applications.
PoL turns block rewards into a programmable emission-routing system. BERA stake secures the validator set and determines how often each validator can produce blocks. When a validator produces a block, WBERA emissions are split into two paths: one portion is routed through Dedicated Emission Streams to globally selected Reward Vaults, while the remainder is routed to Reward Vaults according to the validator’s selected reward allocation.
Businesses can access PoL emissions in two ways: by **funding incentives** on Reward Vaults to compete for validator allocation, or by being included in a **Dedicated Emission Stream** for a more predictable long-term flow. See [Dedicated Emission Streams](/general/proof-of-liquidity/dedicated-emission-stream) for more details.
Users participate by staking eligible assets in Reward Vaults, or by depositing \$BERA into the \$sWBERA Staking Vault.
When a Reward Vault with active incentives receives emissions, those incentives are collected and split. A portion is paid to validators as commission for allocating emissions, while the remaining incentives are converted into \$BERA through the Incentive Auction and accrued into \$sWBERA.
*How one WBERA emission travels from validator stake through Reward Vaults to user yield. Deeper pages cover each segment in detail.*
## Lifecycle
Validators stake \$BERA to enter the active set. Additional stakers can increase a validator’s
stake directly or via [Staking Pools](/nodes/staking-pools/overview), impacting that validator’s
block-production probability.
Active validators propose blocks with probability proportional to their \$BERA stake.
Each proposed block creates a fixed \$WBERA Reward Vault emission. The Distributor applies any
active [Dedicated Emission
Stream](/general/proof-of-liquidity/block-rewards#dedicated-emission-stream-des) carve-out, then
allocates the remainder through validator reward allocation.
Businesses fund Reward Vault incentives to attract validator reward allocation. When emissions
are routed to incentivised vaults, part of the incentives is paid to validators as commission
and the remainder is redirected to the Incentive Auction.
Redirected incentives are converted into $BERA through the Incentive Auction and accrued into
\$sWBERA.
RewardVault stakers can claim \$WBERA rewards.
## Core components
### \$BERA: security and block-production weight
Validators join the active set based on staked \$BERA, subject to protocol minimum and maximum bounds. The active set contains the top 69 validators by stake. More stake increases block-production probability, which increases how often that validator’s reward allocation is applied.
Validator operators configure that allocation through BeraChef; see [Manage Reward Allocations](/nodes/guides/manage-reward-allocations).
### Block Rewards
Each per-block \$WBERA emission splits along two paths: a fixed `baseRate` payment to the validator operator, and a `rewardRate` stream that flows through Dedicated Emission Streams and BeraChef into Reward Vaults.
The `BlockRewardController` splits each block \$WBERA emission in `baseRate()` (0.4 WBERA) to the validator's operator and `rewardRate()` (1.305 WBERA) to the **Reward Vaults** of the validator's active reward allocation.
BeraChef manages validator reward allocations, vault whitelisting, and incentive commission. Validators choose how to split their emission across governance-whitelisted reward vaults; see [BeraChef](/general/proof-of-liquidity/block-rewards#berachef-reward-allocation).
See [Block rewards](/general/proof-of-liquidity/block-rewards) for the reward model and BeraChef allocation mechanics.
### Reward Vaults: where emissions land
Reward Vaults receive allocated \$WBERA emissions from validator reward allocation and, where applicable, Dedicated Emission Streams. Users stake PoL-eligible receipt tokens in these vaults and claim rewards based on their share of vault stake.
See [Reward Vaults](/general/proof-of-liquidity/reward-vaults) for staking and delegated staking.
### Incentives: why validators allocate
Validators define their reward allocation of \$WBERA emissions toward the **Reward Vaults** of their interest to earn a commission in the form of the Protocol's incentive tokens.
Validator allocations are the signal that directs network emissions toward applications the ecosystem values. Stronger allocations to vaults backed by active, valuable protocols compound the PoL loop: more emissions flow to liquidity that produces real demand, and the incentive market keeps validators accountable for which vaults are actually generating ecosystem value.
Incentive tokens not paid out as validator commission are redirected into the **Incentive Auction**, where buyers settle them by paying WBERA. That WBERA becomes yield for \$sWBERA stakers and registered LST stakers, closing the loop between protocol-funded incentives and the broader BERA economy.
See [Incentive Marketplace](/general/proof-of-liquidity/incentives) for incentive rates, commission, and redirection.
### Dedicated Emission Streams
In addition to validator-directed reward allocation, Berachain can route part of its emissions through Dedicated Emission Streams.
Dedicated Emission Streams route predictable emissions to selected businesses and globally selected Reward Vaults outside the standard validator allocation market. This gives teams a clearer growth budget they can plan around, while keeping the remaining emissions available for validator allocation.
See [Dedicated Emission Streams](/general/proof-of-liquidity/dedicated-emission-stream) for eligibility, parameters, repayment mechanics, and revenue-sharing details.
### BGT: deprecated
The previous dual-token PoL system based on \$BGT and \$BERA has been upgraded to a single-token model based on \$WBERA.
The \$BGT token that previously acted as both governance and emission token is being deprecated.
Contracts retain legacy handling so existing Reward Vaults with residual BGT allowance can settle that allowance automatically as \$WBERA during claims.
Existing protocols and Reward Vaults do not need to redeploy. Validator emissions now route to vaults as \$WBERA directly, residual `$BGT` allowances are converted to \$WBERA at the claim path inside `RewardVault`. Protocols whose user-facing surfaces (claim UI, reward-token tags, indexers) referenced \$BGT should retag those surfaces to \$WBERA; no on-chain migration call is required.
See [BGT Token](/general/tokens/bgt).
## Roles at a glance
PoL has distinct participant roles. The same wallet can play more than one role, but each role has a different job and yield path. The table below names each role and links to its canonical page.
| Participant | Main job | Main reason to care |
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| [Validators](/nodes/overview/index) | Stake \$BERA, produce blocks, allocate emissions towards Reward Vaults | Earn base rewards and incentive commission |
| [BERA stakers](/general/tokens/bera#validator-staking) | Deposit \$BERA to validators directly or through Staking Pools | Increase validator block-production probability |
| [Reward Vault stakers](/general/proof-of-liquidity/reward-vaults) | Stake Protocol's receipt tokens in Reward Vaults | Earn allocated \$WBERA emissions |
| [\$sWBERA stakers](/general/tokens/swbera) | Deposit BERA or WBERA into the Staking Vault | Earn WBERA yield from the Incentive Auction |
| [LST stakers](/build/pol/lst-integration) | Stake LST tokens into a registered `LSTStakerVault` | Earn WBERA yield from the Incentive Auction |
| [Staking pool depositors](/nodes/staking-pools/overview) | Deposit BERA into a Staking Pool, receive non-transferable shares | Earn \$WBERA yield from both base rate and auction of the incentives commission |
| [Businesses / protocols](/build/pol/integration-basics) | Create Reward Vaults, fund incentives, or receive Dedicated Emission Streams | Attract emissions, liquidity, users, and long-term growth capital |
| [Governance](/general/governance/reward-vault-governance) | Whitelist vaults and tokens, set system and Dedicated Emission Stream parameters | Keep PoL bounded and accountable |
## Where to go next
From here you can drop into any specific piece of the system. [Reward Vaults](/general/proof-of-liquidity/reward-vaults) is where users actually stake to earn allocated \$WBERA, and [Incentive Marketplace](/general/proof-of-liquidity/incentives) is where protocol-funded incentives get split between validator commission and the auction that turns the rest into more \$WBERA yield for [\$sWBERA](/general/tokens/swbera) holders. [Block rewards](/general/proof-of-liquidity/block-rewards) is the rate model that sits behind all of it — how much the chain pays per block and how BeraChef divides it.
If you build on Berachain, the integrator surface lives under the Build tab. [PoL Integration Basics](/build/pol/integration-basics) is the entry point; most protocols then move to [Set up a Reward Vault](/build/guides/proof-of-liquidity/setup-reward-vault) and [Add incentives](/build/guides/proof-of-liquidity/add-incentives). LST issuers should read [LST Integration](/build/pol/lst-integration) to plug into the same auction yield that flows to sWBERA. Validators and node operators looking at commission and reward-allocation controls should start at the [Nodes overview](/nodes/overview/index).
Businesses can apply for a [Dedicated Emission Stream](/general/proof-of-liquidity/dedicated-emission-stream): predictable emissions in exchange for a premium-repayment obligation and long-term revenue sharing with the network.
# Reward Vaults
Source: https://docs.berachain.com/general/proof-of-liquidity/reward-vaults
Where $WBERA emissions flow, how Reward Vault incentives work, and how vault rewards are claimed.
Reward Vaults are the PoL contracts where users stake eligible receipt tokens and earn allocated \$WBERA rewards. Reward Vaults can receive emissions through validator allocation and, where applicable, Dedicated Emission Streams. Businesses and protocols attach incentive tokens to Reward Vaults to attract validator allocation. When a Reward Vault with active incentives receives emissions, those incentives are split between validator commission and the remaining incentives, which are converted through the Incentive Auction and accrued into \$sWBERA.
Reward Vaults are key infrastructure for businesses and protocols to access PoL. A business or protocol can operate multiple vaults, each with its own PoL-eligible receipt asset — for example, separate BEX pools can each have their own Reward Vault.
Only governance-whitelisted Reward Vaults can receive PoL emissions. Depending on the route, emissions may be allocated through validator reward allocation or through Dedicated Emission Streams.
See [Reward Vault Requirements and Guidelines](/general/help/reward-vault-guidelines) for whitelisting requirements.
For staking on behalf of another address, see [Delegation](#delegation).
## Staking with a reward vault
To earn allocated \$WBERA from a Reward Vault, a user stakes the PoL-eligible asset in that vault. The protocol that deployed the vault decides how users obtain the receipt token to stake—typically by providing liquidity.
1. The user takes an action that yields a PoL-eligible receipt token.
2. The user stakes that token in the corresponding Reward Vault.
3. The user earns a pro-rata share of \$WBERA streamed to the vault, based on [emission timing](#wbera-emission-timing-modes) and vault rules.
The \$WBERA amount a user earns from a Reward Vault depends on:
1. The user’s share of total assets staked in the vault.
2. The \$WBERA emission rate and timing for that vault (see [block rewards](/general/proof-of-liquidity/block-rewards) and [emission modes](#wbera-emission-timing-modes) below).
After staking, users can claim rewards, add to their deposit, or withdraw subject to the vault’s rules. Vault staking is designed to feel like familiar DeFi staking flows.
Reward Vault emissions pay **\$WBERA**.
The [RewardVaultHelper](/build/pol/reward-vault-helper-claim-flow) is an optional UX component
that converts \$WBERA claims atomically into \$sWBERA or native \$BERA. Direct vault claims still
pay \$WBERA.
```mermaid actions={false} theme={null}
%%{init: { "sequence": { "actorFontWeight": "bold", "messageFontWeight": "bold" } } }%%
sequenceDiagram
autonumber
participant U as 👤 User
participant P as 🌊 Protocol
participant RV as 🏦 Reward Vault
participant V as 🖥️ Validator
participant D as 💧 Distributor
participant C as 🔁 Incentive Auction
U->>P: Provide liquidity or protocol activity
P-->>U: Receipt token
U->>RV: Stake receipt token
P->>RV: Fund incentive tokens
V->>D: Produce block + emit allocation
D->>RV: Send WBERA emission
RV-->>U: Claim as sWBERA or native BERA
RV-->>V: Validator commission
RV-->>C: Redirected incentive tokens
```
At the protocol level (numbered to match the diagram above):
1. The user provides liquidity to the protocol or performs another PoL-eligible action.
2. The protocol issues the user a PoL-eligible receipt token.
3. The user stakes the receipt token in the corresponding Reward Vault.
4. The protocol funds the Reward Vault with incentive tokens to attract validator allocation.
5. An active validator produces a block and the Distributor receives the per-block emission.
6. The Distributor sends the \$WBERA Reward Vault emission to the vault.
7. The user claims rewards as \$sWBERA or native \$BERA through [RewardVaultHelper](/build/pol/reward-vault-helper-claim-flow).
8. The Reward Vault pays validator commission out of the funded incentive tokens.
9. The Reward Vault redirects the remaining incentive tokens to the [Incentive Auction](/general/proof-of-liquidity/incentives#incentive-fee-settlement), where they are converted into \$BERA / \$WBERA yield and accrued into \$sWBERA.
After staking, users can claim rewards, add stake, or withdraw based on vault rules.
## Staking on behalf of another address
The RewardVault supports **delegation**: one address (the **delegate**) can stake on behalf of another (the **account**). Common patterns include:
* **Custodial staking**: exchanges or custodians staking on behalf of users.
* **Smart contract integration**: protocols that stake user funds automatically.
* **Managed staking services**: third parties operating staking on behalf of accounts.
```mermaid actions={false} theme={null}
%%{init: { "sequence": { "actorMargin": 5, "actorFontSize": 18, "messageFontWeight": "bold" } } }%%
sequenceDiagram
autonumber
participant D as 👤 Delegate
participant A as 👤 Account
participant RV as 🏦 Reward Vault
D->>RV: Deposit stake credited to Account
RV-->>A: WBERA rewards accrue to Account
opt Withdraw self-staked (Account only)
A->>RV: Withdraw self-deposited stake
end
opt Withdraw delegated (Delegate only)
D->>RV: Withdraw delegated stake they contributed
end
```
**Key delegation concepts**
* **Delegate**: address that deposits and withdraws delegated stake (`msg.sender` for those actions).
* **Account**: address that owns the position and receives rewards.
* **Self-staked balance**: stake deposited directly by the account.
* **Delegated balance**: stake deposited by delegates for that account.
Only the account can withdraw self-staked balance. Delegates can withdraw only delegated stake they contributed.
## WBERA emission timing modes
Reward Vaults operate in one of two mutually exclusive modes for **WBERA reward distribution timing** (streamed rewards).
### Duration-based mode
In this mode, the `rewardDurationManager` sets a fixed `rewardsDuration` (typically 3–7 days). Each time \$WBERA rewards are added to the vault via `notifyRewardAmount`, the \$WBERA is distributed evenly over that period.
Duration-based mode enforces the 3–7 day range: if you switch from target-rate mode where the computed duration exceeded 7 days, the duration is capped at 7 days.
**Example:** If 100 \$WBERA is added with a 5-day duration, the vault distributes 20 \$WBERA per day to stakers.
### Target rate mode
When `targetRewardsPerSecond` is set to a non-zero value, the vault computes the distribution period for each \$WBERA deposit. The vault keeps the effective emission rate from exceeding the target while respecting the minimum duration limit.
```text theme={null}
period = max(minRewardDurationForTargetRate, totalReward / targetRate)
```
The duration is never shorter than the minimum (default 3 days), but can extend beyond 7 days if needed to maintain the target rate.
### Switching between modes
The Reward Vault manager can switch between modes:
* Set a positive target rate to keep WBERA streaming at that rate.
* Set the target rate to zero to return to duration-based distribution.
## Incentive management
Reward Vaults support **incentive tokens**: additional tokens protocols attach so validators have a reason to allocate \$WBERA emissions toward the vault.
### How incentive tokens work
1. **Whitelisting**: incentive tokens are allowed through governance for the vault (see [Incentive Marketplace](/general/proof-of-liquidity/incentives)).
2. **Rate setting**: token managers set incentive rates per unit of allocated emission (`incentiveTokens = emissionAllocatedToVault * incentiveRate`).
3. **Deposits**: protocols fund incentive inventory into the vault system through the token manager flow.
4. **Distribution**: as \$WBERA emissions are allocated to the vault, funded incentive tokens follow the PoL split: validator commission to the validator operator and redirected incentives to the incentive tokens collector for [auction settlement](/general/proof-of-liquidity/incentives#incentive-fee-settlement).
## Incentives
To see why validators allocate \$WBERA toward one vault over another, read [Incentive Marketplace](/general/proof-of-liquidity/incentives): protocols compete for validator reward allocation with funded incentives, validator commission, and auction-settled yield.
## Vault creation
New Reward Vaults can be created permissionlessly at [Berachain Hub](https://hub.berachain.com/earn/create).
Protocols must still **whitelist** new vaults through governance for validator-allocated \$WBERA emissions; see [Reward Vault Guidelines](/general/help/reward-vault-guidelines).
## Calculating WBERA APR
Reward Vault APR depends on several observable inputs:
* Vault `rewardRate` — \$WBERA credited toward stakers’ claims per second (while active).
* `periodFinish` — timestamp when the current vault reward period ends.
* `stakeToken` — token staked into the Reward Vault.
* `totalSupply` — total `stakeToken` amount staked in the vault.
* Price of \$WBERA (equivalent to \$BERA) in USD.
* Price of the stake token.
If `periodFinish` has passed, no rewards are being emitted for that period and **vault APR from
that stream is 0%** until new rewards are notified.
Using the normalized vault reward rate in \$WBERA per second:
$APR = \frac{(vaultRewardRate \times secondsPerYear \times priceOfWBERA)}{(totalSupply \times priceOfStakeToken)}$
This expresses the rate at which the vault credits depositors with \$WBERA for the active reward period.
### Example
| Parameter | Value | Normalized |
| :------------------- | :----------------------------------- | :------------------ |
| Vault reward rate | 272490527103681170793308992914391673 | 0.27249052710368116 |
| Price of \$WBERA | \$7.8 | \$7.8 |
| Total supply | 598626940947001140289 | 598.6269409470011 |
| Price of stake token | \$223,845.58 | \$223,845.58 |
| Seconds per year | 31,536,000 | 31,536,000 |
Use normalized vault values from Hub or your indexer when calculating APR by hand.
$APR = \frac{0.27249052710368116 \times 31536000 \times 7.8}{598.6269409470011 \times 223845.58} \approx 0.5002 \approx 50.02\%$
Vault metrics on [Hub Vaults](https://hub.berachain.com/vaults/) refresh on a short cadence (roughly every few minutes).
## Reward output
Reward Vaults accrue \$WBERA by default. The [RewardVaultHelper](/build/pol/reward-vault-helper-claim-flow) converts \$WBERA atomically during claim, so stakers receive \$sWBERA or native \$BERA without a separate step.
# BERA Token
Source: https://docs.berachain.com/general/tokens/bera
Gas token, staking, tokenomics, and how to get BERA.
BERA serves as the native gas and staking token of Berachain, the first blockchain powered by Proof-of-Liquidity.
## Role of BERA
BERA is both the gas token and the PoL emission token (as WBERA):
### Transaction fees
BERA pays for transactions on Berachain. Fees are burned, reducing circulating supply. [Explore the Berachain Ecosystem](https://ecosystem.berachain.com/).
### PoL emissions
Block rewards are emitted as \$WBERA (wrapped BERA, 1:1). Each block emits a **base rate** (0.4 WBERA) to the validator operator and a **reward rate** (1.305 WBERA) routed through BeraChef to Reward Vaults. WBERA is the single emission token for Proof of Liquidity.
### Validator staking
Validators stake BERA to enter the active set (top 69 by stake). Block-production probability is proportional to staked BERA. Additional stakers can deposit BERA to a validator directly via `BeaconDeposit.deposit()` or through a [Staking Pool](/nodes/staking-pools/overview) via `submit()`.
### \$sWBERA staking
Depositing BERA or WBERA into the [Staking Vault](/general/tokens/swbera) yields \$sWBERA, which earns yield from the [Incentive Auction](/general/proof-of-liquidity/incentives#incentive-fee-settlement).
See [Block Rewards](/general/proof-of-liquidity/block-rewards) for how BERA staking affects block production and emissions.
## Tokenomics
The following documents the fixed supply, allocation, and release schedule for BERA.
### Overview
| Property | Value |
| ----------------------- | ------------------------------------------------------------------------------------ |
| Token name | BERA |
| Total supply at genesis | 500,000,000 BERA |
| Inflation | \~5% annually via PoL reward emissions (distributed as WBERA), subject to governance |
| Decimals | 18 |
### Distribution and allocation
The genesis supply of 500,000,000 BERA is allocated as follows:
**Initial core contributors — 84,000,000 (16.8%)**\
Allocated to advisors and members of Big Bera Labs, the core contributors to the Berachain blockchain.
**Investors — 171,500,000 (34.3%)**\
Allocated to Seed, Series A, and Series B investors.
**Community allocations — 244,500,000 (48.9%)**
**Airdrop — 79,000,000 (15.8%)**\
Distributed to testnet users, Berachain and ecosystem NFT holders, social supporters, ecosystem dApps, and community builders. See the [Blog airdrop overview](https://blog.berachain.com/blog/berachain-airdrop-overview) for details.
**Future community initiatives — 65,500,000 (13.1%)**\
Reserved for incentive programs, grants, and other initiatives, with community input via Snapshots, RFPs, and similar mechanisms.
**Ecosystem & R\&D — 100,000,000 (20%)**\
Used for ecosystem development, R\&D, growth, and Berachain Foundation operations: developer programs ([Boyco](https://boyco.berachain.com/)), node operator delegations, and Proof-of-Liquidity evolution. At launch, 9.5% of total BERA supply from this bucket is unlocked for ecosystem growth, developer tooling, liquidity provisioning, and related uses.
### Token release schedule
All allocated parties share the same vesting terms:
* **Cliff:** 1 year; no tokens unlock before the cliff.
* **Initial unlock:** After the cliff, 1/6 of the allocated amount unlocks.
* **Linear vesting:** The remaining 5/6 vests linearly over the following 24 months.
# BGT Token
Source: https://docs.berachain.com/general/tokens/bgt
BGT is a legacy token that is being **deprecated**. It was used in earlier iterations of Proof of Liquidity for governance and reward routing.
It no longer influences validator reward allocation weight, block rewards, or ecosystem proposals. See [What's New](/general/proof-of-liquidity/changelog#bgt-is-deprecated) for the full deprecation context.
## Redeem BGT for BERA
Users who still hold BGT can visit the [Berachain Hub](https://hub.berachain.com/), which will help you unboost your BGT, redeem it for BERA, and stake into \$sWBERA to continue earning yield for PoL incentives.
# HONEY Stablecoin
Source: https://docs.berachain.com/general/tokens/honey
Minting, redemption, collateral, and basket mode.
`$HONEY` is soft-pegged to the US Dollar and fully collateralized. It is Berachain's native stablecoin, providing a stable means of exchange within the ecosystem and beyond.
## Using and minting `$HONEY`
`$HONEY` serves the same role as other stablecoins — payments, remittances, hedging against volatility — and is widely used across Berachain DeFi as a base trading pair and lending asset. Minting and redeeming `$HONEY` against collateral happens through [honey.berachain.com](https://honey.berachain.com). You can also acquire it by swapping on BEX or another exchange.
The lifecycle is straightforward: deposit a whitelisted collateral asset to mint `$HONEY`, use it however you like, and redeem it for collateral when you're done. Minting and redemption rates are configurable per collateral asset by governance.
### Collateral assets
The following assets can be used as collateral to mint `$HONEY`:
* `$USDC`
* `$BYUSD` (`$pyUSD`)
* `$USDT0`
* `$USDE`
New collateral assets can be added through governance.
## `$HONEY` Architecture
A flow diagram of the `$HONEY` minting process and associated contracts is shown below:
```mermaid actions={false} theme={null}
graph LR
User[User] -->|"collateral"| HoneyFactory[HoneyFactory]
HoneyFactory -->|"HONEY"| User
HoneyFactory -->|"collateral"| USDCVault[USDC Vault]
USDCVault -->|"shares"| HoneyFactory
HoneyFactory -->|"collateral"| BYUSDVault[BYUSD Vault]
BYUSDVault -->|"shares"| HoneyFactory
HoneyFactory -->|"collateral"| USDT0Vault[USDT0 Vault]
USDT0Vault -->|"shares"| HoneyFactory
HoneyFactory -->|"collateral"| USDEVault[USDE Vault]
USDEVault -->|"shares"| HoneyFactory
```
### `$HONEY` vaults
`$HONEY` is minted by depositing eligible collateral into specialized vault contracts. Each vault is specific to a particular collateral type. Mint and redeem rates are configured independently per collateral asset — see [Fees](#fees) for the current values.
### HoneyFactory
At the heart of the `$HONEY` minting process is the [`HoneyFactory`](https://berascan.com/address/0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401) contract (same address on mainnet and Bepolia). This contract acts as a central hub, connecting all the different `$HONEY` Vaults and is responsible for minting new `$HONEY` tokens.
As shown in the diagram, your deposits are routed through the `HoneyFactory` contract to the appropriate vault. The `HoneyFactory` custodies the shares minted by the vault (corresponding to your deposits) and mints `$HONEY` tokens to you.
## Depegging and basket mode
Basket Mode is a safety mechanism that activates when collateral assets become unstable. It affects both minting and redemption of `$HONEY` in specific ways:
**Redemption:**
* When ANY collateral asset depegs, Basket Mode automatically activates
* In this mode, you can't choose which asset you redeem your `$HONEY` for
* Instead, you redeem for a proportional share of ALL collateral assets in the basket
* For example, if you redeem 1 `$HONEY` token with Basket Mode active, you'll get some of each collateral asset based on their relative proportion as collateral
**Minting:**
* Basket Mode for minting is considered an edge case that only occurs if ALL collateral assets are either depegged or blacklisted. Depegged assets cannot be used to mint `$HONEY`
* In this situation, to mint `$HONEY`, you must provide proportional amounts of all collateral assets in the basket, rather than choosing a single asset
* If one asset is depegged, you can mint only with the other asset
## Gasless transfers and approvals
`$HONEY` supports two ERC-20 extensions that allow transactions to be submitted by a third party on behalf of the token holder, removing the need for the holder to pay gas:
* **EIP-2612 `permit`** — lets a holder sign an off-chain message authorizing a spender allowance. A relayer or contract can then submit the permit on-chain, so the holder never sends a transaction. This is the same interface used by USDC, DAI, and most modern ERC-20 tokens.
* **EIP-3009 `transferWithAuthorization` / `receiveWithAuthorization`** — lets a holder sign an off-chain message authorizing a one-time transfer to a specific recipient. The recipient (or a relayer) submits it on-chain. Each authorization includes a unique nonce to prevent replay. This also enables support for [X402](https://www.x402.org/) payment-gated content.
Both extensions use [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed structured data for signatures.
## Fees
Fees are collected from minting and redeeming `$HONEY`. The current fee structure is the following:
| Stablecoin | Mint Fee | Redeem Fee |
| ---------- | -------- | ---------- |
| USDT | 0.1% | 0% |
| byUSD | 0.1% | 0% |
| USDC | 0% | 0.05% |
| USDe | 0% | 0.05% |
Let's walk through minting and redeeming `$HONEY` with `$USDC`:
**Minting:**
* User deposits `1,000 $USDC`
* Receives `1,000 $HONEY` (0% fee)
* No fees collected
**Redeeming:**
* User redeems `1,000 $HONEY` for `$USDC`
* Receives `999.5 $USDC` (0.05% fee = 0.5 \$USDC)
* The `0.5 $USDC` fee is split between polFeeCollector (IncentivesCollector, routing fees into sWBERA yield) and feeReceiver based on governance-configured polFeeCollectorFeeRate
# $sWBERA Token
Source: https://docs.berachain.com/general/tokens/swbera
What is the $sWBERA token and how does it work.
\$sWBERA (Staked WBERA) is a yield-bearing token that represents your staked BERA position in the Staking Vault. It provides non-dilutive yield through Proof of Liquidity incentive redirection. For the Staking Vault (\$WBERA Staker Vault) contract address, see [Deployed contracts](/build/getting-started/deployed-contracts).
## How to Get \$sWBERA
\$sWBERA tokens are issued when you stake BERA or WBERA in the Staking Vault. For detailed instructions on how to stake BERA and receive \$sWBERA tokens, see the [BERA token page](/general/tokens/bera#staking-bera).
You can stake either native BERA (which the system automatically wraps to WBERA) or WBERA directly if you already have wrapped BERA tokens. Both methods result in receiving \$sWBERA tokens representing your staked position.
## How \$sWBERA Works
### Non-Dilutive Yield Mechanism
\$sWBERA earns yield through a **non-dilutive mechanism** that doesn't inflate the token supply. Instead, the underlying value of each \$sWBERA token increases as the vault accumulates more WBERA from incentive fees.
### Incentive Auction yield
Yield comes from the Incentive Auction. When Reward Vaults process funded incentive tokens, validator commission (default 5%, max 20%) goes to the validator operator and the remaining incentive tokens are redirected to `IncentivesCollector`. Auction buyers pay WBERA to claim those tokens; the \$sWBERA vault's share of that WBERA (pro-rata with registered LST staker vaults) increases the underlying WBERA per \$sWBERA.
For direct claim routing from Reward Vaults into \$sWBERA, see [RewardVaultHelper claim flow](/build/pol/reward-vault-helper-claim-flow).
### Auto-Compounding
Your rewards automatically compound without any manual intervention. No claiming is required as rewards are automatically reinvested, causing your \$sWBERA tokens to increase in value over time.
## Unstaking \$sWBERA
### 7-Day Unbonding Period
To convert your \$sWBERA tokens back to BERA or WBERA, you'll need to unstake through the Staking Vault. The unstaking process has a **7-day unbonding period**:
1. **Initiate withdrawal**: Queue a withdrawal request through the [Staking Vault interface](https://hub.berachain.com/stake/)
2. **Wait 7 days**: Your withdrawal request enters a 7-day cooldown period
3. **Complete withdrawal**: After the unbonding period ends, return to the interface to complete the withdrawal and receive your WBERA tokens
### Important Considerations
* **No rewards earned** during the unbonding period — shares are burned at queue time, so the reserved assets do not compound.
* **No expiry** — withdrawal requests remain valid indefinitely after the 7-day cooldown passes.
* **Multiple requests**: You can have multiple withdrawal requests active simultaneously (each represented as an ERC-721 NFT).
* **Cancellation**: Withdrawal requests can be cancelled at any time. On cancellation, new shares are minted at the current exchange rate, which may differ from the original rate if the vault compounded.
For detailed instructions on the unstaking process, see the [BERA token page](/general/tokens/bera).
# Introduction
Source: https://docs.berachain.com/index
Official documentation for Berachain - the first blockchain powered by Proof-of-Liquidity
Berachain is a high-performance EVM-identical Layer 1 blockchain utilizing Proof-of-Liquidity (PoL) consensus.
## Get started
Learn about Berachain's architecture, EVM compatibility, and core concepts.
Understand how Berachain's unique consensus mechanism works.
Set up your wallet to interact with Berachain.
Learn how to acquire BERA tokens to use on the network.
## Native dApps
The native decentralized exchange for trading tokens on Berachain.
Decentralized lending protocol for borrowing and lending assets.
## Tokens
Native gas and staking token.
Native stablecoin.
Yield-bearing staking vault token.
# BeaconKit Consensus
Source: https://docs.berachain.com/nodes/architecture/beaconkit-consensus
Consensus client and framework for EVM chains: CometBFT, Engine API, Eth2 modularity, and execution client diversity.
[BeaconKit](https://github.com/berachain/beacon-kit) is both a consensus client and a framework for building EVM chains.
BeaconKit leverages CometBFT for its consensus algorithm, wrapped to interface with any EVM-compatible execution environment. As a consensus client, it allows the network (an EVM blockchain like Berachain) to reach agreement based on the data provided by the execution client.
By conforming to Eth2 modularity, which separates consensus and execution, BeaconKit can leverage all the benefits that come with EVM execution clients. It achieves this by adhering to the [Engine API](https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md), a JSON-RPC API that enables communication between consensus and execution clients.
## Benefits
Some of the key benefits that come with BeaconKit:
1. **Eth2 modularity** - Adheres to separation of execution and consensus with communication via Engine API
2. **Promotes execution client diversity** - Any EVM execution upgrades can be supported out of the box, avoiding the need to run and maintain a custom forked EVM execution client to work with the chain
3. **CometBFT** - Leverages a trusted consensus algorithm
4. **Instant finality** - Achieves Single Slot Finality / Instant Finality, compared to Ethereum's finality of \~13 minutes
5. **Leverages EVM tooling** - The majority of all EVM tooling is supported
6. **Modular** - BeaconKit is also a modular framework that can allow for the potential implementation of a custom block builder, rollup, data availability layer, and more
## Deposit processing
BeaconKit consumes deposits directly from the execution payload's request list (EIP-6110), one block at a time. Each EL block that includes deposit transactions surfaces them as a custom `Deposit(bytes,bytes,uint64,bytes,uint64)` event from the deposit contract; the CL parses that event into a 192-byte deposit request and applies it in the same block. There is no follow distance, no pre-fork queue, and no per-epoch processing window — once the EL block is finalized, the deposit is reflected in validator state.
The wire formats and parser are documented in the EIP-6110 deposits specification.
## EVM inflation parameters
The CL chain spec carries the per-block inflation address and amount that the consensus layer mints to as a withdrawal each block. The Fulu fork introduces two new fields on the chain spec — `EVMInflationAddressFulu` and `EVMInflationPerBlockFulu`.
# EVM Execution
Source: https://docs.berachain.com/nodes/architecture/evm-execution
Recommended versions and genesis files for mainnet and Bepolia.
Berachain is 100% EVM-compatible, operating on a lightly modified Reth. We keep pace with Ethereum improvements, shipping [Protocol Upgrades](/build/protocol/overview) every 6 months.
Recommended [**Beacon Kit**](https://github.com/berachain/beacon-kit) and [**Bera-Reth**](https://github.com/berachain/bera-reth) versions depend on the network:
| Network | Beacon Kit | Bera-Reth |
| ------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| Mainnet | [v1.4.1](https://github.com/berachain/beacon-kit/releases/tag/v1.4.1) | [v1.4.4](https://github.com/berachain/bera-reth/releases/tag/v1.4.4) |
| Bepolia | [v1.4.2-rc.0](https://github.com/berachain/beacon-kit/releases/tag/v1.4.2-rc.0) | [v1.4.4](https://github.com/berachain/bera-reth/releases/tag/v1.4.4) |
**New with Beacon Kit v1.4.1 & greater:** Use the option `--chain (bepolia|mainnet)` instead of `--chain `. You can continue to specify the genesis file path, but be sure to verify its hash:
| Genesis File | Updated | Download link & md5 hash |
| ------------ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Mainnet | July 29, 2026 | [d367b133f368367c3e973636ad059c79](https://raw.githubusercontent.com/berachain/beacon-kit/2059e2abea2a42513a0617a38cd7f758d1a1e6a1/testing/networks/80094/eth-genesis.json) |
| Bepolia | July 14, 2026 | [f7d31d1039b0253f5f43336e55d28054](https://raw.githubusercontent.com/berachain/beacon-kit/v1.4.2-rc.0/testing/networks/80069/eth-genesis.json) |
# Validator Lifecycle
Source: https://docs.berachain.com/nodes/architecture/validator-lifecycle
Validator states and transitions: Deposited, Eligible, Active, Exited, and Withdrawn; activation queue and stake requirements.
## Overview
A validator in Berachain is a participant responsible for proposing and attesting to new blocks, helping secure the network and maintain consensus. Validators stake a required amount of the network's native token (\$BERA) as collateral, which serves both as an economic incentive to behave honestly and as a mechanism for penalizing malicious behavior.
Validators can operate independently with direct staking, or they can operate **staking pools** that allow community members to stake BERA through smart contracts and receive liquid shares (stBERA). For information about operating staking pools, see the [Staking Pools documentation](/nodes/staking-pools/overview).
Validators have several key responsibilities:
* Proposing new blocks when selected
* Attesting to blocks proposed by other validators
* Participating in consensus by voting on the canonical chain
* Maintaining network security through their staked tokens
The Validator's Voting Power is the amount of \$BERA they have deposited, rounded down to the nearest **10,000 BERA**. Their Voting Power, as a proportion of the total Voting Power among all validators, is their probability of being selected to propose a block.
## The Active Set & Stake Requirements
The **Active Set** is the limited group of validators currently participating in the consensus layer and proposing blocks.
The current limit of validators in the active set is **69**.
To enter the active set, you must meet the minimum stake requirements. This requirement depends on whether the active set is full:
* If the active set is not full, the minimum stake requirement is **250,000** `$BERA`.
* If the active set is full, the minimum stake requirement is **10,000** `$BERA` more than the amount staked by the last validator in the active set.
It can take up to **3** epochs (**192** blocks per epoch) for deposits to be processed and for a validator to be included in the active set.
For step-by-step instructions on spinning up a validator, see the [Become a Validator](/nodes/guides/become-a-validator) guide.
## Direct Staking & Withdrawal Addresses
Berachain follows Proof-of-Stake (PoS) direct staking, which allows `$BERA` holders to directly stake their `$BERA` to a validator.
The validator's journey begins with a deposit transaction on the **Execution Layer** to the deposit contract at `0x4242424242424242424242424242424242424242`. The deposit emits a custom deposit event.
When a validator makes their first deposit, they define a **Withdrawal Credentials Address**. If funds are withdrawn from a validator, or if the validator is evicted from the active set, *all* funds are returned to this single address.
This means that validators must communicate clearly with their delegators about how they will handle and manually return funds if the validator is removed from the active set.
Avoid staking to validators without knowing how they handle funds when a validator is removed from
the active set.
## Lifecycle States
The validator lifecycle flows through five main states:
### 1. Deposited
The validator's journey begins with a deposit transaction on the Execution Layer (via the Deposit Contract). Once this transaction is successful, beacon-kit nodes capture it and process it for signature verification.
The initial deposit transaction establishes a connection between a validator's Consensus Layer identity and its Execution Layer identity and decides the withdrawal address for the \$BERA stake.
* **No Verification Delay**: On the first deposit, the validator's signature is fully verified (similar to ETH2), but deposits are not assessed until end of epoch. Subsequent deposits simply increase the validator's balance (no additional signature verification is done).
* **Minimum Requirement**: A total of **250,000 BERA** is required for a validator to reach the Deposited state. (Multiple deposits can accumulate to this amount.)
* **Signature Verification**: On the first deposit, the validator's signature is fully verified. Subsequent deposits simply increase the validator's balance.
* **Per-block deposit cap**
Up to **8192** deposits can land in a single block. With \~45,000 gas per deposit and a 30M gas block limit, the cap is unreachable in practice.
After remaining in the **Deposited** state for 1 epoch, the validator automatically moves to the **Eligible** state and becomes eligible for activation.
### 2. Eligible
Once the validator enters the **Deposited** state at epoch `N`, the system marks it as eligible for the activation queue as soon as epoch `N+1` starts. This is guaranteed because there is no cap on the activation queue size.
The validator remains in this **Eligible** state for 1 epoch. Afterward, it is added to the **Active** set, provided the validator set cap (**69**) is not exceeded, or if the validator is of higher priority (i.e., higher effective balance or lower-order pubKey among equals).
### 3. Active
After spending 1 epoch in the **Eligible** state (say at `N+1`), the validator is marked **Active** at the start of epoch `N+2` and joins the Active Set (provided the 69-validator limit is not exceeded, or if the validator has a higher priority than an existing validator).
A validator remains active indefinitely until it is forced out by a validator with a higher stake, or until it [voluntarily withdraws its stake](/nodes/guides/withdraw-stake).
Once **Active**:
* **CometBFT Consensus** will use the validator for block proposals, validations, and voting.
* The higher a validator's effective balance, the higher its voting power—and thus, the more frequently it will be polled for block proposals.
### 4. Exited
A Validator may choose to exit by [withdrawing their complete stake](/nodes/guides/withdraw-stake). Otherwise, the **only** reason for a validator to be evicted from the set (and have its funds returned) is if the validator set cap (**69**) is reached and another validator with a higher priority enters. Higher priority is determined by:
1. Larger Effective Balance
2. If Equal Effective Balance, a lower-order pubKey (alphabetically).
When the validator is evicted from the validator set, it is marked **Exited**.
### 5. Withdrawn
Once the validator is marked **Exited** (say at epoch `M`), its funds are fully withdrawn at epoch **M + 256** on mainnet (**M + 32** on Bepolia). Because BeaconKit does not currently enforce a cap on validator churn, this finalizes the validator's lifecycle and returns all `$BERA` to the Withdrawal Credentials Address.
Staking with a previously-used CometBFT identity — a validator that was removed from the active
set — will result in the funds being returned to that validator's withdrawal address at the end of
the current epoch. The old identity can never be re-activated.
## Effective balance and hysteresis
A validator's voting power is its **effective balance**, the actual stake rounded down to a 10,000 BERA increment. Effective balance updates lazily: the consensus state only moves it at the turn of an epoch when actual balance crosses an asymmetric buffer around the next increment, which prevents minor balance fluctuations from churning voting power.
The buffer is asymmetric because protecting against unintentional drops matters more than keeping pace with top-ups:
* **Downward buffer: 100 BERA.** Actual balance must fall more than 100 BERA below an increment boundary before effective balance steps down to the next-lower increment.
* **Upward buffer: 1,000 BERA (\~110% of the 10,000 BERA increment).** Actual balance must exceed an increment boundary by more than 1,000 BERA before effective balance steps up.
For example, a validator who wants to lift effective balance from 250,000 to 260,000 BERA needs an actual balance of **261,000 BERA**: 260,000 BERA at the increment boundary plus 1,000 BERA over the upward buffer. The [Increase validator stake guide](/nodes/guides/increase-validator-stake#worked-example) walks through this.
These buffers are consensus chain spec parameters. See [BRIP-0008](https://github.com/berachain/BRIPs/blob/main/meta/BRIP-0008.md) for the derivation.
## Extended Validator Lifecycle
Putting it all together, the following diagram shows the complete Berachain validator lifecycle:
1. **Deposited** → 1 epoch → **Eligible** → 1 epoch → **Active**
2. Potential forced exit due to the validator set cap (**69**) → **Exited** → **256** (mainnet) / **32** (Bepolia) → **Withdrawn**
3. **Deposited** → 1 epoch → **Eligible** → 1 epoch → **Exited** due to the validator set cap (**69**) + balance too low → **256** (mainnet) / **32** (Bepolia) → **Withdrawn**
Note that the system processes state transitions via a queue, on a FIFO basis, with a cap on the number of transitions in each state to limit excessive churn in the validator set.
## Block Rewards & Distribution
Once active, validators receive block rewards for proposing blocks.
Block rewards are in the form of `$BGT`, with a base reward of **0.4** `$BGT` per block proposed.
The network distributes block rewards automatically through the Distributor contract. Validators no longer need to manually trigger this distribution.
For ongoing operational guides, see the Being a Validator section:
* [Increase Stake](/nodes/guides/increase-validator-stake)
* [Withdraw Stake](/nodes/guides/withdraw-stake)
* [Change Operator Address](/nodes/guides/change-operator-address)
# BeaconKit API
Source: https://docs.berachain.com/nodes/beaconkit/api
# Beacon Kit API Reference
## Enabling the API
Beacon Kit exposes two distinct APIs:
1. **Consensus API**: An implementation of the standard Ethereum [Beacon Node API](https://ethereum.github.io/beacon-APIs/) for consensus clients.
2. **Beacon Kit API**: A custom AvaPI (`bkit/v1`) specific to Berachain's implementation for validators and tooling.
The API server is **default off** and must be enabled by revising your installation's `app.toml` file, in the `[beacon-kit.node-api]` section. When enabled, the suggested port is `3500`.
Note that this API is different from the CometBFT RPC endpoint (also known as the CometBFT Consensus API), which is typically exposed on port `26657`.
## Consensus API
Beacon Kit implements a large surface area of the standard Ethereum Consensus API. For detailed request and response formats, please refer directly to the [official Ethereum Beacon API Specification](https://ethereum.github.io/beacon-APIs/).
### Supported Endpoints
The following namespaces and endpoints are actively supported by the Beacon Kit consensus implementation:
* **Beacon (`/eth/v1/beacon/*` & `/eth/v2/beacon/*`)**:
* `genesis`, `headers`, `blocks`, `blinded_blocks`
* `states/{state_id}/root`, `fork`, `validators`, `validator_balances`, `committees`, `randao`, etc.
* `blob_sidecars/{block_id}`
* `rewards/blocks`, `rewards/attestations`, `rewards/sync_committee`
* `pool/attestations`, `pool/attester_slashings`, `pool/proposer_slashings`, `pool/voluntary_exits`, `pool/bls_to_execution_changes`
* `light_client/bootstrap`, `light_client/updates`, `light_client/finality_update`, `light_client/optimistic_update`
* **Config (`/eth/v1/config/*`)**:
* `spec`, `fork_schedule`, `deposit_contract`
* **Node (`/eth/v1/node/*`)**:
* `identity`, `peers`, `peer_count`, `syncing`, `version`, `health`
* **Validator (`/eth/v1/validator/*`, `/eth/v2/validator/*`, `/eth/v3/validator/*`)**:
* `duties/attester`, `duties/proposer`, `duties/sync`
* `blocks/{slot}`
* `attestation_data`, `aggregate_attestation`, `aggregate_and_proofs`
* `beacon_committee_subscriptions`, `sync_committee_subscriptions`, `beacon_committee_selections`
* `sync_committee_contribution`, `contribution_and_proofs`
* `prepare_beacon_proposer`, `register_validator`
* `liveness/{epoch}`
* **Builder (`/eth/v1/builder/*`)**:
* `states/{state_id}/expected_withdrawals`
* **Events (`/eth/v1/events`)**: Event streaming
* **Debug (`/eth/v1/debug/*`, `/eth/v2/debug/*`)**:
* `fork_choice`, `beacon/heads`, `beacon/states/{state_id}`
***
## Beacon Kit API (`bkit/v1`)
The custom `bkit/v1` namespace exposes specialized endpoints that are unique to the Beacon Kit architecture.
### API Values
1. All balance values are returned in Gwei (1 ETH = 10^9 Gwei)
2. Validator statuses include:
* `active_ongoing`
* `active_exiting`
* `active_slashed`
* `exited_unslashed`
* `exited_slashed`
* `pending_initialized`
* `pending_queued`
* `withdrawal_possible`
* `withdrawal_done`
3. State IDs can be:
* `head`
* `genesis`
* `finalized`
* `justified`
* A specific slot number
### Get Block Proposer Proof
Returns the block proposer's public key for the given timestamp ID, along with Merkle proofs for the public key and proposer index, verifiable against the beacon block root. Also returns the beacon block header and root.
```http theme={null}
GET /bkit/v1/proof/block_proposer/{timestamp_id}
```
```bash theme={null}
curl -s http://localhost:3500/bkit/v1/proof/block_proposer/{timestamp_id}
```
### Get Validator Pubkey Proof
Returns a validator's public key for the given timestamp ID and validator index, along with a Merkle proof verifiable against the beacon block root. Also returns the beacon block header and root.
```http theme={null}
GET /bkit/v1/proof/validator_pubkey/{timestamp_id}/{validator_index}
```
```bash theme={null}
curl -s http://localhost:3500/bkit/v1/proof/validator_pubkey/{timestamp_id}/{validator_index}
```
### Get Validator Credentials Proof
Returns a validator's withdrawal credentials for the given timestamp ID and validator index, along with a Merkle proof verifiable against the beacon block root. Also returns the beacon block header and root.
```http theme={null}
GET /bkit/v1/proof/validator_credentials/{timestamp_id}/{validator_index}
```
```bash theme={null}
curl -s http://localhost:3500/bkit/v1/proof/validator_credentials/{timestamp_id}/{validator_index}
```
### Get Validator Balance Proof
Returns a validator's balance for the given timestamp ID and validator index, along with a Merkle proof and balance leaf verifiable against the beacon block root. Also returns the beacon block header and root.
```http theme={null}
GET /bkit/v1/proof/validator_balance/{timestamp_id}/{validator_index}
```
```bash theme={null}
curl -s http://localhost:3500/bkit/v1/proof/validator_balance/{timestamp_id}/{validator_index}
```
# BeaconKit CLI
Source: https://docs.berachain.com/nodes/beaconkit/cli
# Berachain Node (`beacond`) Reference
`beacond`, is an EVM consensus client implementation used as a basic beacon node for Berachain. See [BeaconKit Consensus Layer](/nodes/architecture/beaconkit-consensus) for more details.
## Global Flags
These flags apply to all commands:
| Flag | Description |
| --------------- | ----------------------------------------------------- |
| `--home string` | Directory for config and data (default: `~/.beacond`) |
## Basic Commands
### `init`
Initialize validator and node configuration files.
```bash theme={null}
beacond init [flags]
```
#### Flags
| Flag | Description |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `--chain-id string` | Genesis file chain-id (randomly created if blank) |
| `--beacon-kit.chain-spec` | Which chain to generate configurations for. This value is also placed in the generated app.toml. `mainnet` or `testnet` or `file` |
| `--beacon-kit.chain-spec-file` | If `chain-spec=file`, which file to use. Should be toml format. This value is also placed in the generated app.toml. |
| `--default-denom string` | Genesis file default denomination (default: "stake") |
| `--initial-height int` | Initial block height at genesis (default: 1) |
| `-o, --overwrite` | Overwrite the genesis.json file |
| `--recover` | Recover existing key using seed phrase instead of creating new one |
### `start`
Run the node application with CometBFT in process.
```bash theme={null}
beacond start [flags]
```
#### Flags
The `start` command has numerous configuration flags for networking, consensus, pruning, and more. Key flags include:
| Flag | Description |
| ------------------------------- | ------------------------------------------------------- |
| `--abci string` | ABCI transport (socket or grpc) (default: "socket") |
| `--address string` | Listen address (default: "tcp\://127.0.0.1:26658") |
| `--db_backend string` | Database backend (default: "pebbledb") |
| `--moniker string` | Node name |
| `--p2p.laddr string` | Node listen address (default: "tcp\://0.0.0.0:26656") |
| `--p2p.persistent_peers string` | Comma-delimited ID\@host:port persistent peers |
| `--p2p.seeds string` | Comma-delimited ID\@host:port seed nodes |
| `--pruning string` | Pruning strategy (default\|nothing\|everything\|custom) |
| `--rpc.laddr string` | RPC listen address (default: "tcp\://127.0.0.1:26657") |
Additional flags are available for detailed engine configuration, beacon-kit settings, and consensus parameters.
### `status`
Query remote node for status information.
```bash theme={null}
beacond status [flags]
```
#### Flags
| Flag | Description |
| --------------------- | ------------------------------------------------------ |
| `-n, --node string` | Node to connect to (default: "tcp\://localhost:26657") |
| `-o, --output string` | Output format (text\|json) (default: "json") |
### `version`
Print the application binary version information.
```bash theme={null}
beacond version [flags]
```
#### Flags
| Flag | Description |
| --------------------- | -------------------------------------------- |
| `--long` | Print long version information |
| `-o, --output string` | Output format (text\|json) (default: "text") |
### `rollback`
Recover from an incorrect application state transition by rolling back one height.
```bash theme={null}
beacond rollback [flags]
```
#### Flags
| Flag | Description |
| -------- | ---------------------------------- |
| `--hard` | Remove last block as well as state |
## CometBFT Commands (`comet`)
Commands for managing the CometBFT consensus engine.
### `comet bootstrap-state`
Bootstrap CometBFT state at an arbitrary block height using a light client.
```bash theme={null}
beacond comet bootstrap-state [flags]
```
#### Flags
| Flag | Description |
| -------------- | ------------------------------------------------------------------------------------------ |
| `--height int` | Block height to bootstrap state at (uses latest block height in app state if not provided) |
### `comet reset-state`
Remove all data and WAL (Write-Ahead Log).
```bash theme={null}
beacond comet reset-state [flags]
```
### `comet show-address`
Show this node's CometBFT validator consensus address.
```bash theme={null}
beacond comet show-address [flags]
```
### `comet show-node-id`
Show this node's ID.
```bash theme={null}
beacond comet show-node-id [flags]
```
### `comet show-validator`
Show this node's CometBFT validator info.
```bash theme={null}
beacond comet show-validator [flags]
```
### `comet unsafe-reset-all`
Remove all data and WAL, reset this node's validator to genesis state (unsafe).
```bash theme={null}
beacond comet unsafe-reset-all [flags]
```
#### Flags
| Flag | Description |
| ----------------------- | ---------------------------------------------------------------------- |
| `--keep-addr-book` | Keep the address book intact |
| `-k, --key-type string` | Private key type (ed25519\|secp256k1\|bls12\_381) (default: "ed25519") |
### `comet version`
Print protocols' and libraries' version numbers this app was compiled against.
```bash theme={null}
beacond comet version [flags]
```
## Deposit Commands (`deposit`)
Commands for managing validator deposits.
### `deposit create-validator`
Create a validator deposit with necessary credentials.
```bash theme={null}
beacond deposit create-validator [withdrawal-address] [amount] ?[beacond/genesis.json] [flags]
```
#### Flags
| Flag | Description |
| ------------------------------------- | --------------------------------------------------------------------------------------- |
| `-g, --genesis-validator-root string` | Use provided genesis validator root (if not set, beacond genesis file must be provided) |
| `-o, --override-node-key` | Override the node private key |
### `deposit validate`
Validate a deposit message for creating a new validator.
```bash theme={null}
beacond deposit validate [pubkey] [withdrawal-credentials] [amount] [signature] ?[beacond/genesis.json] [flags]
```
#### Flags
| Flag | Description |
| ------------------------------------- | --------------------------------------------------------------------------------------- |
| `-g, --genesis-validator-root string` | Use provided genesis validator root (if not set, beacond genesis file must be provided) |
### `deposit validator-keys`
Output validator public key in various formats.
```bash theme={null}
beacond deposit validator-keys [flags]
```
Displays the validator key as Comet address, Comet pubkey, and Eth/Beacon pubkey formats.
## Genesis Commands (`genesis`)
Commands for genesis file management. These are largely only of interest to those of us maintaining Berachain, so we have stuck to the generally useful ones.
### `genesis validator-root`
Get and return the genesis validator root hash.
```bash theme={null}
beacond genesis validator-root [beacond/genesis.json] [flags]
```
## JWT Commands (`jwt`)
Commands for managing JWT authentication.
### `jwt generate`
Generate a new JWT authentication secret.
```bash theme={null}
beacond jwt generate [flags]
```
#### Flags
| Flag | Description |
| -------------------------- | ------------------------------------------------------------------- |
| `-o, --output-path string` | Optional output file path for the JWT secret (default: "./jwt.hex") |
### `jwt validate`
Validate a JWT secret by checking if it's formatted properly.
```bash theme={null}
beacond jwt validate [flags]
```
#### Flags
| Flag | Description |
| ------------------------- | ------------------------------------------------------------------ |
| `-i, --input-path string` | Optional input file path for the JWT secret (default: "./jwt.hex") |
# BeaconKit Configuration
Source: https://docs.berachain.com/nodes/beaconkit/configuration
# Berachain Node Configuration Reference
## app.toml
### Base Configuration (Top-level Options)
These options appear at the top of the file without a section header.
| Option | Description | Default Value |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- | -------------- |
| `pruning` | Pruning strategy for state storage. Only nodes that calculate block reward claim transactions need `default`. | `"everything"` |
| `pruning-keep-recent` | Number of recent states to keep when using `custom` pruning | `"0"` |
| `pruning-interval` | Interval for state pruning when using `custom` pruning | `"0"` |
| `halt-height` | Block height at which to gracefully halt the node | `0` |
| `halt-time` | Unix timestamp at which to gracefully halt the node | `0` |
| `min-retain-blocks` | Minimum block height offset from current block for pruning CometBFT blocks | `0` |
| `inter-block-cache` | Enables inter-block caching | `true` |
| `iavl-cache-size` | Size of the IAVL tree cache (in number of nodes) | `2500` |
| `iavl-disable-fastnode` | Disables the fast node feature of IAVL | `true` |
#### Pruning Options
* `default`: The last 362880 states are kept, pruning at 10 block intervals
* `nothing`: All historic states will be saved, nothing will be deleted (archiving node)
* `everything`: Only 2 latest states will be kept; pruning at 10 block intervals
* `custom`: Manual specification through `pruning-keep-recent` and `pruning-interval`
### Telemetry Configuration `[telemetry]`
| Option | Description | Default Value |
| --------------------------- | ----------------------------------------------------- | ------------- |
| `service-name` | Prefix for keys to separate services | `""` |
| `enabled` | Enables application telemetry functionality | `true` |
| `enable-hostname` | Prefixes gauge values with hostname | `false` |
| `enable-hostname-label` | Adds hostname to labels | `false` |
| `enable-service-label` | Adds service to labels | `false` |
| `prometheus-retention-time` | When positive, enables a Prometheus metrics sink | `0` |
| `global-labels` | Global set of name/value label tuples for all metrics | `[]` |
| `metrics-sink` | Type of metrics sink to use | `""` |
| `statsd-addr` | Address of statsd server for metrics | `""` |
| `datadog-hostname` | Hostname for Datadog metrics | `""` |
### BeaconKit Configuration
BeaconKit settings are organized into several subsections.
#### Core settings `[beacon-kit]`.
| Option | Description | Default Value |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `chain-spec` | The type of chain specification to use. This can be `mainnet`, `testnet`, and `file` | `mainnet` |
| `chain-spec-file` | When `chain-spec` is `file`, this is the file to use. Evaluated relative to the beacond configuration directory. The [mainnet](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80094/spec.toml) and [testnet](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80069/spec.toml) configuration files are available for reference. | "" |
| `shutdown-timeout` | This is the maximum time to wait for the node to gracefully shutdown before forcing an exit. | "5m0s" |
#### Engine `[beacon-kit.engine]`
| Option | Description | Default Value |
| ---------------------------- | ------------------------------------------------------- | ------------------------- |
| `rpc-dial-url` | HTTP URL of the execution client JSON-RPC endpoint | `"http://localhost:8551"` |
| `rpc-retries` | Number of retries before shutting down consensus client | `"3"` |
| `rpc-timeout` | RPC timeout for execution client requests | `"900ms"` |
| `rpc-startup-check-interval` | Interval for the startup check | `"3s"` |
| `rpc-jwt-refresh-interval` | Interval for the JWT refresh | `"30s"` |
| `jwt-secret-path` | Path to the execution client JWT-secret | `"./jwt.hex"` |
#### Logger `[beacon-kit.logger]`
| Option | Description | Default Value |
| ------------- | -------------------------------- | ------------- |
| `time-format` | Format of the time in the logger | `"RFC3339"` |
| `log-level` | Level of logging (verbosity) | `"info"` |
| `style` | Style of the logger | `"pretty"` |
#### KZG `[beacon-kit.kzg]`
| Option | Description | Default Value |
| -------------------- | ------------------------------ | ------------------------------------------ |
| `trusted-setup-path` | Path to the trusted setup file | `"./testing/files/kzg-trusted-setup.json"` |
| `implementation` | KZG implementation to use | `"crate-crypto/go-kzg-4844"` |
**Implementation Options:**
* `crate-crypto/go-kzg-4844`
* `ethereum/c-kzg-4844`
#### Payload Builder `[beacon-kit.payload-builder]`
| Option | Description | Default Value |
| ------------------------- | -------------------------------------------------- | ---------------------------------------------- |
| `enabled` | Enables the local payload builder (for validators) | `true` |
| `suggested-fee-recipient` | Address to receive transaction fees from blocks | `"0x0000000000000000000000000000000000000000"` |
| `payload-timeout` | Timeout for local build payload | `"850ms"` |
#### Validator `[beacon-kit.validator]`
| Option | Description | Default Value |
| ---------------------------------- | -------------------------------------------------------- | ------------- |
| `graffiti` | String included in the graffiti field of beacon blocks | `""` |
| `enable-optimistic-payload-builds` | Enables building the next block's payload optimistically | `"true"` |
#### Block Store Service `[beacon-kit.block-store-service]`
| Option | Description | Default Value |
| --------------------- | ------------------------------------ | ------------- |
| `enabled` | Enables the block store service | `"false"` |
| `availability-window` | Number of slots to keep in the store | `"8192"` |
#### Node API `[beacon-kit.node-api]`
| Option | Description | Default Value |
| --------- | ------------------------------- | ------------------ |
| `enabled` | Enables the node API | `"false"` |
| `address` | Address to bind the node API to | `"127.0.0.1:3500"` |
| `logging` | Enables node API logging | `"false"` |
## config.toml
## Berachain Node CometBFT Configuration Reference
This document describes the configuration options available in the `config.toml` file for the CometBFT consensus engine used by Berachain nodes.
Current CometBFT version: `1.0.1`
### Main Base Config Options (Top-level)
| Option | Description | Default Value |
| --------------------------- | -------------------------------------------------------------------- | ---------------------------------- |
| `proxy_app` | TCP or UNIX socket address of the ABCI application | `"tcp://127.0.0.1:26658"` |
| `moniker` | Custom human readable name for this node | `"poop"` |
| `db_backend` | Database backend type | `"pebbledb"` |
| `db_dir` | Database directory | `"data"` |
| `log_level` | Output level for logging | `"info"` |
| `log_format` | Output format: 'plain' (colored text) or 'json' | `"plain"` |
| `genesis_file` | Path to the JSON file containing the initial validator set | `"config/genesis.json"` |
| `priv_validator_key_file` | Path to the JSON file containing the private validator key | `"config/priv_validator_key.json"` |
| `priv_validator_state_file` | Path to the JSON file containing the last sign state | `"data/priv_validator_state.json"` |
| `priv_validator_laddr` | TCP or UNIX socket address for external PrivValidator process | `""` |
| `node_key_file` | Path to the JSON file containing the node authentication private key | `"config/node_key.json"` |
| `abci` | Mechanism to connect to the ABCI application: socket or grpc | `"socket"` |
| `filter_peers` | If true, query the ABCI app on connecting to new peers | `false` |
#### Database Backend Options
* `badgerdb`: Uses github.com/dgraph-io/badger (stable, pure go)
* `goleveldb`: Uses github.com/syndtr/goleveldb (UNMAINTAINED, stable, pure go)
* `pebbledb`: Uses github.com/cockroachdb/pebble (stable, pure go)
* `rocksdb`: Uses github.com/linxGnu/grocksdb (requires gcc)
* `cleveldb`: Uses levigo wrapper (DEPRECATED, requires gcc)
* `boltdb`: Uses etcd's fork of bolt (DEPRECATED, stable)
### RPC Server Configuration `[rpc]`
| Option | Description | Default Value |
| ------------------------------------------ | ---------------------------------------------------- | --------------------------------------------------------------------------- |
| `laddr` | TCP or UNIX socket address for the RPC server | `"tcp://127.0.0.1:26657"` |
| `cors_allowed_origins` | List of origins for cross-domain requests | `[]` |
| `cors_allowed_methods` | List of allowed methods for cross-domain requests | `["HEAD", "GET", "POST"]` |
| `cors_allowed_headers` | List of allowed headers for cross-domain requests | `["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"]` |
| `unsafe` | Enable unsafe RPC commands | `false` |
| `max_open_connections` | Maximum number of simultaneous connections | `900` |
| `max_subscription_clients` | Maximum number of unique clients that can /subscribe | `100` |
| `max_subscriptions_per_client` | Maximum subscriptions per client | `5` |
| `experimental_subscription_buffer_size` | Maximum events to buffer per subscription | `200` |
| `experimental_websocket_write_buffer_size` | Maximum RPC responses buffered per WebSocket client | `200` |
| `experimental_close_on_slow_client` | Close WebSocket if client can't read fast enough | `false` |
| `timeout_broadcast_tx_commit` | How long to wait for tx commit during broadcast | `"10s"` |
| `max_request_batch_size` | Maximum number of requests in a batch | `10` |
| `max_body_bytes` | Maximum size of request body | `1000000` |
| `max_header_bytes` | Maximum size of request header | `1048576` |
| `tls_cert_file` | Path to TLS certificate file | `""` |
| `tls_key_file` | Path to TLS key file | `""` |
| `pprof_laddr` | pprof listen address | `""` |
### gRPC Server Configuration `[grpc]`
| Option | Description | Default Value |
| ------- | ---------------------------------------------- | ------------- |
| `laddr` | TCP or UNIX socket address for the gRPC server | `""` |
#### gRPC Service Configurations
| Service | Configuration | Enabled |
| ------------------------------ | ------------------------------ | ------- |
| `[grpc.version_service]` | The gRPC version service | `true` |
| `[grpc.block_service]` | The gRPC block service | `true` |
| `[grpc.block_results_service]` | The gRPC block results service | `true` |
#### Privileged gRPC Endpoints `[grpc.privileged]`
| Option | Description | Default Value |
| ------- | --------------------------------------- | ------------- |
| `laddr` | Host/port for privileged gRPC endpoints | `""` |
##### Pruning Service `[grpc.privileged.pruning_service]`
| Option | Description | Default Value |
| --------- | ----------------------------------------------- | ------------- |
| `enabled` | Controls access to pruning service via gRPC API | `false` |
### P2P Configuration `[p2p]`
| Option | Description | Default Value |
| ---------------------------------- | -------------------------------------------------- | ------------------------ |
| `laddr` | Address to listen for incoming connections | `"tcp://0.0.0.0:26656"` |
| `external_address` | Address to advertise to peers | `""` |
| `seeds` | Comma separated list of seed nodes | `""` |
| `persistent_peers` | Comma separated list of persistent peer nodes | `""` |
| `addr_book_file` | Path to address book | `"config/addrbook.json"` |
| `addr_book_strict` | Set true for strict address routability rules | `true` |
| `max_num_inbound_peers` | Maximum number of inbound peers | `120` |
| `max_num_outbound_peers` | Maximum number of outbound peers | `40` |
| `unconditional_peer_ids` | List of node IDs to connect to ignoring limits | `""` |
| `persistent_peers_max_dial_period` | Maximum pause when redialing persistent peers | `"0s"` |
| `flush_throttle_timeout` | Time to wait before flushing messages | `"10ms"` |
| `max_packet_msg_payload_size` | Maximum size of a message packet payload | `1024` |
| `send_rate` | Rate at which packets can be sent | `5120000` |
| `recv_rate` | Rate at which packets can be received | `5120000` |
| `pex` | Set true to enable the peer-exchange reactor | `true` |
| `seed_mode` | Seed mode for network crawling | `false` |
| `private_peer_ids` | List of peer IDs to keep private | `""` |
| `allow_duplicate_ip` | Toggle to disable guard against peers from same IP | `false` |
| `handshake_timeout` | Peer connection handshake timeout | `"20s"` |
| `dial_timeout` | Peer connection dial timeout | `"3s"` |
### Mempool Configuration `[mempool]`
| Option | Description | Default Value |
| ------------------------------------------------------------- | ----------------------------------------------- | ------------- |
| `type` | Type of mempool for this node | `"nop"` |
| `recheck` | Whether to recheck transactions after a block | `false` |
| `recheck_timeout` | Timeout for rechecking process | `"0s"` |
| `broadcast` | Whether to relay transactions to other peers | `false` |
| `wal_dir` | Location of the Write Ahead Log for the mempool | `""` |
| `size` | Maximum number of transactions in the mempool | `0` |
| `max_tx_bytes` | Maximum size of a single transaction | `0` |
| `max_txs_bytes` | Maximum size of all transactions in mempool | `0` |
| `cache_size` | Size of the transaction cache | `0` |
| `keep-invalid-txs-in-cache` | Whether to keep invalid transactions in cache | `false` |
| `experimental_max_gossip_connections_to_persistent_peers` | Limit gossip to number of persistent peers | `0` |
| `experimental_max_gossip_connections_to_non_persistent_peers` | Limit gossip to number of non-persistent peers | `0` |
#### Mempool Types
* `"flood"`: Concurrent linked list mempool with flooding gossip protocol (default)
* `"nop"`: No-operation mempool where the ABCI app handles transactions
### State Sync Configuration `[statesync]`
| Option | Description | Default Value |
| ----------------------- | ----------------------------------------------------------- | ------------- |
| `enable` | Enable state sync for rapid node bootstrapping | `false` |
| `rpc_servers` | RPC servers for light client verification | `""` |
| `trust_height` | Trusted block height for state sync | `0` |
| `trust_hash` | Trusted block hash for state sync | `""` |
| `trust_period` | Trust period for validators (usually 2/3 of unbonding time) | `"168h0m0s"` |
| `discovery_time` | Time for discovering snapshots | `"15s"` |
| `temp_dir` | Temporary directory for state sync chunks | `""` |
| `chunk_request_timeout` | Timeout before re-requesting a chunk | `"10s"` |
| `chunk_fetchers` | Number of concurrent chunk fetchers | `"4"` |
### Block Sync Configuration `[blocksync]`
| Option | Description | Default Value |
| --------- | ------------------------- | ------------- |
| `version` | Block Sync version to use | `"v0"` |
### Consensus Configuration `[consensus]`
| Option | Description | Default Value |
| -------------------------------------- | ------------------------------------------------ | ------------------- |
| `wal_file` | Path to the consensus WAL file | `"data/cs.wal/wal"` |
| `timeout_propose` | How long to wait for a proposal block | `"2s"` |
| `timeout_propose_delta` | How much timeout\_propose increases per round | `"500ms"` |
| `timeout_prevote` | How long to wait after receiving +2/3 prevotes | `"2s"` |
| `timeout_prevote_delta` | How much timeout\_prevote increases per round | `"500ms"` |
| `timeout_precommit` | How long to wait after receiving +2/3 precommits | `"2s"` |
| `timeout_precommit_delta` | How much timeout\_precommit increases per round | `"500ms"` |
| `timeout_commit` | How long to wait after committing a block | `"500ms"` |
| `skip_timeout_commit` | Deprecated: set timeout\_commit to 0 instead | `false` |
| `double_sign_check_height` | How many blocks to check for double signing | `0` |
| `create_empty_blocks` | Whether to create empty blocks | `true` |
| `create_empty_blocks_interval` | Interval between empty blocks | `"0s"` |
| `peer_gossip_sleep_duration` | Sleep duration for peer gossip | `"100ms"` |
| `peer_gossip_intraloop_sleep_duration` | Intraloop sleep duration for peer gossip | `"0s"` |
| `peer_query_maj23_sleep_duration` | Sleep duration for peer query | `"2s"` |
### Storage Configuration `[storage]`
| Option | Description | Default Value |
| ---------------------------- | ----------------------------------------------------- | ------------- |
| `discard_abci_responses` | Whether to discard ABCI responses from state store | `true` |
| `experimental_db_key_layout` | Representation of keys in the database | `"v1"` |
| `compact` | Force compaction for databases that support it | `false` |
| `compaction_interval` | Number of blocks to wait before triggering compaction | `"1000"` |
#### Pruning Configuration `[storage.pruning]`
| Option | Description | Default Value |
| ---------- | ------------------------------------------------ | ------------- |
| `interval` | Time period between automated pruning operations | `"10s"` |
##### Data Companion Configuration `[storage.pruning.data_companion]`
| Option | Description | Default Value |
| ------------------------------------- | ------------------------------------------------ | ------------- |
| `enabled` | Whether pruning respects data companion settings | `false` |
| `initial_block_retain_height` | Initial value for block retain height | `0` |
| `initial_block_results_retain_height` | Initial value for block results retain height | `0` |
### Transaction Indexer Configuration `[tx_index]`
| Option | Description | Default Value |
| ----------- | ------------------------------------ | ------------- |
| `indexer` | What indexer to use for transactions | `"null"` |
| `psql-conn` | PostgreSQL connection string | `""` |
#### Indexer Options
* `"null"`: No indexer
* `"kv"`: Simple key-value storage indexer (default)
* `"psql"`: PostgreSQL-backed indexer
### Instrumentation Configuration `[instrumentation]`
| Option | Description | Default Value |
| ------------------------ | -------------------------------------------- | ------------- |
| `prometheus` | Enable Prometheus metrics | `true` |
| `prometheus_listen_addr` | Address for Prometheus collector connections | `":26660"` |
| `max_open_connections` | Maximum number of simultaneous connections | `800` |
| `namespace` | Instrumentation namespace | `"cometbft"` |
# What is BeaconKit?
Source: https://docs.berachain.com/nodes/beaconkit/overview
Modular consensus layer for Ethereum-based chains.
BeaconKit is a modular framework developed by Berachain for building EVM [consensus clients](/general/help/glossary#consensus-client). It integrates the benefits of CometBFT consensus, including increased composability, [single slot finality (SSF)](https://ethereum.org/en/roadmap/single-slot-finality/), and more.
BeaconKit is an innovative framework that makes the [CometBFT](https://docs.cometbft.com/v0.38/) consensus algorithm available to any EVM execution environment. In other words, BeaconKit is a modular consensus layer that is adaptable for Ethereum-based blockchains.
BeaconKit packages the CometBFT consensus algorithm with a modular middleware layer capable of receiving blocks from any execution environment that conforms to the [Engine API](/general/help/glossary#engine-api) specification. This allows those blocks to be processed through CometBFT consensus. In practice, this enables support for unmodified EVM [execution clients](/general/help/glossary#execution-client) to run on top of BeaconKit, allowing chains to be [EVM identical](/general/introduction/what-is-berachain#evm-identical).
The framework is built with modularity in mind and can be extended with different layers such as a custom block builder, a rollup layer, a data availability layer, and others. This modularity enables the building of not only Layer 1 blockchains but also serves as a framework for Layer 2 solutions.
## BeaconKit advantages
Running a BeaconKit-based chain provides several advantages (assuming the default configuration of pairing with an EVM execution client):
* **Single slot finality** — Compared to Ethereum's \~13 minutes; see [single slot finality](/general/help/glossary#single-slot-finality) in the glossary
* **Optimistic payload building** — Executing block proposal in parallel with voting reduces block times by up to 40%
* **Eth2 modularity** — Conformity to separation of execution and consensus with communication via Engine API
* **Full EIP compatibility** — The majority of EVM tooling is supported
* **Modular** — Can allow for custom block builder, rollup, data availability layer, and more
For running a node, see [BeaconKit Consensus Layer](/nodes/architecture/beaconkit-consensus) in Architecture. For the official implementation, see the [BeaconKit GitHub repository](https://github.com/berachain/beacon-kit).
# Becoming a Validator
Source: https://docs.berachain.com/nodes/guides/become-a-validator
Register and activate a validator: beacond setup, deposit transaction, BeaconDeposit contract, operator and withdrawal address.
This guide walks you through the process of becoming a validator node on Berachain.
## Requirements
* A fully-synced full node — See [Quickstart](/nodes/operations/quickstart)
* [Foundry](https://book.getfoundry.sh/getting-started/installation)
* Berachain Wallet with a minimum of 250,000 \$BERA (or the current minimum to meet the Active Set) + gas to process the transaction
If you haven't already, please read through the [Validator Lifecycle](/nodes/architecture/validator-lifecycle) overview that explains the lifecycle of a validator.
**Your goal** is to deposit your stake and establish a connection between your **validator identity** on the consensus layer, your **operator identity** on the execution layer, and the **withdrawal address** where your stake is returned when you stop being a validator.
The deposit contract emits a custom `Deposit(bytes,bytes,uint64,bytes,uint64)` event that the consensus client parses out of the execution payload directly; deposits land in validator state in the same block the transaction was included in. The deposit-contract address itself is also reported by the EL at the `eth_config` JSON-RPC endpoint if you need to discover it programmatically.
If you would like to test this out locally, consult the guide to a [local Kurtosis-based devnet](/nodes/guides/local-devnet-kurtosis), which includes instructions on testing deposits.
## Step 1: Check beacond and set up environment
A point of reassurance about `beacond`: it *cannot* transact on your behalf on either the execution or consensus layers. By invoking `beacond` on the command line you are not playing with TNT strapped to your stake.
You can ask `beacond help` for a list of commands, then `beacond help command` for help on a certain command. Have a look at `beacond help deposit create-validator`.
`beacond` wants a `--home` option specifying the data directory. This should be provided every time you use this command. In this guide, we will use an environment variable and refer to it with `--home $BEACOND_HOME`.
Below is an example of setting up and verifying the environment. It's okay, even encouraged, that when depositing your stake you do so on your validator's full node, so that you have the CometBFT private keys available to you.
```bash theme={null}
export BEACOND_HOME=/full/path/to/beacond-data;
export RPC_URL="https://rpc.berachain.com"; # mainnet
export DEPOSIT_ADDR="0x4242424242424242424242424242424242424242"; # mainnet
export COMETBFT_PUB_KEY="";
export OPERATOR_ADDRESS="";
export WITHDRAW_ADDRESS="";
alias beacond="/path/to/beacond --home $BEACOND_HOME";
beacond genesis validator-root $BEACOND_HOME/config/genesis.json;
# [Expected Output - MUST BE THIS EXACTLY]
# DO NOT PROCEED UNLESS THIS OUTPUT MATCHES
# 0xdf609e3b062842c6425ff716aec2d2092c46455d9b2e1a2c9e32c6ba63ff0bda
export GENESIS_ROOT=0xdf609e3b062842c6425ff716aec2d2092c46455d9b2e1a2c9e32c6ba63ff0bda # mainnet
```
The `0xdf6..0bda` hash output confirms that you have the right mainnet genesis file.
If you sign a deposit without using the above genesis root, **the deposit will fail and the funds
will be burnt**.
* **BEACOND\_HOME**: The path to your beacond data directory.
* **RPC\_URL**: The URL of the RPC endpoint you are using. You can use your own if you want.
* **DEPOSIT\_ADDR**: The address of the deposit contract.
* **COMETBFT\_PUB\_KEY**: The public key of your validator from CometBFT (see below).
* **OPERATOR\_ADDRESS**: The address of the ETH account (EOA) that will become your **operator address** on the execution layer. This is used to interact with contracts such as [BeraChef](/build/getting-started/deployed-contracts).
* **WITHDRAW\_ADDRESS**: The address where your deposit stake is returned when you stop being a validator (state 'Exited' on the [lifecycle](/nodes/architecture/validator-lifecycle)). This can be an address of your choice, or if you are staking money from investors, they will require a specific address.
Your **CometBFT identity** was created by beacond when you did `beacond init`, and is held in the `priv_validator_key.json` file. You can view the corresponding public key for this identity with `beacond deposit validator-keys`:
```bash theme={null}
beacond deposit validator-keys;
# [Example Output]:
# ...
# Eth/Beacon Pubkey (Compressed 48-byte Hex):
# 0x9308360bb1e8c237c2a4b6734782426e6e42bc7a43008ba5e3d9f3c70143227ea1fb2a08b21cbbedf87cebf27e81669d
```
This is your **beacon chain public key**, and in this tutorial is placed in the `COMETBFT_PUB_KEY` environment variable.
## Step 2 - Prepare registration transaction
The first registration transaction is the most important. It establishes the association between your validator key and your presence in the consensus layer. **Mistakes in this step can result in a permanent loss of funds.**
1. **Set up and fund your funding account on Berachain.** You should be equipped so that you can use MetaMask or `cast` to send transactions from the funding account.
2. **Confirm your CometBFT identity has not been used before.** If your validator has exited the active set or failed a deposit transaction, you will need to create a new identity. Check for previous use of your identity with the following command. If this returns non-zero, start over with a new beacond identity (as described in the Quickstart).
```bash theme={null}
cast call $DEPOSIT_ADDR 'getOperator(bytes)' $COMETBFT_PUB_KEY -r $RPC_URL;
```
Do not change any parameters of your deposit transaction before sending them. Calculate the
signature for the parameters *and deposit amount* you are going to send to the deposit contract.
If you change anything after calculating the signature, **the deposit will fail and the funds will
be burnt**.
3. **Have `beacond` calculate the parameters** for the transaction you will send with `beacond deposit create-validator` which takes:
* **withdrawal-addr**: as described in Step 1
* **stake-amount**: the initial stake amount (as GWei). It is strongly recommended to use the minimum — 10,000 \$BERA.
* **genesis-root**: as confirmed in Step 1.
```bash theme={null}
STAKE_AMOUNT_ETH="10000";
STAKE_AMOUNT_GWEI="${STAKE_AMOUNT_ETH}000000000"
beacond deposit create-validator \
$WITHDRAW_ADDRESS \
$STAKE_AMOUNT_GWEI \
-g $GENESIS_ROOT;
# [Expected Output]:
# "pubkey": "",
# "credentials": "0x010000000000000000000000",
# "amount": "0x9184e72a000",
# "signature": "0x94349ab1...fb4b1d6",
DEPOSIT_SIGNATURE="0x9434..."; # from the above output
WITHDRAW_CREDENTIAL="0x01000..."; # from the above output
```
The credentials should contain the withdrawal address verbatim, and the public key confirms the public key matching the private key used to generate the signature.
4. Finally, **verify that beacond will accept this signature** with `beacond deposit validate` which should produce no error message.
```bash theme={null}
beacond deposit validate \
$COMETBFT_PUB_KEY \
$WITHDRAW_CREDENTIAL \
$STAKE_AMOUNT_GWEI \
$DEPOSIT_SIGNATURE \
-g $GENESIS_ROOT;
echo $?;
# [Expected Output]:
# 0
```
You now have everything needed to deposit the initial 10,000 \$BERA.
## Step 3 - Send registration transaction
You will now send the above transaction onto the chain by calling the `deposit` function on the [BeaconDeposit](/build/getting-started/deployed-contracts) contract (`0x4242424242424242424242424242424242424242`):
```solidity theme={null}
function deposit(
bytes calldata pubkey,
bytes calldata credentials,
bytes calldata signature,
address operator
)
external
payable
```
* **pubkey** is the CometBFT public key.
* **credentials** and **signature** are the values generated by `beacond` in the previous step.
* **operator** is the EOA address you created in the "Preliminaries" step.
```bash Non-Ledger theme={null}
cast send $DEPOSIT_ADDR \
'deposit(bytes,bytes,bytes,address)' \
"$COMETBFT_PUB_KEY" \
"$WITHDRAW_CREDENTIAL" \
"$DEPOSIT_SIGNATURE" \
"$OPERATOR_ADDRESS" \
--private-key $PK \
--value "${STAKE_AMOUNT_ETH}ether" \
-r $RPC_URL;
```
```bash Ledger theme={null}
cast send $DEPOSIT_ADDR \
'deposit(bytes,bytes,bytes,address)' \
"$COMETBFT_PUB_KEY" \
"$WITHDRAW_CREDENTIAL" \
"$DEPOSIT_SIGNATURE" \
"$OPERATOR_ADDRESS" \
--ledger \
--value "${STAKE_AMOUNT_ETH}ether" \
-r $RPC_URL;
```
## Step 4 - Confirm successful registration
The following traits denote a successful registration:
1. The deposit contract tx was successful (check block explorer or observe the cast command's output).
2. The balance in the funding account (wallet that sent the deposit contract tx) decreased by the 10,000 \$BERA.
3. **Most importantly,** the validator's public key is present in the beacon state. The beacon state (available from your node's beacon API at `curl http://localhost:3500/eth/v1/beacon/states/head/validators | jq .data`) should show your validator's public key, likely at the end of the list.
The validator registration is reflected in beacon state once the EL block carrying the deposit
transaction is finalized. The beacon API must be enabled on your node (in `app.toml`:
`[beacon-kit.node-api]`).
## Step 5 - Activation or top-up
Having completed the registration, you can now deposit additional \$BERA. The floor for becoming a validator is **250,000 \$BERA**, and you must be among the top **69** validators, ordered by \$BERA staked.
These subsequent deposits are done by calling the same `deposit` function, with only the **public key** required; the other elements may be zero, but must still be the right length.
When your validator passes the threshold necessary to become active, it passes into the **Activation** part of the lifecycle. At the conclusion of the **second epoch** after you pass the threshold — in other words, no sooner than 13 minutes — you become eligible for minting blocks.
```bash Non-Ledger theme={null}
STAKE_AMOUNT_ETH=240000;
cast send "0x4242424242424242424242424242424242424242" \
'deposit(bytes,bytes,bytes,address)' \
"$COMETBFT_PUB_KEY" \
"0x0000000000000000000000000000000000000000000000000000000000000000" \
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" \
"0x0000000000000000000000000000000000000000" \
--private-key $PK \
--value "${STAKE_AMOUNT_ETH}ether" \
-r $RPC_URL;
```
```bash Ledger theme={null}
STAKE_AMOUNT_ETH=240000;
cast send "0x4242424242424242424242424242424242424242" \
'deposit(bytes,bytes,bytes,address)' \
"$COMETBFT_PUB_KEY" \
"0x0000000000000000000000000000000000000000000000000000000000000000" \
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" \
"0x0000000000000000000000000000000000000000" \
--ledger \
--value "${STAKE_AMOUNT_ETH}ether" \
-r $RPC_URL;
```
## Post-activation checks
After 2+ epochs after your activation deposit, do the following checks:
1. Run `beacond status`. Verify that the voting power is equal to the effective balance. If voting power is 0, the node is NOT active.
2. Your operator address receives WBERA emissions when it produces blocks (this will take a few hours).
3. The Comet API on the node will confirm your validator's presence in the set. Look for your validator's public key in the list of validators:
```bash theme={null}
curl http://localhost:26657/validators?page=1&per_page=100 | jq .
# [Example Output]:
# {
# "jsonrpc": "2.0",
# "id": -1,
# "result": {
# "block_height": "477021",
# "validators": [
# {
# "address": "5951C4349AB792BFB3A63956512663CC3B733D6E",
# "pub_key": {
# "type": "cometbft/PubKeyBls12_381",
# "value": "A/1TcQt1whFb0KrBKLc..."
# },
# "voting_power": "10000000000000000",
# "proposer_priority": "12250000000000000"
# },
# ...
# ],
# "count": "69",
# "total": "69"
# }
```
## Steps after becoming a validator
### Step 1 - Add to default validator list
Make a PR to the following GitHub repository to add your validator to the validator list on the Berachain Hub.
[https://github.com/berachain/metadata](https://github.com/berachain/metadata)
If you have a logo you'd like us to use, attach it to the pull request according to the instructions in [CONTRIBUTING](https://github.com/berachain/metadata/blob/main/CONTRIBUTING.md).
### Step 2 - Send Berachain team wallet addresses
Reach out to the Berachain validator relations team with your validator's CometBFT public keys to help find you if your node begins having trouble. If you aren't known to the validator relations team, speak up on the `#node-support` channel on Discord.
Send the output of `beacond deposit validator-keys`.
# Change Operator Address
Source: https://docs.berachain.com/nodes/guides/change-operator-address
Update the validator operator address on the execution layer (BeaconDeposit) for BeraChef and other contract interactions.
The following walks you through the process of changing your operator address using Foundry `cast`.
## What is an operator address?
The Operator Address is the \$BERA wallet address that is associated with a validator node.
It is responsible for receiving block rewards and is the sole address that has permission to change the BeraChef reward allocation.
## Requirements
Before you begin, ensure you have the following:
* Operator Address Private Key
* Your Validator Withdraw Credential Address (if different than Operator Address)
* [BeaconKit Binary](https://github.com/berachain/beacon-kit/releases) (for Validator PubKey)
* \$BERA to process the transaction
* [Foundry](https://book.getfoundry.sh/getting-started/installation)
## Procedure
This process revokes the current operator's permissions to receive block rewards and change the
validator's Reward Allocation.
### Step 1 - Get your validator PubKey
```bash theme={null}
# FROM: /path/to/beacond
# BeaconKit Configuration
YOUR_BEACOND_HOME_DIR="";
# Withdraw Credential Address - Can be the same as the Operator Address
YOUR_VALIDATOR_WITHDRAW_CRED_ADDRESS="<0xYOUR_VALIDATOR_WITHDRAW_CRED_ADDRESS>";
# Genesis Configurations - DO NOT CHANGE THESE
GENESIS_VALIDATORS_ROOT="0x053397d1ddb01f3910e91ef762070a075e4b17ba3472c3c4dd391a68bd5d95a1";
GENESIS_FORK_VERSION="0x04000000";
VAL_DEPOSIT_GWEI_AMOUNT="250000000000000";
DEPOSIT_CONTRACT_ADDRESS="0x4242424242424242424242424242424242424242";
DEPOSIT_OUTPUT=$(./beacond deposit create-validator $YOUR_VALIDATOR_WITHDRAW_CRED_ADDRESS $VAL_DEPOSIT_GWEI_AMOUNT $GENESIS_FORK_VERSION $GENESIS_VALIDATORS_ROOT --home $YOUR_BEACOND_HOME_DIR);
echo $DEPOSIT_OUTPUT;
# [Expected Similar Output]:
# 7:00AM INF Deposit Message CallData amount=0x773594000
# pubkey=0xYOUR_VALIDATOR_PUBKEY
# signature=0x...
# withdrawal credentials=0x010000000000000000000000YOUR_VALIDATOR_WITHDRAW_CRED_ADDRESS...
YOUR_VALIDATOR_PUBKEY=$(echo "$DEPOSIT_OUTPUT" | grep "pubkey=" | awk -F'pubkey=' '{print $2}' | awk -F' ' '{print $1}');
echo $YOUR_VALIDATOR_PUBKEY;
# [Expected Similar Output]:
# 0xYOUR_VALIDATOR_PUBKEY_92CHARS_INCLUDING_0X
```
### Step 2 - Check your current operator address
This will double check your current operator address.
```bash theme={null}
cast call 0x4242424242424242424242424242424242424242 "getOperator(bytes)" <0xYOUR_VALIDATOR_PUBKEY> --rpc-url http://localhost:8545;
# [Expected Similar Output]:
# 0x000000000000000000000000YOUR_CURRENT_OPERATOR_ADDRESS
```
### Step 3 - Change your operator address
This will change your operator address.
```bash theme={null}
cast send 0x4242424242424242424242424242424242424242 "requestOperatorChange(bytes,address)" <0xYOUR_VALIDATOR_PUBKEY> <0xYOUR_NEW_OPERATOR_ADDRESS> --rpc-url http://localhost:8545 --private-key <0xYOUR_CURRENT_OPERATOR_PRIVATE_KEY>;
```
# Increase Stake
Source: https://docs.berachain.com/nodes/guides/increase-validator-stake
Add BERA to an existing validator via the BeaconDeposit contract to improve position in the active set.
The following steps guide you through the process of increasing a Validator's \$BERA stake to increase their stake weight and improve the probability of proposing blocks.
This guide assumes that a Validator is already in the Active Set.
If you would like to test this out locally, consult the guide to a [local Kurtosis-based devnet](/nodes/guides/local-devnet-kurtosis), which includes instructions on testing deposits.
## Active set and \$BERA stake
Currently, the Active Set consists of **69** Validators, which is the number of Validators that can propose blocks.
The Active Set is determined by the amount of \$BERA staked, where the top stakers are included in the Active Set. To be included in the Active Set, a Validator must stake at least 10,000 \$BERA or 1 \$BERA more than the lowest staker in the Active Set (whichever is greater).
If a Validator is removed from the Active Set, all \$BERA staked to that Validator will be returned to the Validator's Withdrawal Credentials Address.
## Staking considerations
There are a few points to consider with staking:
* **Stake returned to single address** — All \$BERA staked to a Validator is returned to the Validator's Withdrawal Credentials Address. If there are multiple stakers, an agreement must be put in place with the Validator to ensure that the \$BERA is returned to the correct addresses.
* **Liquid staking protocols** — Managing multiple stakers with a single Validator can be difficult. As an alternative, Liquid Staking Protocols are an option.
* **Effective-balance updates lag actual balance** — Voting power tracks effective balance, which only moves when actual balance crosses a hysteresis threshold around the next 10,000 BERA increment. A top-up needs to clear the upward buffer of about 1,000 BERA above the next increment (\~110% of the increment) before voting power steps up. See [Validator lifecycle](/nodes/architecture/validator-lifecycle#effective-balance-and-hysteresis) for the full mechanics.
### Worked example
Take a validator currently sitting at an effective balance of 250,000 BERA who wants to step up to 260,000 BERA. The next increment boundary is 260,000 BERA; the upward buffer is +1,000 BERA above that boundary. Actual balance must reach **261,000 BERA** before effective balance steps to 260,000 BERA. A top-up of 11,000 BERA from a 250,000 BERA actual balance is enough; 10,000 BERA is not.
## Requirements
Before you begin, ensure you have the following:
* A Validator that is already in the Active Set
* A Validator's `pubkey` — PubKeys can be found at [Berachain Hub Validators](https://hub.berachain.com/validators)
* A minimum of 10,000 \$BERA or 1 \$BERA more than the lowest staker in the Active Set (whichever is greater)
* [Foundry](https://book.getfoundry.sh/getting-started/installation)
## Steps to increase a validator's \$BERA stake
```bash theme={null}
VALIDATOR_PUB_KEY=;
YOUR_ETH_WALLET_PRIVATE_KEY=;
YOUR_ETH_RPC_URL=;
YOUR_STAKED_AMOUNT=ether;
# The 0x0.. are NOT typos
cast send "0x4242424242424242424242424242424242424242" \
'deposit(bytes,bytes,bytes,address)' \
"$VALIDATOR_PUB_KEY" \
"0x0000000000000000000000000000000000000000000000000000000000000000" \
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" \
"0x0000000000000000000000000000000000000000" \
--private-key "$YOUR_ETH_WALLET_PRIVATE_KEY" \
--value $YOUR_STAKED_AMOUNT \
-r $YOUR_ETH_RPC_URL;
```
# Local Devnet with Docker
Source: https://docs.berachain.com/nodes/guides/local-devnet-docker
Run a local Berachain devnet with Docker for testing validator flows and deposits.
This tutorial walks you through launching a private local network and promoting one of the nodes in that network to a full validator.
Some features like native dApps, contracts, and more are still a work in progress.
This guide results in a simple deployment of a few validators and one RPC. If you want more control over the network, and additional services such as block explorers and PoL backends, refer to [Local Devnet with Kurtosis](/nodes/guides/local-devnet-kurtosis).
## Requirements
Before starting, ensure that you have the following installed on your computer:
* Have read and understood the [validator activation process](/nodes/guides/become-a-validator)
* [Docker](https://docs.docker.com/get-docker/) `version 25.0.2` or greater
* [Foundry](https://book.getfoundry.sh/getting-started/installation) `v1.0.0` or greater
* MacOS — This script is made for Mac but can be modified to work with Linux
## Launch local devnet
You will launch multiple Docker containers that contain execution and consensus clients for a test chain initialized from genesis.
### Step 1 - Obtain and build source
```bash theme={null}
# FROM: ~
git clone https://github.com/berachain/guides;
mv guides/apps/local-docker-devnet ./devnet;
rm -rf guides;
cd devnet;
```
Review the `env.sh` file, which contains important variables for running the docker-devnet and deposit testing.
**CHAIN\_SPEC and CHAIN\_ID** are used to influence the configuration of the deployed `beacond`. Valid values for **CHAIN\_SPEC** are `mainnet`, `testnet` and `file`. The `file` specification uses the `CHAIN_ID` to look up a chainspec file in `templates/beacond`.
The packaged Berachain genesis templates already include the EL `depositContractAddress` field
under `config` that Fusaka networks require. If you swap in a custom EL genesis, add
`"depositContractAddress": "0x4242424242424242424242424242424242424242"` under `config`.
Build the Docker devnet images:
```bash theme={null}
# FROM: ~/devnet
./build.sh;
# [Expected Result]:
# ...
# *** Build complete
```
### Step 2 - Start containers and monitor chain activity
Start the devnet:
```bash theme={null}
# FROM: ~/devnet
./start.sh;
# [Expected Output]:
# Starting Beacond...
# 0 - Creating config folders...
# 1 - Creating node configurations...
# ...
# Started!
```
Use `docker ps` to view the launched containers and verify that the services are running:
```bash theme={null}
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}";
# [Expected Output]:
# NAMES IMAGE STATUS PORTS
# el-node-rpc-0 reth-docker Up 2 minutes 0.0.0.0:8545-8546->8545-8546/tcp
# el-node-val-2 reth-docker Up 2 minutes
# el-node-val-1 reth-docker Up 2 minutes
# el-node-val-0 reth-docker Up 2 minutes
# cl-node-rpc-0 beacond-docker Up 2 minutes 0.0.0.0:3500->3500/tcp, 0.0.0.0:26657->26657/tcp
# cl-node-val-2 beacond-docker Up 2 minutes
# cl-node-val-1 beacond-docker Up 2 minutes
# cl-node-val-0 beacond-docker Up 2 minutes
```
**Monitor beacond logs for deposits, withdrawals, and blocks:**
```bash theme={null}
docker logs -f cl-node-rpc-0 | egrep '(Commit|deposit|withdraw|exit)';
# [Expected Output]:
# INFO Committed state ... height=10
# INFO Building block body ... num_deposits=1
# INFO Processing partial withdrawal ...
```
### Step 3 - Generate deposit scripts
Invoke the deposit script to generate the deposit transactions (but do not transmit them). The script provides two `cast` calls and a command to view the current validator set. There are two types of deposits: 1) initial registration and 2) top-up.
```bash theme={null}
# FROM: ~/devnet
./generate-deposit-tx.sh;
# [Expected Output]:
# Generating Signature for Parameters: ...
#
# Send this command to register the validator + deposit 10000 BERA:
# cast send ..
#
# Send this command to activate the validator by depositing 240000 BERA:
# cast send..
```
Do not send these transactions as-is on mainnet, or you will burn your funds. The devnet genesis
root differs from mainnet's, and the signature will be invalid on mainnet. Study the registration
and activation process in `generate-deposit-tx` and the [Deposit
Guide](/nodes/guides/become-a-validator) to understand how to apply this process to mainnet.
### Step 4 - Run registration deposit transaction
Transmit the first `cast` call, which calls `deposit()` for the first time with an initial stake of 10,000 \$BERA.
### Step 5 - Run activation deposit transaction
Send the second suggested `cast` call, which stakes an additional 240,000 \$BERA, sufficient to put the validator into the activation queue.
### Step 6 - Observe activation
Continue to monitor the chain's progress for three complete 10-block epochs. Upon activation, the validator status will change to `active_ongoing` — fully active and eligible to propose blocks.
### Step 7 - Send withdrawal transaction
Generate the withdrawal transactions:
```bash theme={null}
# FROM: ~/devnet
./generate-withdraw-tx.sh
# [Expected Output]:
# RPC validator pubkey is 0xaee37...
# Determined withdrawal fee: 1
#
# To send withdrawal request for 10000 BERA:
# cast send ...
#
# To exit the validator and return BERA stake:
# cast send ...
```
### Step 8 - Send exit transaction
Using the provided call to exit the validator, you will see the validator state immediately changes to `exited_unslashed` state, meaning the validator can no longer produce blocks. After the required delay in epochs, the validator's remaining stake is returned, and the status rests at `withdrawal_done`.
## Cleanup
This action will destroy all running Docker containers on your system when executed.
```bash theme={null}
# FROM: ~/devnet
./clean.sh
# [Example Similar Output]:
# Shutting down docker containers:
# el-node-val-0
# cl-node-val-0
# ...
```
# Local Devnet with Kurtosis
Source: https://docs.berachain.com/nodes/guides/local-devnet-kurtosis
Run a local Berachain devnet with Kurtosis for development and testing validator deposits.
The following guide walks you through setting up a local Berachain devnet using Kurtosis.
Some features like native dApps, contracts, and more may still be a work in progress.
## Requirements
Before starting, ensure that you have the following installed on your computer:
* [Docker](https://docs.docker.com/get-docker/) `version 25.0.2` or greater
* [Kurtosis](https://docs.kurtosis.com/install) `v0.90.1` or greater
* [Foundry](https://book.getfoundry.sh/getting-started/installation) `v1.0.0` or greater (for testing purposes)
## Kurtosis local devnet
Going through this process will set up and run multiple services, execution clients, block explorers, databases, and more.
This may require a decent amount of resources to run. If you run into limits, modify the yaml
configuration file to limit the number of services.
If you run the default Kurtosis configuration, it will run the following:
* 5 validator nodes
* 3 full nodes
* 6 additional services
You can modify the [beaconkit-local.yaml](https://github.com/berachain/beacon-kit/blob/main/kurtosis/beaconkit-local.yaml) to fit your system requirements.
The packaged Kurtosis EL genesis template already includes the `depositContractAddress` field that
Fusaka networks require. If you swap in a custom EL genesis, add `"depositContractAddress":
"0x4242424242424242424242424242424242424242"` under `config`.
### Step 1 - Clone repository and run nodes
The first step is to clone the Beacon Kit repository.
```bash theme={null}
git clone https://github.com/berachain/beacon-kit;
cd beacon-kit;
```
After cloning the repository, run `make start-devnet`.
If you encounter issues, please see [Debugging Issues](#debugging-issues) below.
It is highly recommended before running Kurtosis to disable `additional_services:` entirely for
most use-cases. See [Node Setup](#node-setup) for details.
```bash theme={null}
# FROM: ~/beacon-kit (host OS)
make start-devnet;
# [Expected Output]:
# Checking for Kurtosis installation...
# Kurtosis is already installed
# ...
# INFO ========================================================
# INFO || Created enclave: my-local-devnet ||
# INFO ========================================================
```
### Step 2 - Configure wallet
If you want to see a list of all defined wallet addresses and private keys, refer to [constants.star](https://github.com/berachain/beacon-kit/blob/main/kurtosis/src/constants.star).
Start by adding the network to your MetaMask wallet.
Your port number will be different. Use the port number from the list of services output in Step
1. Look at using a JSON-RPC service with `el-full-reth-0` or other and use the `eth-json-rpc`
port.
| Key | Value |
| --------------- | ------------------------ |
| Network | Berachain Local Devnet |
| RPC URL | `http://127.0.0.1:51208` |
| Chain ID | `80087` |
| Currency symbol | `BERA` |
Next import one of the private keys defined in [constants.star](https://github.com/berachain/beacon-kit/blob/main/kurtosis/src/constants.star).
### Step 3 - Deploy contract
To demonstrate deploying a contract to the local devnet, you can use `forge create`:
```bash theme={null}
# FROM: ~/beacon-kit (host OS)
forge create --broadcast \
--private-key fffdbb37105441e14b0ee6330d855d8504ff39e705c3afa8f859ac9865f99306 \
tmp/HelloWorld.sol:HelloWorld \
--rpc-url http://127.0.0.1:51208 \
--constructor-args "Initial greeting message";
# [Expected Output]:
# Deployer: 0x20f33CE90A13a4b5E7697E3544c3083B8F8A51D4
# Deployed to: 0x459C653FaAE6E13b59cf8E005F5f709C7b2c2EB4
# Transaction hash: 0xf18d36b5...
```
### Step 4 - Read contract
Verify that the contract was deployed and the initial message was set:
```bash theme={null}
cast call 0x459C653FaAE6E13b59cf8E005F5f709C7b2c2EB4 "getGreeting()(string)" --rpc-url http://127.0.0.1:51208;
# [Expected Output]:
# Initial greeting message
```
## Testing deposits
In this section, you upgrade one of the full nodes from the default Kurtosis deployment to a full validator, following the process laid out in the [Become a Validator](/nodes/guides/become-a-validator) guide.
### Node setup
Revise `beaconkit-local.yml` to **remove all additional services**, then clean and re-launch:
```bash theme={null}
# FROM: ~/beacon-kit (host OS)
kurtosis clean -a ; docker rm -f $(docker ps -aq);
make start-devnet;
```
Identify a full node from the list of activated containers using `kurtosis enclave inspect my-local-devnet`.
### Collect registration transaction parameters
Log into the beaconkit container:
```bash theme={null}
kurtosis service shell my-local-devnet cl-full-beaconkit-0;
```
Obtain the validator keys and genesis root hash:
```bash theme={null}
# FROM: ~ (cl-full-beaconkit-0 container)
beacond deposit validator-keys;
beacond genesis validator-root ~/.beacond/config/genesis.json;
```
Load those values into environment variables and calculate the deposit signature:
```bash theme={null}
# FROM: ~ (cl-full-beaconkit-0 container)
COMETBFT_PUB_KEY=$(beacond deposit validator-keys|tail -1);
GENESIS_ROOT=$(beacond genesis validator-root ~/.beacond/config/genesis.json);
DEPOSIT_ADDR=0x4242424242424242424242424242424242424242;
OPERATOR_ADDRESS=0x9BcaA41DC32627776b1A4D714Eef627E640b3EF5;
WITHDRAW_ADDRESS=$OPERATOR_ADDRESS;
STAKE_AMOUNT_GWEI=9000000000;
PK=fffdbb37105441e14b0ee6330d855d8504ff39e705c3afa8f859ac9865f99306;
RPC_URL=http://127.0.0.1:8547;
GENERATE_DEPOSIT_SIGNATURE=$(beacond deposit create-validator $WITHDRAW_ADDRESS $STAKE_AMOUNT_GWEI -g $GENESIS_ROOT);
WITHDRAW_CREDENTIAL=$(echo "$GENERATE_DEPOSIT_SIGNATURE" | sed -n 's/credentials: //p');
DEPOSIT_SIGNATURE=$(echo "$GENERATE_DEPOSIT_SIGNATURE" | sed -n 's/signature: //p');
```
Verify the calculated signature:
```bash theme={null}
beacond deposit validate $COMETBFT_PUB_KEY $WITHDRAW_CREDENTIAL $STAKE_AMOUNT_GWEI $DEPOSIT_SIGNATURE -g $GENESIS_ROOT;
# [Expected Output]:
# ✅ Deposit message is valid!
```
### Send registration and activation transactions
Generate the command in the container and execute it from the host OS. Send the registration transaction first, then the activation transaction with the remaining stake. After 2 complete epochs, the validator status should change to `active_ongoing`.
## Debugging issues
If Docker stops working on MacOS, first try running `kurtosis clean -a`. If the problem persists, try removing all containers and restarting Docker.
```bash theme={null}
docker rm -f $(docker ps -aq);
```
To watch logs for deposit activity:
```bash theme={null}
kurtosis service logs my-local-devnet cl-full-beaconkit-0 -f | grep num_deposits;
```
## Teardown and cleanup
To remove all services and clean up the environment:
```bash theme={null}
# FROM: ~/beacon-kit (host OS)
make stop-devnet;
make rm-devnet;
```
# Manage Incentives Commission
Source: https://docs.berachain.com/nodes/guides/manage-incentives-commission
Set and change the validator incentives commission rate (0–20%) via BeraChef; queue and activation delay.
A validator can configure the commission taken on Incentives distributed to its delegators — users who delegate to a validator with \$sWBERA. In this guide, you'll walk through the process of changing a validator's commission.
## Requirements
Before you begin, ensure you have the following:
* An RPC endpoint to Berachain
* \$BERA to process the transaction
* `Operator Address` of the validator wanting to change their commission
* [Foundry](https://book.getfoundry.sh/getting-started/installation)
* BeraChef Contract Address: `0xdf960E8F3F19C481dDE769edEDD439ea1a63426a`
## How validator commissions are updated
When Incentives are activated, all commission rates for Validator Incentives are defaulted to **5%** — meaning Validators receive 5% of all Incentives. This is defined by `DEFAULT_COMMISSION_RATE` in `BeraChef.sol`.
For a validator to change their commission rate, they must first queue the change, wait for the `commissionChangeDelay` (which has a maximum of **16,382 blocks**), and then activate the new commission. If a commission rate is already queued, it must be activated before a new commission rate can be proposed.
**Anyone** can activate a queued commission rate, not just the validator.
## Option A - Change validator commission using Foundry
The following walks you through updating the Validator Commission rate via Foundry's CLI `cast`.
### Step 1 - Get current validator commission rate
```bash theme={null}
# Env Vars
BERACHEF_CONTRACT_ADDRESS=0xdf960E8F3F19C481dDE769edEDD439ea1a63426a
RPC_URL=https://rpc.berachain.com/
VALIDATOR_PUBKEY_KEY=<0xYOUR_VALIDATOR_PUBKEY>
# Command
cast call $BERACHEF_CONTRACT_ADDRESS \
"getValCommissionOnIncentiveTokens(bytes)(uint96)" \
$VALIDATOR_PUBKEY_KEY \
--rpc-url $RPC_URL;
```
### Step 2 - Queue new validator commission rate
Determine the amount you would like to queue:
```bash theme={null}
# Env Vars
BERACHEF_CONTRACT_ADDRESS=0xdf960E8F3F19C481dDE769edEDD439ea1a63426a
RPC_URL=https://rpc.berachain.com/
OPERATOR_WALLET_PRIVATE_KEY=<0xYOUR_OPERATOR_WALLET_PRIVATE_KEY>
VALIDATOR_PUBKEY_KEY=<0xYOUR_VALIDATOR_PUBKEY>
COMMISSION_RATE=0.05e4 # 5% = 0.05e4 or 500, 100% = 1e4 or 10000
# Command
cast send $BERACHEF_CONTRACT_ADDRESS \
"queueValCommission(bytes,uint96)" \
$VALIDATOR_PUBKEY_KEY \
$COMMISSION_RATE \
--private-key $OPERATOR_WALLET_PRIVATE_KEY \
--rpc-url $RPC_URL;
```
### Step 3 - Verify queued validator commission rate
```bash theme={null}
# Env Vars
BERACHEF_CONTRACT_ADDRESS=0xdf960E8F3F19C481dDE769edEDD439ea1a63426a
RPC_URL=https://rpc.berachain.com/
VALIDATOR_PUBKEY_KEY=<0xYOUR_VALIDATOR_PUBKEY>
# Command
cast call $BERACHEF_CONTRACT_ADDRESS \
"getValQueuedCommissionOnIncentiveTokens(bytes)((uint96,uint64))" \
$VALIDATOR_PUBKEY_KEY \
--rpc-url $RPC_URL;
```
### Step 4 - Activate queued validator commission rate
This can only be done after the **`commissionChangeDelay`** has passed, and this **can be executed
by anyone**.
```bash theme={null}
# Env Vars
BERACHEF_CONTRACT_ADDRESS=0xdf960E8F3F19C481dDE769edEDD439ea1a63426a
RPC_URL=https://rpc.berachain.com/
WALLET_PRIVATE_KEY=<0xYOUR_WALLET_PRIVATE_KEY>
VALIDATOR_PUBKEY_KEY=<0xVALIDATOR_PUBKEY>
# Command
cast send $BERACHEF_CONTRACT_ADDRESS \
"activateQueuedValCommission(bytes)" \
$VALIDATOR_PUBKEY_KEY \
--private-key $WALLET_PRIVATE_KEY \
--rpc-url $RPC_URL;
```
# Manage Reward Allocations
Source: https://docs.berachain.com/nodes/guides/manage-reward-allocations
Direct PoL incentives to applications via BeraChef
This guide walks you through managing your validator's reward allocations using BeraChef via Foundry `cast` and the BeraHub UI.
We often call a validator’s BeraChef weights the **cutting board**: the split of Reward Vault emissions across whitelisted vaults.
## Requirements
* Active Validator Node
* Validator Operator Wallet Address & Private Key
* Validator PubKey
* [Foundry](https://book.getfoundry.sh/getting-started/installation) using `cast`
## Understanding reward allocations
Each validator can customize how their rewards are distributed across different reward vaults. Effective routing uses your activated allocation when it is **active** and **valid**. If not, the protocol falls back to a **baseline** allocation maintained network-wide, then to a governance-set **default** on BeraChef when needed. See [Baseline allocation, default fallback, and inactivity](#baseline-allocation-default-fallback-and-inactivity).
**Key concepts:**
* Reward allocations must total 100% (10000 basis points)
* Only whitelisted vaults can receive allocations
* Changes require queuing and a delay period before activation
* Current delay: **500 blocks**
* If you keep the same percentages, you still need to **queue and activate** a new allocation before the **inactivity window** passes (about **seven days** on mainnet today), or routing may switch to baseline
## Baseline allocation, default fallback, and inactivity
What you configure in this guide is your **custom** allocation on BeraChef. At distribution time, `getActiveRewardAllocation` resolves routing in layers:
1. **Baseline allocation** — Held in `RewardAllocatorFactory` and updated by `ALLOCATION_SETTER_ROLE`. This is the network **baseline cutting board**: a fallback for validators who do not self-manage. It applies when you have **never** activated an allocation, or when your active allocation is **inactive** (below).
2. **Default reward allocation on BeraChef** — Set by governance on `BeraChef`. If baseline weights are no longer valid (for example a vault is no longer whitelisted), routing falls back to this default.
**Inactivity:** An allocation is inactive when `block.number` is greater than its `startBlock` plus `rewardAllocationInactivityBlockSpan`. On Berachain mainnet today that span is **302,400 blocks** (about **seven days** at \~2 second blocks). Activating a queued allocation advances `startBlock` and resets the window. Some validators may be exempt on-chain (`isValExemptedFromInactivity`); exemptions are operational exceptions.
To keep your chosen vault weights, **re-queue and activate** before the window elapses—even if the percentages are unchanged.
## Option A - Using Foundry CLI
### Step 1 - Check active allocation
Start by checking your validator's current reward allocation:
```bash theme={null}
cast call 0xdf960E8F3F19C481dDE769edEDD439ea1a63426a \
"getActiveRewardAllocation(bytes)" \
"" \
--rpc-url https://rpc.berachain.com/;
```
The output is your validator's `RewardAllocation` struct, a tuple containing:
1. The allocation start block
2. An array of tuples, each containing the vault address and the percentage numerator (adding up to `10000`)
### Step 2 - Queue new allocation
An example command to queue a new allocation resembles the following:
```bash theme={null}
cast send 0xdf960E8F3F19C481dDE769edEDD439ea1a63426a \
"queueNewRewardAllocation(bytes,uint64,tuple(address,uint96)[])" \
"" \
"$START_BLOCK" \
"[(0x12345...,5000),(0x56789...,5000)]" \
--private-key \
--rpc-url https://rpc.berachain.com/
```
Your `START_BLOCK` must be greater than the current block number + the block delay (500 blocks).
### Step 3 - Check your queued allocation
Check your new pending allocation:
```bash theme={null}
cast call 0xdf960E8F3F19C481dDE769edEDD439ea1a63426a \
"getQueuedRewardAllocation(bytes)" \
"" \
--rpc-url https://rpc.berachain.com/;
```
Once the `startBlock` is reached, the new allocation will be automatically activated the next time rewards are distributed for your validator.
## Option B - Using BeraHub UI
You can also manage your reward allocations through the Berachain Dashboard:
1. Navigate to the [Validator Dashboard](https://hub.berachain.com/validators) on Berachain Hub
2. Connect your validator operator wallet
3. Click **Manage as a validator**
4. Click the **Configuration** tab
5. Select your vaults and choose desired allocation percentages (ensuring they add up to 100%)
6. Click **Queue** and submit the transaction
## See also
* [Block production & rewards](/general/proof-of-liquidity/block-rewards) — How Reward Vault emission and allocations interact at distribution time.
# Withdraw Stake
Source: https://docs.berachain.com/nodes/guides/withdraw-stake
Partial or full withdrawal of validator BERA: withdrawal credentials, fees, minimum stake, and finalization delay.
The holder of the withdrawal credential, set during the initial deposit, can trigger partial or complete withdrawal of \$BERA stake from a Validator. This process is demonstrated in the [Docker Devnet's final steps](/nodes/guides/local-devnet-docker).
Withdrawing your \$BERA stake will reduce the probability that your validator is selected to
produce a block and therefore reduce your Reward Vault emissions. Withdrawing all stake will exit
your validator from the Active Set.
## Requirements
* A fully-synced full node
* [Foundry](https://book.getfoundry.sh/getting-started/installation) v1.0.0 or greater
* Validator Withdrawal Credential Address Private Key or Ledger
## Withdrawal rules and process
Withdrawals occur at the end of the 256th epoch (\~27 hours) after the epoch in which you perform the withdrawal.
Every withdrawal requires a fee, which will increase if the withdrawal service experiences an unusual volume of requests.
The withdrawal transaction must originate from the Withdrawal Credential Address for the validator to perform a withdrawal.
The withdrawal API will silently adjust the withdrawal amount to **maintain a minimum stake of 250,000 \$BERA**. For instance, a validator with 350,000 \$BERA staked that requests a withdrawal of 300,000 \$BERA will only withdraw 100,000 \$BERA.
To exit your validator from the Active Set and return the entire stake, use the special withdrawal amount of `0`. Then, as described in the [lifecycle overview](/nodes/architecture/validator-lifecycle), your validator will immediately be queued to exit the active set and no longer produce blocks. The stake will be returned by the consensus layer on the above schedule.
If you leave the Active Set, **future deposits to your CometBFT public key will be refunded to the
withdrawal address**. If you want to [activate as a validator](/nodes/guides/become-a-validator)
again, you must do so with a new CometBFT identity.
## How to withdraw \$BERA stake
The following walks you through how to withdraw a portion of a validator's \$BERA stake with the validator withdrawal credentials on Berachain Bepolia.
### Step 1 - Configurations
Set up your environment. You will need the private key of your Withdrawal Credential Address, or to have it on an attached ledger. To obtain your CometBFT public key, you can invoke `beacond --home path/to/beacond/data deposit validator-keys`.
```bash theme={null}
export RPC_URL=https://bepolia.rpc.berachain.com/
export WITHDRAW_CONTRACT=0x00000961Ef480Eb55e80D19ad83579A64c007002
export WITHDRAW_AMOUNT_ETH=10000
# Alternatively
export WITHDRAW_AMOUNT_GWEI=${WITHDRAW_AMOUNT_ETH}000000000
export COMETBFT_PUB_KEY=<0xYOUR_COMETBFT_PUBLIC_KEY>
export WITHDRAW_CREDENTIAL_PRIVATE_KEY=
```
Use the special amount of `0` to withdraw the Validator's entire stake.
### Step 2 - Determine withdrawal fee
In addition to the desired amount to withdraw, a withdrawal fee must be sent with the transaction.
```bash theme={null}
WITHDRAW_FEE_HEX=$(cast call -r $RPC_URL $WITHDRAW_CONTRACT);
WITHDRAW_FEE=$(cast to-dec $WITHDRAW_FEE_HEX);
echo $WITHDRAW_FEE;
# [Typical Output, may vary]
# 1
```
### Step 3 - Create withdrawal request
Package the desired withdrawal amount with the Validator's identity as an encoded request.
```bash theme={null}
WITHDRAW_REQUEST=$(cast abi-encode --packed '(bytes,uint64)' $COMETBFT_PUB_KEY $WITHDRAW_AMOUNT_GWEI);
echo $WITHDRAW_REQUEST;
# [Typical Output, will vary]
# 0xacaf2e8ec309513...0009184E72A000
```
### Step 4 - Send withdrawal request
Send the withdrawal request to the contract, from the Validator's Withdraw Credential Address.
Remember to send it from the Withdrawal Credential Address, otherwise the transaction will fail.
```bash theme={null}
cast send $WITHDRAW_CONTRACT $WITHDRAW_REQUEST --rpc-url $RPC_URL --private-key $WITHDRAW_CREDENTIAL_PRIVATE_KEY --value ${WITHDRAW_FEE}wei;
```
Substitute `--ledger` for the `--private-key` if your key is kept on a hardware module.
### Step 5 - Monitor Beacon Kit API
To ensure the withdrawal request succeeded, monitor the Beacon Kit Validator API to observe your stake's change.
The beacon API must be enabled on your node (in `app.toml`: `[beacon-kit.node-api]`).
The beacon state is available from your node's beacon API via:
```bash theme={null}
curl http://localhost:3500/eth/v1/beacon/states/head/validators | jq '.[] | select(type == "object" and .validator? and .validator.pubkey == "$COMETBFT_PUB_KEY")'
```
This request should show your validator's status.
# Validator FAQ
Source: https://docs.berachain.com/nodes/help/faq
Frequently asked questions: getting BERA, technical support, and where to get help for node operations.
## How do I get enough BERA for a validator?
Buy \$BERA on the open market through various DEXs and CEXs, or through the [Berachain Hub](https://hub.berachain.com/).
## I need help with a technical problem
Visit the [Discord](https://discord.gg/berachain) `#node-support` channel. Make sure you have reviewed the [Production Checklist](/nodes/operations/production-checklist) first.
# Migrate Bera-Geth -> Bera-Reth
Source: https://docs.berachain.com/nodes/operations/bera-geth-to-reth
Replace your execution client, match your old configuration, install a snapshot.
You are replacing the execution client only. BeaconKit (`beacond`) keeps the same validator identity, consensus history, and Engine API handshake with the EL.
Reth does not read Geth's chain database. Plan to retire the Geth installation after Reth is healthy.
## Layout
Below is an example layout on disk that we will reason with in this guide. Your installation can, of course, vary. The example below uses `/srv/bera` so systemd units, Compose files, and on-call runbooks all refer to the same paths.
**Suggested tree** (adjust names to match your host):
```
/srv/bera/
├── beacond/ # Consensus home
│ ├── config/
│ │ ├── app.toml
│ │ ├── genesis.json # CL genesis (already present)
│ │ ├── jwt.hex # Engine secret: same file Reth reads
│ │ └── ...
│ └── data/
└── reth/
├── genesis.json # EL genesis for bera-reth --chain
└── data/ # bera-reth node --datadir (empty until init or snapshot)
```
**Consensus home** is the directory you pass to `beacond` as `--home`. It always contains `config/` and `data/`. For a migration, the lowest-risk approach is to **take a filesystem copy of the entire `beacond` tree from the host that currently runs Geth, while `beacond` is stopped.** That copy preserves `priv_validator_key.json`, current `app.toml`, `config.toml`, address book, and CL chain data exactly as they were under Geth.
**Reth datadir** is where reth configuration and chain data is. In later steps you will initialize Reth here or install a snapshot.
1. \*\*jwt.json \*\* is the shared secret so BeaconKit and Geth can communicate. You only need one copy. Set both `jwt-secret-path` in BeaconKit's `app.toml` and Reth's `--authrpc.jwtsecret` to that exact full path.
2. **Execution genesis** — The execution genesis JSON is required. Your copy should be fine, or download the canonical `eth-genesis.json` from [berachain/beacon-kit](https://github.com/berachain/beacon-kit/tree/main/testing/networks), and place it in `reth/genesis.json`. Fusaka networks require the genesis JSON to include `"depositContractAddress": "0x4242424242424242424242424242424242424242"` under `config`; the default Berachain genesis already carries this field.
## Invocation
**Collect launch options from Geth before you decommission it.** Open the unit file, Compose service, or shell wrapper that starts Geth today and note every path and port. Many of these are covered by reasonable Reth defaults.
The guides [`run-reth.sh`](https://github.com/berachain/guides/blob/main/apps/node-scripts/run-reth.sh) is a worked example of flags that work with BeaconKit; treat it as a checklist against your own systemd or kubernetes pod.
Bera-Reth inherits Reth's CLI. The full upstream option list for the node command is documented at **[reth node (CLI reference)](https://reth.rs/cli/reth/node/)**. Many flags are the same between Bera-Geth and Bera-Reth:
| Geth (typical) | Reth equivalent |
| --------------------------------------------------------- | -------------------------------------------------------- |
| `--datadir` | same |
| `--chain /path/genesis.json` | same |
| `--http`, `--http.addr`, `--http.port` | same |
| `--authrpc.addr`, `--authrpc.port`, `--authrpc.jwtsecret` | same |
| `--nat extip:IP` | same; this option is highly recommended |
| `--port` (execution P2P) | `--port` and `--discovery.port` |
| `--static-peers ` | `--trusted-peers ` |
| Full node vs archive | Pruned-style: pass `--full`. Full archive: omit `--full` |
Pass `--chain` with your EL genesis JSON path on every `bera-reth node` start, not only after `init`.
**Bootnodes** — Use the current [mainnet](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80094/el-bootnodes.txt) or [Bepolia](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80069/el-bootnodes.txt) `el-bootnodes.txt` with `--bootnodes` (comma-separated `enode://` lines).
Typical Reth startup structure:
```
EL_BOOTNODES=$(grep '^enode://' "el-bootnodes.txt"|tr '\n' ','|sed 's/,$//')
$RETH_BIN node \
--datadir $RETH_DATA \
--chain $RETH_GENESIS_PATH \
--full \
--bootnodes $BOOTNODES_OPTION \
--nat extip:$EXTERNAL_IP_ADDR \
--authrpc.jwtsecret $JWT_PATH \
--metrics 9101 \
--http \
--http.addr 0.0.0.0 \
--http.port 8545 \
--ipcpath /tmp/reth.ipc \
--log.file.directory $LOG_DIR \
--engine.persistence-threshold 0 \
--engine.memory-block-buffer-target 0
```
## Option 1: Init and sync from genesis
This path is appropriate when you accept a long execution-layer sync from genesis and want minimal moving parts.
1. Remove or rename the Geth datadir so Reth never opens it by mistake.
2. Ensure `eth-genesis.json` is at your EL genesis path ([EVM Execution](/nodes/architecture/evm-execution)).
3. Initialize Reth's database directory:
```bash theme={null}
bera-reth init --datadir /srv/bera/reth/data --chain /srv/bera/reth/genesis.json
```
5. After you **Start** (below), Reth syncs execution state from the network. Expect this to take much longer than restoring a snapshot unless you are very close to genesis.
## Option 2: Install snapshot
Choose this path when you want to land near head on disk instead of waiting for a full EL sync.
1. **Download** — Official Bera snapshots are `.tar.lz4` files. The [snapshot downloader](https://raw.githubusercontent.com/berachain/guides/main/apps/node-scripts/fetch-berachain-snapshot.js) is a standalone Node.js 18+ script. It accepts `--help`.
```Options: theme={null}
-n, --network mainnet or testnet (default: mainnet)
-t, --type pruned or archive (default: pruned)
-o, --output download directory (default: downloads)
--el-client execution row prefix in CSV (default: reth)
--beacon-only beacon-kit snapshot only
--execution-only, --el-only
execution-layer snapshot only
-h, --help show this help
```
Example:
```bash theme={null}
curl -sO https://raw.githubusercontent.com/berachain/guides/main/apps/node-scripts/fetch-berachain-snapshot.js
node fetch-berachain-snapshot.js --execution-only -o ./downloads
```
Using a downloaded snapshot:
1. **Stop** both `beacond` and the execution client before unpacking anything into the Reth datadir.
2. **Extract** — Current official Reth snapshot archives place **`db/`**, **`rocksdb/`**, and **`blobstore/`** at the **tar root**, not under a `data/` folder. Point `tar` **`-C` at your `--datadir`** so those directories land directly inside it. For the suggested tree, `--datadir` is `/srv/bera/reth/data` and thus:
```bash theme={null}
EL_SNAPSHOT=$(ls /path/to/downloads/*reth*.tar.lz4 | head -1)
lz4 -d "$EL_SNAPSHOT" | tar xv -C /srv/bera/reth/data
```
If you ever see tarball paths prefixed with `data/` (e.g. `tar -tv` shows `data/db/...`), unpack with `-C` set to the **parent** of `--datadir` instead so `data/` becomes the datadir. If Reth partially synced earlier, empty `--datadir` before extracting.
## Start
Start `beacond` and `bera-reth` with whatever supervisor you already use. We recommend EL, then CL. Provided the JWT path and auth RPC port agrees between Reth and BeaconKit (see its `rpc-dial-url` setting), they should connect and begin following the chain.
Watch `beacond` logs for a successful execution-client connection. Hit your configured HTTP RPC port on Reth (often `8545`) with a simple `eth_blockNumber` once you expect sync to move.
When Reth is stable and you no longer need rollback to Geth, delete or archive the old Geth datadir to free space.
For a brand-new node built entirely from the guides scripts, see [Quickstart: Run a Node](/nodes/operations/quickstart).
# Monitoring
Source: https://docs.berachain.com/nodes/operations/monitoring
Set up Prometheus and Grafana to monitor BeaconKit and execution layer metrics, endpoints, and sample dashboards.
Berachain nodes operate as a joined pair of execution layer and consensus layer. The healthy operation of these servers can be tracked by monitoring metrics exposed over Prometheus endpoints, which are collected by Prometheus and graphed with Grafana. This guide describes setting up monitoring of a Berachain node with Prometheus and Grafana.
## Prometheus and Grafana
Prometheus is an open-source monitoring and alerting toolkit designed for reliability and scalability. It functions as a time series database that collects and stores metrics from monitored targets at regular intervals. Prometheus works on a pull-based model, where it scrapes HTTP endpoints exposed by services like `beacond` or `bera-reth`. These services listen on dedicated ports and respond with metrics in a simple text-based format. For Berachain nodes, Prometheus is essential for tracking performance metrics, resource utilization, and operational health over time.
Grafana is a visualization and analytics platform often paired with Prometheus. While Prometheus collects and stores metrics, Grafana provides a powerful interface to query, visualize, and understand that data through customizable dashboards. It allows node operators to create graphs, charts, and alerts based on Prometheus metrics, making it easier to monitor node performance, identify issues, and track the health of Berachain nodes over time.
### Setup
Grafana has commercial ("enterprise") and open-source variants. Refer to its [installation instructions](https://grafana.com/docs/grafana/latest/setup-grafana/installation/debian/).
Prometheus is fully open-source. Refer to its [installation instructions](https://prometheus.io/docs/prometheus/latest/installation/).
Once installed, set up Grafana so that you can sign in as an administrator, and set up the Prometheus data source (by default on `localhost:9100`).
The following additional packages are recommended:
* `prometheus-blackbox-exporter` monitors TCP and HTTP endpoints, providing Prometheus metrics
* `prometheus-node-exporter` collects operating system metrics from the host computer
* `prometheus-alertmanager` to identify failure conditions and dispatch alerts
## What to monitor
At minimum:
1. The **public** TCP/IP endpoints for your Beacon Kit, generally on port 26656.
2. The **public** TCP/IP endpoint for your execution layer, usually on TCP port 30303.
3. The **block height** for both of these.
4. **Operating system telemetry**.
It is not sufficient to monitor an internal IP address when the important thing is whether the system is reachable from the Internet.
## Monitoring service endpoints
The following Prometheus configuration sets up monitoring for TCP endpoints:
```yaml /etc/prometheus/prometheus.yml theme={null}
scrape_configs:
- job_name: listening
metrics_path: /probe
params:
module: [tcp_connect]
static_configs:
- targets: ["a.b.c.d:30303", "a.b.c.d:26656"]
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: 127.0.0.1:9115
```
In the above configuration, monitoring is set up to ensure port 26656 (a beacond instance) and 30303 (a bera-reth instance) are listening.
When you restart Prometheus with this configuration, it should begin publishing a `probe_success` metric with a 0 or 1 value to indicate DOWN or UP.
## BeaconKit metrics
BeaconKit must have the Prometheus instrumentation enabled. To do this, revise the configuration:
```toml config.toml theme={null}
[instrumentation]
prometheus = true
prometheus_listen_addr = "0.0.0.0:9107"
```
This enables the BeaconKit client to listen on port 9107. As a precaution, ensure this port can't be reached by the public with a firewall or by scoping the address to your administrative network instead of `0.0.0.0`.
Then, add this endpoint to Prometheus by referring to the metrics port:
```yaml /etc/prometheus/prometheus.yml theme={null}
scrape_configs:
- job_name: beacond
static_configs:
- targets: ["localhost:9107"]
```
With this enabled, beacond exports a considerable number of metrics. Here are some of the more useful ones:
* `cometbft_consensus_height` — the block height of the Beacon Chain
* `cometbft_consensus_rounds` — reports the number of consensus rounds CometBFT has gone through for the current block. This should normally not rise above 1.
* `cometbft_p2p_message_receive_bytes_total` (and `cometbft_p2p_message_send_bytes_total`) — show the network traffic received and sent
* `cometbft_p2p_peers` — the total (incoming + outgoing) peer connections to `beacond`
## Execution layer metrics
Bera-Reth allows you to enable metrics with these command line options:
```bash theme={null}
--metrics
--metrics.port 9108
--metrics.addr 0.0.0.0
```
The address should be either on a private network or not accessible to the public via firewall rule. Bera-Reth publishes the metrics at `/metrics`.
After restarting your EL to begin publishing metrics at your chosen port, add this endpoint to Prometheus:
```yaml /etc/prometheus/prometheus.yml theme={null}
scrape_configs:
- job_name: reth
metrics_path: /metrics
static_configs:
- targets: ["localhost:9108"]
```
### Reth metrics
* `reth_sync_checkpoint` — the chain height, with details available on the height/progress of every sync step (there are \~14)
* `reth_network_outgoing_connections` and `reth_network_incoming_connections` — the number of connections propagating transactions and blocks
* `reth_transaction_pool_pending_pool_transactions` — the number of transactions pending in the pool (waiting to be executed)
* `reth_sync_execution_gas_per_second` — the execution engine's performance, measured in gas/sec
## Sample dashboard
All of the above metrics are collected into a sample Grafana dashboard. If you would like to start with this dashboard as a basis for your system, download the dashboard description file — as a JSON file which can be imported into Grafana — at [https://github.com/berachain/guides/tree/main/apps/grafana/sample-dashboard.json](https://github.com/berachain/guides/tree/main/apps/grafana/sample-dashboard.json).
## Further exploration
[This article](https://research.despread.io/berachain-monitoring/) by Despread, a Berachain Validator, provides useful insight about monitoring what's happening on the Beacon Chain.
Set up alerts in Grafana to dispatch notifications when a service goes down, or you begin to run low on disk space.
Grafana offers a feature called Drilldown that allows you to explore the metrics available to you. Some metrics are more useful than others.
Synthetic metrics combine data from different sources to create new metrics. A good example of the application of this idea to Berachain is available at [StakeLab's monitoring-tools repository](https://github.com/StakeLab-Zone/monitoring-tools/tree/main).
# Osaka1 Upgrade
Source: https://docs.berachain.com/nodes/operations/osaka1
Validator and developer changes from the Osaka1 upgrade on Berachain Mainnet & Bepolia.
Osaka1 is a Berachain execution-layer hardfork that floors the **minimum base fee** and **minimum blob base fee** at **1 gwei** each after activation.
## Upgrade timing
| Network | Activation Date (UTC) | Binaries Available |
| --------------- | -------------------------- | ------------------ |
| Bepolia Testnet | 4pm / 16:00 July 22, 2026 | July 14 |
| Mainnet | 4pm / 16:00 August 5, 2026 | July 29 |
## What changes
After the fork activates, Bera-Reth enforces configurable floors on the two EIP-1559 fee parameters:
* **Minimum base fee:** `1 gwei` (`1_000_000_000` wei). The next-block base fee can no longer decay below this value.
* **Minimum blob base fee:** `1 gwei` (`1_000_000_000` wei). The EIP-4844 blob base fee is lifted to this floor, shifting the entire exponential curve baseline.
Osaka1 is carried in the built-in `--chain bepolia` and `--chain mainnet` presets shipped with Bera-Reth v1.4.4. The Berachain genesis `berachain.osaka1` block for Mainnet is:
```json theme={null}
"osaka1": {
"time": 1785945600, # 2026-08-05 16:00:00 UTC
"minimumBaseFeeWei": 1000000000,
"minimumBlobBaseFeeWei": 1000000000
}
```
## Upgrade instructions
**VALIDATORS:** Since the Osaka1 Hardfork, be sure to remove the following flag when running bera-reth:
```shellscript theme={null}
# REMOVE THIS FLAG
--txpool.minimum-priority-fee 1000000000
```
1. Download the [recommended versions](/nodes/architecture/evm-execution) for your chain.
2. If you use the built-in chain preset, no genesis file change is needed:
1. Mainnet
1. `--chain mainnet`
2. Bepolia
1. `--chain bepolia`.
3. If you use `--chain `, fetch the genesis files for each respective chain:
1. Mainnet
1. [Mainnet genesis from Beacon Kit main branch](https://raw.githubusercontent.com/berachain/beacon-kit/2059e2abea2a42513a0617a38cd7f758d1a1e6a1/testing/networks/80094/eth-genesis.json) and overwrite your current genesis file.
2. Verify the md5: `d367b133f368367c3e973636ad059c79`.
2. Bepolia
1. [Bepolia genesis from Beacon Kit v1.4.2-rc.0](https://raw.githubusercontent.com/berachain/beacon-kit/refs/tags/v1.4.2-rc.0/testing/networks/80069/eth-genesis.json) and overwrite your current genesis file.
2. Verify the md5: `f7d31d1039b0253f5f43336e55d28054`.
4. No changes are needed to BeaconKit options.
5. Start the new `beacond` and `bera-reth` binaries.
# Production Checklist
Source: https://docs.berachain.com/nodes/operations/production-checklist
Actionable pre-support checks for Beacon Kit (beacond) and Bera-Reth: versions, peering, launch flags, and ops hygiene.
## Recommended releases
[Current versions of Beacon Kit and Bera-Reth are listed here.](/nodes/architecture/evm-execution)
## Peering
If you are running in a containerized environment, ensure your services are properly advertising their real network address, and that traffic is being directed into the container, both for Beacon Kit and your execution client.
Both the CL and EL should have **no static or persistent peers** set up, unless they are for your internal network or business partners you want permanent connections to.
Review the following sections for specific peering advice for Beacon-Kit and Bera-Reth.
***
## Beacon Kit (`beacond`)
The [diagnosis script](https://github.com/berachain/guides/tree/main/apps/node-scripts/node-diagnostic.sh) inspects **`beacond`** only. It does not check Bera-Reth, JWT paths, or EL peering.
```bash theme={null}
/path/to/node-diagnostic.sh -d /var/beacond/ -p /opt/bin/beacond
```
Use your Beacon Kit home (the directory with `config/` and `data/` inside it) and your `beacond` binary. Attach the full output when asking for support.
### Consensus-layer peering
| Topic | Action |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Firewall** | Open **TCP 26656** to the internet (or publish correctly through your load balancer / host networking). |
| **`p2p.external_address`** | Set in `config.toml` to the **routable** IP and port peers should dial. Required behind NAT or in containers; without it `beacond` advertises an internal address. |
| **Bootnodes** | `beacond` uses the seed list in `config.toml`. Confirm you are on a current release with the [recommended list](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80094/config.toml#L199). |
| **Peer limits** | **40 inbound + 10 outbound** is recommended to cap memory use. |
```toml theme={null}
max_num_inbound_peers = 40
max_num_outbound_peers = 10
```
***
## Bera-Reth
### Execution-layer peering
The execution layer needs excellent peering so transactions reach your validator for block sealing. Open **TCP** and **UDP** on the devp2p port (default **30303**).
| Topic | Action |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`--bootnodes`** | Build from one current [mainnet `el-bootnodes.txt`](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80094/el-bootnodes.txt) or [Bepolia](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80069/el-bootnodes.txt). Do not merge legacy files or paste third-party **`enode://`** lines. Prefer an env var (**`BOOTNODES`**) and **`--bootnodes "$BOOTNODES"`**. |
| **`--nat extip:`** | Required on cloud or NAT hosts without UPnP (example: **`--nat extip:203.0.113.50`**). Value must be the IPv4 peers dial. |
| **`--trusted-peers`** | Omit unless you need a **private** mesh. Never duplicate public bootnodes here. |
### Reth flags you need
**Minimal example** — only flags you really should set. Relies on secure and reasonable Bera-Reth defaults (see table below). Set PUBLIC\_IPV4 to your node's external address.
```bash theme={null}
bera-reth node \
--chain /srv/bera/reth/genesis.json \
--datadir /srv/bera/reth/data \
--full \
--authrpc.jwtsecret /srv/bera/beacond/config/jwt.hex \
--log.file.directory /var/log/bera-reth \
--metrics 127.0.0.1:9090 \
--nat extip:PUBLIC_IPv4 \
--engine.persistence-threshold 0
```
**Full example** — every flag stated explicitly, useful when you need non-default ports or want a self-documenting unit file. \\
```
bera-reth node \
--chain /srv/bera/reth/genesis.json \
--datadir /srv/bera/reth/data \
--full \
--port 30303 \
--discovery.port 30303 \
--http \
--http.addr 127.0.0.1 \
--http.port 8545 \
--ipcpath /tmp/reth.ipc \
--authrpc.addr 127.0.0.1 \
--authrpc.port 8551 \
--authrpc.jwtsecret /srv/bera/beacond/config/jwt.hex \
--log.file.directory /var/log/bera-reth \
--metrics 127.0.0.1:9090 \
--nat extip:PUBLIC_IPv4
```
If a flag's default already matches your deployment, omit it. Shorter unit files are easier to audit and less likely to drift from upstream changes:
| Flag(s) | Default | Verify |
| ----------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **`--chain`** | `mainnet` | Must point at the EL genesis JSON for this network. Always set explicitly. |
| **`--datadir`** | OS data dir | Must point at the datadir you initialized. Always set explicitly. |
| **`--full`** | off (archive) | **Set** for pruned / non-archive validators. Omit only for a deliberate full-archive EL. |
| **`--port`** | `30303` | Omit if 30303 is fine. Open **TCP+UDP** at the firewall. |
| **`--discovery.port`** | same as **`--port`** | Omit unless you split discovery to a different UDP port. |
| **`--http`** | off | **Set** to enable HTTP JSON-RPC. |
| **`--http.addr`** | `127.0.0.1` | Default is safe. Only change if providing RPC service internally or externally. |
| **`--http.port`** | `8545` | Omit if 8545 is fine. |
| **`--ipcpath`** | `/tmp/reth.ipc` | Change when running multiple Reth instances. |
| **`--authrpc.addr`** | `127.0.0.1` | Default is safe. Do not change unless you are splitting beacon and reth machines. |
| **`--authrpc.port`** | `8551` | Must match Beacon Kit **`rpc-dial-url`**. Omit if 8551 matches. |
| **`--authrpc.jwtsecret`** | none | **Always set.** Must be the same JWT file **`beacond`** uses. |
| **`--log.file.directory`** | OS cache dir | **Set** to a path you manage with **`logrotate`** (or equivalent). Default buries logs in a platform cache directory. |
| **`--metrics`** | off | **Set** to an interface and port that Prometheus can scrape. Do not expose publicly. See [Monitoring](/nodes/operations/monitoring). |
| **`--nat`** | `any` (UPnP) | **Set `extip:`** on cloud/NAT hosts where UPnP does not work. Value must be the routable IPv4. |
| **`--engine.persistence-threshold`** | `2` | **Set `0`** for Beacon Kit alignment. |
| **`--engine.memory-block-buffer-target`** | `0` | Already correct. Omit or set explicitly for clarity. |
### Reth flags you may not need
| Optional flag | When to add |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **`--bootnodes`** | Bera-Reth bakes in our recommended defaults. |
| **`--trusted-peers`** | Omit unless you have a private mesh among multiple nodes you operate. |
| **`--ws`** | Omit unless a local consumer needs WebSocket RPC. |
| **`--http.corsdomain '*'`** | Only when you accept browser callers from any domain. |
| **`--http.api`** | Default is secure and reasonable. Only override if you need to. Never put **`admin`** or **`debug`** on a publicly-reachable RPC. |
***
## Set your validator name and logo on the Hub
Validators should identify themselves so ops can reach you. Send a PR to [Validator Metadata](https://github.com/berachain/metadata), then ping **`#node-support`** on [Discord](https://discord.gg/berachain) so we can let you into dedicated validator support channels.
## Operational hygiene
See **[Monitoring](/nodes/operations/monitoring)** for full Prometheus and Grafana setup.
# Quickstart: Run a Node
Source: https://docs.berachain.com/nodes/operations/quickstart
Prerequisites, scripts, and steps to run BeaconKit and Bera-Reth on Linux or Mac.
A Berachain node consists of two clients running together: a **consensus client** (BeaconKit) and an **execution client** (Bera-Reth). This guide walks you through setting up both on a Linux or Mac computer.
## Prerequisites
### Hardware
| Component | Requirement |
| ------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **OS** | Linux AMD64, Linux ARM64 |
| **CPU / RAM** | 4 Physical Cores, 32GB RAM |
| **Storage** | 1TB minimum (more for long-term); Local SSD or on-instance storage preferred; network volumes require at least 1000 IOPS |
### Software
Check [EVM Execution Clients](/nodes/architecture/evm-execution) for the current recommended **Beacon Kit** and **Bera-Reth** releases for your network, then download each binary from its GitHub release page:
1. **BeaconKit** — on the [Beacon Kit releases page](https://github.com/berachain/beacon-kit/releases), open the tagged release for your network and download the build for your OS and architecture. Make it executable and place it in your PATH (e.g., `~/.local/bin/`).
2. **Bera-Reth** — on the [Bera-Reth releases page](https://github.com/berachain/bera-reth/releases), open the matching tagged release and download the build for your OS and architecture. Make it executable and place it in your PATH.
Verify installation:
```bash theme={null}
beacond version
bera-reth --version
```
Both commands should report the release listed for your network on [EVM Execution Clients](/nodes/architecture/evm-execution).
## What you'll do
1. **Download scripts** — clone helper scripts that automate configuration
2. **Configure environment** — set environment variables for your network (`mainnet` or `bepolia`) and node identity
3. **Fetch parameters** — download consensus-layer genesis and network configuration
4. **Set up BeaconKit** — initialize the consensus client and generate keys
5. **Set up execution client** — initialize the Reth datadir for your chain
6. **Fetch snapshots (optional)** — restore snapshots to speed up initial sync
7. **Run both clients** — launch them in separate terminals; they communicate via JWT auth
Optional step 8 covers testing your node's RPC endpoints.
For production deployments, consider [docker
images](https://github.com/berachain/beacon-kit/pkgs/container/beacon-kit) or
[community-maintained Ansible playbooks](https://github.com/RhinoStake/ansible-berachain) that
deploy docker containers. For local experimentation with validator mechanics, see [Local Devnet
with Kurtosis](/nodes/guides/local-devnet-kurtosis).
## Step 1 - Download scripts
Make an area to work in, then clone the Berachain node scripts. These scripts handle configuration, parameter fetching, and client startup.
If you're a Unix traditionalist, use `/opt/beranode` as your working directory.
```bash theme={null}
# FROM: $HOME
mkdir beranode;
cd beranode;
git clone https://github.com/berachain/guides;
mv guides/apps/node-scripts/* ./;
rm -r guides;
ls;
# [Expected output, edited for clarity]
# README.md run-reth.sh setup-reth.sh
# env.sh run-beacond.sh setup-beacond.sh
# fetch-berachain-params.sh
```
The file `env.sh` contains environment variables used in the other scripts.
`fetch-berachain-params.sh` downloads consensus-layer configuration files.
`setup-` and `run-` scripts start the execution client and `beacond`.
## Step 2 - Configure environment
Edit `env.sh` to set your node's configuration. Open the file and modify these values:
```bash env.sh theme={null}
#!/bin/bash
# CHANGE THESE VALUES
export CHAIN=mainnet # mainnet or bepolia
export MONIKER_NAME=camembera
export WALLET_ADDRESS_FEE_RECIPIENT=0x8b30eb59e9b2354825503d5e60845eb41d4caf36
export EL_ARCHIVE_NODE=false # set to true if you want to run an archive node on CL and EL
export MY_IP=`curl -s canhazip.com`
# VALUES YOU MIGHT WANT TO CHANGE
export LOG_DIR=$(pwd)/logs
export JWT_PATH=$BEACOND_CONFIG/jwt.hex
export BEACOND_BIN=$(command -v beacond || echo $(pwd)/beacond)
export BEACOND_DATA=$(pwd)/var/beacond
export RETH_BIN=$(command -v bera-reth || echo $(pwd)/bera-reth)
```
You need to set these constants:
1. **CHAIN**: Set to `mainnet` or `bepolia`.
2. **MONIKER\_NAME**: A name of your choice for your node.
3. **WALLET\_ADDRESS\_FEE\_RECIPIENT**: The address that receives priority fees for blocks sealed by your node. If your node will not be a validator, this won't matter.
4. **EL\_ARCHIVE\_NODE**: Set to `true` if you want the execution client to be a full archive node.
5. **MY\_IP**: Sets the IP address your chain clients advertise to other peers on the network. In a cloud environment such as AWS or GCP where you are behind a NAT gateway, you **must** specify this address or allow the default `curl canhazip.com` to auto-detect it.
You should verify these constants:
* **LOG\_DIR**: This directory stores log files.
* **BEACOND\_BIN**: Set this to the full path where you installed `beacond`. The expression provided finds it in your \$PATH.
* **BEACOND\_DATA**: Set this to where the consensus data and config should be kept.
* **RETH\_BIN**: Set this to the full path where you installed `bera-reth`. The expression provided finds it in your \$PATH.
## Step 3 - Fetch parameters
The `fetch-berachain-params.sh` script downloads consensus-layer network parameters for the chain you configured:
```bash theme={null}
# FROM: ~/beranode
./fetch-berachain-params.sh;
# [Expected Output for mainnet - verify these checksums]:
# c66dbea5ee3889e1d0a11f856f1ab9f0 seed-data-80094/genesis.json
# 5d0d482758117af8dfc20e1d52c31eef seed-data-80094/kzg-trusted-setup.json
# [Expected Output for bepolia - verify these checksums]:
# a24fb9c7ddf3ebd557300e989d44b619 seed-data-80069/genesis.json
# 5d0d482758117af8dfc20e1d52c31eef seed-data-80069/kzg-trusted-setup.json
```
Verify that the checksums for `genesis.json` and `kzg-trusted-setup.json` match the values above for your chosen network.
## Step 4 - Set up BeaconKit
The script `setup-beacond.sh` invokes `beacond init` and `beacond jwt generate`. This script:
1. Runs `beacond init` to create the file `var/beacond/config/priv_validator_key.json`. This contains your node's private key. Especially if you intend to become a validator, keep this file safe. It cannot be regenerated, and losing it means you will not be able to participate in the consensus process.
2. Runs `beacond jwt generate` to create the file `jwt.hex`. This contains a secret shared between the consensus client and execution client so they can securely communicate. If you suspect it has been leaked, delete it then generate a new one with `beacond jwt generate -o $JWT_PATH`.
3. Rewrites the `beacond` configuration files to reflect settings chosen in `env.sh`.
4. Places network parameters where BeaconKit expects them and shows you an important hash from the genesis file.
```bash theme={null}
# FROM: ~/beranode
./setup-beacond.sh;
# [Expected Output]:
# BEACOND_DATA: /.../var/beacond
# BEACOND_BIN: /.../bin/beacond
# Version: …
# ✓ Private validator key generated in .../priv_validator_key.json
# ✓ JWT secret generated at [...]config]/jwt.hex
# ✓ Config files in [...]beacond/config updated
# [BEPOLIA] Genesis validator root: 0x3cbcf75b02fe4750c592f1c1ff8b5500a74406f80f038e9ff250e2e294c5615e
# [MAINNET] Genesis validator root: 0xdf609e3b062842c6425ff716aec2d2092c46455d9b2e1a2c9e32c6ba63ff0bda
# ✓ BeaconKit set up. Confirm genesis root is correct.
```
Your validator state root **must** agree with the value shown above for your chosen chain.
## Step 5 - Set up the execution client
The `setup-reth.sh` script creates the Reth datadir and initializes it with `--chain` set to your configured network (`mainnet` or `bepolia`). Pruning follows the `EL_ARCHIVE_NODE` setting in `env.sh`.
```bash theme={null}
# FROM: ~/beranode
./setup-reth.sh;
# [Expected Output]:
# RETH_DATA: /.../var/reth/data
# RETH_BIN: /.../bin/bera-reth
# Chain: mainnet
# Version: …
# ✓ Reth set up.
```
## Step 6 - Fetch snapshots (optional)
Snapshots are collections of files from a node's backend that represent its state at a specific time. Restoring a snapshot is much faster than syncing from the network, so this step can dramatically speed up your initial sync on a new node.
Do this step **before** starting your clients (Step 7). If you've already started syncing, you'll
need to stop the clients, clean the data directories, then restore snapshots.
Snapshots can be applied to both the consensus (beacond) and execution clients. Restoring both snapshots simultaneously provides the fastest sync.
### 6a - Obtain snapshots
Berachain and the community offer snapshots for Mainnet and Bepolia. You can download snapshots at the following links.
* [Awesome Berachain Validators](https://github.com/chuck-bear/awesome-berachain-validators) is a community-maintained list; all of them have great download speed.
* Or, use the `fetch-berachain-snapshot.js` script — already on disk from Step 1 — downloads the latest official Berachain snapshots for both the beacon-kit consensus layer and the execution layer. It reads the snapshot index at `snapshots.berachain.com`, picks the most recent files matching your options, and saves them to a `downloads/` directory.
```bash theme={null}
# FROM: ~/beranode (or any directory — use -o to change download folder)
# Default: mainnet, pruned
node fetch-berachain-snapshot.js
# Examples:
node fetch-berachain-snapshot.js --network bepolia --type archive
node fetch-berachain-snapshot.js -o /var/snapshots --execution-only
# [Expected Output]:
# Bera Snapshot Downloader
# -------------------------
# Network: mainnet
# Client: reth
# Type: pruned
#
# Fetching snapshot index from:
# https://snapshots.berachain.com/index.csv
# [...]
# ✓ All downloads completed!
```
Available options:
* `--network` or `-n`: `mainnet` or `bepolia` (default: `mainnet`)
* `--type` or `-t`: `pruned` or `archive` (default: `pruned`)
* `--output` or `-o`: Download directory (default: `downloads` in the current working directory)
* `--el-client`: Execution snapshot type prefix in the CSV (default: `reth`)
* `--beacon-only`: Beacon-kit snapshot only
* `--execution-only` or `--el-only`: Execution-layer snapshot only
* `--help` or `-h`: Show help message
### 6b - Stop clients
If you've already started your clients, shut down `beacond` and your execution client now. Otherwise, skip to 6c.
### 6c - Clean existing chain data
To clean the Beacon Kit and reth data store:
```bash theme={null}
# FROM: ~/beranode
source env.sh;
$BEACOND_BIN --home $BEACOND_HOME comet unsafe-reset-all;
# [Expected Output]:
# Removed all blockchain history dir=var/beacond/data
# Reset private validator file to genesis state key=..
ls var/reth/data;
# [Expected Output]:
# (empty or prior db contents)
rm -r var/reth/data;
mkdir -p var/reth/data;
```
### 6d - Install BeaconKit snapshot
The snapshots distributed by Berachain are designed to be installed in the beacond home directory, which contains both `config` and `data`:
```bash theme={null}
# FROM: ~/beranode
# Find the beacon-kit snapshot file (filename pattern: snapshot_beacon-kit-*)
BEACON_SNAPSHOT=$(ls downloads/snapshot_beacon-kit-*.tar.lz4 | head -1)
lz4 -d "$BEACON_SNAPSHOT" | tar xv -C var/beacond/;
# [Expected Output]:
# x data/
# x data/cs.wal/
# x data/cs.wal/wal.10416
# ...
```
### 6e - Install execution layer snapshot
Official Reth snapshot archives list **`db/`**, **`rocksdb/`**, and **`blobstore/`** at the **root** of the tarball (not under a `data/` directory). Your `bera-reth node --datadir` must be the directory that contains those folders. In this layout that is `var/reth/data` (see `setup-reth.sh` / `env.sh`), so pass **`-C var/reth/data`** to `tar`.
```bash theme={null}
# FROM: ~/beranode
# Find the execution layer snapshot
EL_SNAPSHOT=$(ls downloads/snapshot_reth-*.tar.lz4 | head -1)
lz4 -d "$EL_SNAPSHOT" | tar xv -C var/reth/data;
# [Expected Output] (current official bundles):
# x db/
# x db/mdbx.dat
# x rocksdb/
# x blobstore/
# ...
```
## Step 7 - Run both clients
Launch two terminal windows. In the first, run the consensus client:
```bash theme={null}
# FROM: ~/beranode
./run-beacond.sh;
# [Expected Output]:
# INFO Waiting for execution client to start... 🍺🕔
# INFO Connected to execution client
# INFO Reporting version …
# [AFTER BLOCKS START FLOWING]
# INFO processExecutionPayload ... height=49 ...
# INFO Finalized block ... height=49 ...
```
In the second, run the execution client:
```bash theme={null}
# FROM: ~/beranode
./run-reth.sh;
# [Expected Output]:
# INFO RPC HTTP server started url=0.0.0.0:8545
# INFO Starting consensus engine
# [AFTER BLOCKS START FLOWING]
# INFO Block added to canonical chain number=49 hash=0xfb2ea...
```
Initially this will not appear to respond, but within a minute blocks should begin flowing. There should not be a significant number of error messages, except for occasional minor complaints about disconnecting or slow peers.
Both clients are running and will begin syncing with the network. The node will continue syncing
in the background. You can proceed to the optional testing steps below, or leave the clients
running to complete their sync.
## Step 8 - Testing your node (optional)
Now that your RPC is running, verify that the network is working by performing a few RPC requests.
Make sure that your node is fully synced before proceeding with these steps.
### Check sync status
To check the sync status of the consensus layer, in another terminal run the following to retrieve the current block height from the consensus client:
```bash theme={null}
# FROM: ~/beranode
set -e
. ./env.sh
# Don't have jq? `brew install jq`
$BEACOND_BIN --home=$BEACOND_DATA status | jq;
# [Expected Output]:
# {
# "sync_info": {
# "latest_block_height": "1126228", <---- CURRENT NETWORK BLOCK
# "catching_up": false <---- IF `true` = STILL SYNCING
# }
# }
```
If `catching_up` is set to `true`, it is still syncing.
### EL block number
```bash theme={null}
curl --location 'http://localhost:8545' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc":"2.0",
"method":"eth_blockNumber",
"params":[],
"id":420
}';
# [Expected Output]:
# {
# "jsonrpc": "2.0",
# "result": "0xfae90", <---- compare with block explorer
# "id": 420
# }
```
### CL block number
```bash theme={null}
curl -s http://localhost:26657/status | jq '.result.sync_info.latest_block_height';
# [Expected Output]:
# 1653733
```
## Next steps
Your node is now running and syncing. For production deployments, see:
* **[Production Checklist](/nodes/operations/production-checklist)** — Best practices for running nodes in production
* **[Monitoring](/nodes/operations/monitoring)** — Set up monitoring and alerts for your node
* **[Become a Validator](/nodes/guides/become-a-validator)** — Guide to becoming a validator on Berachain
# Self-Hosted RPC
Source: https://docs.berachain.com/nodes/operations/self-hosted-rpc
Use RPCs efficiently, choose paid or self-hosted nodes, and run your own node when you need isolation or predictable throughput.
## Overview
Berachain operates public RPCs at [https://rpc.berachain.com/](https://rpc.berachain.com/). These serve normal, day-to-day usage by end users. Some commercial projects also route production backend traffic through these public services; that can make it harder to hit strict latency and availability targets on a shared, rate-limited endpoint. This article covers the highest-leverage fixes for app developers. Start with usage patterns, then move on to replacing public RPCs when you need dedicated capacity.
## Using RPC efficiently
You can transmit queries through two common transports. The most common is JSON-RPC over HTTP or HTTPS. Well-behaved clients typically reuse connections with keep-alive enabled, but high request rates still pay per-request overhead from headers, request and response framing, and retry behavior. That overhead can amplify tail latency.
Retry discipline matters at least as much as raw request volume. If you are seeing `429` rate limits or intermittent timeouts, avoid "retry immediately" loops: they create retry storms that make throttling worse and can turn partial degradation into a full outage. Use exponential backoff with jitter, honor `Retry-After` when present, and cap retries. When upstreams stay unhealthy, shed load by queueing work, degrading non-critical reads, or failing fast. Be especially careful retrying write flows; reads are typically safe to retry, but duplicate submissions can surprise you.
If you need streaming updates or you are polling aggressively, [WebSockets](https://ethereum.org/developers/tutorials/using-websockets/) are often a better fit. WebSocket connections are long-lived, which makes push workflows viable and reduces churn from repeated connect/reconnect cycles.
In addition to regular JSON-RPC queries, a new category of RPC call becomes available over WebSockets: `eth_subscribe`. You can subscribe to a variety of events: when new blocks appear, or when transaction logs match given criteria.
Putting this together, we can identify anti-patterns and what to replace them with:
| Anti-pattern | Do this instead |
| ----------------------------------------------- | ----------------------------------------------- |
| Short-lived HTTP connections without keep-alive | Enable keep-alive or use a persistent WebSocket |
| Polling `eth_blockNumber` for new blocks | `eth_subscribe` to `newHeads` over WebSocket |
| Calling `eth_getLogs` frequently | `eth_subscribe` for events over WebSocket |
Subscriptions provide immediate notification of new data and can dramatically reduce load compared to polling. They do not replace `eth_getLogs` for historical backfills, and they are not a delivery guarantee. Production consumers still need reconnect and resubscribe logic, plus a robust way to detect and repair gaps. A common pattern tracks the last processed block and uses `eth_getLogs` to reconcile after reconnects.
For event-heavy workloads such as analytics, indexing, and historical backfills, hammering `eth_getLogs` directly from app servers is usually the wrong tool. Prefer an indexing service and query it over a purpose-built API; Goldsky supports Berachain, and their [Berachain docs](https://docs.goldsky.com/chains/berachain) show the current options. Reserve RPC log queries for narrow reconciliation. If you do use `eth_getLogs`, keep queries tight by filtering on contract address and topics, chunk by block range, checkpoint the last processed block, and run it from a single worker to avoid redundant scans.
If your pain is not reads, but user write flows, reduce round-trips per user action. Where supported, standards like [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792), via `wallet_sendCalls`, can batch multiple calls into a single wallet flow, cutting RPC chatter and wallet friction in multi-step sequences.
## Paid RPC services
Paid services vary in their approach to pricing, but generally work on a model where you purchase API credits and providers price JSON-RPC calls according to their computational cost. Costs vary widely by request mix; simple reads are usually cheaper than log queries, and cacheability and archive access also matter. Treat any single "\$X per 100,000 requests" number as an order-of-magnitude estimate, not a budget.
We have a useful list of paid RPC providers on our [developer tools page](/build/getting-started/developer-tools#rpc-infrastructure-providers).
## Running your own RPC
If you need predictable throughput, hard isolation, or you cannot tolerate multi-tenant rate limits, the next step is operating a private RPC endpoint backed by your own node. A Berachain node has two components: the consensus layer, BeaconKit, and the execution layer, Bera-Reth. For setup instructions, use the node [Quickstart](/nodes/operations/quickstart), which stays current as tooling changes.
## Reliability and pricing
Berachain sets its public RPCs to commercially typical usage limits. These endpoints are designed for end users sending transactions, not for running the backend of a service. Commercial projects should provision their own infrastructure or use paid services.
When choosing an RPC solution, you have several options. Free services from the Berachain community are available and listed on our [developer tools page](/build/getting-started/developer-tools#rpc-infrastructure-providers). Paid services usually price by request credits and redundancy tier; self-hosting shifts cost into compute, storage, and operational overhead. In practice, the "right" choice depends less on raw request count and more on your latency targets, failure tolerance, and whether your workload relies on expensive calls like log queries.
Paid providers build redundancy into their offerings. If you are stitching together multiple RPC backends yourself, whether for cost or redundancy, put a single proxy in front so your app talks to one endpoint and the proxy picks an upstream. [eRPC](https://github.com/erpc/erpc) is one option: it can automatically route around slow or erroring providers, retry some requests, and optionally cache common reads to reduce upstream load.
## Summary
Optimize your usage patterns before you spend money. Persistent WebSocket connections and `eth_subscribe` often reduce load and improve timeliness, but treat subscriptions as best-effort and reconcile after reconnects. For production traffic, use either a paid provider or a dedicated self-hosted node; if you self-host, treat it like a service: add [monitoring](/nodes/operations/monitoring) and alerting, and consider a failover layer like eRPC.
# Introduction
Source: https://docs.berachain.com/nodes/overview/index
Run validators and RPC nodes on Berachain: BeaconKit, architecture, operations, and staking pools.
Berachain runs on validator and RPC nodes. Validators propose blocks and earn rewards; RPC nodes serve the network. Both use execution clients paired with Berachain's consensus framework, BeaconKit.
## Get started
Validator vs RPC nodes, active set, stake requirements, and block rewards.
Berachain's consensus client and framework.
Step-by-step guide to joining the active set.
Run a node: quickstart and operations.
## Architecture & operations
From deposit to active validator.
Self-hosted RPC, production checklist, monitoring.
Run liquid staking for your community.
Common questions about running nodes.
# Node Architecture
Source: https://docs.berachain.com/nodes/overview/node-architecture
Validator and RPC node architecture: consensus and execution separation, Engine API, and dual P2P networks.
Berachain's network relies on validator nodes and RPC nodes. Each can be configured as a full node or archive node.
Berachain follows a decoupled node architecture standard in modern Ethereum-equivalent networks. Every node consists of two separate software clients running in tandem: an **execution client** and a **consensus client**. Berachain supports any EVM execution client paired with [BeaconKit](/nodes/architecture/beaconkit-consensus), a consensus framework built by Berachain.
## RPC vs Validator Nodes
Architecturally, RPC nodes and validator nodes are nearly identical. Both maintain a complete copy of the blockchain state and participate in peer-to-peer transaction gossip.
An RPC node can become a validator node by joining the [Active Set](#active-set) through interaction with the [BeaconDeposit](/build/getting-started/deployed-contracts) contract by
meeting the \$BERA [stake requirements](#validator-stake-requirements). To learn about the economics of becoming a validator,
see the [Validator Lifecycle](/nodes/architecture/validator-lifecycle) and the [Be A Validator](/nodes/guides/become-a-validator) guides.
## Client Separation
The separation of concerns between the two clients is strictly defined:
* **Execution Client (e.g., Bera-Reth, Bera-Geth):** Responsible for transaction execution, managing the Ethereum Virtual Machine (EVM) state, maintaining the state trie, and serving user-facing JSON-RPC requests (like `eth_call` or `eth_sendRawTransaction`).
* **Consensus Client (BeaconKit):** Responsible for the Proof-of-Stake consensus mechanism, determining the chain's head, managing the validator set, and proposing new blocks. It does *not* execute transactions, gossip transactions, or hold EVM state.
Because these are separate databases, "archival" status usually refers to the execution client retaining historical EVM state, whereas the consensus client manages its own independent pruning settings.
## The Engine API and JWT
The execution and consensus clients must communicate continuously to keep the node synced. They do this over a local HTTP interface called the **Engine API**.
Because the Engine API allows the consensus client to command the execution client to build and execute blocks, this API must be highly secured. Communication is authenticated using a **JSON Web Token (JWT) secret**. Both clients must be configured to read the exact same `jwt.hex` file on disk to authorize communication.
## Dual P2P Networks
Because the node is split into two clients, it participates in two entirely separate Peer-to-Peer (P2P) networks simultaneously:
1. **Execution P2P (devp2p):** The execution client connects to other execution clients to gossip pending user transactions (the mempool).
2. **Consensus P2P (CometBFT):** The consensus client connects to other consensus clients to gossip proposed blocks, validator attestations, and consensus votes.
For firewall and networking configurations, operators must ensure both P2P ports are open and publicly dialable. For more details on securing and optimizing these networks, see the [Production Checklist](/nodes/operations/production-checklist).
## Validator active set
If the active set is not full, the minimum stake requirement is **250,000** \$BERA.
If the active set is full, the minimum stake requirement is **10,000** \$BERA more than the amount staked by the last validator in the active set.
Berachain follows Proof-of-Stake (PoS) direct staking, which allows \$BERA holders to directly stake their \$BERA to a validator. However, note that if funds are withdrawn from a validator, currently all funds are returned to a single address: the validator's Withdrawal Credentials Address.
This means that validators will have to communicate how they handle funds when a validator is removed from the active set.
Avoid staking to validators without knowing how they handle funds when a validator is removed from
the active set.
## Staking pools
Validators can also operate **staking pools**, which enable liquid staking services for their communities. Staking pools use smart contracts to manage deposits and withdrawals, allowing stakers to receive liquid shares (stBERA) that automatically grow in value as rewards accumulate. Staking pools provide validators with a way to build and monetize their own community of stakers while offering stakers lower barriers to entry and flexible withdrawals. For information about setting up and operating staking pools, see the [Staking Pools documentation](/nodes/staking-pools/overview).
## Removed from active set
If a validator is removed from the active set, all \$BERA staked to that validator will be returned to the validator's Withdrawal Credentials Address, which is set when the validator makes their first deposit.
A validator can decide to become a validator again but will need to generate new CometBFT validator keys and start the deposit process again as if they were a new validator.
Staking with a previously-used CometBFT identity — a validator that was removed from the active
set — will result in the funds being returned to that validator's withdrawal address at the end of
the current epoch. The validator can never be re-activated.
## Voluntary withdrawals
A Validator can [withdraw all or part of their stake](/nodes/guides/withdraw-stake).
## Validator block rewards and distribution
Block rewards are distributed as WBERA emissions at a fixed rate per block proposed.
The network does not distribute rewards automatically. You must distribute block rewards through the [Distributor](/build/getting-started/deployed-contracts) contract. Distribution must occur before **8,191** seconds have passed, or validators risk losing those rewards.
# Staking Pool Contracts
Source: https://docs.berachain.com/nodes/staking-pools/contracts
Addresses and ABIs for StakingPoolContractsFactory, DelegationHandlerFactory, WithdrawalVault, and per-pool proxies (StakingPool, SmartOperator, etc.).
This reference provides contract addresses and links to detailed documentation for each contract in the staking pools system. For an overview of how the contracts work together, see the [Staking Pools Overview](/nodes/staking-pools/overview).
## Contract addresses
### Singleton contracts
Singleton contracts are deployed once and shared across all staking pools. The **StakingPoolContractsFactory** is the entry point for deploying a staking pool: you call it to create and register your pool's contracts. The **DelegationHandlerFactory** deploys per-validator delegation handler proxies when using foundation delegation. The other singletons below are shared infrastructure.
#### Mainnet
```text theme={null}
0xb79b43dBA821Cb67751276Ce050fF4111445fB99
```
[Berascan](https://berascan.com/address/0xb79b43dBA821Cb67751276Ce050fF4111445fB99) · [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/StakingPoolContractsFactory.json)
```text theme={null}
0xAd17932a5B1aaeEa73D277a6AE670623F176E0D0
```
[Berascan](https://berascan.com/address/0xAd17932a5B1aaeEa73D277a6AE670623F176E0D0) · [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/delegation/DelegationHandlerFactory.json)
```text theme={null}
0xE858802Ed532C6DAD2D196AB5B1F2C15F9cb52b4
```
[Berascan](https://berascan.com/address/0xE858802Ed532C6DAD2D196AB5B1F2C15F9cb52b4) · [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/WithdrawalVault.json)
#### Bepolia
```text theme={null}
0x24b8223864d3936F56e5a24C4245ae7620471D4C
```
[Berascan](https://testnet.berascan.com/address/0x24b8223864d3936F56e5a24C4245ae7620471D4C) · [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/StakingPoolContractsFactory.json)
```text theme={null}
0x0aEf09EC97bAc354d31F180b401454cB76abc395
```
[Berascan](https://testnet.berascan.com/address/0x0aEf09EC97bAc354d31F180b401454cB76abc395) · [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/delegation/DelegationHandlerFactory.json)
```text theme={null}
0xBBed2D94338cdE2926A8C0576432De32C05c66e9
```
[Berascan](https://testnet.berascan.com/address/0xBBed2D94338cdE2926A8C0576432De32C05c66e9) · [ABI JSON](https://github.com/berachain/abis/blob/main/bepolia/contracts-staking-pools/WithdrawalVault.json)
### Deployed contracts (per pool)
When you deploy a staking pool through the StakingPoolContractsFactory, the factory creates proxy contracts for your validator. These proxy contracts are what you and your stakers interact with directly. Each validator receives unique proxy addresses for these contracts when deploying their staking pool.
The factory returns these proxy addresses when you call `deployStakingPoolContracts`. Store these addresses for your operations and provide the StakingPool address to your stakers for deposits.
**Proxy contracts deployed with your pool:**
* **StakingPool**: Main staking functionality and staker interactions. This is the contract address your stakers use to deposit BERA and receive stBERA shares.
* **SmartOperator**: Validator operations and Proof of Liquidity integration.
* **IncentiveCollector**: Incentive token collection and conversion. Handles the incentive auction mechanism where accumulated tokens can be claimed.
* **StakingRewardsVault**: Reward collection and automatic reinvestment. Automatically compounds rewards from the consensus layer.
* **DelegationHandler**: Delegation handling for capital providers. Only deployed if you're using delegated funds from the Berachain Foundation.
For detailed technical documentation of each contract's functions and behavior, see the [Berachain guides repository](https://github.com/berachain/guides/tree/main/apps/staking-pools) and the [install-helpers README](https://github.com/berachain/guides/blob/main/apps/staking-pools/install-helpers/README.md).
# Delegation Guide
Source: https://docs.berachain.com/nodes/staking-pools/delegators
Use Foundation-delegated capital to run a staking pool: delegated create, activate, deposit, and withdraw yield.
This guide shows validator operators how to use delegated capital to stand up and run a staking pool. It builds on the Installation guide and reuses the same helper scripts. It does not cover delegator actions; coordinate with your capital provider as needed.
See [Installation](/nodes/staking-pools/installation) first. At minimum, set up `env.sh` in `install-helpers/`.
All helper scripts write `cast` commands to files; review and run them yourself. Transactions are
never sent automatically. Signing defaults to Ledger; set `PRIVATE_KEY` in `env.sh` if you prefer
a local key.
## Assumptions
* You completed the validator Installation steps (env configured, node synced).
* The Berachain Foundation has prepared a DelegationHandler and delegated funds for your validator pubkey.
## 1. Check readiness
Confirm the chain, validator pubkey, and whether a delegated pool/handler is detected:
```bash theme={null}
./status.sh
```
If a handler exists, the script displays delegated amounts and whether a pool already exists for your pubkey.
## 2. Create the pool with delegated funds (first 10,000 BERA)
If the pool is not yet created, use the delegated creation script. It consumes the first 10,000 BERA from the handler to register the validator and writes a ready-to-run command file. This step is the delegated equivalent of the self-funded flow's `register.sh` (it performs the 10,000 BERA deposit and deploys the pool contracts).
```bash theme={null}
./delegated-create-pool.sh
```
This will create a script with deployment commands. Review it. Then run it to submit the transaction, then wait for confirmation.
## 3. Wait for validator registration
After deploying the contracts, wait for your validator to be registered on the beacon chain. You can check registration status with:
```bash theme={null}
./status.sh
```
The validator must appear on the beacon chain before you can activate the pool.
## 4. Activate the pool
Activation mirrors the Installation flow. Once the validator is recognised as registered on the beacon chain, run `activate.sh`:
```bash theme={null}
./activate.sh
```
`activate.sh` is a permissionless call keyed on your validator pubkey and no longer takes `--sr` or `--op`. It preflights the activation locally (pool deployed, pool not already active, operator and withdrawal-credentials match, proofs verify against a single pinned slot) and writes `generated/activation-command.sh` for you to review and execute. Proofs are contract-checked against `MAX_TIMESTAMP_AGE` = 10 minutes; if you delay broadcasting beyond that window, re-run `activate.sh` to regenerate.
## 5. Deposit remaining delegated funds
After activation, deposit the remaining delegated funds to reach your target balance (for example, 250,000 BERA on Bepolia). The script writes a command file for you to execute.
```bash theme={null}
./delegated-deposit.sh --amount 240000
```
## 6. Verify status
```bash theme={null}
./status.sh
```
You should see contract addresses, the operator match on the beacon deposit contract, and the pool marked ACTIVE. The script also reports delegated amounts when a handler is present.
## 7. Withdraw yield (operator)
Operators can withdraw earned yield independently of principal. The helper script writes two commands (request, then complete after cooldown):
```bash theme={null}
./delegated-withdraw-yield.sh --pubkey 0xYOUR_VALIDATOR_PUBKEY
```
Follow the prompts to execute the generated request and completion scripts after the cooldown.
Yield withdrawal redeems only the **share excess** over the delegated principal still on the handler: the `DelegationHandler` computes `lockedShares = previewWithdraw(delegatedAmountUsed)` and excludes those shares from the redeemable amount. Any growth above the locked principal — protocol-fee shares accrued to the default recipient, reward-vault yield, operator-side WBERA — is freely redeemable; the delegated principal itself is not.
Principal withdrawals are controlled by the delegator and are out of scope here. Once you have attracted additional depositors, the foundation can redeem its stake without causing your validator to exit.
If you are self-funded, use `stake.sh` from the [Installation
guide](/nodes/staking-pools/installation) instead of the delegated deposit flow.
## Where to next
* [Contract reference](/nodes/staking-pools/contracts) explains what you've deployed
* [Operator Guide](/nodes/staking-pools/operators) explains what a staking pool operator can do
# Staking Pool Installation
Source: https://docs.berachain.com/nodes/staking-pools/installation
Install and activate a staking pool with helper scripts: register, activate, set min effective balance, and optional stake.
This guide walks validator operators through installing and activating a staking pool using the helper scripts.
## What you'll use
The helper scripts are available through GitHub:
```bash theme={null}
git clone https://github.com/berachain/guides/
cd guides/apps/staking-pools
ls -F
# frontend/ install-helpers/
```
There are many useful scripts under `install-helpers/` which wrap multi-step operations into safe, review-first commands:
* **register.sh**: Deploys (registers) staking pool contracts and generates the deployment transaction
* **activate.sh**: Activates a deployed pool using validator proofs from the beacon chain
* **status.sh**: Verifies deployment, registration, and activation status
* **stake.sh**: Generates a staking transaction to add BERA to your pool
All scripts write ready-to-run `cast` commands to files for you to review and execute. They do not
send transactions without your confirmation. By default, scripts use Ledger for signing; set
`PRIVATE_KEY` in `env.sh` if you prefer a local key.
## Requirements
* beacond (validator client) running and synced with your validator keys **backed up somewhere safe**
* Foundry `cast` ([getfoundry.sh](https://getfoundry.sh/))
* `jq`, `bc`, `curl`
* Ledger hardware wallet (default) or a private key set via `PRIVATE_KEY`
* Funds: at least 10,000 BERA for the initial deposit; additional stake as desired
## 1. Configure environment
In `install-helpers/`:
```bash theme={null}
cp env.sh.template env.sh
# Edit env.sh and set at minimum:
# BEACOND_HOME="/path/to/your/beacond/home"
# NODE_API_ADDRESS="127.0.0.1:3500" # If not auto-detected from app.toml
# Optionally set:
# PRIVATE_KEY="0x..." # Defaults to Ledger if unset
```
Ensure the beacon node API is enabled in your `app.toml` (`[beacon-kit.node-api]`) or provide `NODE_API_ADDRESS` in `env.sh`. The scripts will auto-detect when possible and verify connectivity before proceeding.
## 2. Register (deploy contracts)
Run `register.sh` with your addresses to deploy the staking pool contracts:
```bash theme={null}
./register.sh --sr 0xSHARES_RECIPIENT --op 0xOPERATOR
```
1. The chain (mainnet or Bepolia) is auto-detected from your beacond configuration.
2. The initial stake is 10,000 BERA (fixed by the consensus layer). The script writes `deployment-command.sh` with this deposit. Review it. Simulate it if you want. Then run it. This should dump a successful transaction receipt.
3. The script also predicts and shows your staking pool contract addresses ahead of time.
## 3. Wait for validator registration
After deploying the contracts, wait for your validator to be registered on the beacon chain. You can check registration status with:
```bash theme={null}
./status.sh
```
The validator must appear on the beacon chain before you can activate the pool.
## 4. Activate the pool
Once your validator is registered, run `activate.sh` to activate the pool:
```bash theme={null}
./activate.sh
```
Activation is a permissionless on-chain call keyed by your validator pubkey, so `activate.sh` no longer takes `--sr` or `--op`; the pool's shares recipient and operator are already baked in from `register.sh`. The script:
1. Verifies the pool contract is deployed and not already active (`isActive()`).
2. Confirms `BeaconDeposit.getOperator(pubkey)` matches the pool's SmartOperator and that the validator's withdrawal credentials point at the pool's WithdrawalVault.
3. Pins the beacon-chain head slot, fetches validator pubkey, withdrawal-credentials, and balance proofs at that single slot, and preflights `activateStakingPool()` locally so a bad proof or stale timestamp surfaces before you broadcast.
4. Writes `generated/activation-command.sh` for you to review and execute.
Proofs include a timestamp enforced by the contract (`MAX_TIMESTAMP_AGE` = 10 minutes). The generated script also hard-expires after that window; if you hit it, re-run `activate.sh` to regenerate.
## 5. Verify installation
Use `status.sh` to check deployment, registration, and activation:
```bash theme={null}
./status.sh
```
You should see the SmartOperator, StakingPool, StakingRewardsVault, and IncentiveCollector addresses, confirmation that the beacon deposit operator matches your SmartOperator, the pool's active status, and various telemetry.
## 6. Set minimum effective balance
The `minEffectiveBalance` parameter determines when your staking pool activates its validator on
the consensus layer. If you don't set this value correctly, your pool may accumulate deposits
without ever activating.
Set the value using your SmartOperator contract:
```bash theme={null}
cast send $SMART_OPERATOR_ADDRESS \
"setMinEffectiveBalance(uint256)" $CALCULATED_MIN_STAKE \
--ledger # or --private-key $PRIVATE_KEY
```
Replace `$SMART_OPERATOR_ADDRESS` with your SmartOperator address and `$CALCULATED_MIN_STAKE` with the calculated minimum stake amount in wei (multiply BERA amount by 10^18).
For details on why this matters, how to determine the correct value, and how the consensus layer minimum works, see the [Setting Minimum Effective Balance](/nodes/staking-pools/operators#setting-minimum-effective-balance) section in the operator guide.
## 7. (Optional) Stake additional BERA
Add stake to your pool and send stBERA to a receiver address:
```bash theme={null}
# With BEACOND_HOME configured (pool auto-detected):
./stake.sh --amount 100 --receiver 0xRECEIVER
# Or specify an explicit pool address:
./stake.sh --amount 100 --receiver 0xRECEIVER --staking-pool 0xPOOL
```
The script writes `stake-command.sh`. Review and execute the command to submit your stake.
## Troubleshooting (quick)
* **Node API not reachable**: Enable `[beacon-kit.node-api]` and confirm the address/port; or set `NODE_API_ADDRESS` in `env.sh`. The script will examine your files and tell you how to activate the API if it isn't enabled yet.
* **Missing tools**: Install Foundry (`cast`), `jq`, `bc`, and `curl` and ensure they are on your PATH.
## What's next
* [Contract reference](/nodes/staking-pools/contracts) explains what you've deployed
* [Delegation guide](/nodes/staking-pools/delegators) explains how to receive a Foundation delegation, which is very similar to this flow
# Staking Pools Operator Guide
Source: https://docs.berachain.com/nodes/staking-pools/operators
Configure and operate a staking pool: roles, commission, reward allocation, min effective balance, and front-end.
This guide helps validators set up and manage staking pools to offer liquid staking services to their communities.
## Quick reference
### Key parameters
| Parameter | Range | Purpose |
| ------------------------- | ------------------------------------------- | -------------------------------------------- |
| Validator Commission | 0-20% | Commission on incentive token distribution |
| Protocol Fee | 0-20% | Fee on eligible staking-pool reward growth |
| Minimum Effective Balance | ≥ 250,000 BERA | Activation threshold and full exit safeguard |
| Withdrawal Delay | 129,600 blocks (≈3 days at \~2s block time) | Time before withdrawals can be finalized |
### Key roles
| Role | Controls | Function |
| ---------------------------------- | --------------------- | -------------------------------------- |
| `VALIDATOR_ADMIN_ROLE` | All other roles | Grant/revoke operational roles |
| `REWARDS_ALLOCATION_MANAGER_ROLE` | Reward allocation | Direct PoL incentives to applications |
| `COMMISSION_MANAGER_ROLE` | Commission rate | Adjust validator commission (0-20%) |
| `PROTOCOL_FEE_MANAGER_ROLE` | Protocol fee | Adjust protocol fee percentage (0-20%) |
| `INCENTIVE_COLLECTOR_MANAGER_ROLE` | Payout amount | Adjust incentive collector payout |
| `DELEGATION_MANAGER_ROLE` | Delegation operations | Manage delegations |
### Essential functions
Core lifecycle and PoL reward allocation:
| Function | Contract | Purpose |
| ---------------------------- | ------------- | ---------------------------- |
| `setMinEffectiveBalance()` | SmartOperator | Set activation threshold |
| `queueValCommission()` | SmartOperator | Queue commission rate change |
| `queueRewardsAllocation()` | SmartOperator | Queue reward allocation |
| `setProtocolFeePercentage()` | SmartOperator | Set protocol fee rate |
Protocol fee accrual (same percentage applies to both tracks during migration):
| Function | Contract | Purpose |
| ------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `accrueEarnedWBERAFees()` | SmartOperator | Accrue protocol fees on **WBERA** balance growth — primary path after PoL Next. |
| `accrueEarnedBGTFees()` | SmartOperator | Accrue protocol fees on remaining **BGT** balance — **legacy / transitional** until BGT allowances are exhausted on your pool. |
Manual lever for compounding operator WBERA into pool assets:
| Function | Contract | Purpose |
| ------------------ | ----------- | ------------------------------------------------------------------------------------------------------------- |
| `processRewards()` | StakingPool | Pull buffered staking rewards and any operator-held WBERA into the pool. Permissionless; callable any caller. |
Operator action is not required to make WBERA reach stakers. Compounding fires automatically inside any user `deposit()` and inside `processRewards()`; covering withdrawals from operator-side WBERA fires automatically inside `WithdrawalVault.requestWithdrawal()`. See [Automatic WBERA flows](#automatic-wbera-flows) below.
Legacy incentive surface (kept for transition; see [Deprecated BGT entry points](#deprecated-bgt-entry-points)):
| Function | Contract | Purpose |
| --------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `claimBoostRewards()` | SmartOperator | **Legacy:** forward **BGT-era** boost incentive claims toward **`IncentiveCollector`**. Prefer WBERA-native operator flows for ongoing activity; treat as transitional. |
## Prerequisites
Before setting up a staking pool, ensure you have a fully operational Berachain validator node. You'll need at least 10,000 BERA to register the pool, though activation requires at least 250,000 BERA. See the [Validator Lifecycle](/nodes/architecture/validator-lifecycle) and [Become a Validator](/nodes/guides/become-a-validator) guides.
Staking pools follow the standard Berachain validator lifecycle. After deployment, your validator
will progress through the Deposited → Eligible states, but activation to the Active state depends
on the ValidatorSetCap and your validator's priority relative to other validators.
## Validator lifecycle
Your staking pool integrates with Berachain's validator lifecycle. For details on validator states (Deposited, Eligible, Active, Exited, Withdrawn) and transitions, see the [Validator Lifecycle documentation](/nodes/architecture/validator-lifecycle).
The key consideration for staking pools is ensuring sufficient stake for activation. See [Setting Minimum Effective Balance](#setting-minimum-effective-balance) below.
## Key terms and concepts
* **Active Threshold**: The point at which your pool has sufficient stake (`totalDeposits >= minEffectiveBalance`) to activate the validator. When `activeThresholdReached()` returns `true`, your validator enters a cooldown period before activation.
* **Minimum Effective Balance (`minEffectiveBalance`)**: The minimum stake amount required for validator activation and a safeguard that triggers full exit if deposits fall below it. This must match or exceed the current consensus layer minimum (250,000 BERA when the set is not full; when the set is full, the minimum is 10,000 BERA more than the lowest active validator).
* **Withdrawal Delay**: 129,600 blocks (≈3 days at \~2s block time) that must pass after a withdrawal request before it can be finalized.
* **Cooldown Period**: After `activeThresholdReached()` becomes `true`, there is a cooldown period before the validator activates.
## Yield model
After the May 2026 PoL upgrade, per-block validator emission is a **flat fixed rate** (no boost curve).
Pools no longer differentiate on attracted BGT delegation; what you control is:
* **Validator reward allocation (cutting board)** — direct emissions to vaults you and your community care about. See [Manage Reward Allocations](/nodes/guides/manage-reward-allocations).
* **Incentives attracted to vaults you allocate to** — protocols still incentivize specific Reward Vaults; a strong allocation strategy increases incentive yield to your stakers.
* **Validator commission on incentive tokens** — capped at 20%, denominated in the WBERA-era incentive flow. See [Manage Validator Incentives Commission Rate](/nodes/guides/manage-incentives-commission).
Validator emissions accrue as \$WBERA on your `SmartOperator`. Protocol fee accrual on the WBERA track uses `accrueEarnedWBERAFees()`. The same WBERA fee update also runs automatically inside `withdrawRewards`, `pullBeraToWithdrawalVault`, and `setProtocolFeePercentage()`, so calling `accrueEarnedWBERAFees()` directly is mostly a forced-settlement / monitoring tool. `accrueEarnedBGTFees()` is a no-op once chargeable BGT is exhausted.
## Automatic WBERA flows
`SmartOperator` exposes two state-changing entry points that move WBERA out of the operator: `withdrawRewards(uint256)` and `pullBeraToWithdrawalVault(uint256)`. Both are restricted by sender — `withdrawRewards` accepts the StakingPool only; `pullBeraToWithdrawalVault` accepts the WithdrawalVault only. Operators cannot call them directly, and no operator action is required to make WBERA reach stakers.
* **Compounding into pool assets** — Any user `deposit()` and any call to `StakingPool.processRewards()` invokes `_collectRewards(...)`, which calls `SmartOperator.withdrawRewards(...)` on operator WBERA first, then drains the staking rewards vault. Operator WBERA is pulled first so `_getTotalAssets()` is correct before protocol fees mint staker shares via `mintFeeShares` during WBERA fee accrual. The unwrapped BERA lands in the pool, lifts `_getTotalAssets()`, and lifts share price for every staker.
* **Deposit ordering and fee fairness** — Inside `_submit`, the pool mints the depositor's stBERA **before** calling `_collectRewards(...)`. The depositor's shares are priced against the pre-collection NAV (which already includes pending operator WBERA via `rebaseableWberaAmount()`), so they pay a fair price for the yield about to settle. The collection that follows charges WBERA-track protocol fees and mints fee shares to the default recipient against the post-deposit NAV, so the new depositor's principal is not taxed as if it were accrued yield. `processRewards()` runs the same `_collectRewards → bufferedAssets bump → _processDeposit` sequence without minting user shares.
* **Covering withdrawals from operator liquidity** — `WithdrawalVault.requestWithdrawal()` precomputes `pulledFromOperator = min(amountInWei, availableWBERABalance)` and calls `pullBeraToWithdrawalVault(...)` on the normal partial-withdrawal branch. The execution-layer withdrawal request to the consensus layer is reduced to the uncovered remainder; if the operator covers in full, no consensus-layer request is issued and the EIP-7002 fee is refunded to the requester. Short-circuit (pool not yet at active threshold) and full-exit branches do not consume operator WBERA.
* **Full exit sweep** — Triggering a full exit sweeps any idle operator WBERA into the pool before the pool forwards its native balance to the WithdrawalVault.
For monitoring, watch `availableWBERABalance()`, `rebaseableWberaAmount()`, and `getEarnedWBERAFeeState()` on the SmartOperator. The only operator-side write you may want to invoke explicitly is `processRewards()` on the StakingPool (permissionless `whenNotPaused`) — it forces a compounding cycle without waiting for the next user deposit.
## Configuration
### Commission rates
You can set commission rates within 0-20%. Commission applies to the distribution of incentive tokens from Proof of Liquidity rewards. For step-by-step instructions, see [Manage Validator Incentives Commission Rate](/nodes/guides/manage-incentives-commission).
### Reward allocations
Direct PoL incentives to specific applications. For instructions, see [Managing Validator Reward Allocations](/nodes/guides/manage-reward-allocations).
### Setting minimum effective balance
The `minEffectiveBalance` parameter is critical for validator activation. The consensus layer enforces a base minimum of **250,000 BERA**. When the validator set is full (69 validators), the minimum required increases in increments of **10,000 BERA**. Set `minEffectiveBalance` to match the current consensus layer requirement. You can check the current lowest active stake on [Berachain Hub](https://hub.berachain.com/boost/).
## Routine operations
* **Monitor pool status**: Use `isActive()`, `totalAssets()`, `bufferedAssets()`, `activeThresholdReached()` on your StakingPool and SmartOperator. For operator-side liquidity, also watch **`availableWBERABalance()`**, **`rebaseableWberaAmount()`**, and **`getEarnedWBERAFeeState()`** on SmartOperator. See [Automatic WBERA flows](#automatic-wbera-flows) for how this liquidity moves.
* **Delegation operations**: Manage delegations as needed. **PoL Next:** validator emissions accrue as **\$WBERA** on **`SmartOperator`**. Accrue protocol fees on the WBERA track with **`accrueEarnedWBERAFees()`**; use **`accrueEarnedBGTFees()`** only while your pool still has chargeable **BGT** on the operator. **`claimBoostRewards()`** is a **legacy** entry point for the **BGT** incentive-distributor surface — confirm it still matches your deployed implementation before relying on it for new workflows.
* **Emission token context**: PoL Reward Vault emissions are distributed in **\$WBERA**.
* **Protocol fee**: Set via `setProtocolFeePercentage()` (up to 20%). The fee setter charges both tracks before applying the new rate. Call `accrueEarnedWBERAFees()` to force WBERA-track settlement; call `accrueEarnedBGTFees()` if you want to settle the legacy BGT track explicitly.
## Deprecated BGT entry points
The May 2026 PoL upgrade deprecates the BGT-era surface on `SmartOperator`. The following entry points stay callable for transition (all `whenNotFullyExited` where applicable) but become inert once chargeable BGT is exhausted on your operator:
* `queueBoost()`, `activateBoost()`, `queueDropBoost(uint128)` — boost-management calls; per-block emission no longer scales with delegation, so these have no effect on yield.
* `claimBgtStakerReward()` — BGT-staker reward forwarder.
* `claimBoostRewards(IBGTIncentiveDistributor.Claim[], address[])` — BGT-era boost incentive distributor forwarder.
* `accrueEarnedBGTFees()` — fee accrual on the BGT track; no-op when chargeable BGT is zero.
Berachain recommends winding down BGT positions held on your `SmartOperator` (unboost, drop-boost, redeem) using the same calls during the transition window.
## Withdrawal system
The shared **`WithdrawalVault`** is a per-chain singleton (a single proxy + implementation) that finalizes withdrawal requests for every staking pool. Three sources of liquidity can satisfy a request:
* **Short-circuit** — pool not yet at the active threshold. Liquidity comes only from the pool's on-chain **`bufferedAssets`**. BeaconKit honors EIP-7002 withdrawal requests only for **active** validators; a validator that has not activated cannot withdraw via the consensus layer, so this branch never issues a CL request. If the buffer cannot cover the request, the transaction reverts rather than falling through into post-threshold or full-exit logic.
* **Standard consensus-layer withdrawal** — execution-layer request to the validator's pubkey.
* **Operator-side WBERA cover** — at request time, the vault pulls from **`availableWBERABalance()`** on your pool's `SmartOperator`, unwraps to native BERA, and applies it to the request. Coverage may be **full** (the consensus-layer request is skipped and the EIP-7002 fee is refunded), **partial** (the remainder still exits via the consensus layer), or **none** when operator liquidity is empty.
**Post-exit pools:** Once the validator has fully exited (`isFullyExited`), withdrawal handling skips the pre-threshold default-recipient gate and the post-threshold activation cooldown. BERA is already in the `WithdrawalVault`; requests proceed without re-entering activation-era guards.
**Finalization delay:** `finalizeWithdrawalRequest` / `finalizeWithdrawalRequests` enforce `block.number >= requestBlock + WITHDRAWAL_REQUEST_FINALIZATION_BLOCK_DELAY` (currently **129,600** blocks, ≈3 days at \~2s block time) for every request. The cover source affects only **where the BERA comes from**; it does not shorten the cooldown. The pull from operator WBERA happens at **request** time, so covered BERA sits in the vault until the cooldown elapses.
**Retrying a full exit (`retryFullExit`):** When a full exit is triggered, the pool marks the validator fully exited and the vault submits an EIP-7002 full-exit request (withdrawal amount **0**). If that consensus-layer request fails or is dropped, anyone can call **`retryFullExit(pubkey, maxFeeToPay)`** on the shared `WithdrawalVault` to re-submit — permissionless, sending the EIP-7002 **withdrawal fee** with the call (`maxFeeToPay` is the most you are willing to pay). The validator must already be marked fully exited (`NotFullyExited` otherwise). The call reverts with **`PendingWithdrawalInFlight`** if a partial withdrawal was requested within the last **49,153** blocks (`WITHDRAWAL_FLIGHT_BLOCK_DELAY`), giving in-flight partial exits time to settle before retrying a full exit. Success emits **`FullExitRequestRetried`**. This path is for recovering a stuck post-exit CL sweep; normal staker withdrawal requests do not use it.
## Building your front-end
Your front-end should:
* Display withdrawal status and when each request can be finalized (use `getWithdrawalRequest(requestId)` and `requestBlock + 129600`).
* Support batch finalization with `finalizeWithdrawalRequests([...])`.
* Show staker balance, share price, and total rewards (e.g. via `previewRedeem(shares)`).
Berachain provides a React-based example template in the [guides repository](https://github.com/berachain/guides/tree/main/apps/staking-pools/frontend). Use `generate-frontend-config.sh` from `install-helpers` to generate `config.json` from your environment.
## Delegation
If you have received a delegation from the Berachain Foundation, see the [Delegation Guide](/nodes/staking-pools/delegators) and the [DelegationHandler](https://github.com/berachain/guides/tree/main/apps/staking-pools) contract reference.
## More information
* [Staking Pools Overview](/nodes/staking-pools/overview)
* [Smart Contract Reference](/nodes/staking-pools/contracts)
* [Install-helpers README](https://github.com/berachain/guides/blob/main/apps/staking-pools/install-helpers/README.md)
# Staking Pools Overview
Source: https://docs.berachain.com/nodes/staking-pools/overview
Validator-operated liquid staking: stBERA, contract architecture, security, staker experience, and PoL integration.
Staking Pools enable validators to offer liquid staking services to their communities. As a validator, you can build and monetize your own community of stakers, earning commission on rewards. Your stakers deposit BERA through smart contracts and receive liquid shares (stBERA) that automatically grow in value as rewards accumulate.
## What are staking pools?
Staking Pools are validator-operated services that allow your community members to stake BERA through smart contracts, receiving liquid shares (stBERA) that represent their stake and automatically grow in value as rewards accumulate. Stakers can stake any amount without running their own validator.
As a validator, staking pools provide you with a way to build and monetize your own community of stakers, earning commission on rewards. Your stakers benefit from lower barriers to entry, automatic reward reinvestment, and flexible withdrawals.
## Post-BGT Deprecation
The May 2026 Proof of Liquidity upgrade changes how staking pools earn and how stakers receive rewards. The contract surface and pool operations stay broadly the same.
* **Reward emissions are paid in \$WBERA.** Validator emissions accrue as \$WBERA on the `SmartOperator`. Operator monitoring views and the automatic flows that compound this WBERA into pool assets and into withdrawal coverage are documented in the [Operator Guide](/nodes/staking-pools/operators#automatic-wbera-flows).
* **Per-block emission is a flat fixed rate (no boost curve).** The pre-upgrade boost curve, which made BGT delegation a yield differentiator, is gone. Per-block emission no longer scales with delegation. Pools compete on **reward allocation strategy**, **attracted incentives**, and **commission rate** rather than boost.
* **BGT is deprecated.** We recommend unboosting and burning the BGT held by your `SmartOperator`. The BGT-era entry points stay callable but become inert once chargeable BGT is exhausted; see [Deprecated BGT entry points](/nodes/staking-pools/operators#deprecated-bgt-entry-points).
* **Withdrawals can be covered from operator-side WBERA.** The shared `WithdrawalVault` may pull from `availableWBERABalance()` on your `SmartOperator`, unwrap to native BERA, and apply that toward an outstanding request — fully, partially, or not at all when liquidity is insufficient. The standard consensus-layer withdrawal path remains for any uncovered remainder.
For broader PoL changes, see [What's New](/general/proof-of-liquidity/changelog).
## How it works
When you create a staking pool, the system deploys several interconnected contracts. Your pool includes a StakingPool contract that manages deposits and shares, a SmartOperator that handles validator operations and Proof of Liquidity integration, and a StakingRewardsVault that collects and reinvests rewards. Shared infrastructure includes a WithdrawalVault that processes withdrawal requests for all pools and an Accounting Oracle that provides consensus layer data updates.
The system automates staking, reward distribution, and withdrawal management, requiring minimal manual intervention from you while providing your stakers with a seamless staking experience.
### Contract architecture
The staking pools system uses a factory pattern where the **StakingPoolContractsFactory** serves as the deployment mechanism, creating and managing the complete suite of contracts needed for each validator's staking pool.
The core functionality revolves around the **StakingPool** contract, which handles staker deposits, share management, and the fundamental operations of your staking pool. Stakers interact with this contract to stake their BERA tokens and receive stBERA shares in return. The StakingPool contract maintains the accounting for staker positions and manages the conversion between BERA and stBERA shares.
Validator operations are coordinated through the **SmartOperator** contract, which manages integration with Berachain's Proof of Liquidity system. This contract handles reward allocation, commission management, and validator-specific PoL operations.
The **StakingPool** contract inherits from the **stBERA** base contract, which provides the core token functionality for staked BERA shares, including share minting, burning, and conversion between assets and shares.
### Security model
The staking pools system implements a security model designed to protect staker funds while maintaining operational flexibility for validators and administrators.
Access control is managed through a role-based system that provides granular permissions for different types of operations. The `DEFAULT_ADMIN_ROLE` provides governance control over contract upgrades and emergency actions. Validators receive the `VALIDATOR_ADMIN_ROLE`, which grants them operational control over their specific staking pool while preventing interference with other validators' operations.
Specialized roles handle specific aspects of the system. The `REWARDS_ALLOCATION_MANAGER_ROLE` manages reward allocation to specific applications, while the `COMMISSION_MANAGER_ROLE` handles validator commission management. The `PROTOCOL_FEE_MANAGER_ROLE` controls protocol fee settings, and the `INCENTIVE_COLLECTOR_MANAGER_ROLE` manages incentive collector operations.
Emergency controls provide additional protection. Contracts can be paused in emergency situations. The system includes automatic full exit triggers that activate if the minimum balance threshold is breached, protecting stakers from potential losses.
## Staker experience
When stakers interact with your staking pool, they deposit BERA and immediately receive liquid stBERA shares. Rewards automatically compound as you earn rewards, increasing the value of shares over time without manual claiming or reinvestment. Stakers can withdraw at any time. **Withdrawal liquidity** can come from the **consensus layer**, from **buffered funds** on the pool before activation, and — after PoL Next on the staking-pools stack — from **WBERA held on your pool's `SmartOperator`**, which the shared **`WithdrawalVault`** may use to help satisfy requests (including **partial** coverage, with any remainder still exiting via the consensus layer). **Finalizing** a withdrawal request remains subject to the on-chain delay from the request block (commonly on the order of **three days** / **129,600** blocks at \~2s block time); see the [Operator Guide](/nodes/staking-pools/operators#withdrawal-system) for operator responsibilities and how coverage is sourced. Stakers can stake any amount without validator minimums, with full transparency through on-chain verification.
**Staking pool shares (stBERA) are not LSTs.** They cannot be registered with
`IncentivesCollector` as an `LSTStakerVault` asset and cannot be deposited into the \$sWBERA
Staking Vault. Pool-level yield reaches stBERA holders through the pool's own `SmartOperator` and
`IncentiveCollector` — a separate path from the PoL Incentive Auction yield.
## Validator operations
You deploy a staking pool through the factory contract, which creates all necessary contracts and registers your validator with the consensus layer. You configure commission rates (up to 20% of staker rewards) and can direct Proof of Liquidity incentives to specific applications. Commission is collected automatically on staker rewards, allowing you to focus on community building rather than manual reward management.
## Integration with Proof of Liquidity
Staking pools integrate with Berachain's Proof of Liquidity system. Pool reward flows route validator emissions to configured vault paths, and smart contracts claim and distribute PoL incentives. You can direct rewards to specific applications or ecosystem initiatives.
## Provided tools
Berachain provides tools to help you operate your staking pool. A React-based **example frontend template** provides a starting point for building your staking interface. The template demonstrates how stakers can connect wallets, deposit BERA, view positions, and request withdrawals. See the [Building your front-end](/nodes/staking-pools/operators#building-your-front-end) section in the operator guide for requirements and template usage. Bash scripts automate deployment and management operations, generating ready-to-review `cast` commands for safe execution. An interactive Python CLI tool is available for managing SmartOperator contracts. These tools are available in the [Berachain guides repository](https://github.com/berachain/guides/tree/main/apps/staking-pools), with detailed documentation in the [install-helpers README](https://github.com/berachain/guides/blob/main/apps/staking-pools/install-helpers/README.md).
## Getting started
Validators can set up staking pools using the [Installation Guide](/nodes/staking-pools/installation) and manage operations with the [Operator Guide](/nodes/staking-pools/operators).
## Smart contract reference
For detailed information about the smart contracts and their functions, see the [Smart Contract Reference](/nodes/staking-pools/contracts).
## Support and resources
If you don't have contact with Berachain Validator Relations, ask on our [Discord](https://discord.gg/berachain) **#node-support** channel.