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
5 changes: 5 additions & 0 deletions .changeset/violet-parents-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@layerzerolabs/oft-alt-example": minor
---

Feat: add better support for simple config and the latest tooling found in OFT and OFT-Solana
1 change: 1 addition & 0 deletions examples/oft-alt/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require('@rushstack/eslint-patch/modern-module-resolution');

module.exports = {
root: true,
extends: ['@layerzerolabs/eslint-config-next/recommended'],
rules: {
// @layerzerolabs/eslint-config-next defines rules for turborepo-based projects
Expand Down
665 changes: 317 additions & 348 deletions examples/oft-alt/README.md

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions examples/oft-alt/contracts/MyOFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol";

/// @notice Standard OFT for regular EVM chains (uses native gas for fees).
/// @dev This contract is deployed on chains with standard EndpointV2 (not Alt).
/// It connects to OFTAlt contracts on chains with Alt Endpoints.
contract MyOFT is OFT {
constructor(
string memory _name,
string memory _symbol,
address _lzEndpoint,
address _delegate
) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {}
}
53 changes: 53 additions & 0 deletions examples/oft-alt/deploy/MyOFT.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import assert from 'assert'

import { type DeployFunction } from 'hardhat-deploy/types'

const contractName = 'MyOFT'

const deploy: DeployFunction = async (hre) => {
const { getNamedAccounts, deployments } = hre

const { deploy } = deployments
const { deployer } = await getNamedAccounts()

assert(deployer, 'Missing named deployer account')

console.log(`Network: ${hre.network.name}`)
console.log(`Deployer: ${deployer}`)

// This is an external deployment pulled in from @layerzerolabs/lz-evm-sdk-v2
//
// @layerzerolabs/toolbox-hardhat takes care of plugging in the external deployments
// from @layerzerolabs packages based on the configuration in your hardhat config
//
// For this to work correctly, your network config must define an eid property
// set to `EndpointId` as defined in @layerzerolabs/lz-definitions
//
// For example:
//
// networks: {
// 'optimism-testnet': {
// ...
// eid: EndpointId.OPTSEP_V2_TESTNET
// }
// }
const endpointV2Deployment = await hre.deployments.get('EndpointV2')

const { address } = await deploy(contractName, {
from: deployer,
args: [
'MyOFT', // name
'MOFT', // symbol
endpointV2Deployment.address, // LayerZero's EndpointV2 address
deployer, // owner
],
log: true,
skipIfAlreadyDeployed: false,
})

console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${address}`)
}

deploy.tags = [contractName]

export default deploy
15 changes: 12 additions & 3 deletions examples/oft-alt/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { HardhatUserConfig, HttpNetworkAccountsUserConfig } from 'hardhat/types'
import { EndpointId } from '@layerzerolabs/lz-definitions'

import './type-extensions'
import './tasks/index'

// Set your preferred authentication method
//
Expand Down Expand Up @@ -54,16 +55,24 @@ const config: HardhatUserConfig = {
],
},
networks: {
// ========== STANDARD EVM CHAINS ==========
// These chains use standard EndpointV2 with native gas fee payment.
// Deploy MyOFT (standard OFT) on these chains.
'arbitrum-sepolia': {
eid: EndpointId.ARBSEP_V2_TESTNET,
url: process.env.RPC_URL_ARB_SEPOLIA || 'https://arbitrum-sepolia.gateway.tenderly.co',
accounts,
},
'base-sepolia': {
eid: EndpointId.BASESEP_V2_TESTNET,
url: process.env.RPC_URL_BASE_SEPOLIA || 'https://base-sepolia.gateway.tenderly.co',

// ========== ALT ENDPOINT CHAINS ==========
// Chains with Alt Endpoints use ERC-20 tokens for fee payment instead of native gas.
// Deploy MyOFTAlt on these chains.
'tempo-testnet': {
eid: EndpointId.TEMPO_V2_TESTNET,
url: process.env.RPC_URL_TEMPO_TESTNET || 'https://rpc.testnet.tempo.xyz',
accounts,
},

hardhat: {
// Need this for testing because TestHelperOz5.sol is exceeding the compiled contract size limit
allowUnlimitedContractSize: true,
Expand Down
134 changes: 84 additions & 50 deletions examples/oft-alt/layerzero.config.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,94 @@
import { EndpointId } from '@layerzerolabs/lz-definitions'
import { ExecutorOptionType } from '@layerzerolabs/lz-v2-utilities'
import { TwoWayConfig, generateConnectionsConfig } from '@layerzerolabs/metadata-tools'
import { OAppEnforcedOption } from '@layerzerolabs/toolbox-hardhat'

import type { OAppOmniGraphHardhat, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat'
import type { OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat'

const sepoliaContract: OmniPointHardhat = {
eid: EndpointId.SEPOLIA_V2_TESTNET,
contractName: 'MyOFTAltAdapter',
}
// ======================================
// SECTION 1: CONTRACT DEFINITIONS
// ======================================
// This example demonstrates the primary use case for OFTAlt:
// Connecting an OFTAlt on a chain with an Alt Endpoint (ERC20 fee payment)
// to standard OFT contracts on regular EVM chains (native gas fee payment).
//
// Primary scenario:
// - OFTAlt deployed on Tempo (Alt Endpoint - fees paid in stablecoins)
// - Standard OFT deployed on Arbitrum (regular EndpointV2 - fees paid in native gas)

const fujiContract: OmniPointHardhat = {
eid: EndpointId.AVALANCHE_V2_TESTNET,
contractName: 'MyOFTAlt',
// ========== ALT ENDPOINT CHAINS ==========
// OFTAlt on chains with EndpointV2Alt (ERC20 fee payment)
// Tempo is a payments-focused blockchain where fees are paid in TIP-20 stablecoins
const tempoContract: OmniPointHardhat = {
eid: EndpointId.TEMPO_V2_TESTNET,
contractName: 'MyOFTAlt', // OFTAlt for Alt Endpoint chains (ERC20 fee payment)
}

const amoyContract: OmniPointHardhat = {
eid: EndpointId.AMOY_V2_TESTNET,
contractName: 'MyOFTAlt',
// ========== STANDARD EVM CHAINS ==========
// Standard OFT on chains with regular EndpointV2 (native gas fee payment)
const arbitrumContract: OmniPointHardhat = {
eid: EndpointId.ARBSEP_V2_TESTNET,
contractName: 'MyOFT', // Standard OFT for regular EVM chains
}

const config: OAppOmniGraphHardhat = {
contracts: [
{
contract: fujiContract,
},
{
contract: sepoliaContract,
},
{
contract: amoyContract,
},
],
connections: [
{
from: fujiContract,
to: sepoliaContract,
},
{
from: fujiContract,
to: amoyContract,
},
{
from: sepoliaContract,
to: fujiContract,
},
{
from: sepoliaContract,
to: amoyContract,
},
{
from: amoyContract,
to: sepoliaContract,
},
{
from: amoyContract,
to: fujiContract,
},
// ======================================
// SECTION 2: ENFORCED OPTIONS
// ======================================
// Define the gas options for destination chain execution.
// These options ensure a minimum gas amount is provided for lzReceive execution.
//
// For production, profile your contract's lzReceive gas usage on each destination
// chain and set appropriate values. The values below are examples.
//
// Learn more: https://docs.layerzero.network/v2/concepts/applications/oapp-standard#execution-options-and-enforced-settings

const EVM_ENFORCED_OPTIONS: OAppEnforcedOption[] = [
{
msgType: 1, // SEND message type
optionType: ExecutorOptionType.LZ_RECEIVE,
gas: 80000, // Gas limit for lzReceive on destination
value: 0, // Native value to send (usually 0 for OFT)
},
]

// ======================================
// SECTION 3: PATHWAY CONFIGURATION
// ======================================
// Define bidirectional pathways between contracts.
// The Simple Config Generator automatically creates both directions (A→B and B→A).
//
// Each pathway specifies:
// - Contract pair (source, destination)
// - DVN configuration: [ [requiredDVNs], [optionalDVNs, threshold] ]
// - Block confirmations: [A→B confirmations, B→A confirmations]
// - Enforced options: [B's enforcedOptions, A's enforcedOptions]
//
// For production, configure appropriate DVNs for your security requirements.
// See: https://docs.layerzero.network/v2/developers/evm/configuration/dvn-executor-config

const pathways: TwoWayConfig[] = [
// Tempo (Alt Endpoint) <-> Arbitrum (Standard Endpoint)
// This demonstrates the primary use case: OFTAlt on Alt chain ↔ OFT on standard chain
[
tempoContract, // Alt Endpoint chain (OFTAlt with ERC20 fees)
arbitrumContract, // Standard chain (OFT with native gas fees)
[['LayerZero Labs'], []], // DVNs: Required=[LayerZero Labs], Optional=[]
[1, 1], // Block confirmations (increase for mainnet)
[EVM_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Enforced options
],
}
]

export default config
// ======================================
// SECTION 4: EXPORT CONFIGURATION
// ======================================
// The generateConnectionsConfig function creates the full connection configuration
// from the pathway definitions, including DVN addresses and executor settings
// resolved from LayerZero metadata.

export default async function () {
const connections = await generateConnectionsConfig(pathways)
return {
contracts: [{ contract: tempoContract }, { contract: arbitrumContract }],
connections,
}
}
16 changes: 10 additions & 6 deletions examples/oft-alt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,26 @@
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@layerzerolabs/devtools": "~2.0.4",
"@layerzerolabs/devtools-evm-hardhat": "^4.0.4",
"@layerzerolabs/eslint-config-next": "~2.3.39",
"@layerzerolabs/lz-definitions": "^3.0.12",
"@layerzerolabs/lz-evm-messagelib-v2": "^3.0.12",
"@layerzerolabs/lz-evm-protocol-v2": "^3.0.12",
"@layerzerolabs/lz-evm-v1-0.7": "^3.0.12",
"@layerzerolabs/lz-v2-utilities": "^3.0.12",
"@layerzerolabs/io-devtools": "~0.3.2",
"@layerzerolabs/lz-definitions": "^3.0.151",
"@layerzerolabs/lz-evm-messagelib-v2": "^3.0.148",
"@layerzerolabs/lz-evm-protocol-v2": "^3.0.148",
"@layerzerolabs/lz-evm-v1-0.7": "^3.0.148",
"@layerzerolabs/lz-v2-utilities": "^3.0.148",
"@layerzerolabs/metadata-tools": "^3.0.0",
"@layerzerolabs/oapp-alt-evm": "^0.0.5",
"@layerzerolabs/oapp-evm": "^0.4.1",
"@layerzerolabs/oapp-evm-upgradeable": "^0.1.3",
"@layerzerolabs/oft-alt-evm": "^0.0.5",
"@layerzerolabs/oft-evm": "^4.0.1",
"@layerzerolabs/prettier-config-next": "^2.3.39",
"@layerzerolabs/protocol-devtools-evm": "^5.0.2",
"@layerzerolabs/solhint-config": "^3.0.12",
"@layerzerolabs/test-devtools-evm-foundry": "~8.0.1",
"@layerzerolabs/toolbox-foundry": "~0.1.9",
"@layerzerolabs/toolbox-foundry": "~0.1.13",
"@layerzerolabs/toolbox-hardhat": "~0.6.13",
"@nomicfoundation/hardhat-ethers": "^3.0.5",
"@nomiclabs/hardhat-ethers": "^2.2.3",
Expand Down
Loading
Loading