Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@
[submodule "exp/gas-burner/lib/forge-std"]
path = exp/gas-burner/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "apps/layerzero-oft/lib/solidity-bytes-utils"]
path = apps/layerzero-oft/lib/solidity-bytes-utils
url = https://github.com/GNSPS/solidity-bytes-utils.git
127 changes: 125 additions & 2 deletions apps/layerzero-oft/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,126 @@
# =============================================================================
# LAYERZERO OFT BRIDGE CONFIGURATION - BASE TO BERACHAIN
# =============================================================================
#
# This file contains all environment variables needed for the LayerZero OFT
# bridge setup from Base to Berachain. Follow the deployment guide step-by-step
# and populate these values as you progress.
#
# IMPORTANT: Never commit your .env file with real values to version control!
# =============================================================================

# -----------------------------------------------------------------------------
# 1. WALLET CONFIGURATION
# -----------------------------------------------------------------------------
# Your private key for signing transactions on both chains
# Format: 0x followed by 64 hex characters (no 0x prefix needed in some tools)
# WARNING: Keep this secure! Never share or commit this value.
PRIVATE_KEY=
SEPOLIA_ADAPTER_ADDRESS=
BERACHAIN_OFT_ADDRESS=

# Optional: Your wallet address (derived from PRIVATE_KEY)
# This is automatically computed but can be set for reference
# SIGNER=

# -----------------------------------------------------------------------------
# 2. CONTRACT ADDRESSES (Populated during deployment)
# -----------------------------------------------------------------------------
# These addresses are generated when you deploy the contracts.
# Update them after each deployment step.

# Step 2: Deploy token on Base
# Run: forge script script/MyToken.s.sol --rpc-url https://mainnet.base.org --broadcast
BASE_TOKEN_ADDRESS=

# Step 3: Deploy adapter on Base
# Run: forge script script/MyAdapter.s.sol --rpc-url https://mainnet.base.org --broadcast
BASE_ADAPTER_ADDRESS=

# Step 4: Deploy OFT on Berachain
# Run: forge script script/MyOFT.s.sol --rpc-url https://rpc.berachain.com/ --broadcast
BERACHAIN_OFT_ADDRESS=

# -----------------------------------------------------------------------------
# 3. LAYERZERO ENDPOINT ADDRESSES (Pre-configured)
# -----------------------------------------------------------------------------
# These are the official LayerZero V2 endpoint addresses for each chain.
# Do not change these unless LayerZero updates them.

BASE_ENDPOINT_ADDRESS=0x1a44076050125825900e736c501f859c50fE728c
BERACHAIN_ENDPOINT_ADDRESS=0x6F475642a6e85809B1c36Fa62763669b1b48DD5B

# -----------------------------------------------------------------------------
# 4. LIBRARY ADDRESSES (Discover via scripts)
# -----------------------------------------------------------------------------
# These are the send/receive library addresses for LayerZero messaging.
# Discover them by running the library discovery scripts:
#
# For Base:
# forge script script/GetBaseLibraries.s.sol --rpc-url https://mainnet.base.org
#
# For Berachain:
# forge script script/GetBerachainLibraries.s.sol --rpc-url https://rpc.berachain.com/
#
# Copy the output values into these variables before configuring libraries.

# Base libraries (used by Base adapter)
BASE_SEND_LIB_ADDRESS=0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda2
BASE_RECEIVE_LIB_ADDRESS=0xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf

# Berachain libraries (used by Berachain OFT)
BERACHAIN_SEND_LIB_ADDRESS=0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de7
BERACHAIN_RECEIVE_LIB_ADDRESS=0xe1844c5D63a9543023008D332Bd3d2e6f1FE1043

# -----------------------------------------------------------------------------
# 5. DVN ADDRESSES (Decentralized Verifier Networks)
# -----------------------------------------------------------------------------
# DVNs verify cross-chain messages. These are the official LayerZero DVN addresses.
# Required DVNs must verify all messages. Optional DVNs can verify but aren't required.
#
# NOTE: DVN addresses are chain-specific. The DVNs listed here are for the
# SOURCE chain when receiving messages FROM the destination chain.
#
# For Base adapter receiving from Berachain, use BERACHAIN_* DVNs
# For Berachain OFT receiving from Base, use BASE_* DVNs

# Base DVNs (used when Berachain OFT receives messages from Base)
# These are the DVNs that verify messages sent FROM Base
BASE_LAYERZERO_DVN=0x9e059a54699a285714207b43b055483e78faac25
BASE_NETHERMIND_DVN=0xcd37ca043f8479064e10635020c65ffc005d36f6

