# 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 BGT yield, 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 $BGT 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 \$BGT 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 \$BGT yield
You should see your deposited value (supplied and staked) and the receipt token amount.
With your deposit staked, you can claim \$BGT yield.
## 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 \$BGT.
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 BGT yield from staking vault shares in PoL reward vaults.
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. \*\*\[$BGT Yield](#bgt-yield)** — Yield from staking vault shares in an eligible [Proof-of-Liquidity Reward Vault](/general/proof-of-liquidity/reward-vaults) in the form of $BGT.
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.
### \$BGT Yield
$BGT yield comes from [validators directing $BGT emission / block rewards]\(/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 $BGT yield.
You must stake the receipt tokens and claim $BGT to receive $BGT yield.
# 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 `$BGT`
* **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 `$BGT`.
* **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 \$BGT 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) `$BGT` holders.
## 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, where the fees are auctioned for `$HONEY` and subsequently distributed to `$BGT` stakers.
# 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 BGT by providing liquidity and staking LP tokens in reward vaults.
Proof of liquidity (PoL) is a mechanism that rewards you for your liquidity contributions to the Berachain ecosystem through native chain rewards, `$BGT`. As the native dApp powering trading liquidity in the Berachain ecosystem, a number of BEX pools will be eligible for `$BGT` rewards.
## Earning BGT
The process for earning `$BGT` 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 `$BGT` rewards. This process is conducted through Berachain governance, requiring a proposal to be submitted and voted on by `$BGT` holders.
# 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. ABIs are in [berachain/doc-abis](https://github.com/berachain/doc-abis).
Deployed contracts have been audited by multiple parties. Reports are on
[GitHub](https://github.com/berachain/security-audits).
## Mainnet contracts
### Morpho
| Name | Address | ABI |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --- |
| **Morpho (Vault)** | [`0x24147243f9c08d835C218Cda1e135f8dFD0517D0`](https://berascan.com/address/0x24147243f9c08d835C218Cda1e135f8dFD0517D0) | N/A |
| **Adaptive Curve IRM** | [`0xcf247Df3A2322Dea0D408f011c194906E77a6f62`](https://berascan.com/address/0xcf247Df3A2322Dea0D408f011c194906E77a6f62) | N/A |
| **Bundler3** | [`0xF920140A65D0f412f2AB3e76C4fEAB5Eef0657ae`](https://berascan.com/address/0xF920140A65D0f412f2AB3e76C4fEAB5Eef0657ae) | N/A |
| **General Adapter 1** | [`0xd2B9667F5214115E27937C410cAeE83E3a901Df7`](https://berascan.com/address/0xd2B9667F5214115E27937C410cAeE83E3a901Df7) | N/A |
| **Meta Morpho V1.1** | N/A | N/A |
| **Meta Morpho Factory V1.1** | [`0x5EDd48C6ACBd565Eeb31702FD9fa9Cbc86fbE616`](https://berascan.com/address/0x5EDd48C6ACBd565Eeb31702FD9fa9Cbc86fbE616) | N/A |
| **Meta Fee Partitioner** | [`0x80108Ee81A92091Db6B8B2326B1875ce9388f461`](https://berascan.com/address/0x80108Ee81A92091Db6B8B2326B1875ce9388f461) | N/A |
| **Public Allocator** | [`0xB62F34Ab315eaDeAc698e8EaEB6Fc2650951BFe7`](https://berascan.com/address/0xB62F34Ab315eaDeAc698e8EaEB6Fc2650951BFe7) | N/A |
| **Morpho Chainlink Oracle V2** | N/A | N/A |
| **Morpho Chainlink Oracle V2 Factory** | [`0xAf2FDC54f7bc9d6e8C2D2760E908f4e1beB04d9E`](https://berascan.com/address/0xAf2FDC54f7bc9d6e8C2D2760E908f4e1beB04d9E) | N/A |
| **Universal Reward Distributer (URD)** | N/A | N/A |
| **Universal Reward Distributer (URD) Factory** | [`0x46fE2bc33b661E01A8946BbC3Bf43F2B8382d802`](https://berascan.com/address/0x46fE2bc33b661E01A8946BbC3Bf43F2B8382d802) | N/A |
More vaults may be deployed, please check
[https://bend.berachain.com/lend](https://bend.berachain.com/lend) for the latest deployed vaults.
### Vaults
| Name | Address | ABI |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------- | --- |
| **Re7 Honey Vault** | [`0x30BbA9CD9Eb8c95824aa42Faa1Bb397b07545bc1`](https://berascan.com/address/0x30BbA9CD9Eb8c95824aa42Faa1Bb397b07545bc1) | N/A |
# 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.
A full list of contract ABIs can be found at
[github.com/berachain/doc-abis](https://github.com/berachain/doc-abis).
## Mainnet contracts
| Name | Address | ABI |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Vault | [`0x4Be03f781C497A489E3cB0287833452cA9B9E80B`](https://berascan.com/address/0x4Be03f781C497A489E3cB0287833452cA9B9E80B) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IVault.abi.json) |
| ProtocolFeesCollector | [`0xB8Cf46Cf1b1476E707619913a70B2085d26f1707`](https://berascan.com/address/0xB8Cf46Cf1b1476E707619913a70B2085d26f1707) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IProtocolFeesCollector.abi.json) |
| BalancerHelpers | [`0x5083737EC75a728c265BE578C9d0d5333a2c5951`](https://berascan.com/address/0x5083737EC75a728c265BE578C9d0d5333a2c5951) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IBalancerHelpers.abi.json) |
| PoolCreationHelper | [`0x55dccE8165C88aAd4403a15A9cE3A8E244657dD2`](https://berascan.com/address/0x55dccE8165C88aAd4403a15A9cE3A8E244657dD2) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IPoolCreationHelper.abi.json) |
| ProtocolFeesWithdrawer | [`0x1635F0E1B3e8A6713d03aE155ba79458Ba3240C7`](https://berascan.com/address/0x1635F0E1B3e8A6713d03aE155ba79458Ba3240C7) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IProtocolFeesWithdrawer.abi.json) |
| ComposableStablePoolFactory | [`0xDfA30BDa0375d4763711AB0CC8D91B20bfCC87E1`](https://berascan.com/address/0xDfA30BDa0375d4763711AB0CC8D91B20bfCC87E1) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IComposableStablePoolFactory.abi.json) |
| BatchRelayerLibrary | [`0xCB4AE3030bA06F7EEE54A7B96AfcA7457f9525cf`](https://berascan.com/address/0xCB4AE3030bA06F7EEE54A7B96AfcA7457f9525cf) | N/A |
| BatchRelayerQueryLibrary | [`0x4151083172b2CEFB83A33fD7FC9F6cBabb3Fd08d`](https://berascan.com/address/0x4151083172b2CEFB83A33fD7FC9F6cBabb3Fd08d) | N/A |
| BalancerRelayer | [`0x6044f181aB5E9C05A4ed9Ce295f3B178d2492EE7`](https://berascan.com/address/0x6044f181aB5E9C05A4ed9Ce295f3B178d2492EE7) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IBalancerRelayer.abi.json) |
| WeightedPoolFactory | [`0xa966fA8F2d5B087FFFA499C0C1240589371Af409`](https://berascan.com/address/0xa966fA8F2d5B087FFFA499C0C1240589371Af409) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IWeightedPoolFactory.abi.json) |
| ProtocolFeePercentagesProvider | [`0x33C88ffdEe710ed3908C791137Bd1D4421AabBBf`](https://berascan.com/address/0x33C88ffdEe710ed3908C791137Bd1D4421AabBBf) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IProtocolFeePercentagesProvider.abi.json) |
| BalancerQueries | [`0x3C612e132624f4Bd500eE1495F54565F0bcc9b59`](https://berascan.com/address/0x3C612e132624f4Bd500eE1495F54565F0bcc9b59) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IBalancerQueries.abi.json) |
## Bepolia testnet contracts
| Name | Address | ABI |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Vault | [`0x708cA656b68A6b7384a488A36aD33505a77241FE`](https://testnet.berascan.com/address/0x708cA656b68A6b7384a488A36aD33505a77241FE) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IVault.abi.json) |
| ProtocolFeesCollector | [`0x05A607aCf3548E84DD1E44c3706F850c849058Da`](https://testnet.berascan.com/address/0x05A607aCf3548E84DD1E44c3706F850c849058Da) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IProtocolFeesCollector.abi.json) |
| BalancerHelpers | [`0xC7c981ADcDC5d48fed0CD52807fb2bAB22676C8f`](https://testnet.berascan.com/address/0xC7c981ADcDC5d48fed0CD52807fb2bAB22676C8f) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IBalancerHelpers.abi.json) |
| PoolCreationHelper | [`0x0dC9964F6CA33d9EF38DEB4925234766127C6B36`](https://testnet.berascan.com/address/0x0dC9964F6CA33d9EF38DEB4925234766127C6B36) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IPoolCreationHelper.abi.json) |
| ProtocolFeesWithdrawer | [`0x8c2D77f0CfcD4Af9cF41494EfE500FE324012c06`](https://testnet.berascan.com/address/0x8c2D77f0CfcD4Af9cF41494EfE500FE324012c06) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IProtocolFeesWithdrawer.abi.json) |
| ComposableStablePoolFactory | [`0xB60DbBaCEaeC23486a64d12089F467ef949f1bb1`](https://testnet.berascan.com/address/0xB60DbBaCEaeC23486a64d12089F467ef949f1bb1) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IComposableStablePoolFactory.abi.json) |
| BatchRelayerLibrary | [`0xfD772657FC8c4Ed3884AfF151b680883814052FA`](https://testnet.berascan.com/address/0xfD772657FC8c4Ed3884AfF151b680883814052FA) | N/A |
| BatchRelayerQueryLibrary | [`0x263a1C5B2c5851beA2177eb8D6caefdfF2A25601`](https://testnet.berascan.com/address/0x263a1C5B2c5851beA2177eb8D6caefdfF2A25601) | N/A |
| BalancerRelayer | [`0x343215E156Ff586711a5B8C49Fe3099BAF22624C`](https://testnet.berascan.com/address/0x343215E156Ff586711a5B8C49Fe3099BAF22624C) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IBalancerRelayer.abi.json) |
| WeightedPoolFactory | [`0xf1d23276C7b271B2aC595C78977b2312E9954D57`](https://testnet.berascan.com/address/0xf1d23276C7b271B2aC595C78977b2312E9954D57) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IWeightedPoolFactory.abi.json) |
| ProtocolFeePercentagesProvider | [`0x8119E412E00fe3c857739E95dB147817Bf615dB8`](https://testnet.berascan.com/address/0x8119E412E00fe3c857739E95dB147817Bf615dB8) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IProtocolFeePercentagesProvider.abi.json) |
| BalancerQueries | [`0xE3723383a0EA73D5c0dE424BAA97F97f86f6cF92`](https://testnet.berascan.com/address/0xE3723383a0EA73D5c0dE424BAA97F97f86f6cF92) | [ABI](https://github.com/berachain/doc-abis/blob/main/bex/IBalancerQueries.abi.json) |
# 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.
This is a list of addresses where you can read from or write to these contracts.
> A full list of Contract ABIs can be found at [https://github.com/berachain/abis](https://github.com/berachain/abis)
Various parties have audited the deployed contracts. All audit reports are publicly available on
[Github](https://github.com/berachain/security-audits).
## Mainnet contracts
### Proof of Liquidity
| Name | Address |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| BeraChef | [`0xdf960E8F3F19C481dDE769edEDD439ea1a63426a`](https://berascan.com/address/0xdf960E8F3F19C481dDE769edEDD439ea1a63426a) |
| BlockRewardController | [`0x1AE7dD7AE06F6C58B4524d9c1f816094B1bcCD8e`](https://berascan.com/address/0x1AE7dD7AE06F6C58B4524d9c1f816094B1bcCD8e) |
| Distributor | [`0xD2f19a79b026Fb636A7c300bF5947df113940761`](https://berascan.com/address/0xD2f19a79b026Fb636A7c300bF5947df113940761) |
| DedicatedEmissionStreamManager | [`0x813dCdBa9197947792985c866cE98D6739cA821A`](https://berascan.com/address/0x813dCdBa9197947792985c866cE98D6739cA821A) |
| FeeCollector | [`0x7Bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650`](https://berascan.com/address/0x7Bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650) |
| RewardVaultFactory | [`0x94Ad6Ac84f6C6FbA8b8CCbD71d9f4f101def52a8`](https://berascan.com/address/0x94Ad6Ac84f6C6FbA8b8CCbD71d9f4f101def52a8) |
| BGTStaker | [`0x44F07Ce5AfeCbCC406e6beFD40cc2998eEb8c7C6`](https://berascan.com/address/0x44F07Ce5AfeCbCC406e6beFD40cc2998eEb8c7C6) |
| HoneyFactory | [`0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401`](https://berascan.com/address/0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401) |
### Tokens
| Name | Address |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| BGT Token | [`0x656b95E550C07a9ffe548bd4085c72418Ceb1dba`](https://berascan.com/address/0x656b95E550C07a9ffe548bd4085c72418Ceb1dba) |
| WBERA | [`0x6969696969696969696969696969696969696969`](https://berascan.com/address/0x6969696969696969696969696969696969696969) |
| HONEY | [`0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce`](https://berascan.com/address/0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce) |
| WBERA Staker Vault (sWBERA) | [`0x118D2cEeE9785eaf70C15Cd74CD84c9f8c3EeC9a`](https://berascan.com/address/0x118D2cEeE9785eaf70C15Cd74CD84c9f8c3EeC9a) |
### Governance
| Name | Address |
| ---------- | ----------------------------------------------------------------------------------------------------------------------- |
| Governance | [`0x4f4A5c2194B8e856b7a05B348F6ba3978FB6f6D5`](https://berascan.com/address/0x4f4A5c2194B8e856b7a05B348F6ba3978FB6f6D5) |
| Timelock | [`0xb5f2000b5744f207c931526cAE2134cAa8b6862a`](https://berascan.com/address/0xb5f2000b5744f207c931526cAE2134cAa8b6862a) |
### Other
| Name | Address |
| ------------- | ----------------------------------------------------------------------------------------------------------------------- |
| BeaconDeposit | [`0x4242424242424242424242424242424242424242`](https://berascan.com/address/0x4242424242424242424242424242424242424242) |
| Create2 | [`0x4e59b44847b379578588920cA78FbF26c0B4956C`](https://berascan.com/address/0x4e59b44847b379578588920cA78FbF26c0B4956C) |
| Multicall3 | [`0xcA11bde05977b3631167028862bE2a173976CA11`](https://berascan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11) |
| Permit2 | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://berascan.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) |
## Bepolia testnet contracts
### Proof of Liquidity
| Name | Address |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| BeraChef | [`0xdf960E8F3F19C481dDE769edEDD439ea1a63426a`](https://testnet.berascan.com/address/0xdf960E8F3F19C481dDE769edEDD439ea1a63426a) |
| BlockRewardController | [`0x1AE7dD7AE06F6C58B4524d9c1f816094B1bcCD8e`](https://testnet.berascan.com/address/0x1AE7dD7AE06F6C58B4524d9c1f816094B1bcCD8e) |
| Distributor | [`0xD2f19a79b026Fb636A7c300bF5947df113940761`](https://testnet.berascan.com/address/0xD2f19a79b026Fb636A7c300bF5947df113940761) |
| DedicatedEmissionStreamManager | [`0xfe83d31669b52B7a619119Bc71805fD29eeEB9Dd`](https://testnet.berascan.com/address/0xfe83d31669b52B7a619119Bc71805fD29eeEB9Dd) |
| FeeCollector | [`0x7bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650`](https://testnet.berascan.com/address/0x7bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650) |
| RewardVaultFactory | [`0x94Ad6Ac84f6C6FbA8b8CCbD71d9f4f101def52a8`](https://testnet.berascan.com/address/0x94Ad6Ac84f6C6FbA8b8CCbD71d9f4f101def52a8) |
| BGTStaker | [`0x44F07Ce5AfeCbCC406e6beFD40cc2998eEb8c7C6`](https://testnet.berascan.com/address/0x44F07Ce5AfeCbCC406e6beFD40cc2998eEb8c7C6) |
| HoneyFactory | [`0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401`](https://testnet.berascan.com/address/0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401) |
### Tokens
| Name | Address |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| BGT Token | [`0x656b95E550C07a9ffe548bd4085c72418Ceb1dba`](https://testnet.berascan.com/address/0x656b95E550C07a9ffe548bd4085c72418Ceb1dba) |
| WBERA | [`0x6969696969696969696969696969696969696969`](https://testnet.berascan.com/address/0x6969696969696969696969696969696969696969) |
| HONEY | [`0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce`](https://testnet.berascan.com/address/0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce) |
| WBERA Staker Vault (sWBERA) | [`0x118D2cEeE9785eaf70C15Cd74CD84c9f8c3EeC9a`](https://testnet.berascan.com/address/0x118D2cEeE9785eaf70C15Cd74CD84c9f8c3EeC9a) |
### Governance
| Name | Address |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Governance | [`0x4f4A5c2194B8e856b7a05B348F6ba3978FB6f6D5`](https://testnet.berascan.com/address/0x4f4A5c2194B8e856b7a05B348F6ba3978FB6f6D5) |
| Timelock | [`0xb5f2000b5744f207c931526cAE2134cAa8b6862a`](https://testnet.berascan.com/address/0xb5f2000b5744f207c931526cAE2134cAa8b6862a) |
### Other
| Name | Address |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| BeaconDeposit | [`0x4242424242424242424242424242424242424242`](https://testnet.berascan.com/address/0x4242424242424242424242424242424242424242) |
| Create2 | [`0x4e59b44847b379578588920cA78FbF26c0B4956C`](https://testnet.berascan.com/address/0x4e59b44847b379578588920cA78FbF26c0B4956C) |
| Multicall3 | [`0xcA11bde05977b3631167028862bE2a173976CA11`](https://testnet.berascan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11) |
| Permit2 | [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://testnet.berascan.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) |
### 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` |
## Staking pool contracts
Shared singleton addresses for staking pools are listed below. For per-pool proxies, behavior, and links to the guides repo, see [Staking pool contracts](/nodes/staking-pools/contracts).
#### Mainnet
| Name | Address | ABI |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **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) |
#### Bepolia
| Name | Address | ABI |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **StakingPoolContractsFactory** | [`0x24b8223864d3936F56e5a24C4245ae7620471D4C`](https://testnet.berascan.com/address/0x24b8223864d3936F56e5a24C4245ae7620471D4C) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/StakingPoolContractsFactory.json) |
| **DelegationHandlerFactory** | [`0x0aEf09EC97bAc354d31F180b401454cB76abc395`](https://testnet.berascan.com/address/0x0aEf09EC97bAc354d31F180b401454cB76abc395) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/delegation/DelegationHandlerFactory.json) |
| **WithdrawalVault** | [`0xBBed2D94338cdE2926A8C0576432De32C05c66e9`](https://testnet.berascan.com/address/0xBBed2D94338cdE2926A8C0576432De32C05c66e9) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/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)
* [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)
* [Goldsky](https://docs.goldsky.com/chains/berachain)
* [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)
* [The Graph](https://thegraph.com/)
* [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.
## 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.
# 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
Source: https://docs.berachain.com/build/guides/berascan-token-update
# 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/BGT 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/doc/indexer/quickstart/quickstart_chains/berachain-artio-testnet.html#berachain-artio-testnet-quick-start) |
## 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
Source: https://docs.berachain.com/build/pol/advanced-pol
# Proof of Liquidity Integration Guide for Non-ERC20 Protocols
## Introduction
Users typically engage with Proof of Liquidity by staking ERC20 receipt tokens into [Reward Vaults](/learn/pol/rewardvaults) to earn `$BGT`. 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 who don't naturally produce stakeable ERC20 receipt tokens or otherwise need to track balances internally. For example, a perpetual futures exchange may wish to reward users who open trading positions with `$BGT`, and cease 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 `$BGT` as if they had staked an ERC20 receipt token in a PoL vault themselves. This approach is enabled by the `delegateStake` and `delegateWithdraw` methods in the [RewardVault](/developers/contracts/reward-vault) 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 with BGT through Reward Vaults. 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)
# Partial reward claims
Source: https://docs.berachain.com/build/pol/partial-reward-claims
# Partial Reward Claims (BGT Splitting)
## Introduction
The `getPartialReward` function in [Reward Vaults](/developers/contracts/reward-vault) allows users to claim specific amounts of their earned BGT rewards instead of claiming all accumulated rewards at once. This enables more granular reward management and opens up new strategies for BGT utilization.
::: tip User Guide Available
For end users wanting to understand BGT claiming options, see our [user guide on claiming BGT rewards](/learn/guides/claim-bgt).
:::
## Key Differences from Full Reward Claims
### `getReward()` vs `getPartialReward()`
| Method | Amount Claimed | Remaining Rewards | Use Case |
| -------------------- | ---------------- | --------------------- | --------------------- |
| `getReward()` | All accumulated | 0 | Full withdrawal |
| `getPartialReward()` | Specified amount | Remaining after claim | Controlled withdrawal |
**Key Benefits**:
* Fine-grained control over reward timing
* Enables streaming and vesting strategies
* Supports dollar-cost averaging patterns
* Reduces gas costs for large reward balances
## Use Cases
### 1. Streaming Reward Systems
Protocols can implement reward streaming by claiming partial amounts over time:
```solidity theme={null}
contract RewardStreamer {
struct Stream {
uint256 totalAmount;
uint256 claimedAmount;
uint256 startTime;
uint256 duration;
address recipient;
}
mapping(address => Stream) public streams;
IRewardVault public rewardVault;
function createStream(
address user,
uint256 duration,
address recipient
) external {
uint256 totalRewards = rewardVault.earned(user);
require(totalRewards > 0, "No rewards to stream");
streams[user] = Stream({
totalAmount: totalRewards,
claimedAmount: 0,
startTime: block.timestamp,
duration: duration,
recipient: recipient
});
}
function claimStreamedRewards(address user) external {
Stream storage stream = streams[user];
require(stream.totalAmount > 0, "No active stream");
uint256 elapsed = block.timestamp - stream.startTime;
uint256 claimable = (stream.totalAmount * elapsed) / stream.duration;
claimable = claimable > stream.totalAmount ? stream.totalAmount : claimable;
uint256 toClaim = claimable - stream.claimedAmount;
if (toClaim > 0) {
stream.claimedAmount += toClaim;
rewardVault.getPartialReward(user, stream.recipient, toClaim);
}
}
}
```
### 2. Dollar-Cost Averaging (DCA) BGT Management
Automatically convert BGT to BERA over time to reduce price impact:
```solidity theme={null}
contract BGTDCAManager {
struct DCAConfig {
uint256 intervalSeconds;
uint256 amountPerInterval;
uint256 lastClaim;
bool active;
}
mapping(address => DCAConfig) public dcaConfigs;
IRewardVault public rewardVault;
function setupDCA(
uint256 intervalSeconds,
uint256 amountPerInterval
) external {
dcaConfigs[msg.sender] = DCAConfig({
intervalSeconds: intervalSeconds,
amountPerInterval: amountPerInterval,
lastClaim: block.timestamp,
active: true
});
}
function executeDCA(address user) external {
DCAConfig storage config = dcaConfigs[user];
require(config.active, "DCA not active");
require(
block.timestamp >= config.lastClaim + config.intervalSeconds,
"Too early"
);
uint256 available = rewardVault.earned(user);
uint256 toClaim = available < config.amountPerInterval
? available
: config.amountPerInterval;
if (toClaim > 0) {
config.lastClaim = block.timestamp;
rewardVault.getPartialReward(user, address(this), toClaim);
// Convert BGT to BERA and send back to user
_convertBGTToBERA(toClaim, user);
}
}
}
```
### 3. Threshold-Based Reward Management
Claim rewards only when they reach certain thresholds:
```solidity theme={null}
contract ThresholdRewardManager {
mapping(address => uint256) public thresholds;
IRewardVault public rewardVault;
event RewardsClaimed(address indexed user, uint256 amount);
function setThreshold(uint256 _threshold) external {
thresholds[msg.sender] = _threshold;
}
function claimIfThresholdMet(address user) external {
uint256 threshold = thresholds[user];
require(threshold > 0, "No threshold set");
uint256 earned = rewardVault.earned(user);
if (earned >= threshold) {
rewardVault.getPartialReward(user, user, threshold);
emit RewardsClaimed(user, threshold);
}
}
function claimMultipleThresholds(address user, uint256 count) external {
uint256 threshold = thresholds[user];
require(threshold > 0, "No threshold set");
uint256 totalToClaim = threshold * count;
uint256 earned = rewardVault.earned(user);
require(earned >= totalToClaim, "Insufficient rewards");
rewardVault.getPartialReward(user, user, totalToClaim);
emit RewardsClaimed(user, totalToClaim);
}
}
```
## Implementation Examples
### Basic Partial Reward Claiming
```solidity theme={null}
pragma solidity ^0.8.26;
import {IRewardVault} from "./interfaces/IRewardVault.sol";
contract PartialRewardManager {
IRewardVault public immutable rewardVault;
constructor(address _rewardVault) {
rewardVault = IRewardVault(_rewardVault);
}
/// @notice Claim a specific amount of rewards
/// @param amount The amount of BGT to claim
/// @param recipient The address to receive the BGT
function claimPartialReward(uint256 amount, address recipient) external {
uint256 available = rewardVault.earned(msg.sender);
require(amount <= available, "Amount exceeds available rewards");
rewardVault.getPartialReward(msg.sender, recipient, amount);
}
/// @notice Claim a percentage of available rewards
/// @param percentage The percentage to claim (in basis points, e.g., 1000 = 10%)
/// @param recipient The address to receive the BGT
function claimPercentageReward(
uint256 percentage,
address recipient
) external {
require(percentage <= 10000, "Percentage too high"); // Max 100%
uint256 available = rewardVault.earned(msg.sender);
uint256 amount = (available * percentage) / 10000;
if (amount > 0) {
rewardVault.getPartialReward(msg.sender, recipient, amount);
}
}
}
```
### Advanced Vesting Contract
```solidity theme={null}
pragma solidity ^0.8.26;
import {IRewardVault} from "./interfaces/IRewardVault.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract BGTVestingManager is Ownable {
struct VestingSchedule {
uint256 totalAmount; // Total BGT to vest
uint256 claimedAmount; // Amount already claimed
uint256 startTime; // When vesting starts
uint256 duration; // Vesting duration in seconds
uint256 cliffDuration; // Cliff period in seconds
address beneficiary; // Who receives the vested BGT
bool revocable; // Can the vesting be revoked
bool revoked; // Has the vesting been revoked
}
mapping(address => VestingSchedule) public vestingSchedules;
IRewardVault public immutable rewardVault;
event VestingCreated(address indexed user, uint256 amount, uint256 duration);
event VestingClaimed(address indexed user, uint256 amount);
event VestingRevoked(address indexed user);
constructor(address _rewardVault) {
rewardVault = IRewardVault(_rewardVault);
}
function createVestingSchedule(
address user,
uint256 duration,
uint256 cliffDuration,
address beneficiary,
bool revocable
) external onlyOwner {
require(vestingSchedules[user].totalAmount == 0, "Vesting already exists");
uint256 totalRewards = rewardVault.earned(user);
require(totalRewards > 0, "No rewards to vest");
vestingSchedules[user] = VestingSchedule({
totalAmount: totalRewards,
claimedAmount: 0,
startTime: block.timestamp,
duration: duration,
cliffDuration: cliffDuration,
beneficiary: beneficiary,
revocable: revocable,
revoked: false
});
emit VestingCreated(user, totalRewards, duration);
}
function claimVestedRewards(address user) external {
VestingSchedule storage schedule = vestingSchedules[user];
require(schedule.totalAmount > 0, "No vesting schedule");
require(!schedule.revoked, "Vesting revoked");
require(
block.timestamp >= schedule.startTime + schedule.cliffDuration,
"Cliff period not ended"
);
uint256 vestedAmount = _getVestedAmount(schedule);
uint256 claimableAmount = vestedAmount - schedule.claimedAmount;
if (claimableAmount > 0) {
schedule.claimedAmount += claimableAmount;
rewardVault.getPartialReward(user, schedule.beneficiary, claimableAmount);
emit VestingClaimed(user, claimableAmount);
}
}
function revokeVesting(address user) external onlyOwner {
VestingSchedule storage schedule = vestingSchedules[user];
require(schedule.revocable, "Vesting not revocable");
require(!schedule.revoked, "Already revoked");
// Claim any vested amount up to this point
uint256 vestedAmount = _getVestedAmount(schedule);
uint256 claimableAmount = vestedAmount - schedule.claimedAmount;
if (claimableAmount > 0) {
schedule.claimedAmount += claimableAmount;
rewardVault.getPartialReward(user, schedule.beneficiary, claimableAmount);
}
schedule.revoked = true;
emit VestingRevoked(user);
}
function _getVestedAmount(
VestingSchedule memory schedule
) internal view returns (uint256) {
if (schedule.revoked) {
return schedule.claimedAmount;
}
uint256 elapsed = block.timestamp - schedule.startTime;
if (elapsed < schedule.cliffDuration) {
return 0;
}
if (elapsed >= schedule.duration) {
return schedule.totalAmount;
}
return (schedule.totalAmount * elapsed) / schedule.duration;
}
function getVestingInfo(address user) external view returns (
uint256 totalAmount,
uint256 claimedAmount,
uint256 vestedAmount,
uint256 claimableAmount
) {
VestingSchedule memory schedule = vestingSchedules[user];
totalAmount = schedule.totalAmount;
claimedAmount = schedule.claimedAmount;
vestedAmount = _getVestedAmount(schedule);
claimableAmount = vestedAmount - claimedAmount;
}
}
```
## Error Handling
The `getPartialReward` function can revert with `AmountGreaterThanReward` if the requested amount exceeds available rewards:
```solidity theme={null}
contract SafePartialClaimer {
IRewardVault public rewardVault;
function safeClaimPartial(
address user,
uint256 amount,
address recipient
) external returns (bool success) {
uint256 available = rewardVault.earned(user);
// Check if amount is available
if (amount > available) {
return false;
}
try rewardVault.getPartialReward(user, recipient, amount) {
return true;
} catch {
return false;
}
}
function claimMaxAvailable(
address user,
uint256 maxAmount,
address recipient
) external returns (uint256 claimed) {
uint256 available = rewardVault.earned(user);
uint256 toClaim = available < maxAmount ? available : maxAmount;
if (toClaim > 0) {
rewardVault.getPartialReward(user, recipient, toClaim);
return toClaim;
}
return 0;
}
}
```
## Gas Optimization Considerations
### Batch Partial Claims
For users with multiple reward positions, batch operations can save gas:
```solidity theme={null}
contract BatchPartialClaimer {
struct ClaimRequest {
address user;
uint256 amount;
address recipient;
}
function batchClaimPartial(ClaimRequest[] calldata requests) external {
for (uint256 i = 0; i < requests.length; i++) {
ClaimRequest memory req = requests[i];
// Verify caller has permission (operator or user)
require(
msg.sender == req.user ||
rewardVault.operator(req.user) == msg.sender,
"Not authorized"
);
uint256 available = rewardVault.earned(req.user);
if (req.amount <= available) {
rewardVault.getPartialReward(req.user, req.recipient, req.amount);
}
}
}
}
```
### Operator Integration
Operators can manage partial rewards on behalf of users:
```solidity theme={null}
contract OperatorRewardManager {
mapping(address => mapping(address => bool)) public operatorApprovals;
modifier onlyApprovedOperator(address user) {
require(
msg.sender == user ||
operatorApprovals[user][msg.sender] ||
rewardVault.operator(user) == msg.sender,
"Not authorized"
);
_;
}
function approveOperator(address operator) external {
operatorApprovals[msg.sender][operator] = true;
}
function claimPartialForUser(
address user,
uint256 amount,
address recipient
) external onlyApprovedOperator(user) {
rewardVault.getPartialReward(user, recipient, amount);
}
}
```
## Interface Definition
```solidity theme={null}
interface IRewardVault {
/// @notice Claim a partial reward amount
/// @param account The account to get the reward for
/// @param recipient The address to send the reward to
/// @param amount The amount of the reward to claim
function getPartialReward(
address account,
address recipient,
uint256 amount
) external;
/// @notice Get the total earned rewards for an account
/// @param account The account to check
/// @return The amount of earned rewards
function earned(address account) external view returns (uint256);
/// @notice Get the operator for an account
/// @param account The account to check
/// @return The operator address
function operator(address account) external view returns (address);
}
```
## Testing Example
```solidity theme={null}
pragma solidity ^0.8.26;
import "forge-std/Test.sol";
import {IRewardVault} from "../src/interfaces/IRewardVault.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract PartialRewardTest is Test {
IRewardVault rewardVault;
IERC20 bgt;
address user = makeAddr("user");
address recipient = makeAddr("recipient");
function setUp() public {
// Setup contracts (implementation specific)
rewardVault = IRewardVault(/* deployed address */);
bgt = IERC20(/* BGT address */);
}
function testPartialRewardClaim() public {
// Setup: user has earned rewards
uint256 totalEarned = 1000e18;
uint256 partialAmount = 300e18;
// Verify user has rewards
assertEq(rewardVault.earned(user), totalEarned);
// Claim partial amount
vm.prank(user);
rewardVault.getPartialReward(user, recipient, partialAmount);
// Verify recipient received BGT
assertEq(bgt.balanceOf(recipient), partialAmount);
// Verify remaining rewards
assertEq(rewardVault.earned(user), totalEarned - partialAmount);
}
function testPartialRewardFailsIfAmountTooHigh() public {
uint256 totalEarned = 1000e18;
uint256 excessiveAmount = 1500e18;
vm.prank(user);
vm.expectRevert("AmountGreaterThanReward");
rewardVault.getPartialReward(user, recipient, excessiveAmount);
}
}
```
## Best Practices
1. **Always Check Available Balance**: Verify earned rewards before claiming
2. **Handle Zero Amounts**: Check for zero claim amounts to avoid unnecessary transactions
3. **Implement Access Control**: Ensure proper permissions for operator-based claims
4. **Use Events**: Emit events for transparency and tracking
5. **Gas Optimization**: Batch operations when possible
6. **Error Handling**: Implement proper error handling for failed claims
## Integration Patterns
### Frontend 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(),
});
// Check available rewards
const availableRewards = await publicClient.readContract({
address: "0x...", // Reward Vault address
abi: rewardVaultAbi,
functionName: "earned",
args: [userAddress],
});
// Claim 25% of available rewards
const claimAmount = (availableRewards * 25n) / 100n;
// Execute partial claim
const { request } = await publicClient.simulateContract({
address: "0x...", // Reward Vault address
abi: rewardVaultAbi,
functionName: "getPartialReward",
args: [userAddress, recipientAddress, claimAmount],
});
const hash = await walletClient.writeContract(request);
```
#### 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);
const rewardVault = new ethers.Contract(vaultAddress, rewardVaultAbi, signer);
// Check available rewards
const availableRewards = await rewardVault.earned(userAddress);
// Claim 25% of available rewards
const claimAmount = availableRewards.mul(25).div(100);
// Execute partial claim
const tx = await rewardVault.getPartialReward(userAddress, recipientAddress, claimAmount);
await tx.wait();
console.log("Partial reward claimed:", tx.hash);
```
#### Foundry Integration
```solidity theme={null}
// Test script for partial reward claiming
pragma solidity ^0.8.26;
import "forge-std/Script.sol";
import "./interfaces/IRewardVault.sol";
contract PartialClaimScript is Script {
IRewardVault rewardVault = IRewardVault(0x...); // Vault address
function run() external {
vm.startBroadcast();
address user = msg.sender;
address recipient = msg.sender;
// Check available rewards
uint256 availableRewards = rewardVault.earned(user);
console.log("Available rewards:", availableRewards);
// Claim 25% of available rewards
uint256 claimAmount = (availableRewards * 25) / 100;
// Execute partial claim
rewardVault.getPartialReward(user, recipient, claimAmount);
console.log("Claimed amount:", claimAmount);
vm.stopBroadcast();
}
}
```
### Automated Strategies
```solidity theme={null}
// Weekly claim automation
function autoClaimWeekly(address user) external {
uint256 lastClaim = lastClaimTime[user];
require(block.timestamp >= lastClaim + 1 weeks, "Too early");
uint256 available = rewardVault.earned(user);
uint256 weeklyAmount = weeklyClaimAmounts[user];
uint256 toClaim = available < weeklyAmount ? available : weeklyAmount;
if (toClaim > 0) {
lastClaimTime[user] = block.timestamp;
rewardVault.getPartialReward(user, user, toClaim);
}
}
```
## Related Documentation
**Contract References:**
* [Reward Vault Contract](/developers/contracts/reward-vault) - Complete API documentation
* [`getPartialReward` function](/developers/contracts/reward-vault#getpartialreward) - Function reference
* [BGT Token Documentation](/developers/contracts/bgt-token) - Reward token details
**Integration Guides:**
* [Staking for Other Accounts](/developers/guides/staking-for-other-accounts) - Complementary feature
* [Advanced PoL Integration](/developers/guides/advanced-pol) - Non-ERC20 protocols
**User Guides:**
* [Claim BGT Rewards](/learn/guides/claim-bgt) - End user claiming guide
# Staking for other accounts
Source: https://docs.berachain.com/build/pol/staking-for-other-accounts
# Staking for Other Accounts
## Introduction
The `stakeOnBehalf` function in [Reward Vaults](/developers/contracts/reward-vault) 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.
::: tip User Guide Available
For end users wanting to understand how protocols can claim BGT on their behalf, see the [BGT claiming guide](/learn/guides/claim-bgt#protocol-claiming).
:::
## 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 Vault Contract](/developers/contracts/reward-vault) - Complete API documentation
* [`stakeOnBehalf` function](/developers/contracts/reward-vault#stakeonbehalf) - Function reference
**Integration Guides:**
* [Advanced PoL Integration](/developers/guides/advanced-pol) - Non-ERC20 protocols
* [PoL Integration Quickstart](/developers/quickstart/pol-integration) - Getting started
* [Partial Reward Claims](/developers/guides/partial-reward-claims) - Complementary feature
**User Guides:**
* [BGT Claiming Guide](/learn/guides/claim-bgt#protocol-claiming) - End user perspective on protocol claiming
# 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 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 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 onchain today, with detailed guides in the sidebar.
## Upgrade Timeline
| Upgrade | Mainnet activation | Bepolia activation | 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 | Coming soon |
## Fusaka Upgrade Features
The Fusaka upgrade activates 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
# Reward Vault Governance
Source: https://docs.berachain.com/general/governance/reward-vault-governance
Whitelisting and managing reward vaults.
While creating a [Reward Vault](/general/proof-of-liquidity/reward-vaults) is permissionless, they must be whitelisted through governance proposals to receive `$BGT` emissions from validators. This process ensures community oversight and alignment over projects joining the Proof-of-Liquidity (PoL) ecosystem.
## Request for reward vault
The Reward Vault whitelisting process involves 1) submitting a **Request for Reward Vault** (RFRV)
form **AND** 2) posting on the [Governance Forum](https://forum.berachain.com/c/proposals/).
There are two types of RFRVs - **BEX Pool RFRVs** (For token pairs trading on BEX) and **General (non-BEX) RFRVs** (For other protocol integrations).
### Application Form
Here is the official to submit an application for Request For Reward Vault (RFRV):\
[RFRV Application Form](https://dub.sh/berarfrv)
### 1. BEX pool RFRVs Requirements
These are some of the requirements and more details can be found in the form.
* Pool must be live on BEX
* Incentive tokens must be active on BEX pool
* Recommended to pair with major tokens (BERA, HONEY, BYUSD, USDC, wETH, wBTC)
* Demonstrate economic value through TVL or trading volume
* Meet security and decentralization criteria
### 2. General (non-BEX) RFRVs Requirements
These are some of the requirements and more details can be found in the form.
* Contract must be deployed and live
* Incentive tokens must be live
* Demonstrate economic value and ecosystem synergy
* Meet security and decentralization criteria
Proposals are reviewed weekly (deadline Thursday 00:00 UTC) by the BGT Foundation and Guardians.
For technical implementation details of Reward Vaults, please see the [developer documentation](/build/getting-started/deployed-contracts).
# Frequently Asked Questions
Source: https://docs.berachain.com/general/help/faqs
Common questions about Berachain.
Berachain has the following properties:
* Block time: Block times vary, check [Berascan](https://berascan.com) for the latest.
* 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
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.
`$HONEY` is the native stablecoin of the Berachain ecosystem. It is a multicollateral backed
stablecoin, and is used throughout the Berachain ecosystem.
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`.
\$BGT is Berachain's staking & governance token. That means it is used to secure the network & earn
rewards via Proof of Liquidity as well as to vote on governance proposals.
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
Delegating `$BGT` allows you to participate in Proof of Liquidity while helping secure the
network.
Rewards are the main reason. With Proof of Liquidity, you can earn many different types of rewards:
* A share of protocol-provided [incentives](/general/proof-of-liquidity/incentives), provided in exchange for `$BGT` emissions directed to those protocols' Reward Vaults
* A share of Berachain core dApp fees, namely fees from BEX and HoneySwap
You earn `$BGT` through Reward Vaults when validators direct `$BGT` emissions towards them. See
[Earning \$BGT](/general/tokens/bgt#earning-bgt) for more.
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.
Each eligible (whitelisted) pool on BEX has an associated LP token. Once you deposit liquidity
into a BEX pool, the pool issues an LP token relative to your total contribution percentage. You
must then stake this LP token into its respective Reward Vault to become eligible to receive
`$BGT`. As validators direct `$BGT` emissions to Reward Vaults, you accumulate `$BGT` to claim.
You must perform an additional action to claim `$BGT` — the protocol does *NOT* automatically send
it to you.
Anyone with the required minimum amounts of `$BGT` can propose and vote on proposals.
* Validators stake `$BERA` - Network incentives come in `$BGT`
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. Validators receive rewards in `$BGT`.
# 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.
## BERA token
`$BERA` is the native gas token of Berachain's L1 and serves multiple purposes:
* Used for paying transaction fees
* Initial validator staking token to secure the network
* More BERA staked = more blocks proposed
* Validators earn base emissions and transaction fees (i.e. MEV) for each block proposed
* Can be obtained by burning BGT (one-way conversion)
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` is Berachain's staking and governance token, which is non-transferrable and can only be earned by participating in Proof of Liquidity (PoL):
* Validator delegation and rewards
* More BGT delegated = more reward emissions for reward vaults
* Governance participation (proposals and voting)
* Can be burned for `$BERA` (one-way conversion)
* Can only be earned through participating in Proof of Liquidity
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.
## 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.
## Delegation
The process by which a token holder grants voting or validation power to another participant in the network.
## 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.
## 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.
## 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.
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.
## 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) using `$BGT` tokens for participation.
## 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. Premised on a two-token model, validators have varying probabilities of being selected based on the amount of `$BERA` they have staked. Block rewards are distributed as `$BGT` tokens, the amount of which is influenced by the amount of `$BGT` delegated to them by users.
## 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 into the WBERA Staking Vault, BERA holders gain direct yield opportunities: 33% of protocol incentives are automatically redirected during the incentive distribution process and distributed to these stakers.
# 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 BGT emissions.
Reward Vaults drive meaningful economic activity on Berachain. To qualify for **BGT 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 **BGT farming**, the majority of that yield must be **passed through to stakers**, not recycled.
* Yield from **non-BGT sources** can be recycled into incentives.
* This prevents **double-dipping BGT rewards** 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.
### BGT use clarity
* When applying, be specific about exactly what BGT will be used for.
### 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 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)
* [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](/general/introduction/what-is-berachain#evm-identical) Layer 1 built on [Proof-of-Liquidity](/general/introduction/what-is-berachain#proof-of-liquidity) (PoL) and the modular consensus framework [BeaconKit](/general/introduction/what-is-berachain#beaconkit). It delivers high performance while keeping full compatibility with Ethereum tooling and upgrades.
## EVM Identical
Berachain's execution runtime environment is identical to the Ethereum Virtual Machine (EVM) as seen on Ethereum Mainnet. Berachain uses a lightly modified fork of the popular [execution client](/general/help/glossary#execution-client) Reth (Bera-Reth) to handle executing smart contracts. Berachain supports all the [usual tooling](/build/getting-started/developer-tools) that is familiar to EVM developers.
Identical means that whenever the EVM is upgraded, Berachain can adopt the latest version—for example, Dencun—straight out of the box. 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
Proof-of-Liquidity radically changes the way L1 economics are structured, prioritizing users and applications over validator rewards at baseline. Network incentives go towards enriching ecosystem liquidity, contributing to efficient trading, price stability, securing the chain, and increasing network/user growth.
PoL strongly aligns the incentives of [network participants](/general/proof-of-liquidity/participants) (validators, protocols, users) and contributes to the overall long-term health of the chain.
Beyond providing seamless day-one utility, the native dApps, such as [BEX](/bex/learn/overview), serve as reference implementations of how developers can build on top of Proof-of-Liquidity.
Read more in [What Is Proof-of-Liquidity](/general/introduction/what-is-proof-of-liquidity).
## BeaconKit
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.
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
Two-token model, validator incentives, and reward vaults.
Proof-of-Liquidity (PoL) is a novel economic mechanism that uses network incentives to align the interests of ecosystem participants and bolster both the application-layer and chain security.
## Two token model
Berachain's consensus borrows from the Proof-of-Stake (PoS) model and contains two key components:
1. `$BERA` - Validators secure the chain by staking the native gas token
2. `$BGT` - A soulbound governance token distributed by validators for proposing new blocks, which is ultimately rewarded to those who provide ecosystem liquidity (see [Reward Vaults](/general/proof-of-liquidity/reward-vaults))
A validator's `$BGT` emissions increase with the amount of `$BGT` delegated to them. Protocol-provided Incentives are received for these emissions, and validators pass these to their delegators after collecting a commission.
This model creates meaningful economic alignment between previously isolated groups. Validators who return the maximum value to their `$BGT` delegators are likely to receive more delegations.
### Separation of concerns
Significantly, Proof-of-Liquidity separates the token responsible for gas and security from the token used to govern chain rewards and economic incentives.
The following diagram illustrates the roles of tokens in Berachain's PoL compared to Ethereum's PoS:
Read more in [Proof-of-Liquidity Overview](/general/proof-of-liquidity/overview).
# Block Production & Rewards
Source: https://docs.berachain.com/general/proof-of-liquidity/block-rewards
Block production, BGT emissions, and reward distribution.
Proof-of-Liquidity governs block rewards and token emissions on Berachain using the `$BGT` token. This page explains the mathematical principles behind validator selection, block rewards, and emissions calculations.
## Validator selection
The network maintains an active set of **69 validators** who are eligible for block production. Selection criteria include:
* Only top **69 validators** by `$BERA` stake are included in active set
* Block proposal probability is proportional to staked `$BERA` and does not affect reward amounts
* Stake limitations per validator:
* Minimum: 250,000 `$BERA`
* Maximum: 10,000,000 `$BERA`
A given Validator's probability of being selected to produce a block is the proportion of its stake's weight to the total stakes of the active set.
## \$BGT emissions structure
When a validator produces a block, `$BGT` tokens are emitted through two emission components:
1. Base Emission
* **Fixed amount** equal to a `base rate` parameter (B)
* Paid directly to block-producing validator
2. Reward Vault Emission
* **Variable amount** dependent on validator's boost (x)
* i.e. percentage of total `$BGT` delegated to the validator
* Distributed to [Reward Vaults](/general/proof-of-liquidity/reward-vaults) selected by validator
* Proportional to weights configured in the validator's Reward Allocation
* Validators receive [Incentives](/general/proof-of-liquidity/incentives) from projects based on amounts directed to their Reward Vaults
## Validator boosts
Boost is a crucial metric that determines a validator's reward emissions:
* Calculated as the percentage of `$BGT` delegation a validator has compared to the total `$BGT` delegated in the network
* Expressed as a decimal between 0 and 1
* Example: If a validator has 1000 `$BGT` delegated and the network has 10000 total `$BGT` delegated, their boost would be 0.1 (10%). Higher boost leads to higher reward emissions, subject to the emission formula.
## BeraChef: Reward allocation management
BeraChef is the core contract that manages how validators direct their BGT rewards across different Reward Vaults. It serves as the configuration layer that determines reward distribution based on validator preferences.
### Core responsibilities
BeraChef manages three key aspects of the reward system:
1. **Reward Allocations** - Maintains lists of weights that determine the percentage of rewards going to each Reward Vault
2. **Validator Commission** - Manages commission rates that validators can charge on incentive tokens
3. **Vault Whitelisting** - Controls which vaults are eligible to receive BGT rewards
### How reward allocations work
Each validator can set a custom reward allocation that specifies how their BGT rewards should be distributed.
Reward Allocations are described by:
1. A list of selected vaults and the percentage of a given block's BGT reward to send to each. The weights must sum to 100.
2. The block number the allocation becomes effective.
Validators exercise control over the BeraChef reward allocation subject to a delay of 450 blocks.
**If a validator doesn't update its cutting board within 302,400 blocks (approximately 7 days),** BeraChef will begin to apply the *baseline* cutting board. This *baseline* allocation is chosen to direct emissions efficiently to reward vaults with active incentives.
### Commission management
BeraChef manages validator commission rates on incentive tokens with the following constraints:
* **Default Commission**: 5% if not explicitly set
* **Maximum Commission**: 20% hard cap enforced by the contract
* **Change Delay**: Required waiting period before commission changes take effect
## \$BGT emissions per block
The total `$BGT` emitted per block is calculated using the following formula:
$emission = \left[B + \max\left(m, (a + 1)\left(1 - \frac{1}{1 + ax^b}\right)R\right)\right]$
### Parameters
| Parameter | Description | Impact |
| ------------------------------- | --------------------------------------------------------------- | ---------------------------------------------- |
| x (boost) | Fraction of total `$BGT` delegated to validator (range: \[0,1]) | Determines `$BGT` emissions to Reward Vaults |
| B (base rate) | Fixed amount of `$BGT` for block production | Determines baseline validator rewards |
| R (reward rate) | Base `$BGT` amount for reward vaults | Sets foundation for reward emissions |
| a (boost multiplier) | Boost impact coefficient | Higher values increase boost importance |
| b (convexity parameter) | Boost impact curve steepness | Higher values penalize low boost more severely |
| m (minimum boosted reward rate) | Floor for reward vault emissions | Higher values benefit low-boost validators |
This formula describes the total variable emission for a block. If the [Dedicated emission stream](#dedicated-emission-stream) is active, a fraction is directed to DES vaults before the remainder reaches the validator's reward allocation.
### Sample emissions chart
Using the current on-chain parameters (as of February 2026), the chart below shows how emissions scale with `$BGT` delegation:
$B = 0.4, R = 0.65, a = 3.5, b = 0.4, m = 0$
## Max block inflation
`$BGT` emissions grow with the amount of boost a validator has, up to a cap. The maximum theoretical block emission occurs at 100% boost:
$\max \mathbb{E}[\text{emission}] = \left[B + \max(m, aR)\right]$
## Dedicated emission stream
Before the validator's own reward allocation is applied, the Distributor may carve out a fraction of the variable emission and direct it to a governance-designated set of reward vaults. This mechanism is called the **Dedicated Emission Stream (DES)**.
```mermaid theme={null}
flowchart TD
BlockReward["Variable emission"] --> DESCheck{"DES active?"}
DESCheck -->|Yes| DESCarveOut["DES carve-out"]
DESCarveOut --> DESVaults["DES Reward Vaults"]
DESCarveOut -->|"excess (cap reached)"| Remainder
DESCheck -->|No| Remainder["Remaining reward"]
Remainder --> ValidatorRA["Validator Reward Allocation"]
```
The `DedicatedEmissionStreamManager` contract controls three parameters, all set by governance:
* **`emissionPerc`** — the percentage of each block's variable reward that is carved out, expressed in basis points out of 10,000. A value of 500 means 5%.
* **`targetEmission`** — a per-vault cumulative cap. Once a vault has received its target amount of DES emissions, it stops receiving further distributions. Any excess is returned to the validator's own reward allocation for that block.
* **Reward allocation weights** — a list of whitelisted reward vaults and the share each receives within the carve-out, following the same weight format used by BeraChef (percentages must sum to 100%).
### Impact on validators
The DES carve-out reduces the effective variable reward available for validator-directed allocation. If `emissionPerc` is set to 5%, a validator producing a block receives 95% of the variable emission to distribute according to its BeraChef reward allocation. The base emission (paid directly to the validator's operator) is unaffected.
The current DES parameters — including the carve-out percentage, target vaults, and per-vault caps — can be read from the [`DedicatedEmissionStreamManager`](/build/getting-started/deployed-contracts) contract on-chain.
## \$BGT distribution
The Distributor emits `$BGT` to reward vaults on a per-block basis. The network processes distributions for a given block during the following block. This creates `$BGT` that Reward Vault stakers can then claim.
The network creates rewards on a per-block basis; however, it distributes them **over a three-day period.** Depositors receive rewards streamed linearly over this period, proportionally to their deposit amounts. The reward window resets each time new rewards arrive.
### Distribution example
On Berachain, `$BGT` is distributed per block, meaning that the three-day distribution period is consistently being pushed to "start" on the current block. Thus, this period should be viewed as a sliding window based on the emissions at any given time during the previous three days.
A more real-world example with simplified numbers:
* 3 `$BGT` distributed daily, for a total of 27 over 9 days
* 1 depositor, owning all the deposits
**Legend**
* Emitted: Total number of `$BGT` distributed and available
* Claimable: Total number of `$BGT` able to be claimed by depositors
* Daily Reward: Daily number of `$BGT` marked as claimable based on emitted tokens unlocks
This results in the depositor receiving an increasing amount of `$BGT` daily until rewards reach a saturation point after three days where all rewards are actively being distributed.
Reward duration periods incentivize ecosystem alignment with depositors via this distribution mechanism rather than allowing rewards to be instantly claimed.
## Calculating boost APR
Boost APR is shown throughout the [Berachain Hub](https://hub.berachain.com).
Boost APR % is calculated using ranges of blocks, defined by a starting block and an ending block. At the time the percentages are calculated, the APR calculator samples the prices of all tokens (in \$BERA).
# Incentive Marketplace
Source: https://docs.berachain.com/general/proof-of-liquidity/incentives
How protocols bid for validator BGT emissions.
Proof-of-Liquidity (PoL) enables protocols to encourage validators to direct their `$BGT` emissions to a protocol's Reward Vault using whitelisted Incentive tokens. In doing so, protocols attract users to their protocol with `$BGT` rewards.
## How incentives work
Validators can capture Incentives offered by Whitelisted Reward Vaults by directing their `$BGT` emissions towards those Reward Vaults, defined by a validator's Reward Allocation - the list of Reward Vaults a validator directs percentages of its `$BGT` emissions to.
### Incentive distribution flow
The distribution of Incentives follows this process:
1. A **User** Boosts (by associating their `$BGT` with a validator) to increase a validator's `$BGT` emissions they receive when proposing a block.
2. A **Validator** receives block rewards in the form of `$BGT` emissions, where the amount is influenced by Boost (see [`$BGT` Emissions Per Block](/general/proof-of-liquidity/block-rewards)), and directs emissions toward Whitelisted Reward Vaults chosen by the validator.
3. A **Protocol** can offer up to two different Incentive Tokens to encourage validators to direct `$BGT` emissions to their Reward Vault.
4. When the `$BGT` block reward emissions are distributed:
* The validator's operator address receives a commission (percentage of all the Incentive Tokens captured).
* The remaining Incentives, for those who boosted the validator, are sent to a contract and backend service that manages the distribution and claiming of these tokens. The backend API updates proofs of your entitlement to Incentive tokens every 24 hours. This eligibility never expires.
5. To claim your Incentive token rewards, you retrieve this proof and claim through the BGTIncentiveDistributor contract. All proof handling is done by interacting with [Berachain Hub](https://hub.berachain.com).
### Incentives and users
As an overview, Incentives involve 3 different parties:
| User | Description & Motivation |
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Booster | Any user that boosts a validator with `$BGT`. This increases a validator's `$BGT` emissions, allowing them to capture more Incentives from Reward Vaults. Boosters receive a portion of these Incentives. |
| Validator | A node in the Active Set that directs `$BGT` emissions to different Reward Vaults defined by their Reward Allocation distribution, captures Incentives from Reward Vaults, and takes a percentage (Commission Rate) of the Incentives captured. |
| Protocol | An entity, group, or organization that offers Incentive tokens for their respective Reward Vault with the goal of capturing `$BGT` emissions for their protocol and/or users. |
## Incentive mechanics
Behind Incentives, there are additional mechanics to consider beyond the high-level overview of how distribution works.
### Whitelisting incentive tokens
Only Whitelisted Reward Vaults can offer Incentives. A governance process must whitelist each Incentive Token, where the proposer needs to specify both the Token and a Token Manager.
A Reward Vault can have up to two unique Incentive Tokens whitelisted. Adding or replacing a token requires passing a governance proposal.
### Incentive token managers
Only Incentive Token Managers of a Reward Vault can offer Incentives. One wallet address is responsible for offering an Incentive Token. The same wallet address can be set as a Token Manager for multiple Incentive Tokens.
A Governance proposal will specify Token Managers when proposing an Incentive Token. Changing a Token or Token Manager requires passing a governance proposal.
### Offering incentives
A Reward Vault can offer up to two Incentive Tokens simultaneously. The offered amount cannot be withdrawn or revoked.
The Incentive Token Manager must define an **Incentive Rate** (the exchange rate of incentive tokens per individual BGT), which must be greater than the minimum approved in the Governance proposal. This rate determines how many incentive tokens are distributed for each BGT received by the vault, regardless of BGT emission timing.
Key aspects of incentive rates:
* **Exchange Rate Formula**: `incentiveTokens = bgtReceived × incentiveRate`
* **Rate Constraints**: Cannot decrease the rate until the supply of Incentive Tokens has been exhausted
* **Rate Updates**: Can increase the rate when depositing additional tokens
* **Independent of BGT Timing**: The exchange rate operates the same whether BGT is distributed over 3 days or 7 days
Key takeaways are that Token Managers:
* Can increase the Incentive Tokens by any number, keeping the same Incentive Rate.
* Can increase the Incentive Rate when they deposit additional Incentive Tokens.
* Can set a lower Incentive Rate only when the Incentive Tokens have been fully exhausted.
* Cannot decrease the Incentive Rate while an existing Incentive is active.
* Cannot reclaim or return the Incentive Tokens once they are added.
### Incentive commission and distribution
Each validator can set a percentage that they take as a commission of all Incentive Tokens received for directing `$BGT` emissions to different Reward Vaults offering Incentives. Every time `$BGT` block rewards are distributed, the validator will receive their commission rate of Incentive Tokens.
Validator commission cannot exceed **20%** (`MAX_COMMISSION_RATE = 0.2e4`). Any attempt to queue a higher value will revert, and stored values above the cap are clamped when read.
**Example:**
| Incentive Tokens | Incentive Rate |
| ---------------- | -------------- |
| 100 `$USDC` | 100 /`$BGT` |
Validator A has an Incentive Commission of `5%` and directs 1 `$BGT` of emissions toward the Reward Vault.
From `100 $USDC`, the validator would get `5 $USDC`, based on their commission, leaving `95 $USDC` for anyone who boosted the validator.
| Party | `$BGT` Boost To Val A | % of Total Boost | Total Incentive Token Rewards |
| ----------- | --------------------: | ---------------: | -----------------------------------: |
| Validator A | 20 `$BGT` | 25% | .05 × 100 + .25 × 95 = 28.75 `$USDC` |
| Manny | 40 `$BGT` | 50% | .5 × 95 = 47.50 `$USDC` |
| Cami | 10 `$BGT` | 12.5% | .125 × 95 = 11.875 `$USDC` |
| Jintao | 10 `$BGT` | 12.5% | .125 × 95 = 11.875 `$USDC` |
A validator can change their commission percentage by first queueing the rate to notify users of the upcoming change, waiting `16,382` blocks, and then anyone may activate the new rate for the validator.
Validator commission cannot exceed **20%** (`MAX_COMMISSION_RATE = 0.2e4`). Any attempt to queue a
higher value will revert, and stored values above the cap are clamped when read.
The **timing** of BGT emissions (which triggers incentive distribution) is controlled by the
Reward Vault's emission mode. While incentive token exchange rates are set by protocol token
managers, the distribution timing is managed separately. See [BGT Emission
Modes](/general/proof-of-liquidity/reward-vaults#bgt-emission-modes) for complete details on
duration-based and target rate modes.
## Incentive redirection
A portion of protocol incentives is automatically redirected during the incentive distribution process for BERA stakers:
* **Redirection Rate**: 33% of the incentive amount
* **Collection Process**: Automatically deducted when incentives are distributed
* **Distribution**: Redirected incentives are auctioned for WBERA and distributed to BERA stakers
* **Remaining Amount**: Validators receive the remaining 67% of incentives
This mechanism ensures that BERA stakers receive direct yield from PoL incentives while maintaining the competitive incentive marketplace.
# Proof-of-Liquidity Overview
Source: https://docs.berachain.com/general/proof-of-liquidity/overview
Two-token model, participants, and incentive alignment.
Proof-of-Liquidity (PoL) is an extension of Proof-of-Stake (PoS) that realigns economic incentives among validators, applications, and users. This is enabled through a two-token model - a token responsible for chain security (`$BERA`) and a token responsible for governance and rewards (`$BGT`). Further information about the design goals for Proof of Liquidity are in the [Honeypaper](https://honeypaper.berachain.com/).
## Core components
### Security layer (\$BERA)
Berachain's Active Set of validators (validators participating in consensus) is determined by validators' `$BERA` stake, with a minimum of 250,000 `$BERA` and a maximum cap of 10,000,000 `$BERA`. The top 69 validators ranked by stake are in the Active Set. Within the Active Set, a validator's probability of proposing a block is proportional to their staked `$BERA` — more `$BERA` staked increases the likelihood of proposing a block.
### Reward layer (\$BGT)
The size of a validator's `$BGT` block reward is determined by their Boost, which is a percentage calculated from the validator's `$BGT` boost divided by the total `$BGT` boosted to all validators. `$BGT` holders provide boosts by delegating to validators.
Learn more about how emissions are calculated on the [emissions page](/general/proof-of-liquidity/block-rewards).
### Reward allocation management (BeraChef)
BeraChef manages the configuration layer of PoL, controlling how validators direct their BGT rewards across different Reward Vaults. It handles validator reward allocation preferences, vault whitelisting, and commission management, ensuring that only governance-approved protocols can participate in the ecosystem.
## PoL lifecycle
The journey begins when a Prospective Validator stakes their `$BERA` as a security bond. Validators are chosen to propose blocks with a probability proportional to their staked amount. For each block proposed, the validator receives both a base emission and a variable reward emission based on their boost percentage (see [emissions](/general/proof-of-liquidity/block-rewards)).
**For Validators**: Learn about the validator lifecycle.
After collecting the base `$BGT` rewards for themselves, validators direct the remaining variable `$BGT` rewards to whitelisted Reward Vaults of their choosing. In exchange for directing their emissions, validators receive protocol-provided Incentives from Reward Vaults (the `$BGT` is earned by users supplying liquidity to the protocol).
**For Validators**: Learn about managing your reward allocations and setting commission rates.
The ecosystem's liquidity providers (i.e., users) play a crucial role in PoL. You can provide liquidity to protocols like BEX and receive receipt tokens as proof of your contribution. These receipt tokens are then staked in Reward Vaults, where you earn `$BGT` proportional to your share of the vault.
Learn about [integrating with Berachain's incentive system](/general/proof-of-liquidity/incentives) and [Reward Vault design](/general/proof-of-liquidity/reward-vaults).
As `$BGT` Holders accumulate tokens, they can delegate them to validators, directly influencing the validator's boost. This creates a virtuous cycle where higher delegation leads to increased validator boost, resulting in larger `$BGT` emissions when that validator proposes blocks. Validators are incentivized to share their received protocol Incentives with delegators to attract more boosts, fostering a collaborative ecosystem.
**For BGT Holders**: Learn how to boost a validator with BGT to participate in the delegation cycle.
# Proof-of-Liquidity Participants
Source: https://docs.berachain.com/general/proof-of-liquidity/participants
Validators, protocols, and users in the PoL ecosystem.
This article explores the different players in the Proof-of-Liquidity (PoL) ecosystem and their roles. The following diagram shows a breakdown of the different participants and their priorities/responsibilities:
## Validators
The active set of validators consists of the top **69 validators** ordered by BERA staked. Being part of the active set entitles validators to earn block rewards, so a key priority for validators is to obtain sufficient `$BERA` stake to be in the active set. Validators earn through three primary means:
1. Gas fees and priority fees
2. Collecting incentives provided by protocols for directing BGT rewards to their [Reward Vaults](/general/proof-of-liquidity/reward-vaults)
3. A base block reward (in `$BGT`) for successfully proposing a block
### Validator incentives
When a validator directs `$BGT` emissions to a reward vault, they receive project-supplied [Incentives](/general/proof-of-liquidity/incentives) provided to attract these emissions (thus increasing the attractiveness of depositing into that project). These Incentives can be in the form of the protocol's native token or any other whitelisted ERC20 token.
Successful validators in PoL optimize for:
* Securing `$BGT` delegation to increase their block rewards
* Efficiently exchanging `$BGT` block rewards for protocol incentives
* Distributing value back to their `$BGT` delegators
## BGT holders and farmers
BGT holders play a crucial role in:
* Voting on governance proposals
* Influencing economic incentives through `$BGT` delegation
* Supplying ecosystem liquidity in reward vaults to earn `$BGT`
`$BGT` that is delegated to validators is not subject to slashing. Only validators' `$BERA` stakes
can be slashed.
### Earning and delegating `$BGT`
You can earn `$BGT` by staking PoL-eligible receipt tokens in reward vaults. These receipt tokens are generated by performing actions that benefit the Berachain ecosystem, such as providing liquidity to a BEX pool.
When selecting a validator to delegate `$BGT` to, you typically consider:
* Which reward vaults validators direct emissions to
* Validator commission rates and incentive distribution strategies
* Validator uptime and performance
The primary goal is to earn as many Incentives as possible through delegation.
## BERA stakers
The BERA Yield Module empowers `$BERA` holders to participate directly in the Proof-of-Liquidity ecosystem. BERA stakers help:
* Provide direct utility and demand for the network's native asset
* Earn yield from redirected PoL incentives
* Strengthen the economic foundation of the Berachain network
### BERA staking mechanics
BERA stakers use the **Staking Vault**, an ERC4626-compliant vault that:
* Takes both native `$BERA` and wrapped `$WBERA` deposits
* Earns yield from 33% incentive redirection of PoL protocol incentives
* Has a 7-day unbonding period for withdrawals
* Automatically compounds rewards for better yields
### Earning yield through PoL
BERA stakers earn yield through the incentive redirection mechanism:
1. **Protocols pay incentives** to validators for directing BGT emissions
2. **33% redirected** during incentive distribution and sent to the Incentive Collector
3. **Incentives auctioned** for WBERA
4. **WBERA distributed** to Staking Vault stakers
The yield rate depends on:
* Total PoL incentive volume across all protocols
* Number of BERA stakers in the vault
* Market conditions and protocol activity
### Strategic considerations for BERA stakers
Successful BERA stakers consider:
* **Long-term alignment**: The 7-day unbonding period encourages long-term staking
* **Yield optimization**: Monitor vault performance and incentive volume
* **Risk management**: Understand the smart contract risks and emergency controls
* **Diversification**: Balance BERA staking with other yield opportunities
BERA staking is completely separate from traditional validator staking. You can participate in
both systems simultaneously.
## Ecosystem projects
Projects participate in PoL by:
1. Creating a reward vault through the factory contract
2. Submitting a governance proposal to whitelist the vault
3. Supplying incentive tokens and managing rates in their vault
The [Incentives marketplace](/general/proof-of-liquidity/incentives) allows protocols to bid for validators' emissions using whitelisted tokens, creating alignment between all stakeholders to increase the overall value of the network. Projects must also gain the favor of `$BGT` holders to enter into the PoL system.
# Reward Vaults
Source: https://docs.berachain.com/general/proof-of-liquidity/reward-vaults
Where BGT flows and how protocols receive emissions.
Reward Vaults are smart contracts in which you can stake your Proof-of-Liquidity (PoL) eligible assets to receive `$BGT` rewards. Reward Vaults are the only way anyone can earn `$BGT` rewards, and therefore serve the important function of gating entry into the PoL ecosystem.
Reward Vaults are key infrastructure that allows protocols to leverage PoL, enabling teams to incentivize actions in exchange for `$BGT`. A protocol can have multiple Reward Vaults, each with its own PoL-eligible asset to be staked. For example, BEX can have multiple pools earning `$BGT`, each with its own Reward Vault and respective PoL-eligible asset.
Only Reward Vaults approved through governance are eligible to receive `$BGT` emissions from Validators.
Please see the full [Reward Vault Requirements and Guidelines](/general/help/reward-vault-guidelines) for the application process.
## User interactions
### Staking with a reward vault
To receive `$BGT`, a user must stake the PoL-eligible asset in its Reward Vault. The protocol that deployed the Reward Vault decides how users acquire the PoL-eligible asset to stake. The idea is that protocols would leverage this to attract liquidity or stimulate activity, and in return award users with the asset they can stake in their vault.
1. The user takes some action that results in receiving a PoL-eligible asset, generally referred to as a receipt token.
2. The user stakes the PoL-eligible asset in the corresponding vault.
3. The user earns a portion of all the BGT emitted to that vault.
### Earning BGT
The amount of `$BGT` rewards a user earns from a Reward Vault is a function of:
1. The user's share of total assets staked in the Reward Vault
2. The amount of `$BGT` rewards emitted to the Reward Vault
After staking assets in a Reward Vault, users are free to claim their earned rewards, add to their deposits, or withdraw their assets whenever they wish.
`$BGT` farming with Reward Vaults is meant to resemble familiar DeFi actions, providing a low barrier to entry.
## Delegation
The RewardVault supports **delegation**, which allows one address (the delegate) to stake tokens on behalf of another address (the account holder). This enables use cases such as:
* **Custodial staking**: Exchanges or custodians staking on behalf of users
* **Smart contract integration**: Protocols automatically staking user funds
* **Managed staking services**: Third-party services handling staking operations
**Key delegation concepts**:
* **Delegate**: The address that deposits/withdraws tokens (msg.sender)
* **Account**: The address that owns the staked position and receives rewards
* **Self-staked balance**: Tokens staked directly by the account holder
* **Delegated balance**: Tokens staked by delegates on behalf of the account
**Important**: Only the account holder can withdraw their self-staked tokens. Delegates can only withdraw tokens they deposited on behalf of the account.
## \$BGT flow
When a validator is chosen to propose a block, they direct a portion of their `$BGT` emissions to specific Reward Vaults of their choice. To learn more about how `$BGT` is calculated in block production, check out the docs on [block rewards](/general/proof-of-liquidity/block-rewards).
## BGT emission modes
Reward Vaults operate in one of two mutually-exclusive modes for **BGT reward distribution timing**:
### Duration-based mode (legacy)
In this mode, the `rewardDurationManager` sets a fixed `rewardsDuration` (typically 3-7 days). Each time BGT rewards are added to the vault via `notifyRewardAmount`, the BGT is distributed evenly over this predetermined period.
Duration-based mode enforces the 3-7 day range: if switching from target rate mode where the duration exceeded 7 days, the duration will be capped at 7 days.
**Example**: If 100 BGT is added with a 5-day duration, the vault distributes 20 BGT per day to stakers.
### Target rate mode
When `targetRewardsPerSecond` is set to a non-zero value, the vault automatically calculates the optimal distribution period for each BGT deposit. The vault ensures the emission rate never exceeds the target while respecting the minimum duration limit.
The computation follows this formula:
```text theme={null}
period = max(minRewardDurationForTargetRate, totalReward / targetRate)
```
This guarantees 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
* `setTargetRewardsPerSecond(x)` enables target rate mode
* `setTargetRewardsPerSecond(0)` re-enables duration-based mode
* Only the `rewardVaultManager` can switch modes
## Incentive management
RewardVaults support **incentive tokens**, which are additional tokens that protocols can offer to BGT stakers beyond the base BGT rewards. This creates a powerful mechanism for protocols to attract liquidity and user engagement.
### How incentive tokens work
1. **Whitelisting**: Protocol tokens must first be whitelisted in the vault by governance
2. **Exchange Rate Setting**: Protocol managers set exchange rates (e.g., "10 USDC per BGT earned")
3. **Token Deposits**: Protocols deposit their tokens into the vault
4. **Automatic Distribution**: When users earn BGT, they automatically receive proportional incentive tokens
## Incentives
To understand why validators would choose to emit `$BGT` to one Reward Vault over another, refer to [Incentives](/general/proof-of-liquidity/incentives) in PoL, which discusses how protocols can influence validator behavior with economic incentives.
## Vault creation
New Reward Vaults can be created permissionlessly at [Berachain Hub](https://hub.berachain.com/vaults/create).
Protocols creating Reward Vaults must additionally whitelist their vaults through `$BGT` governance to be eligible to receive emissions from validators.
## Calculating \$BGT APR
The RewardVault APR is determined by several factors. The components of this APR calculation include:
* `rewardRate` - The BGT amount added to Reward Vault Staker's total claims per second
* `periodFinish` - The timestamp when the `rewardRate` expires
* `stakeToken` - The token you stake into the Reward Vault
* `totalSupply` - The total amount of `stakeToken` staked in the Reward Vault
* Price of `$BGT` (`$BERA`) - The assumption is made the price of `$BGT` is equivalent the `$BERA` price
* Price of Stake Token
If the `periodFinish` timestamp has elapsed no rewards are being emitted. As a result, the `$BGT`
APR is 0%.
The units of `rewardRate` is denominated as `$BGT per second`. The above pieces of data allow you to calculate the APR on the Reward Vault in the following way:
$APR = \frac{rewardRate \times secondsPerYear \times priceOfBGT}{totalSupply \times priceOfStakeToken}$
This formula provides the current rate that the Reward Vault is crediting depositors with `$BGT`.
### Example
As a concrete example of the above formula, a reward vault with the following values can be used:
| Parameter | Value | Normalized |
| :------------------- | :----------------------------------- | :------------------ |
| Reward Rate | 272490527103681170793308992914391673 | 0.27249052710368116 |
| Price of `$BERA` | \$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 |
The `rewardRate` returned is in gwei and includes an extra precision factor of `1e18`, so is
effectively in wei. Normalizing it requires dividing by `1e36`.
Using the formula above:
$APR = \frac{0.27249052710368116 \times 31536000 \times 7.8}{598.6269409470011 \times 223845.58} = 0.500204 = 50.02\%$
These values are updated on the [Vaults](https://hub.berachain.com/vaults/) page roughly every five minutes.
# 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
The BERA token serves two main purposes on the Berachain network:
### Transaction fees
BERA is used to pay for transactions on the Berachain network (hence its designation as the "gas token"). Tokens used for transaction fees are burned, removing them from the circulating supply. [Check out the hundreds of projects and dapps in the Berachain Ecosystem](https://ecosystem.berachain.com/).
### Staking BERA
BERA can be staked a number of ways:
1. To provide liquidity in BERA-denominated pools. The [complete list of pools approved by governance](https://hub.berachain.com/pools/) is shown on the hub. When you stake to pools, you earn a portion of the trading fees from that pool.
2. Staking directly into the BERA POL Yield Vault, where the yield is a share of all PoL incentives. When you stake BERA in this vault, you receive [sWBERA](/general/tokens/swbera) tokens representing your staked position.
### Validator staking
Validators stake BERA to operate a validator. Within the active set, the more BERA a validator has staked, the more frequently they are chosen to propose blocks. A validator's probability of block production is directly proportional to their share of the total staked BERA. The economic value of all staked BERA tokens forms the economic security of the chain, with [BGT](/general/tokens/bgt) dynamics controlling its inflation.
To learn more about how BERA staking affects block production and emissions, see [Block Rewards](/general/proof-of-liquidity/block-rewards).
## Tokenomics
BGT governs emissions and economic incentives. 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 [BGT emissions](/general/proof-of-liquidity/block-rewards), 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
Governance and rewards token; earning and delegation.
Proof-of-Stake blockchains typically have a single token that is used to secure the network through staking and is additionally used for gas, governance, and economic incentives. `$BGT` and the two-token model bifurcate the first two functions from the latter two.
Through Berachain's two-token Proof-of-Liquidity (PoL) model, the functions of governance and economic incentives (emissions & block rewards) are separated into their own token - `$BGT` (Bera Governance Token). `$BGT` is non-transferrable and can only be acquired by engaging in productive activities within the Berachain ecosystem.
## Earning `$BGT`
`$BGT` is earned by performing certain actions in dApps with whitelisted [Reward Vaults](/general/proof-of-liquidity/reward-vaults). Most of the time, this is related to providing liquidity, but it is not limited to this. Reward Vault deposits correspond to some form of productive activity on Berachain.
Typically, you supply liquidity and receive a receipt token for that activity, which you can then stake in reward vaults to earn `$BGT`. Some examples include:
* Depositing liquidity in the native BEX for an LP pair whitelisted to earn `$BGT` emissions
* Supplying assets to a lending market and staking the interest-bearing receipt tokens in a reward vault
You can see available earning options at [Berachain Hub Pools](https://hub.berachain.com/pools).
You can claim accumulated `$BGT` from Berahub.
## What can you do with `$BGT`?
### Governance
`$BGT` is used to vote on governance proposals. `$BGT` holders are responsible for a wide variety of ecosystem decisions (see [Reward Vault Governance](/general/governance/reward-vault-governance)).
`$BGT` holders can either vote on proposals themselves or delegate their voting power to another address. This governance delegation operates independently of boosting validators for controlling their `$BGT` emissions.
### Earn
#### Boost a validator for incentives
You can select validators to "boost" with your `$BGT`, increasing the validator's [block rewards](/general/proof-of-liquidity/block-rewards). The amount of [Incentives](/general/proof-of-liquidity/incentives) earned is determined by validators' aggregate boost. These incentives are returned to the `$BGT` holders who boosted the validator.
#### dApp fees
If you boost validators with your `$BGT`, you earn a share of Berachain core dApp fees, namely fees from BEX and HoneySwap. This is done via the `FeeCollector` contract.
At a high level, `FeeCollector` auctions fees collected from dApps for `$WBERA` and then distributes them pro rata to `$BGT` holders who have boosted validators.
### Redeem `$BGT` for `$BERA`
`$BGT` can be redeemed (burned) 1:1 for `$BERA`. This is a one-way function, and `$BERA` cannot be converted into `$BGT`. This limits the ability to earn the chain's economic incentives solely to `$BGT` holders.
# 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 [BHoneySwap at 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 `$BGT` 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 theme={null}
graph LR
User[User] -->|Asset| HoneyFactory[HoneyFactory]
HoneyFactory -->|Mint| User
HoneyFactory -->|Shares| USDCVault[USDC Vault]
USDCVault -->|Shares| HoneyFactory
HoneyFactory -->|Shares| BYUSDVault[BYUSD Vault]
BYUSDVault -->|Shares| HoneyFactory
HoneyFactory -->|Shares| USDT0Vault[USDT0 Vault]
USDT0Vault -->|Shares| HoneyFactory
HoneyFactory -->|Shares| 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.
### Collateral custody
Governance can designate a vault as a custody vault by setting a custodian address. When custody is enabled for a vault, deposited collateral is automatically forwarded from the vault contract to the custodian. On redemption, collateral is pulled back from the custodian before being returned to the user. The vault's share accounting is unaffected — the 1:1 exchange rate between collateral and vault shares is maintained regardless of where the underlying assets are held.
Custody can be removed by governance, which transfers all collateral back from the custodian to the vault contract.
### 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.
Both extensions use [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed structured data for signatures.
## Fees
`$BGT` holders receive fees 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)
* `0.5 $USDC` fee is distributed to `$BGT` holders
# 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.
### Proof of Liquidity Incentive Redirection
The yield comes from the **33% incentive tax** collected from PoL protocols. When protocols pay incentives to validators for directing $BGT emissions, a 33% fee is collected during incentive distribution and sent to the Incentive Fee Collector. These fees are then auctioned for $WBERA through automated market mechanisms, and the $WBERA is distributed to Staking Vault stakers, increasing the value of $sWBERA tokens.
### 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
* **Withdrawal requests expire** after 7 days if not completed
* **Multiple requests**: You can have multiple withdrawal requests active simultaneously
* **Cancellation**: Withdrawal requests can be cancelled before completion
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.
Governance and rewards token.
Native stablecoin.
# 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.
The execution layer consists of an EVM execution client. This client handles transactions, transaction gossiping, state management, and support for the Ethereum Virtual Machine — it is not responsible for block building.
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.3.9](https://github.com/berachain/beacon-kit/releases/tag/v1.3.9) | [v1.3.3](https://github.com/berachain/bera-reth/releases/tag/v1.3.3) |
| Bepolia | [v1.4.0-rc3](https://github.com/berachain/beacon-kit/releases/tag/v1.4.0-rc3) | [v1.4.0-rc2](https://github.com/berachain/bera-reth/releases/tag/v1.4.0-rc2) |
As of 1.4.0, Bera-Reth includes the genesis file, which can be used with 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 | Nov 7, 2025 | [6b333924b81a1935e51ac70e7d9d7cb0](https://raw.githubusercontent.com/berachain/beacon-kit/v1.3.9/testing/networks/80094/eth-genesis.json) |
| Bepolia | May 13, 2026 | [dfa4cb7a11217c9eab6cf4b405368870](https://raw.githubusercontent.com/berachain/beacon-kit/v1.4.0-rc3/testing/networks/80069/eth-genesis.json) |
Installing a more recent patch version — the `z` in a version number `x.y.z` — is generally safe.
# 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). Staking pools follow the same validator lifecycle described in this document. 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 labeled states are as follows:
* **Deposited**\
Deposit event captured by Beacon-kit and deposit message signature is verified.
* **Eligible**\
Validator has been marked as eligible for the activation queue.
* **Active**\
The validator is marked as Active after 1 epoch from the Eligible state. Currently, the Active Set consists of **69** Validators, which is the number of Validators that can propose blocks.
* **Exited**\
The validator is marked for exit if the validator set cap (**69**) is hit and the validator has the lowest effective balance or a lower-order pubKey if it has the same effective balance as another validator.
* **Withdrawn**\
Once the validator is marked as exited, after **256** epochs on mainnet (**32** on Bepolia), validator funds are fully withdrawn. All `$BERA` staked to a Validator is returned to the Validator's Withdrawal Credentials Address.
## Deposited state
The validator's journey begins with a deposit transaction on the **Execution Layer** (via the Deposit Contract). Once this deposit transaction is successful and emits an event, 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.
* **Verification Delay**\
It takes 2 ETH1 blocks (on the EVM layer) from the event emission to verify the event on the Consensus Layer. If the deposit event is processed at epoch `N`, the validator is then considered in the **Deposited** state, provided the validator's balance equals (or exceeds) the minimum required for staking.
* **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 (similar to ETH2).
* Subsequent deposits simply increase the validator's balance (no additional signature verification is done).
After remaining in the **Deposited** state for 1 epoch, the validator automatically moves to the **Eligible** state and becomes eligible for activation.
## Eligible state
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).
## Active state
After spending 1 epoch in the **Eligible** state (say at `N+1`), the validator is marked **Active** at the start of epoch `N+2`.
A validator remains active indefinitely until it is forced out by a validator with a higher stake or voluntarily exits.
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.
## Exited state
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**.
## Withdrawn state
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.
## 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.
# 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).
# Become 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.
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 the Beacon Chain 2 blocks after the deposit contract
transaction is processed. 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 received `$BGT` 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 Reward Allocation to distribute `$BGT` emissions to Reward Vaults.
## 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 Validator 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.
## 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`.
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.
### 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 `$BGT` boosters — users who Boost a validator with `$BGT`. 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 `MAX_COMMISSION_CHANGE_DELAY` 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 **16,382 blocks** have 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: queue and activate reward allocation changes for your validator.
This guide walks you through managing your validator's reward allocations using BeraChef via Foundry `cast` and the BeraHub UI.
## 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. If no custom allocation is set, a default allocation is used.
**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**
## 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
# 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 `$BGT` 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 from Bera-Geth to 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).
# Fusaka Upgrade
Source: https://docs.berachain.com/nodes/operations/fusaka
Validator and developer changes from the Fusaka upgrade on Berachain.
Fusaka is the Berachain upgrade that activates the [Fulu + Osaka EL/CL changes](https://github.com/berachain/BRIPs/blob/main/meta/BRIP-0010.md) shipped to [Ethereum](https://ethereum.org/roadmap/fusaka/) in December 2025.
## Upgrade timing
| Network | Activation Date (UTC) | Binaries Available |
| --------------- | --------------------- | ------------------ |
| Bepolia Testnet | 4pm May 27, 2026 | May 11 |
| Mainnet | 4pm June 24, 2026 | June 8 |
The official binaries will ship at Github ([Bera-Reth](https://github.com/berachain/bera-reth/releases), [BeaconKit](https://github.com/berachain/beacon-kit/releases)),
with announcements posted to [Telegram](https://t.me/beranodes/) and [Discord](https://discord.gg/berachain/), along with updates to our [recommended versions](/nodes/architecture/evm-execution) page.
## Bera-Geth Will Stop Working
With this upgrade, Berachain is completing the **Bera-Geth deprecation** [previously announced](https://forum.berachain.com/t/brip-0009-deprecation-of-geth-execution-client/1564). After this fork, **Bera-Geth clients will stop following the chain**.
Follow our [migration guide](/nodes/operations/bera-geth-to-reth) to install Bera-Reth.
## For developers
Bera-Reth in this upgrade bumps the upstream [Reth](https://github.com/paradigmxyz/reth) dependency to **v1.11.4**.
**EIP-6110: Deposits process in the block they are submitted in.** Pre-Fusaka, deposits and withdrawals were processed every epoch (192 blocks). No longer. A deposit transaction emits its event, the execution client parses it into a deposit request, and the consensus client consumes that request — all in the same block. (A validator's *effective balance*, which determines voting power and activation status, is still only updated at the turn of an epoch.)
Reference: [Become a validator](/nodes/guides/become-a-validator).
**Additional EIPs being activated:**
* **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.
Reference: [Protocol Features](/build/protocol/overview).
## For validators
**Effective balance updates respond more closely to actual stake.** The hysteresis buffers that govern when a balance change triggers an effective-balance update are tightened. A drop of -100 BERA or +1000 BERA will update the validator's voting power.
Reference: [Validator lifecycle](/nodes/architecture/validator-lifecycle#effective-balance-and-hysteresis), [Increase validator stake](/nodes/guides/increase-validator-stake).
## Upgrade instructions
This release of Bera-Reth introduces a major quality-of-life improvement for node operators: you can now specify the network chain directly with `--chain bepolia` or `--chain mainnet`.
With this approach, there is no longer any need to manually fetch or deploy updated genesis files when upgrading. Bera-Reth will select the correct built-in configuration based on your chosen network. In addition, when using the new `--chain` option, you do not need to provide `--bootnodes`; Bera-Reth will automatically use the appropriate bootnode list for the selected chain.
The old style of `--chain ` still works, but you must be sure to fetch and deploy the genesis files linked from our [release page](/nodes/architecture/evm-execution).
1. **If you are running Bera-Geth, you [have to migrate to Bera-Reth](/nodes/operations/bera-geth-to-reth).**
2. **DO NOT deploy this on a mainnet node** until 1.4.0 (non-rc) binaries designated for mainnet are released.
3. Download the current releases of BeaconKit and Bera-Reth from the [recommended versions](/nodes/architecture/evm-execution) page.
4. Adjust Bera-Reth startup:
* Change your `--chain` option to `--chain bepolia` and remove `--bootnodes`.
* OR download and fetch the current bepolia genesis file from the [recommended versions](/nodes/architecture/evm-execution) page, and overwrite your current genesis file with it.
5. No changes are needed to BeaconKit options.
6. Start the new `beacond` and `bera-reth` binaries.
## Support
If you have questions about this upgrade, the best place for support is our [Discord's](https://discord.gg/berachain) #node-support channel.
# Monitoring
Source: https://docs.berachain.com/nodes/operations/monitoring
Set up Prometheus and Grafana to monitor Beacon-Kit 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.
## Beacon-Kit metrics
Beacon-Kit 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 Beacon-Kit 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).
# Production Checklist
Source: https://docs.berachain.com/nodes/operations/production-checklist
Pre-support checks: diagnosis script, client versions, peering, recommended options, and operational hygiene for production nodes.
Before reaching out for support, here are some steps to check your installation.
## Quick diagnosis script
Berachain distributes a stand-alone [diagnosis script](https://github.com/berachain/guides/tree/main/apps/node-scripts/node-diagnostic.sh). Drop this into your Unix-based system and run:
```bash theme={null}
/path/to/node-diagnostic.sh -d /var/beacond/ -p /opt/bin/beacond
```
Substitute the path to your Beacon Kit data directory (containing `config` and `data` directories), and the path to the `beacond` binary (if not in your \$PATH).
This script produces clearly marked recommendations. Provide the output of this script when communicating with the team for support.
## Beacon Kit version check
Ensure you are running the latest version of [Beacon Kit](https://github.com/berachain/beacon-kit).
## Execution client version check
Check that you are running a supported version of your [execution client](/nodes/architecture/evm-execution). Note that Berachain doesn't *always* support the latest version of a given client.
## Peering
There are several ingredients to successful 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.
1. **Check bootnodes for initial chain sync**: Check that you have a current list of [bootnodes](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80094/el-bootnodes.txt). Bera-Reth accepts the `--bootnodes` option. `beacond` has the boot node list baked into the distributed [config](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80094/config.toml).
2. **Check Execution Layer peering**: The execution layer needs excellent peering to ensure that transactions flow to your validator for sealing in blocks. Ensure port 30303 TCP (for transactions) and UDP (for peer exchange) is open. Check that you have a current list of [bootnodes](https://github.com/berachain/beacon-kit/blob/main/testing/networks/80094/el-bootnodes.txt).
3. **Indicate your Execution Layer's external IP address**: Execution clients need to know the publicly routable IP address they can be reached at. Most execution clients try to determine your public IP with UPnP, which is not available in cloud computing environments. Therefore, you must *tell your execution client* what your external IP address is. For Bera-Reth, this is done with the `--nat extip:` option.
4. **Check beacond peering**: `beacond` needs good peering to organize and perform consensus actions. This is carried out over TCP port 26656. Also, correctly advertise your node's external IP with `p2p.external_address` in `config.toml`. To limit `beacond`'s memory consumption, **40 inbound + 10 outbound peers** is recommended:
```toml theme={null}
max_num_inbound_peers = 40
max_num_outbound_peers = 10
```
5. **No static or persistent peers**: 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.
## Recommended options
For **Reth**, the following options are recommended in the Reth launch:
```bash theme={null}
--engine.persistence-threshold 0
--engine.memory-block-buffer-target 0
```
Ensure the `debug` API is not enabled in the `--http.api` option. It is better to **remove** the `--http.api` option entirely and allow the secure default to apply, unless you have specific reasons to enable additional modules.
**Open ports.** Two ports are required to be open: the Beacon Kit P2P port (`26656/TCP`) and the Bera-Reth P2P port (`30303/TCP` and `30303/UDP` for discovery). It is not recommended to leave port 8545 and 8546 (JsonRPC and websocket RPC) open to the public.
## Register with the team
If you have launched a validator on the chain, it's in your interest to let the team know who you are, so they know who to contact in case there's trouble with on-chain performance.
Steps to do that:
1. (Optional) Formulate a pull request to the [Validator Metadata repository](https://github.com/berachain/metadata) with a public handle for your validator. You can use an anon if you want. Provide a logo attached to the pull request if you would like it posted on your validator profile.
2. Reach out on the `#node-support` channel on [Discord](https://discord.gg/berachain). The team will set you up with dedicated support, chat, and announcement channels for validators.
## Operational hygiene
Make sure Beacon Kit and your execution client are configured to start when your operating system starts.
Cause your operating system to rotate logs, and slim the log output.
* Read the `beacond` config files to find log verbosity settings.
* Consult the instructions for your chosen chain node to adjust logging settings.
* Set up the `logrotate` service to rotate logs.
* Read our [monitoring guide](/nodes/operations/monitoring#what-to-monitor).
# Quickstart: Run a Node
Source: https://docs.berachain.com/nodes/operations/quickstart
Prerequisites, scripts, and steps to run Beacon-Kit and Bera-Reth on Linux or Mac.
A Berachain node consists of two clients running together: a **consensus client** (Beacon-Kit) 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, 16GB RAM |
| **Storage** | 1TB minimum (more for long-term); Local SSD or on-instance storage preferred; network volumes require at least 1000 IOPS |
### Software
Install the required binaries before starting:
1. **Beacon-Kit**: Download the appropriate binary from the [releases page](https://github.com/berachain/beacon-kit/releases) for your OS and architecture. Make it executable and place it in your PATH (e.g., `~/.local/bin/`).
2. **Execution client**: Download [Bera-Reth](https://github.com/berachain/bera-reth/releases) for your OS and architecture. Make it executable and place it in your PATH.
See [EVM Execution Clients](/nodes/architecture/evm-execution) for recommended versions.
Verify installation:
```bash theme={null}
beacond version
bera-reth --version
```
## 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 genesis files and network configuration
4. **Set up Beacon-Kit** — initialize the consensus client and generate keys
5. **Set up execution client** — initialize Reth with the genesis state
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` obtains copies of the genesis file and other configuration files.
Then we have `setup-` and `run-` scripts for 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_SPEC=mainnet # or "testnet"
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\_SPEC**: Set to `testnet` or `mainnet`.
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 the key network parameters for the chain you have configured:
```bash theme={null}
# FROM: ~/beranode
./fetch-berachain-params.sh;
# [Expected Output for mainnet - verify these checksums]:
# c66dbea5ee3889e1d0a11f856f1ab9f0 seed-data-80094/genesis.json
# 6b333924b81a1935e51ac70e7d9d7cb0 seed-data-80094/eth-genesis.json
# 5d0d482758117af8dfc20e1d52c31eef seed-data-80094/kzg-trusted-setup.json
# [Expected Output for bepolia - verify these checksums]:
# a24fb9c7ddf3ebd557300e989d44b619 seed-data-80069/genesis.json
# c27c1162af33f7f5401bcef974a64454 seed-data-80069/eth-genesis.json
# 5d0d482758117af8dfc20e1d52c31eef seed-data-80069/kzg-trusted-setup.json
```
Verify that the checksums for `genesis.json`, `eth-genesis.json`, and `kzg-trusted-setup.json` match the values above for your chosen network.
## Step 4 - Set up Beacon-Kit
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 the mainnet parameters where Beacon-Kit 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: v1.1.3
# ✓ 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
# ✓ Beacon-Kit 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 a runtime directory and configuration for the execution client. It configures the node with pruning settings according to the `EL_ARCHIVE_NODE` setting in `env.sh`.
```bash theme={null}
# FROM: ~/beranode
./setup-reth.sh;
# [Expected Output]:
# INFO [BEPOLIA] Genesis block written hash=0x0207661de38f0e54ba91c8286096e72486784c79dc6a9681fc486b38335c042f
# INFO [MAINNET] Genesis block written hash=0xd57819422128da1c44339fc7956662378c17e2213e669b427ac91cd11dfcfb38
# ✓ bera-reth set up.
```
Your genesis block hash **must** agree with the above for your chosen chain.
## 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 testnet --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 `testnet` (default: `mainnet`; selects the snapshot index host as above)
* `--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;
# [Expected Output]:
# data genesis.json
rm -r var/reth/data;
```
### 6d - Install Beacon-Kit 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`. Extracting into `var/reth/` would place `db/` next to `genesis.json` and would not match `--datadir`.
```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 v1.1.3
# [AFTER BLOCKS START FLOWING]
# INFO processExecutionPayload ... height=49 ...
# INFO Finalized block ... height=49 ...
```
In the second, run the execution client. Here it is for Bera-Reth:
```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, Beacon-Kit, 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 (BeaconKit) and execution clients, active set, staking, and block rewards.
Berachain's network relies on validator nodes and RPC nodes. Each can be configured as a full node or archive node.
Each of these types of nodes is a pair of both an [execution client](/general/help/glossary#execution-client) and a [consensus client](/general/help/glossary#consensus-client). Berachain is a Layer 1 EVM Identical chain, which means that for the execution layer, it supports any EVM execution client paired with a consensus client and framework built by Berachain called [BeaconKit](/nodes/architecture/beaconkit-consensus).
## RPC vs validator nodes
The main difference between an RPC node and a validator node is that a validator can propose blocks and receive block rewards.
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).
## Active set
The active set is the set of validators that are currently participating in the consensus layer of the network.
The current limit of validators in the active set is **69**.
Validators can choose to leave the active set voluntarily — [learn how](/nodes/guides/withdraw-stake) — or be ejected from the set if a validator with more stake joins.
## Validator stake requirements
The minimum stake requirement depends on whether the active set is completely 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.
## Direct staking
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 in the form of `$BGT`, with a base reward of **0.5** `$BGT` 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
| Name | Address | ABI |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **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) |
#### Bepolia
| Name | Address | ABI |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **StakingPoolContractsFactory** | [`0x24b8223864d3936F56e5a24C4245ae7620471D4C`](https://testnet.berascan.com/address/0x24b8223864d3936F56e5a24C4245ae7620471D4C) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/StakingPoolContractsFactory.json) |
| **DelegationHandlerFactory** | [`0x0aEf09EC97bAc354d31F180b401454cB76abc395`](https://testnet.berascan.com/address/0x0aEf09EC97bAc354d31F180b401454cB76abc395) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/contracts-staking-pools/delegation/DelegationHandlerFactory.json) |
| **WithdrawalVault** | [`0xBBed2D94338cdE2926A8C0576432De32C05c66e9`](https://testnet.berascan.com/address/0xBBed2D94338cdE2926A8C0576432De32C05c66e9) | [ABI JSON](https://github.com/berachain/abis/blob/main/mainnet/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. Use this contract to manage BGT operations, commission rates, reward allocations, and protocol fees.
* **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.
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, BGT, 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 BGT balance 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 |
| `BGT_MANAGER_ROLE` | BGT operations | Queue drop boost, redeem BGT |
### Essential functions
| Function | Contract | Purpose |
| ---------------------------- | ------------- | ------------------------------------- |
| `setMinEffectiveBalance()` | SmartOperator | Set activation threshold |
| `queueValCommission()` | SmartOperator | Queue commission rate change |
| `queueRewardsAllocation()` | SmartOperator | Queue reward allocation |
| `claimBoostRewards()` | SmartOperator | Forward rewards to IncentiveCollector |
| `setProtocolFeePercentage()` | SmartOperator | Set protocol fee rate |
## 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.
## 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.
* **BGT operations**: Use `queueDropBoost()`, `redeemBGT()`, `queueBoost()`, `activateBoost()` as needed. Forward rewards to IncentiveCollector by calling `claimBoostRewards()` on SmartOperator.
* **Protocol fee**: Set via `setProtocolFeePercentage()` (up to 20%). Call `accrueEarnedBGTFees()` to trigger fee calculation and minting.
## Withdrawal system
The centralized WithdrawalVault handles all withdrawal operations. Withdrawals can use a **short-circuit** path (when the pool has not reached active threshold and has sufficient buffered funds) or the **standard** path (consensus layer processing). In both cases, stakers must wait the full withdrawal delay (129,600 blocks ≈ 3 days) before finalization.
## 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.
## 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 the integration with Berachain's Proof of Liquidity system. This contract handles BGT boosting, reward allocation, commission management, and other validator-specific operations that require coordination with the broader Berachain ecosystem.
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; withdrawals process through the consensus layer in approximately 3 days (129,600 blocks at \~2s block time). Stakers can stake any amount without validator minimums, with full transparency through on-chain verification.
## 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 BGT rewards automatically boost your PoL performance, and smart contracts automatically 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.