{"id":29747397,"url":"https://github.com/coredao-org/LZ-OFT-V2-Core-Guide","last_synced_at":"2025-07-26T08:40:03.751Z","repository":{"id":259061642,"uuid":"876164447","full_name":"coredao-org/LZ-OFT-V2-Core-Guide","owner":"coredao-org","description":"This guide walks through setting up and deploying an OFT (Omnichain Fungible Token) using LayerZero's V2 protocol, Hardhat, and two test networks: Base Sepolia (Ethereum Testnet) and Core Testnet.","archived":false,"fork":false,"pushed_at":"2024-10-21T16:23:50.000Z","size":214,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2024-10-22T03:38:25.821Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coredao-org.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-21T14:02:08.000Z","updated_at":"2024-10-21T16:23:54.000Z","dependencies_parsed_at":"2024-10-22T21:51:05.127Z","dependency_job_id":null,"html_url":"https://github.com/coredao-org/LZ-OFT-V2-Core-Guide","commit_stats":null,"previous_names":["coredao-org/lz-oft-v2-core-guide"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/coredao-org/LZ-OFT-V2-Core-Guide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coredao-org%2FLZ-OFT-V2-Core-Guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coredao-org%2FLZ-OFT-V2-Core-Guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coredao-org%2FLZ-OFT-V2-Core-Guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coredao-org%2FLZ-OFT-V2-Core-Guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coredao-org","download_url":"https://codeload.github.com/coredao-org/LZ-OFT-V2-Core-Guide/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coredao-org%2FLZ-OFT-V2-Core-Guide/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267141225,"owners_count":24041983,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-26T02:00:08.937Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-26T08:39:27.780Z","updated_at":"2025-07-26T08:40:03.730Z","avatar_url":"https://github.com/coredao-org.png","language":"TypeScript","funding_links":[],"categories":["Use Cases"],"sub_categories":["Github Repositories from Ecosystem Projects"],"readme":"# Complete Guide: Cross-Chain Token Transfers Using LayerZero's OFT V2 (Core Testnet to Base Sepolia Testnet)\n\n![Core LayerZero OFT](https://github.com/user-attachments/assets/ac7382c0-6825-4fb8-91c2-5e022a2eca66)\n\n## Introduction\n\nThis guide walks you through deploying and configuring an **OFT** (Omnichain Fungible Token) to facilitate cross-chain transfers of an ERC-20 token between **Core Testnet**, and **Base Sepolia Testnet** using **LayerZero's OFT V2 messaging protocol**. You'll learn how to set up your development environment with **Hardhat**, configure LayerZero endpoints, and execute cross-chain token transfers.\n\nBy the end of this guide, you will be able to:\n- Set up and configure Hardhat for cross-chain deployments.\n- Deploy an OFT contract for cross-chain token transfers.\n- Configure LayerZero endpoints and set up cross-chain peers.\n- Execute cross-chain transfers between Base Sepolia and Core Testnet.\n\nThis guide is suitable for both beginners and experienced developers. We will clearly mark success criteria to ensure you're on track.\n\n## Prerequisites\n\nBefore starting, make sure you have the following:\n\n- VSCode: Installed for editing and running your code.\n- Node.js: Download and install Node.js (which includes npm) from [Node.js official website](https://nodejs.org/).\n- Infura Account: [Sign up here](https://infura.io/) for access to Base Sepolia Testnet.\n- Metamask Wallet: Install [Metamask](https://metamask.io/) to manage your tokens.\n- Test Funds:\n  - Base Sepolia Testnet ETH: Required for deploying contracts and covering gas fees. You can get test funds from [Base Sepolia Faucet](https://thirdweb.com/base-sepolia-testnet).\n  - Core Testnet Funds: Required for deploying contracts on Core Testnet. You can get these from the [Core Faucet](https://scan.test.btcs.network/faucet).\n\nEnsure you have test tokens on both **Core Testnet** and **Base Sepolia Testnet** to complete the guide.\n\n## Guide Start\n\n1. Navigate to the root of the working directory, for this example purposes, we'll be creating a directory on the Desktop.\n\nRun\n\n```zsh\nnpx create-lz-oapp@latest\n```\n\nAn ERC20 extended with core bridging logic from OApp, creating an [Omnichain Fungible Token (OFT)](https://docs.layerzero.network/v2/developers/evm/oft/quickstart):\n\nAn ERC20 extended with core bridging logic from OApp, creating an Omnichain Fungible Token (OFT):\n\n```bash\n✔ Where do you want to start your project? … ./my-lz-oapp ← rename your project here\n\n✔ Which example would you like to use as a starting point? › **OFT**\n\n✔ What package manager would you like to use in your project? › **pnpm**\n```\n\nThis will initialize a repo with example contracts, cross-chain unit tests for sample contracts, custom LayerZero configuration files, deployment scripts, and more.\n\nHere, it gives you the option of selecting how you’d like to store/name your project. For example, we'd be using **./core-layerzero-example**\n\nPress **Enter** on your keyboard to proceed.\n\n\u003cimg width=\"1512\" alt=\"coreOft1\" src=\"https://github.com/user-attachments/assets/456be22c-bed4-48d7-b100-52c7c2dff8f5\"\u003e\n\n1.1 Select **OFT**\n\nPress **Enter** on your keyboard to proceed.\n\n\u003cimg width=\"1512\" alt=\"coreOft1 1\" src=\"https://github.com/user-attachments/assets/2fbe5585-d116-4807-aebe-c0d5bb50cc33\"\u003e\n\n1.2 select **pnpm (recommended)**\n\n1.3 Layerzero will then use pnpm to install recommended dependencies, press **enter/return** to proceed.\n\n\u003cimg width=\"1512\" alt=\"CoreOft1 3\" src=\"https://github.com/user-attachments/assets/af0c0adb-083d-426d-ac4f-dd2b3f220d36\"\u003e\n\n1.4 I am now going to change into the directory using cd , and open Visual Studio Code from the current working directory using cd **core-layerzero-example/** and clicking enter\n\nInside the new working directory, open Visual Studio code using `code .`\n\n\u003cimg width=\"1512\" alt=\"coreOft1 5\" src=\"https://github.com/user-attachments/assets/b2eb6293-608d-4d85-a5b2-f5a1419a4c4f\"\u003e\n\n2. Inside Visual Studio Code, we can now see the whole repo. Click on the **hardhat.config.ts** file, and navigate to the networks section within the file.\n\nNotice our currently set networks: **Sepolia Testnet (sepolia-testnet)**, **Avalanche Testnet (avalanche-testnet)**, and **Amoy Testnet (amoy-testnet)\n**\n\nFor `eid:`, you can see the EndpointId is being retrieved from the corresponding name that is passed, that LayerZero has support for, found [here](https://docs.layerzero.network/v2/developers/evm/technical-reference/deployed-contracts).\n\nFor this guide, we will just be doing a token transfer between two networks, **Core Testnet**, and **Base Sepolia Testnet**. Reason being, tokens are free, and easily accessible on both networks to pay for gas fees, when deploying and transferring tokens.\n\nFor the ‘url’, you’re able to place one in the .env file, and call upon it here. We will be placing the hard coded value for our networks that LayerZero will automatically fall back on if there are no .env configurations.\n\n2.1 We will be adding external networks, CoreDAO Testnet (core-testnet), and Base Sepolia Testnet (base-sepolia)\n\nFollow the standard Hardhat process for adding external networks to your hardhat.config.ts, with the only additional requirement being that a LayerZero Endpoint has been deployed to the chain:\n\n```typescript\n// hardhat.config.ts\n networks: {\n        // Base Sepolia Testnet Configuration using Infura\n        'base-sepolia': {\n            eid: EndpointId.BASESEP_V2_TESTNET, // Ensure this EndpointId exists in @layerzerolabs/lz-definitions\n            url: 'process.env.RPC_URL_BASE', // Infura endpoint with API key\n            accounts,\n        },\n\n        //CoreDAO Testnet Configuration\n        'coredao-testnet': {\n            eid: EndpointId.COREDAO_V2_TESTNET,\n            url: process.env.RPC_URL_COREDAO_V2_TESTNET || 'https://rpc.test.btcs.network',\n            accounts,\n        },\n\n     // Add other network configurations here if needed\n    },\n```\n\n\u003cimg width=\"1512\" alt=\"coreOft2\" src=\"https://github.com/user-attachments/assets/e83136b2-555c-4984-89fe-e8bdc7bf2acb\"\u003e\n\n2.2 Also, ensure that your evmVersion is set to ‘**paris**’\n\nYour full `hardhat.config.ts` file should look something like this:\n\n```typescript\n\n// Get the environment configuration from .env file\n//\n// To make use of automatic environment setup:\n// - Duplicate .env.example file and name it .env\n// - Fill in the environment variables\nimport 'dotenv/config'\n\n// Import Hardhat plugins\nimport 'hardhat-deploy'\nimport 'hardhat-contract-sizer'\nimport '@nomiclabs/hardhat-ethers'\nimport '@layerzerolabs/toolbox-hardhat'\n// eslint-disable-next-line import/no-unresolved\n// import './tasks/sendOFT'\n\n// Import necessary types and constants\nimport { HardhatUserConfig, HttpNetworkAccountsUserConfig } from 'hardhat/types'\n\nimport { EndpointId } from '@layerzerolabs/lz-definitions'\n\n// Set your preferred authentication method\n//\n// If you prefer using a mnemonic, set a MNEMONIC environment variable\n// to a valid mnemonic\nconst MNEMONIC = process.env.MNEMONIC\n\n// If you prefer to be authenticated using a private key, set a PRIVATE_KEY environment variable\nconst PRIVATE_KEY = process.env.PRIVATE_KEY\n\n// Configure accounts based on available authentication method\nconst accounts: HttpNetworkAccountsUserConfig | undefined = MNEMONIC\n    ? { mnemonic: MNEMONIC }\n    : PRIVATE_KEY\n      ? [PRIVATE_KEY]\n      : undefined\n\nif (accounts == null) {\n    console.warn(\n        'Could not find MNEMONIC or PRIVATE_KEY environment variables. It will not be possible to execute transactions in your example.'\n    )\n}\n\n// Define Hardhat configuration\nconst config: HardhatUserConfig = {\n    paths: {\n        cache: 'cache/hardhat',\n    },\n    solidity: {\n        compilers: [\n            {\n                version: '0.8.22',\n                settings: {\n                    evmVersion: 'paris',\n                    optimizer: {\n                        enabled: true,\n                        runs: 200,\n                    },\n                },\n            },\n        ],\n    },\n    networks: {\n        // Base Sepolia Testnet Configuration using Infura\n        'base-sepolia': {\n            eid: EndpointId.BASESEP_V2_TESTNET, // Ensure this EndpointId exists in @layerzerolabs/lz-definitions\n            url: process.env.RPC_URL_BASE, // Infura endpoint with API key\n            accounts,\n        },\n\n        // CoreDAO Testnet Configuration\n        'coredao-testnet': {\n            eid: EndpointId.COREDAO_V2_TESTNET,\n            url: process.env.RPC_URL_COREDAO_V2_TESTNET || 'https://rpc.test.btcs.network',\n            accounts,\n        },\n\n        // Add other network configurations here if needed\n    },\n    namedAccounts: {\n        deployer: {\n            default: 0, // Wallet address at index 0 from the mnemonic or the first private key\n        },\n    },\n}\n\nexport default config\n```\n**Save your file**.\n\n2.3 You now need to configure your `.env` file. Included in this example repo, is a `.env.example` file. Rename this to `.env` and include the following:\n\n\u003cimg width=\"1512\" alt=\"coreOft2 3\" src=\"https://github.com/user-attachments/assets/f993d35a-542b-401d-beef-da4ed54fb560\"\u003e\n\nGet your **private key** from your **Metamask / EVM compatible wallet**. In this guide, we are using [Infura](https://app.infura.io/) to connect to Base Sepolia Testnet, to easily deploy.\n\n2.4 Once you’ve signed up with Infura, configure the free API Key they give you, with Base Sepolia Testnet:\n\n\u003cimg width=\"1512\" alt=\"coreOft2 4\" src=\"https://github.com/user-attachments/assets/1f072714-6168-43d3-be10-c6b6061b0c7c\"\u003e\n\nAfter saving your endpoint as **Base Sepolia**, click ‘**Save Changes**’. Navigate to ‘**Active Endpoints**’.\n\n\u003cimg width=\"1512\" alt=\"coreOft2 4 1\" src=\"https://github.com/user-attachments/assets/f48b8dcc-06fc-44d4-90cb-463fca4bd097\"\u003e\n\nCopy the **Base** Active Endpoint URL.\n\nAfter grabbing this, paste this after the **BASE_SEPOLIA_URL**= and save your `.env` file.\n\nYour `.env` file should now be populated with your **Private Key** and **Infura API Key**.\n\nRefer to these steps for adding **Base Sepolia** to your metamask: https://docs.base.org/docs/using-base/\n\n2.5 Open a terminal inside of the root of your project, and write ‘**pnpm install**’\n\n\u003cimg width=\"1512\" alt=\"coreOft2 5\" src=\"https://github.com/user-attachments/assets/37a6253a-5cdb-45d3-9fe8-7b64d74382c0\"\u003e\n\nIf you see '**Already up to date**', all necessary dependencies are good to go.\n\n3 We will now modify our OFT contract to your requirements for your use case.\n\nNavigate to contracts, and select `MyOFT.sol`.\n\nThis contract is the default setup for an **OFT token**. It doesn't include any initial minting, which means that, by default, no tokens exist until an external function call or separate minting process creates them.\n\nBelow, is an updated version of this contract, with the added minting function, so that we have **100,000 tokens** to transfer between chains, upon deployment. \n\n\u003cimg width=\"1512\" alt=\"coreOft3\" src=\"https://github.com/user-attachments/assets/0c84d918-9420-4024-99fa-7394b2d6cc3b\"\u003e\n\nYour whole contract should be similar to the following if you want to retain the same basic functionality:\n\n```solidity\n\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { OFT } from \"@layerzerolabs/oft-evm/contracts/OFT.sol\";\n\ncontract MyOFT is OFT {\n    constructor(\n        string memory _name,\n        string memory _symbol,\n        address _lzEndpoint,\n        address _delegate\n    ) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {\n        // Mint tokens to the deployer's address (msg.sender)\n        _mint(msg.sender, 100_000 * 10 ** 18);\n    }\n}\n```\n\n3.1 run \n\n```zsh\npnpm compile:hardhat\n```\n\nIf successful, you should see something similar to: **Compiled 40 Solidity files successfully (evm target: paris)**.\n\n\u003cimg width=\"1512\" alt=\"coreOft3 1\" src=\"https://github.com/user-attachments/assets/c95a4c08-c2f8-46ca-918c-2c2d7a168697\"\u003e\n\n3.2 Moving on, prior to deployment, let's ensure that we have enough gas for both chains.\n\nFirst, for **Core Testnet Tokens**, navigate to: https://scan.test.btcs.network/faucet\n\nAfter entering your Core testnet address, and completing the captcha, **1 tCORE** will be deposited into your Core Testnet wallet.\n\n\u003cimg width=\"1509\" alt=\"coreOft3 2\" src=\"https://github.com/user-attachments/assets/6062343e-3a01-4d58-8acc-de4cef986a2d\"\u003e\n\n3.3 Let's do the same for **Base Sepolia**. Navigate to this faucet link:\nhttps://thirdweb.com/base-sepolia-testnet\n\nFirst click **Add to wallet**, to ensure that Base Sepolia is recognized in your Metamask.\n\nNext, click Claim tokens, I already claimed mine, so the UI will be a bit different, but this should send ~$30 worth of Base Sepolia to your address, which will be sufficient for our use.\n\nSwitch your network to **Base Sepolia**, and ensure your tokens have been received.\n\n\u003cimg width=\"1512\" alt=\"coreOft3 3\" src=\"https://github.com/user-attachments/assets/83962cd0-65a2-4be6-bff1-c4a1742f9781\"\u003e\n\n4 Back in Visual Studio code, open the terminal from the root of the repo. We now want to deploy our LayerZero contracts. However, **we must still initialize our `layerzero.config.ts` file for core testnet and base sepolia**.\n\nTo simplify things, we will first delete the default `layerzero.config.ts` file in our root directory.\n\n4.1 Next, in your terminal, run:\n\n```zsh\nnpx hardhat lz:oapp:config:init --contract-name MyOFT --oapp-config my_oft_config.ts\n```\n\n\u003cimg width=\"1512\" alt=\"coreOft4 1\" src=\"https://github.com/user-attachments/assets/e1eb4a62-c8bc-49ce-b27e-6a04d0723ac2\"\u003e\n\n4.2 If the command is ran successfully, you should see the LayerZero CLI UI appear with the networks defined from your hardhat.config.ts file. (base-sepolia, coredao-testnet) \n\n\u003eUse the up/down arrow keys, and select both networks by clicking spacebar on each – both options should have the dot highlighted.\n\nClick '**return/enter**' on your keyboard to proceed.\n\n\u003cimg width=\"1512\" alt=\"coreOft4 2\" src=\"https://github.com/user-attachments/assets/832d4e4b-b577-4b2d-b1c7-29cbf419206e\"\u003e\n\n4.3 Confirm your `config.ts` file has been created. You may have named yours differently, but it should look something like this:\n\n\u003cimg width=\"1512\" alt=\"coreOft4 3\" src=\"https://github.com/user-attachments/assets/c6f5361b-978f-4fea-8ee5-0d5405cf17f2\"\u003e\n\n4.3.1 You may need to fix the formatting, as you can see on line 10 in the image above, it’s not wrapping. Right click on the code, ctrl + A to select all contents in file, select **Format Document**. This should fix any issues.\n\n\u003eTo return the default configuration for all possible network pathways from your hardhat.config.ts in your terminal, run:\n\u003e\n\u003e```npx hardhat lz:oapp:config:get:default```\n\n\u003cimg width=\"1512\" alt=\"coreOft4 3 1\" src=\"https://github.com/user-attachments/assets/12a64c9f-ea89-4757-ac45-cd1412daa3c6\"\u003e\n\n5 To deploy your LayerZero contracts, in your terminal, you can now run:\n\n`npx hardhat lz:deploy`\n\n\u003eDeployment Process: Running `npx hardhat lz:deploy` is used to execute the deployment script, ensuring that the MyOFT contract is deployed to the desired network.\n\u003eSee [Deploying Contracts](https://docs.layerzero.network/v2/developers/evm/create-lz-oapp/deploying) to learn more.\n\n5.1 Hardhat will now compile your project again, and you will be presented to the **LayerZero CLI UI** again, using the network names defined in your `hardhat.config.ts` and the contract defined in your `layerzero.config.ts`.\n\nOnce again, select both **base-sepolia**, and **coredao-testnet** and click '**enter/return**' on your keyboard to confirm.\n\n\u003cimg width=\"1512\" alt=\"coreOft5 1\" src=\"https://github.com/user-attachments/assets/4969a819-74c0-486e-a7c7-af441ddbaff5\"\u003e\n\n\u003cimg width=\"1512\" alt=\"coreOft5 1 1\" src=\"https://github.com/user-attachments/assets/48bdb82d-3238-481a-bd17-aa286f2d5091\"\u003e\n\nPress '**Enter**' here on your keyboard again.\n\nThe UI will confirm that you want to deploy to **base-sepolia**, **coredao-testnet** press Y to confirm.\n5.2 If successful, you will see your 2 deployed contracts for each network, displayed in the terminal, similar to what we have here:\n\nDeployed contract: MyOFT, network: **coredao-testnet**, address: `0x08690B5069146005f57f62548d176708490e1e00`\n\nDeployed contract: MyOFT, network: **base-sepolia**, address: `0xe07BFA9B56F162efDE07B3b2F7DF1D584BE04C16`\n \n\u003cimg width=\"1512\" alt=\"coreOft5 2\" src=\"https://github.com/user-attachments/assets/3a521e96-0525-4183-bec8-c05cdeb7b283\"\u003e\n\nIf you paste this each contract address into either https://scan.test.btcs.network/(**Core Testnet Explorer**), or https://sepolia.basescan.org/ (**Base Sepolia Testnet Explorer**)\n\nYou can see your newly deployed contracts on their respective chains.\n\n6 We must now configure **Cross-Chain Pathways**.\n\nBefore transferring tokens between chains, it's essential to configure your LayerZero contracts for each unique pathway. Note that LayerZero contracts have distinct configurations for each direction (e.g., from Core Testnet to Base Sepolia has different properties than from Base Sepolia to Core Testnet).\n\nFor a detailed overview of all possible configuration commands, refer to the LayerZero [Configuring Contracts](https://docs.layerzero.network/v2/developers/evm/create-lz-oapp/configuring-pathways) documentation.\n\nTo configure your LayerZero contracts, modify the pathway configurations in your layerzero.config.ts file. Once you have updated the pathway configurations, you can wire them together using the following command:\n\n`npx hardhat lz:oapp:wire --oapp-config \u003cFILE_NAME\u003e.config.ts`\n\u003eExample – (`npx hardhat lz:oapp:wire --oapp-config my_oft_config.ts`)\n\nFor each pathway on your config file, this task will call:\n\nfromContract.OApp.setPeer\nfromContract.OApp.setEnforcedOptions\nfromContract.EndpointV2.setSendLibrary\nfromContract.EndpointV2.setReceiveLibrary\nfromContract.EndpointV2.setReceiveLibraryTimeout\nfromContract.EndpointV2.setConfig(OApp, sendLibrary, sendConfig)\nfromContract.EndpointV2.setConfig(OApp, receiveLibrary, receiveConfig)\n\nfor each contract instance, applying the custom configurations added.\n\nExample Configuration for Core Testnet to Base Sepolia Testnet\n\nBelow is an example of configuring a pathway between Core Testnet and Base Sepolia Testnet:\n\n\n```typescript\n// connections[] in layerzero.config.ts\n// configContracts is a constants file with contract addresses\n{\n    from: coreContract,\n    to: baseSepoliaContract,\n    // the config below is SET on Core Testnet\n    config: {\n        sendConfig: {\n            ulnConfig: {\n                confirmations: BigInt(5), // block confirmations to wait for finality\n                optionalDVNThreshold: 0,\n                requiredDVNs: [ // DVNs to pay to verify\n                    configContracts.coreContracts.lzDvn,\n                    configContracts.coreContracts.nethermindDvn,\n                ],\n                optionalDVNs: [],\n            },\n        },\n    },\n},\n{\n    from: baseSepoliaContract,\n    to: coreContract,\n    // the config below is SET on Base Sepolia Testnet\n    config: {\n        receiveConfig: {\n            ulnConfig: {\n                confirmations: BigInt(5), // enforces DVNs waited for finality\n                optionalDVNThreshold: 0,\n                requiredDVNs: [ // enforces specific DVNs have verified\n                    configContracts.baseSepoliaContracts.lzDvn,\n                    configContracts.baseSepoliaContracts.nethermindDvn,\n                ],\n                optionalDVNs: [],\n            },\n        },\n    },\n},\n```\nRemember to apply configurations for both directions if the pathway is bi-directional, meaning you need to add sendConfig for Core Testnet to Base Sepolia and receiveConfig for Base Sepolia to Core Testnet.\n\n6.1 Open your terminal in Visual Studio code, from the root of the repo.\n\nRun `npx hardhat lz:oapp:wire --oapp-config my_oft_config.ts` (or whatever you named your config.ts file)\n\n6.1.1 You will see OApp doing some checks, and ask to proceed to preview transactions, click ‘**Y**’ to proceed.\n\n\u003cimg width=\"1512\" alt=\"coreOft6 1 1\" src=\"https://github.com/user-attachments/assets/2b6aeeb2-75a8-4938-9c4e-9522ba9c4f7d\"\u003e\n\n6.1.2 Click ‘**Y**’ on your keyboard again to submit the required transactions\n\n\u003cimg width=\"1512\" alt=\"coreOft6 1 3\" src=\"https://github.com/user-attachments/assets/3c625751-e674-4e14-a6f4-fec07460c991\"\u003e\n\n6.1.3 If successful, your terminal should be saying ‘**Your OApp is now configured**’.\n\n6.2 We will now send our tokens between our contracts. We will need to create a hardhat task.\n\nWithin your root directory, create a subfolder called `**tasks**`.\n\nInside of it, create a file called `sendOFT.ts`.\n\n\u003cimg width=\"1512\" alt=\"coreOFT6 2\" src=\"https://github.com/user-attachments/assets/a2a282d6-9a4f-43e4-8780-aa1573456adf\"\u003e\n\nYou should now have an empty `sendOFT.ts` file.\n\n6.3 Copy the following **Hardhat task**, and paste the contents in `sendOFT.ts`.\n\n```typescript\nimport { ethers } from 'ethers'\nimport { task } from 'hardhat/config'\n\nimport { createGetHreByEid, createProviderFactory, getEidForNetworkName } from '@layerzerolabs/devtools-evm-hardhat'\nimport { Options } from '@layerzerolabs/lz-v2-utilities'\n\n// Send tokens from a contract on one network to another\ntask('lz:oft:send', 'Send tokens cross-chain using LayerZero technology')\n    .addParam('contractA', 'Contract address on network A')\n    .addParam('recipientB', 'Recipient address on network B')\n    .addParam('networkA', 'Name of the network A')\n    .addParam('networkB', 'Name of the network B')\n    .addParam('amount', 'Amount to transfer in token decimals')\n    .addParam('privateKey', 'Private key of the sender')\n    .setAction(async (taskArgs, hre) =\u003e {\n        const eidA = getEidForNetworkName(taskArgs.networkA)\n        const eidB = getEidForNetworkName(taskArgs.networkB)\n        const contractA = taskArgs.contractA\n        const recipientB = taskArgs.recipientB\n\n        const environmentFactory = createGetHreByEid()\n        const providerFactory = createProviderFactory(environmentFactory)\n        const provider = await providerFactory(eidA)\n        const wallet = new ethers.Wallet(taskArgs.privateKey, provider)\n\n        const oftContractFactory = await hre.ethers.getContractFactory('MyOFT', wallet)\n        const oft = oftContractFactory.attach(contractA)\n\n        const decimals = await oft.decimals()\n        const amount = hre.ethers.utils.parseUnits(taskArgs.amount, decimals)\n        const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toString()\n        const recipientAddressBytes32 = hre.ethers.utils.hexZeroPad(recipientB, 32)\n\n        // Estimate the fee\n        try {\n            console.log(\"Attempting to call quoteSend with parameters:\", {\n                dstEid: eidB,\n                to: recipientAddressBytes32,\n                amountLD: amount,\n                minAmountLD: amount.mul(98).div(100),\n                extraOptions: options,\n                composeMsg: '0x',\n                oftCmd: '0x',\n            });\n            const nativeFee = (await oft.quoteSend(\n                [eidB, recipientAddressBytes32, amount, amount.mul(98).div(100), options, '0x', '0x'],\n                false\n            ))[0]\n            console.log('Estimated native fee:', nativeFee.toString())\n\n            // Overkill native fee to ensure sufficient gas\n            const overkillNativeFee = nativeFee.mul(2)\n\n            // Fetch the current gas price and nonce\n            const gasPrice = await provider.getGasPrice()\n            const nonce = await provider.getTransactionCount(wallet.address)\n\n            // Prepare send parameters\n            const sendParam = [eidB, recipientAddressBytes32, amount, amount.mul(98).div(100), options, '0x', '0x']\n            const feeParam = [overkillNativeFee, 0]\n\n            // Sending the tokens with increased gas price\n            console.log(`Sending ${taskArgs.amount} token(s) from network ${taskArgs.networkA} to network ${taskArgs.networkB}`)\n            const tx = await oft.send(sendParam, feeParam, wallet.address, {\n                value: overkillNativeFee,\n                gasPrice: gasPrice.mul(2),\n                nonce,\n                gasLimit: hre.ethers.utils.hexlify(7000000),\n            })\n            console.log('Transaction hash:', tx.hash)\n            await tx.wait()\n            console.log(\n                `Tokens sent successfully to the recipient on the destination chain. View on LayerZero Scan: https://layerzeroscan.com/tx/${tx.hash}`\n            )\n        } catch (error) {\n            console.error('Error during quoteSend or send operation:', error)\n            if (error?.data) {\n                console.error(\"Reverted with data:\", error.data)\n            }\n        }\n    })\n```\n\u003cimg width=\"1512\" alt=\"coreOft6 3\" src=\"https://github.com/user-attachments/assets/11f47e60-0430-420b-8bd9-7bc8f3b4ace8\"\u003e\n\n6.4 Go back to your `hardhat.config.ts` file, and uncomment: `import './tasks/sendOFT`\n\nOpen your terminal in the root of your working directory, and run the following command:\n\n```zsh\nnpx hardhat lz:oft:send --contract-a 0x… --recipient-b 0x… --network-a coredao-testnet --network-b base-sepolia --amount 100 --private-key \u003cPRIVATE_KEY\u003e\n```\n\nThe inputs on this script correspond with the **.addParam**’s within the Hardhat task. Hardhat will flag you if you enter certain information incorrectly, so ensure there is no accidental dashes, or missing whitespace etc.\n\nIf successful, you will see the terminal return that you are sending **100 tokens**, with a transaction hash, and a link to LayerZeroScan – Testnet (https://testnet.layerzeroscan.com/tx/0x4d82f6bb174fb6600eb36b3d7bc641c1e7757229de1ef4a24dc7e59dae7350fc)\n\n6.5 On LayerZero Scan, this page shows:\n\nThe fee paid for the transaction (0.0114178 tCORE).\nThe sender's address and the destination of the tokens.\nConfirmation that the transaction was successful (status: Delivered).\nYou can click on the globes to check the token amounts in each contract on both the source and destination chains.\nIt's a quick way to verify the success and details of your cross-chain transfer.\n\n\u003cimg width=\"1512\" alt=\"coreOft6 5\" src=\"https://github.com/user-attachments/assets/c4fdf7f4-1f76-42e7-8128-0b6cb95a50d7\"\u003e\n\n6.5.1 You can see on the [Core Testnet explorer](https://scan.test.btcs.network/token/0x08690b5069146005f57f62548d176708490e1e00), the total supply of **MOFT (99,800)** indicating my **2** uses of the send task to transfer **100 tokens** each time, from **Core Testnet**, to **Base Sepolia**.\n\n\u003cimg width=\"1512\" alt=\"coreOft6 5 1\" src=\"https://github.com/user-attachments/assets/51d9935f-8053-48fb-aedf-a5983e641819\"\u003e\n\n6.5.2 When we check the MOFT token on the [Base Sepolia Explorer](https://sepolia.basescan.org/token/0xe07bfa9b56f162efde07b3b2f7df1d584be04c16), we can indeed verify that we are now at **100,200 Max Total Supply** confirming that we have successfully burned, and minted 200 MOFT tokens, “transferring” between Core Testnet, and Base Sepolia.\n\n\u003cimg width=\"1512\" alt=\"coreOft6 5 2\" src=\"https://github.com/user-attachments/assets/8b1f39cf-97e5-4636-aa35-bc28dce65d97\"\u003e\n\n## Conclusion\n\nBy integrating LayerZero’s OFT on **Core Testnet** and **Mainnet**, we are opening new pathways for cross-chain interactions between Core and major networks. Currently, connections between **Core Mainnet** and the mainnets of **Ethereum, Optimism, Base, and Arbitrum** are fully operational. Likewise, **Core Testnet** is linked to the testnets of these networks, including **Sepolia**. This creates a connection between Core and other ecosystems, allowing for efficient token transfers and interactions across different chains.\n\nIf you encounter any issues with endpoints or would like support for additional network pathways connecting to Core, we encourage you to submit a request. The LayerZero team must be manually informed to enable or troubleshoot new or existing connections.\n\nThe LayerZero contracts provided can be easily modified to accommodate a variety of token use cases, from fungible tokens to governance models or more complex token systems. Additionally, the Hardhat tasks demonstrated in this guide can be customized for different workflows, such as automating token transfers, managing token supplies, or adding unique functionality across networks.\n\nBy unlocking these cross-chain capabilities, developers on Core now have the ability to build advanced decentralized applications that are fully interoperable with multiple blockchain ecosystems. This enables increased liquidity, extended reach for applications, and the ability to leverage assets across both testnets and mainnets, opening new opportunities for innovation and growth within the Core ecosystem.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoredao-org%2FLZ-OFT-V2-Core-Guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoredao-org%2FLZ-OFT-V2-Core-Guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoredao-org%2FLZ-OFT-V2-Core-Guide/lists"}