# Berachain DVNs (used when Base adapter receives messages from Berachain)
# These are the DVNs that verify messages sent FROM Berachain
BERACHAIN_LAYERZERO_DVN=0x282b3386571f7f794450d5789911a9804fa346b4
BERACHAIN_NETHERMIND_DVN=0xdd7b5e1db4aafd5c8ec3b764efb8ed265aa5445b

# Optional Berachain DVN (can be used for additional verification)
BERACHAIN_OPTIONAL_BERA_DVN=0x10473bd2f7320476b5e5e59649e3dc129d9d0029

# -----------------------------------------------------------------------------
# 6. CHAIN CONFIGURATION (Pre-configured)
# -----------------------------------------------------------------------------
# LayerZero Endpoint IDs (EIDs) for each chain
# These identify chains in the LayerZero network

BASE_EID=30110
BERACHAIN_EID=30362

# Grace period for library changes (0 = immediate, higher = delay)
# Typically set to 0 for new deployments
GRACE_PERIOD=0

# -----------------------------------------------------------------------------
# DEPLOYMENT CHECKLIST
# -----------------------------------------------------------------------------
# Use this checklist to track your progress:
#
# [ ] 1. Set PRIVATE_KEY
# [ ] 2. Deploy BASE_TOKEN_ADDRESS (Step 2)
# [ ] 3. Deploy BASE_ADAPTER_ADDRESS (Step 3)
# [ ] 4. Deploy BERACHAIN_OFT_ADDRESS (Step 4)
# [ ] 5. Discover and set library addresses (Step 4.5)
# [ ] 6. Configure libraries (Step 5)
# [ ] 7. Configure DVNs (Step 6)
# [ ] 8. Verify configuration (Step 7)
# [ ] 9. Test bridge (Step 8)
#
# For detailed instructions, see README.md
204 changes: 189 additions & 15 deletions apps/layerzero-oft/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Bridging ERC20 Tokens to Berachain with LayerZero V2
# Bridging ERC20 Tokens from Base to Berachain with LayerZero V2

This repository contains an example of how to bridge an existing ERC20 token (in this case, $UNI) from the Sepolia Testnet to the Berachain Testnet using LayerZero V2 and their Omnichain Fungible Token (OFT) standard.
This repository contains an example of how to deploy a custom ERC20 token on Base, create a LayerZero adapter, deploy an OFT on Berachain, and migrate tokens from Base to Berachain using LayerZero V2 and their Omnichain Fungible Token (OFT) standard.

πŸ‘‰ Learn more about [LayerZero V2](https://docs.layerzero.network/v2)

Expand All @@ -10,8 +10,8 @@ This repository contains an example of how to bridge an existing ERC20 token (in

- Node `v20.11.0` or greater
- npm
- Wallet with Berachain Testnet $BERA tokens - See the [Berachain bArtio Faucet](https://bartio.faucet.berachain.com)
- Wallet with Sepolia Testnet $UNI tokens - See the [Sepolia Testnet Faucet](https://faucet.quicknode.com/ethereum/sepolia), trade on [Uniswap](https://app.uniswap.org/swap?outputCurrency=0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984&inputCurrency=ETH)
- Wallet with Berachain Mainnet $BERA tokens - See [Berachain Bridge](https://bridge.berachain.com)
- Wallet with Base Mainnet $ETH tokens - See [Base Bridge](https://bridge.base.org/deposit)
- [Foundry](https://book.getfoundry.sh/getting-started/installation) - ensure `foundryup` is run to install binaries

### Step 1 - Setup Project & Install Dependencies
Expand All @@ -24,44 +24,218 @@ Install project dependencies:
npm install;
```

### Step 2 - Deploy Adapter to Sepolia
### Step 2 - Deploy Custom Token to Base

Create a `.env` file at the root of `./layerzero-oft` with the following and populate it with your `PRIVATE_KEY`:
Create a `.env` file at the root of `./layerzero-oft` with the following and populate it with your values:

```toml
# Required for all operations
PRIVATE_KEY=
SEPOLIA_ADAPTER_ADDRESS=

# Contract addresses (populated after deployment)
BASE_TOKEN_ADDRESS=
BASE_ADAPTER_ADDRESS=
BERACHAIN_OFT_ADDRESS=

# Optional: Bridge configuration
TO_ADDRESS= # Recipient address (defaults to signer if not set)
TOKENS_TO_SEND= # Amount to bridge in wei (defaults to 100 tokens if not set)
```

**Note**: Library addresses and DVN addresses are already configured in `.env.example`. Copy those values to your `.env` file or reference the `.env.example` file for the complete configuration.

Deploy your custom ERC20 token to Base Mainnet:

```bash
# FROM: ./layerzero-oft

forge script script/MyToken.s.sol --rpc-url https://mainnet.base.org --broadcast
```

Deploy `MyAdapter.sol` to Sepolia:
Update `BASE_TOKEN_ADDRESS` in your `.env` file with the address of your token deployment.

**Example Deployment:**
- **Token Address**: `0xB855AD471a3a865A81F6057ee3868531784447fA`
- **BaseScan**: [View Contract](https://basescan.org/address/0xb855ad471a3a865a81f6057ee3868531784447fa)

### Step 3 - Deploy Adapter to Base

Deploy `MyAdapter.sol` to Base Mainnet:

```bash
# FROM: ./layerzero-oft

forge script script/MyAdapter.s.sol --rpc-url https://rpc.sepolia.org/ --broadcast
forge script script/MyAdapter.s.sol --rpc-url https://mainnet.base.org --broadcast
```

Update `SEPOLIA_ADAPTER_ADDRESS` in your `.env` file with the address of your `MyAdapter` deployment.
Update `BASE_ADAPTER_ADDRESS` in your `.env` file with the address of your `MyAdapter` deployment.

**Example Deployment:**
- **Adapter Address**: `0x031A382C7C1AfE8587A663355804878efB56ce52`
- **BaseScan**: [View Contract](https://basescan.org/address/0x031A382C7C1AfE8587A663355804878efB56ce52)

### Step 3 - Deploy OFT to Berachain
### Step 4 - Deploy OFT to Berachain

Deploy `MyOFT.sol` to Berachain:

```bash
# FROM: ./layerzero-oft

forge script script/MyOFT.s.sol --rpc-url https://bartio.rpc.berachain.com/ --broadcast
forge script script/MyOFT.s.sol --rpc-url https://rpc.berachain.com/ --broadcast
```

Update `BERACHAIN_OFT_ADDRESS` in your `.env` file with the address of your `MyOFT` deployment.

### Step 4 - Bridge Tokens from Sepolia to Berachain
**Example Deployment:**
- **OFT Address**: `0x6CB0268387BAEFaace08b2368F21E8983Ec05988`
- **Berachain Explorer**: [View Contract](https://berascan.com/address/0x6cb0268387baefaace08b2368f21e8983ec05988)
- **Verification GUID**: `aglxnljk3fxvqejt18xsgxzqvp4cvdehrpdrmkjk8bgbh7kcpb`

### Step 5 - Wire Messaging Libraries and Configurations

Configure your contracts for cross-chain messaging. This step includes setting up peers and messaging configurations.

**Note**: Library addresses and DVN addresses are pre-configured in `.env.example`. Copy those values to your `.env` file.

#### 5.1 - Set Peer Connections

Configure peer connections so your contracts know where to send messages:

**Set Base Adapter Peer (Base β†’ Berachain):**
```bash
# FROM: ./layerzero-oft

forge script script/SetPeersBase.s.sol --rpc-url https://mainnet.base.org --broadcast
```

**Set Berachain OFT Peer (Berachain β†’ Base):**
```bash
# FROM: ./layerzero-oft

forge script script/SetPeersBerachain.s.sol --rpc-url https://rpc.berachain.com/ --broadcast
```

**Verify Peer Configuration:**

You can verify that peers are set correctly using `cast`:

Finally, run the `Bridge.s.sol` script to bridge your $UNI tokens to Berachain:
```bash
# Verify Base Adapter peer (should return Berachain OFT address)
cast call <BASE_ADAPTER_ADDRESS> "peers(uint32)(bytes32)" 30362 --rpc-url https://mainnet.base.org

# Verify Berachain OFT peer (should return Base Adapter address)
cast call <BERACHAIN_OFT_ADDRESS> "peers(uint32)(bytes32)" 30184 --rpc-url https://rpc.berachain.com/
```

#### 5.2 - Configure Send and Receive Settings

Configure the executor and ULN (DVN) settings for both send and receive operations:

**Configure Base Send Settings (Base β†’ Berachain):**
```bash
# FROM: ./layerzero-oft

forge script script/Bridge.s.sol --rpc-url https://rpc.sepolia.org/ --broadcast
forge script script/SetBaseSendConfig.s.sol --rpc-url https://mainnet.base.org --broadcast
```

This script configures:
- **Executor Config**: Max message size (100,000 bytes) and executor address
- **ULN Config**: Send confirmations (20 blocks) and required DVNs (LayerZero + Nethermind from Base)

**Configure Base Receive Settings (Base ← Berachain):**
```bash
# FROM: ./layerzero-oft

forge script script/SetBaseReceiveConfig.s.sol --rpc-url https://mainnet.base.org --broadcast
```

This script configures:
- **ULN Config**: Receive confirmations (20 blocks) and required DVNs (LayerZero + Nethermind from Berachain)

**Configure Berachain Send Settings (Berachain β†’ Base):**
```bash
# FROM: ./layerzero-oft

forge script script/SetBerachainSendConfig.s.sol --rpc-url https://rpc.berachain.com/ --broadcast --via-ir
```

This script configures:
- **Executor Config**: Max message size (100,000 bytes) and executor address
- **ULN Config**: Send confirmations (20 blocks), required DVNs (LayerZero + Nethermind from Berachain), and optional BERA DVN

**Configure Berachain Receive Settings (Berachain ← Base):**
```bash
# FROM: ./layerzero-oft

forge script script/SetBerachainReceiveConfig.s.sol --rpc-url https://rpc.berachain.com/ --broadcast --via-ir
```

This script configures:
- **ULN Config**: Receive confirmations (20 blocks), required DVNs (LayerZero + Nethermind from Berachain), and optional BERA DVN

**Note**: The `--via-ir` flag is required for Berachain scripts due to Solidity compiler stack depth limitations.

#### 5.3 - Bera DVN Availability Limitation

**Important**: The optional Bera DVN can only be configured on chains where the Bera DVN is deployed. If the Bera DVN is not available on a destination chain, you cannot use it as an optional DVN in your configuration.

This limitation exists because:
- DVNs must be paid on the source chain for providing verification services
- If a DVN is not deployed on the source chain, it cannot receive payment
- Without payment, the DVN will not deliver verification services

**Bera DVN Supported Chains:**

The Bera DVN is currently deployed on the following chains:

| Chain | DVN Address |
|-------|-------------|
| Arbitrum Mainnet | `0xf2e8...ccb3` |
| Avalanche Mainnet | `0xf18f...0d3d` |
| Berachain Mainnet | `0x1047...0029` |
| BNB Smart Chain (BSC) Mainnet | `0x8ed0...4d76` |
| Ethereum Mainnet | `0xe2e5...2538` |
| Fantom Mainnet | `0x1a53...8b6b` |
| Optimism Mainnet | `0x5f55...6ee0` |
| Polygon Mainnet | `0xcf46...fedd` |

**Note**: Base Mainnet is **not** in the list above, which means the Bera DVN cannot be used as an optional DVN when bridging from Berachain to Base. The configuration scripts in this repository reflect this limitation and do not include the Bera DVN for Base destinations.

### Step 6 - Send Tokens

Now that your contracts are fully configured, you can bridge tokens between chains.

#### 6.1 - Bridge Tokens from Base to Berachain

Run the `Bridge.s.sol` script to bridge your custom tokens from Base to Berachain:

```bash
# FROM: ./layerzero-oft

forge script script/Bridge.s.sol --rpc-url https://mainnet.base.org --broadcast
```

**Configuration Options:**

The bridge script supports the following environment variables (all optional with defaults):

- `TO_ADDRESS`: Recipient address on Berachain (defaults to signer address if not set)
- `TOKENS_TO_SEND`: Amount to bridge in wei (defaults to 100 tokens if not set)

**Example Bridge Transactions:**
- **Transaction Hash**: `0x3e67d334cdc456a0d68c6f57166d7b16ec65512daf3904e339e31863878cece7`
- **LayerZero Scan**: [View Transaction](https://layerzeroscan.com/tx/0x3e67d334cdc456a0d68c6f57166d7b16ec65512daf3904e339e31863878cece7)

- **Transaction Hash**: `0x97839ee1064b61d7ac6acf339a9e7e985ed8dee7c809bc5c62a56a40b50bb063`
- **Block Number**: `36269727`
- **Amount Bridged**: `100 MCT` (100 tokens)
- **BaseScan**: [View Transaction](https://basescan.org/tx/0x97839ee1064b61d7ac6acf339a9e7e985ed8dee7c809bc5c62a56a50bb063)

**Bridge Script Features:**

- Automatic token approval
- Fee calculation and validation
- 5% slippage tolerance
- Balance checks before sending
- Detailed transaction logging

Loading
Loading