diff --git a/README.md b/README.md index 5127ad0..ca87469 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,40 @@ This repo contains token contracts to be deployed on Etherlink Mainnet by the Etherlink team. This project is a hardhat project built on the [template](https://docs.layerzero.network/v2/developers/evm/create-lz-oapp/start) provided by LayerZero. The token deployments, including addresses, can be found under `deployments/` and are summarized as follows: + - `WXTZ`: Wrapped XTZ, exchange XTZ for WXTZ 1:1 on Etherlink. This should be deployed as a [classic OFT](#classic-ofts). - `tzBTC`: Wrapped BTC, the contract owner can influence the supply of the token on Etherlink. This should be deployed as a [custom OApp](#custom-oapps). +## Deployments + +### Mainnets + +| Network | Chain ID | Contract | Address | +| --- | --- | --- | --- | +| Etherlink | 42793 | `WXTZ` | [`0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb`](https://explorer.etherlink.com/address/0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb) | +| Ethereum | 1 | `WXTZ` | [`0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb`](https://etherscan.io/address/0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb) | +| Arbitrum One | 42161 | `WXTZ` | [`0x7424f00845777A06E21F0bd8873f814A8A814B2D`](https://arbiscan.io/address/0x7424f00845777A06E21F0bd8873f814A8A814B2D) | +| Base | 8453 | `WXTZ` | [`0x91F9cc2649ac70a071602cadE9b0C1A5868af51D`](https://basescan.org/address/0x91F9cc2649ac70a071602cadE9b0C1A5868af51D) | +| BNB Smart Chain | 56 | `WXTZ` | [`0x91F9cc2649ac70a071602cadE9b0C1A5868af51D`](https://bscscan.com/address/0x91F9cc2649ac70a071602cadE9b0C1A5868af51D) | + +### Testnets + +| Network | Chain ID | Contract | Address | +| --- | --- | --- | --- | +| Etherlink Testnet (Ghostnet) | 128123 | `WXTZ` | [`0x40fAfA2E6b2F0734caDDC0a1c572D3BCeABa5313`](https://testnet-explorer.etherlink.com/address/0x40fAfA2E6b2F0734caDDC0a1c572D3BCeABa5313) | +| Etherlink Testnet (Ghostnet) | 128123 | `tzBTCTokenProxy` | [`0xEa5e33525D44dbc4cD4598202AD99f077c58be15`](https://testnet-explorer.etherlink.com/address/0xEa5e33525D44dbc4cD4598202AD99f077c58be15) | +| Etherlink Testnet (Ghostnet) | 128123 | `tzBTCTokenImplem` | [`0x492e4bD1395080B6F6A517e29392C82FAcecc887`](https://testnet-explorer.etherlink.com/address/0x492e4bD1395080B6F6A517e29392C82FAcecc887) | +| Etherlink Testnet (Ghostnet) | 128123 | `tzBTCAdapter` | [`0x632E0216ed9a71b257DD00B79b056231040aBD6a`](https://testnet-explorer.etherlink.com/address/0x632E0216ed9a71b257DD00B79b056231040aBD6a) | +| Etherlink Shadownet | 127823 | `WXTZ` | [`0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb`](https://shadownet.explorer.etherlink.com/address/0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb) | +| Sepolia | 11155111 | `WXTZ` | [`0x5E51503409Acf2457C5cEf6c1DFb0e3F6330fD65`](https://sepolia.etherscan.io/address/0x5E51503409Acf2457C5cEf6c1DFb0e3F6330fD65) | +| BSC Testnet | 97 | `WXTZ` | [`0x66b48b4b33d075f6c87699ad4e669355d2794f39`](https://testnet.bscscan.com/address/0x66b48b4b33d075f6c87699ad4e669355d2794f39) | +| TezosX Previewnet [*](#tezosx-previewnet-stub) | 128064 | `WXTZ` | [`0xf4a2e3BA5C14f11c23c8c4351093C058D20542f2`](https://blockscout.previewnet.tezosx.nomadic-labs.com/address/0xf4a2e3BA5C14f11c23c8c4351093C058D20542f2) | +| TezosX Previewnet [*](#tezosx-previewnet-stub) | 128064 | `EndpointV2Stub` | [`0xF01b3160F2926420C7416ddA62544969063269ED`](https://blockscout.previewnet.tezosx.nomadic-labs.com/address/0xF01b3160F2926420C7416ddA62544969063269ED) | + +#### TezosX Previewnet stub + +TezosX Previewnet is an ephemeral chain (≈1–2 month lifespan) and has no LayerZero infrastructure. To deploy the unmodified `WXTZ` contract there, a minimal `EndpointV2Stub` (see [`contracts/mocks/tezosXPreviewnet/`](contracts/mocks/tezosXPreviewnet/)) is deployed first and registered under the `EndpointV2` deployment name so `OAppCore`'s constructor call to `setDelegate` succeeds. Cross-chain `send`/`lzReceive` do not work on previewnet; local `deposit`/`withdraw` and the full ERC20/permit surface do. The stub and its deploy script ([`deploy/tezosXPreviewnet/`](deploy/tezosXPreviewnet/)) are scoped to that network via a `skip` guard, and should be removed when previewnet is decommissioned. + ## Module Guide - `docs/`: Documentation of the tokens @@ -51,6 +82,7 @@ npx hardhat lz:deploy ### Custom OApps If the contract is not a classic OFT, it is recommended to deploy it manually with hardhat-deploy instead as follows: + ``` npx hardhat deploy --tags --network ``` diff --git a/chain-config.ts b/chain-config.ts index 682b738..0eb69fb 100644 --- a/chain-config.ts +++ b/chain-config.ts @@ -1,3 +1,3 @@ -export const testnetChains = [127823, 11155111, 11155420, 97, 80002, 84532, 421614, 128123] +export const testnetChains = [128064, 127823, 11155111, 11155420, 97, 80002, 84532, 421614, 128123] export const mainnetChains = [1, 10, 56, 137, 8453, 42161, 42793] diff --git a/contracts/mocks/tezosXPreviewnet/EndpointV2Stub.sol b/contracts/mocks/tezosXPreviewnet/EndpointV2Stub.sol new file mode 100644 index 0000000..a67b382 --- /dev/null +++ b/contracts/mocks/tezosXPreviewnet/EndpointV2Stub.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/// @title EndpointV2Stub (tezosXPreviewnet ONLY — DO NOT DEPLOY ELSEWHERE) +/// @notice Stand-in for `ILayerZeroEndpointV2` so OFT-based contracts can be +/// deployed on the ephemeral tezosXPreviewnet chain, which has no real +/// LayerZero infrastructure. +/// @dev Implements only `setDelegate(address)` — the single endpoint method +/// invoked from `OAppCore`'s constructor. Cross-chain send/receive, +/// quoting, and config flows are intentionally absent and will not +/// work. Delete this contract and its deployment when previewnet is +/// decommissioned. +contract EndpointV2Stub { + event DelegateSet(address indexed oapp, address indexed delegate); + + mapping(address oapp => address delegate) public delegates; + + function setDelegate(address _delegate) external { + delegates[msg.sender] = _delegate; + emit DelegateSet(msg.sender, _delegate); + } +} diff --git a/deploy/WXTZ.ts b/deploy/WXTZ.ts index 49554e6..5421645 100644 --- a/deploy/WXTZ.ts +++ b/deploy/WXTZ.ts @@ -1,9 +1,10 @@ import assert from 'assert' +import { error } from 'console' + import { type DeployFunction } from 'hardhat-deploy/types' + +import { mainnetChains, testnetChains } from '../chain-config' import { updateDeploymentFile } from '../scripts/utils' -import { testnetChains, mainnetChains } from '../chain-config' -import { network } from 'hardhat' -import { error } from 'console' const contractName = 'WXTZ' @@ -43,6 +44,8 @@ const deploy: DeployFunction = async (hre) => { let etherlinkChainId if (currentNetwork == 127823) { etherlinkChainId = 127823 + } else if (currentNetwork == 128064) { + etherlinkChainId = 128064 } else if (testnetChains.includes(currentNetwork)) { etherlinkChainId = 128123 } else if (mainnetChains.includes(currentNetwork)) { diff --git a/deploy/tezosXPreviewnet/EndpointV2Stub.ts b/deploy/tezosXPreviewnet/EndpointV2Stub.ts new file mode 100644 index 0000000..9f9c0ad --- /dev/null +++ b/deploy/tezosXPreviewnet/EndpointV2Stub.ts @@ -0,0 +1,34 @@ +import assert from 'assert' + +import { type DeployFunction } from 'hardhat-deploy/types' + +const TEZOS_X_PREVIEWNET = 'tezosXPreviewnet' + +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}`) + + // Deploys the previewnet-only LayerZero endpoint stub and registers it under + // the name "EndpointV2" so that hre.deployments.get('EndpointV2') resolves + // to the stub on this network. The OApp constructor's only endpoint call + // (setDelegate) is satisfied; cross-chain send/receive will not work. + const result = await deploy('EndpointV2', { + contract: 'EndpointV2Stub', + from: deployer, + args: [], + log: true, + }) + + console.log(`EndpointV2Stub deployed to ${result.address} (saved as 'EndpointV2')`) +} + +deploy.tags = ['EndpointV2Stub'] +deploy.skip = async (hre) => hre.network.name !== TEZOS_X_PREVIEWNET + +export default deploy diff --git a/deploy/tzBTCToken.ts b/deploy/tzBTCToken.ts index fe13b02..f6a2092 100644 --- a/deploy/tzBTCToken.ts +++ b/deploy/tzBTCToken.ts @@ -1,63 +1,69 @@ import assert from 'assert' + import { upgrades } from 'hardhat' import { type DeployFunction } from 'hardhat-deploy/types' + import { updateDeploymentFile, verifyContract } from '../scripts/utils' const contractName = 'tzBTCToken' const deploy: DeployFunction = async (hre) => { - const { getNamedAccounts, ethers, deployments } = hre + const { getNamedAccounts, ethers, deployments } = hre - const { deploy } = deployments - const { deployer } = await getNamedAccounts() + const { deploy } = deployments + const { deployer } = await getNamedAccounts() - assert(deployer, 'Missing named deployer account') + assert(deployer, 'Missing named deployer account') - console.log(`Network: ${hre.network.name}`) - console.log(`Deployer: ${deployer}`) + 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: { - // fuji: { - // ... - // eid: EndpointId.AVALANCHE_V2_TESTNET - // } - // } - const endpointV2Deployment = await hre.deployments.get('EndpointV2') + // 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: { + // fuji: { + // ... + // eid: EndpointId.AVALANCHE_V2_TESTNET + // } + // } + const endpointV2Deployment = await hre.deployments.get('EndpointV2') - // Deploy the ERC20 Upgradable contract tzBTC - const Token = await hre.ethers.getContractFactory("tzBTCToken"); - const tzBTCToken = await upgrades.deployProxy(Token, [deployer], { initializer: 'initialize', kind: 'uups' }); - await tzBTCToken.deployed(); - const tzBTCTokenLogic = await upgrades.erc1967.getImplementationAddress(tzBTCToken.address); + // Deploy the ERC20 Upgradable contract tzBTC + const Token = await hre.ethers.getContractFactory('tzBTCToken') + const tzBTCToken = await upgrades.deployProxy(Token, [deployer], { initializer: 'initialize', kind: 'uups' }) + await tzBTCToken.deployed() + const tzBTCTokenLogic = await upgrades.erc1967.getImplementationAddress(tzBTCToken.address) - // Deploy OFT Adapter - const Adapter = await hre.ethers.getContractFactory("tzBTCAdapter"); - const adapter = await Adapter.deploy(tzBTCToken.address, endpointV2Deployment.address, deployer); - await tzBTCToken.deployed(); + // Deploy OFT Adapter + const Adapter = await hre.ethers.getContractFactory('tzBTCAdapter') + const adapter = await Adapter.deploy(tzBTCToken.address, endpointV2Deployment.address, deployer) + await tzBTCToken.deployed() - console.log(`Deployed contract: ${contractName} proxy, network: ${hre.network.name}, address: ${tzBTCToken.address}`); - console.log(`Deployed contract: ${contractName} implementation, network: ${hre.network.name}, address: ${tzBTCTokenLogic}`); - console.log(`Deployed contract: ${contractName} adapter, network: ${hre.network.name}, address: ${adapter.address}`); + console.log( + `Deployed contract: ${contractName} proxy, network: ${hre.network.name}, address: ${tzBTCToken.address}` + ) + console.log( + `Deployed contract: ${contractName} implementation, network: ${hre.network.name}, address: ${tzBTCTokenLogic}` + ) + console.log(`Deployed contract: ${contractName} adapter, network: ${hre.network.name}, address: ${adapter.address}`) - updateDeploymentFile(hre.network.name, { - tzBTCTokenProxy: tzBTCToken.address, - tzBTCTokenImplem: tzBTCTokenLogic, - tzBTCAdapter: adapter.address - }); + updateDeploymentFile(hre.network.name, { + tzBTCTokenProxy: tzBTCToken.address, + tzBTCTokenImplem: tzBTCTokenLogic, + tzBTCAdapter: adapter.address, + }) - await verifyContract(tzBTCToken.address, []); - await verifyContract(tzBTCTokenLogic, []); - await verifyContract(adapter.address, [tzBTCToken.address, endpointV2Deployment.address, deployer]); + await verifyContract(tzBTCToken.address, []) + await verifyContract(tzBTCTokenLogic, []) + await verifyContract(adapter.address, [tzBTCToken.address, endpointV2Deployment.address, deployer]) } deploy.tags = [contractName] diff --git a/deployments/tezosXPreviewnet.json b/deployments/tezosXPreviewnet.json new file mode 100644 index 0000000..ca34c51 --- /dev/null +++ b/deployments/tezosXPreviewnet.json @@ -0,0 +1,3 @@ +{ + "WXTZ": "0xf4a2e3BA5C14f11c23c8c4351093C058D20542f2" +} \ No newline at end of file diff --git a/deployments/tezosXPreviewnet/.chainId b/deployments/tezosXPreviewnet/.chainId new file mode 100644 index 0000000..f7746d4 --- /dev/null +++ b/deployments/tezosXPreviewnet/.chainId @@ -0,0 +1 @@ +128064 \ No newline at end of file diff --git a/deployments/tezosXPreviewnet/EndpointV2.json b/deployments/tezosXPreviewnet/EndpointV2.json new file mode 100644 index 0000000..63d30d6 --- /dev/null +++ b/deployments/tezosXPreviewnet/EndpointV2.json @@ -0,0 +1,117 @@ +{ + "address": "0xF01b3160F2926420C7416ddA62544969063269ED", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oapp", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegate", + "type": "address" + } + ], + "name": "DelegateSet", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "oapp", + "type": "address" + } + ], + "name": "delegates", + "outputs": [ + { + "internalType": "address", + "name": "delegate", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegate", + "type": "address" + } + ], + "name": "setDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0x976e9d5b9974bdd25f8952ab954f5758bbd37546b9494be73b1b5caad89186ec", + "receipt": { + "to": null, + "from": "0x6CB66f82a59C5B4F487FAC590E02e385460169fe", + "contractAddress": "0xF01b3160F2926420C7416ddA62544969063269ED", + "transactionIndex": 0, + "gasUsed": "1929087", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1c93c98950db6dfdd540c0e1f71cb06cfc04b22d7787ac5a7559e6cfb498f54d", + "transactionHash": "0x976e9d5b9974bdd25f8952ab954f5758bbd37546b9494be73b1b5caad89186ec", + "logs": [], + "blockNumber": 98513, + "cumulativeGasUsed": "1929087", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "61fb5add52fdcf3bbc6c52a0aa4e4dab", + "metadata": "{\"compiler\":{\"version\":\"0.8.22+commit.4fc1097e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oapp\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"}],\"name\":\"DelegateSet\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oapp\",\"type\":\"address\"}],\"name\":\"delegates\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_delegate\",\"type\":\"address\"}],\"name\":\"setDelegate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Implements only `setDelegate(address)` \\u2014 the single endpoint method invoked from `OAppCore`'s constructor. Cross-chain send/receive, quoting, and config flows are intentionally absent and will not work. Delete this contract and its deployment when previewnet is decommissioned.\",\"kind\":\"dev\",\"methods\":{},\"title\":\"EndpointV2Stub (tezosXPreviewnet ONLY \\u2014 DO NOT DEPLOY ELSEWHERE)\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"Stand-in for `ILayerZeroEndpointV2` so OFT-based contracts can be deployed on the ephemeral tezosXPreviewnet chain, which has no real LayerZero infrastructure.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/mocks/tezosXPreviewnet/EndpointV2Stub.sol\":\"EndpointV2Stub\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[],\"viaIR\":true},\"sources\":{\"contracts/mocks/tezosXPreviewnet/EndpointV2Stub.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.20;\\n\\n/// @title EndpointV2Stub (tezosXPreviewnet ONLY \\u2014 DO NOT DEPLOY ELSEWHERE)\\n/// @notice Stand-in for `ILayerZeroEndpointV2` so OFT-based contracts can be\\n/// deployed on the ephemeral tezosXPreviewnet chain, which has no real\\n/// LayerZero infrastructure.\\n/// @dev Implements only `setDelegate(address)` \\u2014 the single endpoint method\\n/// invoked from `OAppCore`'s constructor. Cross-chain send/receive,\\n/// quoting, and config flows are intentionally absent and will not\\n/// work. Delete this contract and its deployment when previewnet is\\n/// decommissioned.\\ncontract EndpointV2Stub {\\n event DelegateSet(address indexed oapp, address indexed delegate);\\n\\n mapping(address oapp => address delegate) public delegates;\\n\\n function setDelegate(address _delegate) external {\\n delegates[msg.sender] = _delegate;\\n emit DelegateSet(msg.sender, _delegate);\\n }\\n}\\n\",\"keccak256\":\"0xaaefbd38b2ec446b479021061d7022b4b8a2f8d40fdf9b16b6307eb7866ca7bb\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x6080806040523461001657610114908161001c8239f35b600080fdfe6080806040526004361015601257600080fd5b600090813560e01c908163587cde1e1460a2575063ca5eb5e114603457600080fd5b34609f576020366003190112609f576004356001600160a01b03811690819003609b573380835260208390526040832080546001600160a01b031916831790557f6ee10e9ed4d6ce9742703a498707862f4b00f1396a87195eb93267b3d79839818380a380f35b5080fd5b80fd5b905034609b576020366003190112609b576001600160a01b036004358181169081900360da5783604091602095528085522054168152f35b8380fdfea26469706673582212209dc9b2c6d4b9f0c089a5a25a80be3c9fada5c77039a3d0ec88486a876da07bde64736f6c63430008160033", + "deployedBytecode": "0x6080806040526004361015601257600080fd5b600090813560e01c908163587cde1e1460a2575063ca5eb5e114603457600080fd5b34609f576020366003190112609f576004356001600160a01b03811690819003609b573380835260208390526040832080546001600160a01b031916831790557f6ee10e9ed4d6ce9742703a498707862f4b00f1396a87195eb93267b3d79839818380a380f35b5080fd5b80fd5b905034609b576020366003190112609b576001600160a01b036004358181169081900360da5783604091602095528085522054168152f35b8380fdfea26469706673582212209dc9b2c6d4b9f0c089a5a25a80be3c9fada5c77039a3d0ec88486a876da07bde64736f6c63430008160033", + "devdoc": { + "details": "Implements only `setDelegate(address)` — the single endpoint method invoked from `OAppCore`'s constructor. Cross-chain send/receive, quoting, and config flows are intentionally absent and will not work. Delete this contract and its deployment when previewnet is decommissioned.", + "kind": "dev", + "methods": {}, + "title": "EndpointV2Stub (tezosXPreviewnet ONLY — DO NOT DEPLOY ELSEWHERE)", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "notice": "Stand-in for `ILayerZeroEndpointV2` so OFT-based contracts can be deployed on the ephemeral tezosXPreviewnet chain, which has no real LayerZero infrastructure.", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 12, + "contract": "contracts/mocks/tezosXPreviewnet/EndpointV2Stub.sol:EndpointV2Stub", + "label": "delegates", + "offset": 0, + "slot": "0", + "type": "t_mapping(t_address,t_address)" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + } + } + } +} \ No newline at end of file diff --git a/deployments/tezosXPreviewnet/solcInputs/61fb5add52fdcf3bbc6c52a0aa4e4dab.json b/deployments/tezosXPreviewnet/solcInputs/61fb5add52fdcf3bbc6c52a0aa4e4dab.json new file mode 100644 index 0000000..4fc5ba8 --- /dev/null +++ b/deployments/tezosXPreviewnet/solcInputs/61fb5add52fdcf3bbc6c52a0aa4e4dab.json @@ -0,0 +1,37 @@ +{ + "language": "Solidity", + "sources": { + "contracts/mocks/tezosXPreviewnet/EndpointV2Stub.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n/// @title EndpointV2Stub (tezosXPreviewnet ONLY — DO NOT DEPLOY ELSEWHERE)\n/// @notice Stand-in for `ILayerZeroEndpointV2` so OFT-based contracts can be\n/// deployed on the ephemeral tezosXPreviewnet chain, which has no real\n/// LayerZero infrastructure.\n/// @dev Implements only `setDelegate(address)` — the single endpoint method\n/// invoked from `OAppCore`'s constructor. Cross-chain send/receive,\n/// quoting, and config flows are intentionally absent and will not\n/// work. Delete this contract and its deployment when previewnet is\n/// decommissioned.\ncontract EndpointV2Stub {\n event DelegateSet(address indexed oapp, address indexed delegate);\n\n mapping(address oapp => address delegate) public delegates;\n\n function setDelegate(address _delegate) external {\n delegates[msg.sender] = _delegate;\n emit DelegateSet(msg.sender, _delegate);\n }\n}\n" + } + }, + "settings": { + "viaIR": true, + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/docs/WXTZ.md b/docs/WXTZ.md index 734b38d..51dbd48 100644 --- a/docs/WXTZ.md +++ b/docs/WXTZ.md @@ -19,14 +19,14 @@ You can find a complete overview of the configuration in the `WXTZ_fullConfig.lo ### Gasless Approval with ERC20Permit -The token also implements the [ERC20Permit](https://docs.openzeppelin.com/contracts/5.x/api/token/erc20#ERC20Permit) standard from Openzeppelin. This allows users to approve token transfers via gasless signatures instead of on-chain transactions. +The token also implements the [ERC20Permit](https://docs.openzeppelin.com/contracts/5.x/api/token/erc20#ERC20Permit) standard from Openzeppelin. This allows users to approve token transfers via gasless signatures instead of on-chain transactions. This feature leverages the [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) standard to facilitate off-chain authorization for token transfers. - ## Deployment & setup The first step is to deploy the contract on all the chains you want to support. To do so, run the LayerZero deployment tool, and use `WXTZ` as the deployment tag: + ``` npx hardhat lz:deploy ``` @@ -34,12 +34,13 @@ npx hardhat lz:deploy ### Peer Then, you need to create a link between the contracts by running `setPeer()`. We created a script to make this easier: + ``` targetNetworkName= npx hardhat run --network scripts/setPeer.ts ``` > 🚨🚨 **NOTE** 🚨🚨 -> +> > Each `setPeer()` registers a one way connection between the contract on chain A and the contract on chain B. To create a link, you'll need to run the script twice by swapping the source and target networks. > > For security purposes, there is a 2 day timelock when you call `setPeer()`. Therefore, for each one-way connection, you'll need to run the script twice with a 2 day delay between calls. @@ -47,12 +48,13 @@ targetNetworkName= npx hardhat run --network sc ### Config (optionnal) You can also setup the config of the token. The WXTZ config was setup by the Etherlink team manually because the default config on the pathways including Etherlink is not setup yet, the script used is for the moment private. If you want to apply the default config (if this default config has been setup by LayerZero on the pathway), you can use the `applyDefaultConfig.ts` script. You would need to modify it so that the script uses your OFT. Note that this script is doing the 2 sides of the pathway (e.g. Etherlink <> Ethereum AND Ethereum <> Etherlink). If you want to run it: + ``` targetNetworkName= npx hardhat run --network scripts/applyDefaultConfig.ts ``` > 🚨🚨 **NOTE** 🚨🚨 -> +> > For Etherlink, the block confirmation number is set to 1 because we know that the DVNs used in our config are running **a high latency node**. Be careful though; if you set up a number too low for the chain and the DVNs are using classic nodes, it could lead to money lost. ### Options @@ -60,17 +62,19 @@ targetNetworkName= npx hardhat run --network sc Another important point is the "option" part. You have to specify the amount of gas you will use on the destination chain. We enforced that option on all the pathways for the WXTZ so you don't have to worry about. Here is the [LayerZero official documentation](https://docs.layerzero.network/v2/developers/evm/protocol-gas-settings/options) about this subject. You can enforce the option by running: + ``` targetNetworkName= npx hardhat run --network scripts/setEnforcedOption.ts ``` > 🚨🚨 **NOTE** 🚨🚨 -> +> > The gas calculation is different on Etherlink so you will have to rise the gas amount for the option compare to the other EVM chains. ### Debug If you want to check all the information about your OFT, you can use the `getFullConfig.ts` script. First you need to modify the addresses, the chains and the providers in the script. Then you can run it: + ``` npx hardhat run scripts/getFullConfig.ts >> WXTZ_fullConfig_after.log ``` @@ -104,6 +108,7 @@ targetNetworkName= npx hardhat run --network sc ## Tests If you want to run the tests for the WXTZ: + ``` npx hardhat test test/WXTZ.test.ts ``` @@ -111,6 +116,7 @@ npx hardhat test test/WXTZ.test.ts **NB:** we do not test the OFT part here except for some basic errors because we can't easily reproduce a multi-chain setup with on one side Etherlink and on the other side a non-etherlink chain in hardhat tests. But if you need to do a real test to be sure the contracts are well connected on the different chains, you can run a script to send a token like this: + ``` targetNetworkName= npx hardhat run --network etherlink scripts/sendToken.ts ``` @@ -146,7 +152,8 @@ Whether the WXTZs are bridged or not, this token, like all the other tokens on E ## Helpers We added some scripts at the root of the project (for the moment all related to the WXTZ to facilitate his deployment and setup): + - `mapWXTZ.sh` - use to generate a file with all the details of the pathways - `fullMesh.sh` - use to setPeer the different WXTZ on all the pathways - `setEnforcedOption.sh` - use to enforce the option on the WXTZ on all the pathways -- `applyDefaultConfig.sh` - use to apply the default config on the WXTZ, apply the send and receive libraries and the default config on them (Security stack and Executor) \ No newline at end of file +- `applyDefaultConfig.sh` - use to apply the default config on the WXTZ, apply the send and receive libraries and the default config on them (Security stack and Executor) diff --git a/hardhat.config.ts b/hardhat.config.ts index 28c458f..7ac7805 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -9,9 +9,9 @@ import 'hardhat-deploy' import 'hardhat-contract-sizer' import '@nomiclabs/hardhat-ethers' import '@layerzerolabs/toolbox-hardhat' -import "@nomicfoundation/hardhat-verify"; -import "@nomicfoundation/hardhat-chai-matchers"; -import "@openzeppelin/hardhat-upgrades"; +import '@nomicfoundation/hardhat-verify' +import '@nomicfoundation/hardhat-chai-matchers' +import '@openzeppelin/hardhat-upgrades' import { HardhatUserConfig, HttpNetworkAccountsUserConfig } from 'hardhat/types' import { EndpointId } from '@layerzerolabs/lz-definitions' @@ -26,163 +26,177 @@ const MNEMONIC = process.env.MNEMONIC const PRIVATE_KEY = process.env.PRIVATE_KEY const accounts: HttpNetworkAccountsUserConfig | undefined = MNEMONIC - ? { mnemonic: MNEMONIC } - : PRIVATE_KEY - ? [PRIVATE_KEY] - : undefined + ? { mnemonic: MNEMONIC } + : PRIVATE_KEY + ? [PRIVATE_KEY] + : undefined if (accounts == null) { - console.warn( - 'Could not find MNEMONIC or PRIVATE_KEY environment variables. It will not be possible to execute transactions in your example.' - ) + console.warn( + 'Could not find MNEMONIC or PRIVATE_KEY environment variables. It will not be possible to execute transactions in your example.' + ) } const config: HardhatUserConfig = { - solidity: { - compilers: [ - { - version: '0.8.22', - settings: { - viaIR: true, - optimizer: { - enabled: true, - runs: 200, - }, + solidity: { + compilers: [ + { + version: '0.8.22', + settings: { + viaIR: true, + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: '0.8.9', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: '0.4.18', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + ], + }, + networks: { + hardhat: { + gasPrice: 0, + initialBaseFeePerGas: 0, + chainId: 128123, // override the chain id because some contract have restricted functionalities on other chain }, - }, - { - version: '0.8.9', - settings: { - optimizer: { - enabled: true, - runs: 200, - }, + sepolia: { + eid: EndpointId.SEPOLIA_V2_TESTNET, + url: process.env.SEPOLIA_RPC_URL || 'https://rpc.sepolia.org/', + chainId: 11155111, + accounts, }, - }, - { - version: '0.4.18', - settings: { - optimizer: { - enabled: true, - runs: 200, - }, + etherlinkTestnet: { + eid: EndpointId.ETHERLINK_V2_TESTNET, + url: process.env.ETHERLINK_TESTNET_RPC_URL || 'https://node.ghostnet.etherlink.com', + chainId: 128123, + accounts, + }, + etherlinkShadownetTestnet: { + eid: EndpointId.ETHERLINKSHADOWNET_V2_TESTNET, + url: process.env.ETHERLINK_SHADOWNET_TESTNET_RPC_URL || 'https://node.shadownet.etherlink.com', + chainId: 127823, + accounts, + }, + tezosXPreviewnet: { + url: process.env.TEZOS_X_PREVIEWNET_RPC_URL || 'https://evm.previewnet.tezosx.nomadic-labs.com', + chainId: 128064, + accounts, + }, + bscTestnet: { + eid: EndpointId.BSC_V2_TESTNET, + url: process.env.BSC_TESTNET_RPC_URL || 'https://bsc-testnet.publicnode.com', + chainId: 97, + accounts, + }, + etherlink: { + eid: EndpointId.ETHERLINK_V2_MAINNET, + url: process.env.ETHERLINK_RPC_URL || 'https://node.mainnet.etherlink.com', + chainId: 42793, + accounts, + }, + mainnet: { + eid: EndpointId.ETHEREUM_V2_MAINNET, + url: process.env.ETHEREUM_RPC_URL || 'https://ethereum-rpc.publicnode.com', + chainId: 1, + accounts, + }, + arbitrumOne: { + eid: EndpointId.ARBITRUM_V2_MAINNET, + url: process.env.ARBITRUM_ONE_RPC_URL || 'https://arbitrum-one-rpc.publicnode.com', + chainId: 42161, + accounts, + }, + base: { + eid: EndpointId.BASE_V2_MAINNET, + url: process.env.BASE_RPC_URL || 'https://base-rpc.publicnode.com', + chainId: 8453, + accounts, + }, + bsc: { + eid: EndpointId.BSC_V2_MAINNET, + url: process.env.BSC_RPC_URL || 'https://bsc-rpc.publicnode.com', + chainId: 56, + accounts, }, - }, - ], - }, - networks: { - hardhat: { - gasPrice: 0, - initialBaseFeePerGas: 0, - chainId: 128123 // override the chain id because some contract have restricted functionalities on other chain - }, - sepolia: { - eid: EndpointId.SEPOLIA_V2_TESTNET, - url: process.env.SEPOLIA_RPC_URL || 'https://rpc.sepolia.org/', - chainId: 11155111, - accounts, - }, - etherlinkTestnet: { - eid: EndpointId.ETHERLINK_V2_TESTNET, - url: process.env.ETHERLINK_TESTNET_RPC_URL || "https://node.ghostnet.etherlink.com", - chainId: 128123, - accounts, - }, - etherlinkShadownetTestnet: { - eid: EndpointId.ETHERLINKSHADOWNET_V2_TESTNET, - url: process.env.ETHERLINK_SHADOWNET_TESTNET_RPC_URL || "https://node.shadownet.etherlink.com", - chainId: 127823, - accounts, - }, - bscTestnet: { - eid: EndpointId.BSC_V2_TESTNET, - url: process.env.BSC_TESTNET_RPC_URL || "https://bsc-testnet.publicnode.com", - chainId: 97, - accounts, - }, - etherlink: { - eid: EndpointId.ETHERLINK_V2_MAINNET, - url: process.env.ETHERLINK_RPC_URL || "https://node.mainnet.etherlink.com", - chainId: 42793, - accounts, - }, - mainnet: { - eid: EndpointId.ETHEREUM_V2_MAINNET, - url: process.env.ETHEREUM_RPC_URL || "https://ethereum-rpc.publicnode.com", - chainId: 1, - accounts, - }, - arbitrumOne: { - eid: EndpointId.ARBITRUM_V2_MAINNET, - url: process.env.ARBITRUM_ONE_RPC_URL || "https://arbitrum-one-rpc.publicnode.com", - chainId: 42161, - accounts, - }, - base: { - eid: EndpointId.BASE_V2_MAINNET, - url: process.env.BASE_RPC_URL || "https://base-rpc.publicnode.com", - chainId: 8453, - accounts, }, - bsc: { - eid: EndpointId.BSC_V2_MAINNET, - url: process.env.BSC_RPC_URL || "https://bsc-rpc.publicnode.com", - chainId: 56, - accounts, - } - }, - namedAccounts: { - deployer: { - default: 0, // wallet address of index[0], of the mnemonic in .env + namedAccounts: { + deployer: { + default: 0, // wallet address of index[0], of the mnemonic in .env + }, }, - }, - etherscan: { - apiKey: { - etherlinkTestnet: "YOU_CAN_COPY_ME", - etherlinkShadownetTestnet: "YOU_CAN_COPY_ME", - bscTestnet: process.env.BSCSCAN_API_KEY || "", - sepolia: process.env.ETHERSCAN_API_KEY || "", - etherlink: "YOU_CAN_COPY_ME", - mainnet: process.env.ETHERSCAN_API_KEY || "", - arbitrumOne: process.env.ARBISCAN_API_KEY || "", - base: process.env.BASESCAN_API_KEY || "", - bsc: process.env.BSCSCAN_API_KEY || "", + etherscan: { + apiKey: { + etherlinkTestnet: 'YOU_CAN_COPY_ME', + etherlinkShadownetTestnet: 'YOU_CAN_COPY_ME', + tezosXPreviewnet: 'YOU_CAN_COPY_ME', + bscTestnet: process.env.BSCSCAN_API_KEY || '', + sepolia: process.env.ETHERSCAN_API_KEY || '', + etherlink: 'YOU_CAN_COPY_ME', + mainnet: process.env.ETHERSCAN_API_KEY || '', + arbitrumOne: process.env.ARBISCAN_API_KEY || '', + base: process.env.BASESCAN_API_KEY || '', + bsc: process.env.BSCSCAN_API_KEY || '', + }, + customChains: [ + { + network: 'etherlinkTestnet', + chainId: 128123, + urls: { + apiURL: 'https://testnet-explorer.etherlink.com/api', + browserURL: 'https://testnet-explorer.etherlink.com', + }, + }, + { + network: 'etherlinkShadownetTestnet', + chainId: 127823, + urls: { + apiURL: 'https://shadownet.explorer.etherlink.com/api', + browserURL: 'https://shadownet.explorer.etherlink.com', + }, + }, + { + network: 'tezosXPreviewnet', + chainId: 128064, + urls: { + apiURL: 'https://blockscout.previewnet.tezosx.nomadic-labs.com/api', + browserURL: 'https://blockscout.previewnet.tezosx.nomadic-labs.com', + }, + }, + { + network: 'etherlink', + chainId: 42793, + urls: { + apiURL: 'https://explorer.etherlink.com/api', + browserURL: 'https://explorer.etherlink.com', + }, + }, + { + network: 'base', + chainId: 8453, + urls: { + apiURL: 'https://api.basescan.org/api', + browserURL: 'https://basescan.org/', + }, + }, + ], }, - customChains: [ - { - network: "etherlinkTestnet", - chainId: 128123, - urls: { - apiURL: "https://testnet-explorer.etherlink.com/api", - browserURL: "https://testnet-explorer.etherlink.com" - } - }, - { - network: "etherlinkShadownetTestnet", - chainId: 127823, - urls: { - apiURL: "https://shadownet.explorer.etherlink.com/api", - browserURL: "https://shadownet.explorer.etherlink.com" - } - }, - { - network: "etherlink", - chainId: 42793, - urls: { - apiURL: "https://explorer.etherlink.com/api", - browserURL: "https://explorer.etherlink.com" - } - }, - { - network: "base", - chainId: 8453, - urls: { - apiURL: "https://api.basescan.org/api", - browserURL: "https://basescan.org/" - } - } - ] - } } export default config diff --git a/layerzero.config.ts b/layerzero.config.ts index 4ffd125..29eb26d 100644 --- a/layerzero.config.ts +++ b/layerzero.config.ts @@ -1,52 +1,51 @@ import { EndpointId } from '@layerzerolabs/lz-definitions' - import type { OAppOmniGraphHardhat, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat' const etherlinkTestnetContract: OmniPointHardhat = { - eid: EndpointId.ETHERLINK_V2_TESTNET, - contractName: "WXTZ" + eid: EndpointId.ETHERLINK_V2_TESTNET, + contractName: 'WXTZ', } const sepoliaContract: OmniPointHardhat = { - eid: EndpointId.SEPOLIA_V2_TESTNET, - contractName: 'WXTZ', + eid: EndpointId.SEPOLIA_V2_TESTNET, + contractName: 'WXTZ', } const bscTestnetContract: OmniPointHardhat = { - eid: EndpointId.BSC_V2_TESTNET, - contractName: 'WXTZ', + eid: EndpointId.BSC_V2_TESTNET, + contractName: 'WXTZ', } const config: OAppOmniGraphHardhat = { - contracts: [ - { - contract: etherlinkTestnetContract, - }, - { - contract: sepoliaContract, - }, - { - contract: bscTestnetContract, - }, - ], - connections: [ - { - from: etherlinkTestnetContract, - to: sepoliaContract, - }, - { - from: sepoliaContract, - to: etherlinkTestnetContract, - }, - { - from: etherlinkTestnetContract, - to: bscTestnetContract, - }, - { - from: bscTestnetContract, - to: etherlinkTestnetContract, - }, - ], + contracts: [ + { + contract: etherlinkTestnetContract, + }, + { + contract: sepoliaContract, + }, + { + contract: bscTestnetContract, + }, + ], + connections: [ + { + from: etherlinkTestnetContract, + to: sepoliaContract, + }, + { + from: sepoliaContract, + to: etherlinkTestnetContract, + }, + { + from: etherlinkTestnetContract, + to: bscTestnetContract, + }, + { + from: bscTestnetContract, + to: etherlinkTestnetContract, + }, + ], } export default config diff --git a/scripts/applyDefaultConfig.ts b/scripts/applyDefaultConfig.ts index cce41f1..35cdc93 100644 --- a/scripts/applyDefaultConfig.ts +++ b/scripts/applyDefaultConfig.ts @@ -1,72 +1,74 @@ -import { providers, Contract, utils, Wallet, ethers } from 'ethers'; import dotenv from 'dotenv' -import { network } from 'hardhat'; -import LzEndpointV2ABI from '../abi/endpointV2.json'; -import etherlinkTokens from '../deployments/etherlink.json'; -import mainnetTokens from '../deployments/mainnet.json'; -import arbitrumOneTokens from '../deployments/arbitrumOne.json'; -import baseTokens from '../deployments/base.json'; -import bscTokens from '../deployments/bsc.json'; +import { Wallet, ethers, providers } from 'ethers' +import { network } from 'hardhat' + +import LzEndpointV2ABI from '../abi/endpointV2.json' +import arbitrumOneTokens from '../deployments/arbitrumOne.json' +import baseTokens from '../deployments/base.json' +import bscTokens from '../deployments/bsc.json' +import etherlinkTokens from '../deployments/etherlink.json' +import mainnetTokens from '../deployments/mainnet.json' dotenv.config() // Supported networks -const SUPPORTED_NETWORKS = ['mainnet', 'arbitrumOne', 'bsc', 'base']; +const SUPPORTED_NETWORKS = ['mainnet', 'arbitrumOne', 'bsc', 'base'] // Define the addresses and parameters const addresses: { [key: string]: { oappAddress: string } } = { - mainnet: { - oappAddress: mainnetTokens.WXTZ, - }, - arbitrumOne: { - oappAddress: arbitrumOneTokens.WXTZ, - }, - bsc: { - oappAddress: bscTokens.WXTZ, - }, - base: { - oappAddress: baseTokens.WXTZ, - }, - etherlink: { - oappAddress: etherlinkTokens.WXTZ, - } + mainnet: { + oappAddress: mainnetTokens.WXTZ, + }, + arbitrumOne: { + oappAddress: arbitrumOneTokens.WXTZ, + }, + bsc: { + oappAddress: bscTokens.WXTZ, + }, + base: { + oappAddress: baseTokens.WXTZ, + }, + etherlink: { + oappAddress: etherlinkTokens.WXTZ, + }, } // Providers for each network const providerList: { [key: string]: providers.JsonRpcProvider } = { - mainnet: new providers.JsonRpcProvider('https://eth-pokt.nodies.app'), - arbitrumOne: new providers.JsonRpcProvider('https://arbitrum.drpc.org'), - bsc: new providers.JsonRpcProvider('https://bsc.drpc.org'), - base: new providers.JsonRpcProvider('https://mainnet.base.org'), - etherlink: new providers.JsonRpcProvider('https://node.mainnet.etherlink.com'), -}; + mainnet: new providers.JsonRpcProvider('https://eth-pokt.nodies.app'), + arbitrumOne: new providers.JsonRpcProvider('https://arbitrum.drpc.org'), + bsc: new providers.JsonRpcProvider('https://bsc.drpc.org'), + base: new providers.JsonRpcProvider('https://mainnet.base.org'), + etherlink: new providers.JsonRpcProvider('https://node.mainnet.etherlink.com'), +} // LayerZero endpoints ids const endpointIds: { [key: string]: number } = { - mainnet: 30101, - arbitrumOne: 30110, - bsc: 30102, - base: 30184, - etherlink: 30292, + mainnet: 30101, + arbitrumOne: 30110, + bsc: 30102, + base: 30184, + etherlink: 30292, } -const METADATA_URL = 'https://metadata.layerzero-api.com/v1/metadata'; +const METADATA_URL = 'https://metadata.layerzero-api.com/v1/metadata' function getEndpointIdDeployment(eid: number, metadata: any) { - const srcEidString = eid.toString(); - for (const objectKey in metadata) { - const entry = metadata[objectKey]; - - if (typeof entry.deployments !== 'undefined') { - for (const deployment of entry.deployments) { - if (srcEidString === deployment.eid) { - return deployment; + const srcEidString = eid.toString() + for (const objectKey in metadata) { + const entry = metadata[objectKey] + + if (typeof entry.deployments !== 'undefined') { + for (const deployment of entry.deployments) { + if (srcEidString === deployment.eid) { + return deployment + } + } } - } } - } - throw new Error(`Can't find endpoint with eid: "${eid}",`); -}`` + throw new Error(`Can't find endpoint with eid: "${eid}",`) +} +;`` /** * This function create the transactions to set the default lib config. Source chain represente the source in the pathway @@ -76,82 +78,91 @@ function getEndpointIdDeployment(eid: number, metadata: any) { * @returns A promise of the data of the transactions to be executed */ async function setLibraryConfig(srcChain: string, dstChain: string) { - // Get metadata from url - const response = await fetch(METADATA_URL); - const metadata = await response.json(); - // Get endpoint for src id - const lzDeployment = getEndpointIdDeployment(endpointIds[srcChain], metadata); - - // Get address and libraries from endpoint - const endpointAddress = lzDeployment.endpointV2.address; - - // Create contract for endpoint - const provider = providerList[srcChain]; - const endpointContract = new ethers.Contract(endpointAddress, LzEndpointV2ABI, provider); - - // Try to retrieve the lib from endpoint contract, undefined take the one in the metadata file - const sendLibraryAddress = (await endpointContract.getSendLibrary(addresses[srcChain].oappAddress, endpointIds[dstChain])).lib || lzDeployment.sendUln302.address; - const receiveLibraryAddress = (await endpointContract.getReceiveLibrary(addresses[srcChain].oappAddress, endpointIds[dstChain])).lib || lzDeployment.receiveUln302.address; - - // Send - - // Retrieve default send Uln config - const sendUlnConfig = await endpointContract.getConfig( - addresses[srcChain].oappAddress, - sendLibraryAddress, - endpointIds[dstChain], - 2, - ); - - const setSendConfigParamUln = { - eid: endpointIds[dstChain], - configType: 2, // CONFIG_TYPE_ULN - config: sendUlnConfig// ulnSendConfigEncoded, - }; - - // Receive - - // Retrieve default receive Uln config - const receiveUlnConfig = await endpointContract.getConfig( - addresses[srcChain].oappAddress, - receiveLibraryAddress, - endpointIds[dstChain], - 2, - ); - - const setReceiveConfigParamUln = { - eid: endpointIds[dstChain], - configType: 2, // CONFIG_TYPE_ULN - config: receiveUlnConfig// ulnReceiveConfigEncoded, - }; - - // Executor (on send) - - // Retrieve default executor config - const configExecutor = await endpointContract.getConfig( - addresses[srcChain].oappAddress, - sendLibraryAddress, - endpointIds[dstChain], - 1, - ); - - const setConfigParamExecutor = { - eid: endpointIds[dstChain], - configType: 1, // CONFIG_TYPE_EXECUTOR - config: configExecutor//executorConfigEncoded, - }; - - console.log("Executor config:", setConfigParamExecutor); - - console.log("Send uln config:", setSendConfigParamUln); - - console.log("Receive uln config:", setReceiveConfigParamUln); - - // Return transaction for endpoint contract - return [ - endpointContract.populateTransaction.setConfig(addresses[srcChain].oappAddress, sendLibraryAddress, [setSendConfigParamUln, setConfigParamExecutor]), - endpointContract.populateTransaction.setConfig(addresses[srcChain].oappAddress, receiveLibraryAddress, [setReceiveConfigParamUln]), - ]; + // Get metadata from url + const response = await fetch(METADATA_URL) + const metadata = await response.json() + // Get endpoint for src id + const lzDeployment = getEndpointIdDeployment(endpointIds[srcChain], metadata) + + // Get address and libraries from endpoint + const endpointAddress = lzDeployment.endpointV2.address + + // Create contract for endpoint + const provider = providerList[srcChain] + const endpointContract = new ethers.Contract(endpointAddress, LzEndpointV2ABI, provider) + + // Try to retrieve the lib from endpoint contract, undefined take the one in the metadata file + const sendLibraryAddress = + (await endpointContract.getSendLibrary(addresses[srcChain].oappAddress, endpointIds[dstChain])).lib || + lzDeployment.sendUln302.address + const receiveLibraryAddress = + (await endpointContract.getReceiveLibrary(addresses[srcChain].oappAddress, endpointIds[dstChain])).lib || + lzDeployment.receiveUln302.address + + // Send + + // Retrieve default send Uln config + const sendUlnConfig = await endpointContract.getConfig( + addresses[srcChain].oappAddress, + sendLibraryAddress, + endpointIds[dstChain], + 2 + ) + + const setSendConfigParamUln = { + eid: endpointIds[dstChain], + configType: 2, // CONFIG_TYPE_ULN + config: sendUlnConfig, // ulnSendConfigEncoded, + } + + // Receive + + // Retrieve default receive Uln config + const receiveUlnConfig = await endpointContract.getConfig( + addresses[srcChain].oappAddress, + receiveLibraryAddress, + endpointIds[dstChain], + 2 + ) + + const setReceiveConfigParamUln = { + eid: endpointIds[dstChain], + configType: 2, // CONFIG_TYPE_ULN + config: receiveUlnConfig, // ulnReceiveConfigEncoded, + } + + // Executor (on send) + + // Retrieve default executor config + const configExecutor = await endpointContract.getConfig( + addresses[srcChain].oappAddress, + sendLibraryAddress, + endpointIds[dstChain], + 1 + ) + + const setConfigParamExecutor = { + eid: endpointIds[dstChain], + configType: 1, // CONFIG_TYPE_EXECUTOR + config: configExecutor, //executorConfigEncoded, + } + + console.log('Executor config:', setConfigParamExecutor) + + console.log('Send uln config:', setSendConfigParamUln) + + console.log('Receive uln config:', setReceiveConfigParamUln) + + // Return transaction for endpoint contract + return [ + endpointContract.populateTransaction.setConfig(addresses[srcChain].oappAddress, sendLibraryAddress, [ + setSendConfigParamUln, + setConfigParamExecutor, + ]), + endpointContract.populateTransaction.setConfig(addresses[srcChain].oappAddress, receiveLibraryAddress, [ + setReceiveConfigParamUln, + ]), + ] } /** @@ -163,129 +174,143 @@ async function setLibraryConfig(srcChain: string, dstChain: string) { */ async function setLibrary(srcChain: string, dstChain: string) { // Get metadata from url - const response = await fetch(METADATA_URL); - const metadata = await response.json(); + const response = await fetch(METADATA_URL) + const metadata = await response.json() // Get endpoint for src id - const lzDeployment = getEndpointIdDeployment(endpointIds[srcChain], metadata); - + const lzDeployment = getEndpointIdDeployment(endpointIds[srcChain], metadata) + // Get address and libraries from endpoint - const endpointAddress = lzDeployment.endpointV2.address; - + const endpointAddress = lzDeployment.endpointV2.address + // Create contract for endpoint - const provider = providerList[srcChain]; - const endpointContract = new ethers.Contract(endpointAddress, LzEndpointV2ABI, provider); - + const provider = providerList[srcChain] + const endpointContract = new ethers.Contract(endpointAddress, LzEndpointV2ABI, provider) + // Try to retrieve the lib from endpoint contract, undefined take the one in the metadata file - const sendLibraryAddress = await endpointContract.defaultSendLibrary(endpointIds[dstChain]) || lzDeployment.sendUln302.address; - const receiveLibraryAddress = await endpointContract.defaultReceiveLibrary(endpointIds[dstChain]) || lzDeployment.receiveUln302.address; + const sendLibraryAddress = + (await endpointContract.defaultSendLibrary(endpointIds[dstChain])) || lzDeployment.sendUln302.address + const receiveLibraryAddress = + (await endpointContract.defaultReceiveLibrary(endpointIds[dstChain])) || lzDeployment.receiveUln302.address - console.log("Default sendLibraryAddress:", sendLibraryAddress); - console.log("Default receiveLibraryAddress:", receiveLibraryAddress); + console.log('Default sendLibraryAddress:', sendLibraryAddress) + console.log('Default receiveLibraryAddress:', receiveLibraryAddress) return [ - endpointContract.populateTransaction.setSendLibrary(addresses[srcChain].oappAddress, endpointIds[dstChain], sendLibraryAddress), - endpointContract.populateTransaction.setReceiveLibrary(addresses[srcChain].oappAddress, endpointIds[dstChain], receiveLibraryAddress, 0) + endpointContract.populateTransaction.setSendLibrary( + addresses[srcChain].oappAddress, + endpointIds[dstChain], + sendLibraryAddress + ), + endpointContract.populateTransaction.setReceiveLibrary( + addresses[srcChain].oappAddress, + endpointIds[dstChain], + receiveLibraryAddress, + 0 + ), ] } async function executeTransactions(transactions: any[], chain: string, privateKey: string) { - const provider = providerList[chain]; - const signer = new Wallet(privateKey, provider); - - for (const tx of transactions) { - try { - console.log('Gas estimation: ', await provider.estimateGas({ ...await tx, from: await signer.getAddress() })); - - // Send the transaction - const transaction = await signer.sendTransaction(await tx); - console.log(`Transaction sent: ${transaction.hash}`); - await transaction.wait(); - console.log(`Transaction confirmed: ${transaction.hash}`); - } catch (error: any) { - const errorData = error?.error?.error?.error?.data || error?.error?.error?.data; - console.log({ errorData }); - if (errorData) { - const res = await fetch(`https://www.4byte.directory/api/v1/signatures/?hex_signature=${errorData}`, { - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - }); - - if (res.status === 200) { - const json: any = await res.json(); - const errorMessage = json.results?.[0]?.text_signature; - - if (errorMessage) { - throw new Error(errorMessage); - } + const provider = providerList[chain] + const signer = new Wallet(privateKey, provider) + + for (const tx of transactions) { + try { + console.log( + 'Gas estimation: ', + await provider.estimateGas({ ...(await tx), from: await signer.getAddress() }) + ) + + // Send the transaction + const transaction = await signer.sendTransaction(await tx) + console.log(`Transaction sent: ${transaction.hash}`) + await transaction.wait() + console.log(`Transaction confirmed: ${transaction.hash}`) + } catch (error: any) { + const errorData = error?.error?.error?.error?.data || error?.error?.error?.data + console.log({ errorData }) + if (errorData) { + const res = await fetch(`https://www.4byte.directory/api/v1/signatures/?hex_signature=${errorData}`, { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }) + + if (res.status === 200) { + const json: any = await res.json() + const errorMessage = json.results?.[0]?.text_signature + + if (errorMessage) { + throw new Error(errorMessage) + } + } + } + + throw error } - } - - throw error; } - } } async function executeSourceTransactions(privateKey: string, srcChain: string, dstChain: string) { - console.log(`Setting from ${srcChain} to ${dstChain}:\n`); - - // Libs - console.log("Enforcing receive and send libraries..."); - let sourceChainTransactions = await setLibrary(srcChain, dstChain); - await executeTransactions(sourceChainTransactions, srcChain, privateKey); - console.log(); - - // Config (Uln and Executor) - console.log("Enforcing configuration on the send and receive libraries..."); - sourceChainTransactions = await setLibraryConfig(srcChain, dstChain); - await executeTransactions(sourceChainTransactions, srcChain, privateKey); - console.log(); + console.log(`Setting from ${srcChain} to ${dstChain}:\n`) + + // Libs + console.log('Enforcing receive and send libraries...') + let sourceChainTransactions = await setLibrary(srcChain, dstChain) + await executeTransactions(sourceChainTransactions, srcChain, privateKey) + console.log() + + // Config (Uln and Executor) + console.log('Enforcing configuration on the send and receive libraries...') + sourceChainTransactions = await setLibraryConfig(srcChain, dstChain) + await executeTransactions(sourceChainTransactions, srcChain, privateKey) + console.log() } async function executeDestinationTransactions(privateKey: string, srcChain: string, dstChain: string) { - console.log(`Setting from ${dstChain} to ${srcChain}:\n`); - - // Libs - console.log("Enforcing receive and send libraries..."); - let destinationChainTransactions = await setLibrary(dstChain, srcChain); - await executeTransactions(destinationChainTransactions, dstChain, privateKey); - console.log(); - - console.log("Enforce configuration on the send and receive libraries..."); - destinationChainTransactions = await setLibraryConfig(dstChain, srcChain); - await executeTransactions(destinationChainTransactions, dstChain, privateKey); - console.log(); + console.log(`Setting from ${dstChain} to ${srcChain}:\n`) + + // Libs + console.log('Enforcing receive and send libraries...') + let destinationChainTransactions = await setLibrary(dstChain, srcChain) + await executeTransactions(destinationChainTransactions, dstChain, privateKey) + console.log() + + console.log('Enforce configuration on the send and receive libraries...') + destinationChainTransactions = await setLibraryConfig(dstChain, srcChain) + await executeTransactions(destinationChainTransactions, dstChain, privateKey) + console.log() } async function main() { - // Get the src & dst chains - const dstChain = process.env.targetNetworkName || ""; - const srcChain = network.name; - if (!SUPPORTED_NETWORKS.includes(dstChain) || !SUPPORTED_NETWORKS.includes(srcChain)) { - console.error("Unsupported chain provided, the chains supported are:", SUPPORTED_NETWORKS.join(', ')); - throw new Error('Unsupported chain'); - } - console.log(`Enforcing default libs, default config for WXTZ on the pathway ${srcChain} to ${dstChain}\n`); - - try { - // Execute source transactions - await executeSourceTransactions(process.env.PRIVATE_KEY as string, srcChain, dstChain); - - // Execute destination transactions - await executeDestinationTransactions(process.env.PRIVATE_KEY as string, srcChain, dstChain); - - console.log('All transactions completed successfully'); - } catch (error) { - console.error('Error occurred:', error); - } + // Get the src & dst chains + const dstChain = process.env.targetNetworkName || '' + const srcChain = network.name + if (!SUPPORTED_NETWORKS.includes(dstChain) || !SUPPORTED_NETWORKS.includes(srcChain)) { + console.error('Unsupported chain provided, the chains supported are:', SUPPORTED_NETWORKS.join(', ')) + throw new Error('Unsupported chain') + } + console.log(`Enforcing default libs, default config for WXTZ on the pathway ${srcChain} to ${dstChain}\n`) + + try { + // Execute source transactions + await executeSourceTransactions(process.env.PRIVATE_KEY as string, srcChain, dstChain) + + // Execute destination transactions + await executeDestinationTransactions(process.env.PRIVATE_KEY as string, srcChain, dstChain) + + console.log('All transactions completed successfully') + } catch (error) { + console.error('Error occurred:', error) + } } // Execute the function main() - .then(() => { - // console.log("Script completed successfully."); - }) - .catch((error) => { - console.error("Error occurred:", error); - }); \ No newline at end of file + .then(() => { + // console.log("Script completed successfully."); + }) + .catch((error) => { + console.error('Error occurred:', error) + }) diff --git a/scripts/getConfig.ts b/scripts/getConfig.ts index 608c589..9dd40f3 100644 --- a/scripts/getConfig.ts +++ b/scripts/getConfig.ts @@ -1,164 +1,155 @@ -import { providers, Contract, utils, Wallet, } from 'ethers'; -import { network, ethers } from 'hardhat'; -import etherlinkTokens from '../deployments/etherlink.json'; -import mainnetTokens from '../deployments/mainnet.json'; -import arbitrumOneTokens from '../deployments/arbitrumOne.json'; -import baseTokens from '../deployments/base.json'; -import bscTokens from '../deployments/bsc.json'; +import { providers } from 'ethers' +import { ethers, network } from 'hardhat' + +import arbitrumOneTokens from '../deployments/arbitrumOne.json' +import baseTokens from '../deployments/base.json' +import bscTokens from '../deployments/bsc.json' +import etherlinkTokens from '../deployments/etherlink.json' +import mainnetTokens from '../deployments/mainnet.json' // Supported networks -const SUPPORTED_NETWORKS = ['mainnet', 'arbitrumOne', 'bsc', 'base', 'etherlink']; +const SUPPORTED_NETWORKS = ['mainnet', 'arbitrumOne', 'bsc', 'base', 'etherlink'] // Contract ABI const LzEndpointABI = [ - 'function getConfig(address _oapp, address _lib, uint32 _eid, uint32 _configType) external view returns (bytes memory config)', -]; + 'function getConfig(address _oapp, address _lib, uint32 _eid, uint32 _configType) external view returns (bytes memory config)', +] // Define the addresses and parameters -const addresses: { [key: string]: { oappAddress: string, sendLibAddress: string, receiveLibAddress: string } } = { - mainnet: { - oappAddress: mainnetTokens.WXTZ, - sendLibAddress: '0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1', - receiveLibAddress: '0xc02Ab410f0734EFa3F14628780e6e695156024C2' - }, - arbitrumOne: { - oappAddress: arbitrumOneTokens.WXTZ, - sendLibAddress: '0x975bcD720be66659e3EB3C0e4F1866a3020E493A', - receiveLibAddress: '0x7B9E184e07a6EE1aC23eAe0fe8D6Be2f663f05e6' - }, - bsc: { - oappAddress: bscTokens.WXTZ, - sendLibAddress: '0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE', - receiveLibAddress: '0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1' - }, - base: { - oappAddress: baseTokens.WXTZ, - sendLibAddress: '0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda2', - receiveLibAddress: '0xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf' - }, - etherlink: { - oappAddress: etherlinkTokens.WXTZ, - sendLibAddress: '0xc1B621b18187F74c8F6D52a6F709Dd2780C09821', - receiveLibAddress: '0x377530cdA84DFb2673bF4d145DCF0C4D7fdcB5b6' - } +const addresses: { [key: string]: { oappAddress: string; sendLibAddress: string; receiveLibAddress: string } } = { + mainnet: { + oappAddress: mainnetTokens.WXTZ, + sendLibAddress: '0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1', + receiveLibAddress: '0xc02Ab410f0734EFa3F14628780e6e695156024C2', + }, + arbitrumOne: { + oappAddress: arbitrumOneTokens.WXTZ, + sendLibAddress: '0x975bcD720be66659e3EB3C0e4F1866a3020E493A', + receiveLibAddress: '0x7B9E184e07a6EE1aC23eAe0fe8D6Be2f663f05e6', + }, + bsc: { + oappAddress: bscTokens.WXTZ, + sendLibAddress: '0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE', + receiveLibAddress: '0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1', + }, + base: { + oappAddress: baseTokens.WXTZ, + sendLibAddress: '0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda2', + receiveLibAddress: '0xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf', + }, + etherlink: { + oappAddress: etherlinkTokens.WXTZ, + sendLibAddress: '0xc1B621b18187F74c8F6D52a6F709Dd2780C09821', + receiveLibAddress: '0x377530cdA84DFb2673bF4d145DCF0C4D7fdcB5b6', + }, } // LayerZero endpoints ids const endpointIds: { [key: string]: number } = { - mainnet: 30101, - arbitrumOne: 30110, - bsc: 30102, - base: 30184, - etherlink: 30292, + mainnet: 30101, + arbitrumOne: 30110, + bsc: 30102, + base: 30184, + etherlink: 30292, } -const executorConfigType = 1; // 1 for executor -const ulnConfigType = 2; // 2 for UlnConfig +const executorConfigType = 1 // 1 for executor +const ulnConfigType = 2 // 2 for UlnConfig -const METADATA_URL = 'https://metadata.layerzero-api.com/v1/metadata'; +const METADATA_URL = 'https://metadata.layerzero-api.com/v1/metadata' function getEndpointIdDeployment(eid: number, metadata: any) { - const srcEidString = eid.toString(); - for (const objectKey in metadata) { - const entry = metadata[objectKey]; + const srcEidString = eid.toString() + for (const objectKey in metadata) { + const entry = metadata[objectKey] - if (typeof entry.deployments !== 'undefined') { - for (const deployment of entry.deployments) { - if (srcEidString === deployment.eid) { - return deployment; + if (typeof entry.deployments !== 'undefined') { + for (const deployment of entry.deployments) { + if (srcEidString === deployment.eid) { + return deployment + } + } } - } } - } - throw new Error(`Can't find endpoint with eid: "${eid}",`); + throw new Error(`Can't find endpoint with eid: "${eid}",`) } async function getConfigAndDecode() { - // Get the src & dst chains - const dstChain = process.env.targetNetworkName || ""; - const srcChain = network.name; - if (!SUPPORTED_NETWORKS.includes(dstChain) || !SUPPORTED_NETWORKS.includes(srcChain)) { - console.error("Unsupported chain provided, the chains supported are:", SUPPORTED_NETWORKS.join(', ')); - throw new Error('Unsupported chain'); - } - console.log(`WXTZ config from ${srcChain} to ${dstChain}\n`); - // Get metadata from url - const response = await fetch(METADATA_URL); - const metadata = await response.json(); - // Get endpoint for src id - const lzDeployment = getEndpointIdDeployment(endpointIds[srcChain], metadata); - // Get address and libraries from endpoint - const endpointAddress = lzDeployment.endpointV2.address; - const provider = new providers.JsonRpcProvider((network.config as any).url) - const contract = new ethers.Contract(endpointAddress, LzEndpointABI, provider); - try { - // Fetch and decode for sendLib (both Executor and ULN Config) - const sendExecutorConfigBytes = await contract.getConfig( - addresses[srcChain].oappAddress, - addresses[srcChain].sendLibAddress, - endpointIds[dstChain], - executorConfigType, - ); - const executorConfigAbi = ['tuple(uint32 maxMessageSize, address executorAddress)']; - const executorConfigArray = ethers.utils.defaultAbiCoder.decode( - executorConfigAbi, - sendExecutorConfigBytes, - ); - console.log('Send Library Executor Config:'); - console.log('maxMessageSize:', executorConfigArray[0][0]); - console.log('executorAddress:', executorConfigArray[0][1], '\n'); - - const sendUlnConfigBytes = await contract.getConfig( - addresses[srcChain].oappAddress, - addresses[srcChain].sendLibAddress, - endpointIds[dstChain], - ulnConfigType, - ); - const ulnConfigStructType = [ - 'tuple(uint64 confirmations, uint8 requiredDVNCount, uint8 optionalDVNCount, uint8 optionalDVNThreshold, address[] requiredDVNs, address[] optionalDVNs)', - ]; - const sendUlnConfigArray = ethers.utils.defaultAbiCoder.decode( - ulnConfigStructType, - sendUlnConfigBytes, - ); - console.log('Send Library ULN Config:'); - console.log(`confirmations: ${parseInt(sendUlnConfigArray[0][0])} (${sendUlnConfigArray[0][0]._hex})`); - console.log('requiredDVNCount:', sendUlnConfigArray[0][1]); - console.log('optionalDVNCount:', sendUlnConfigArray[0][2]); - console.log('optionalDVNThreshold:', sendUlnConfigArray[0][3]); - console.log('requiredDVNs:', sendUlnConfigArray[0][4]); - console.log('optionalDVNs:', sendUlnConfigArray[0][5], '\n'); + // Get the src & dst chains + const dstChain = process.env.targetNetworkName || '' + const srcChain = network.name + if (!SUPPORTED_NETWORKS.includes(dstChain) || !SUPPORTED_NETWORKS.includes(srcChain)) { + console.error('Unsupported chain provided, the chains supported are:', SUPPORTED_NETWORKS.join(', ')) + throw new Error('Unsupported chain') + } + console.log(`WXTZ config from ${srcChain} to ${dstChain}\n`) + // Get metadata from url + const response = await fetch(METADATA_URL) + const metadata = await response.json() + // Get endpoint for src id + const lzDeployment = getEndpointIdDeployment(endpointIds[srcChain], metadata) + // Get address and libraries from endpoint + const endpointAddress = lzDeployment.endpointV2.address + const provider = new providers.JsonRpcProvider((network.config as any).url) + const contract = new ethers.Contract(endpointAddress, LzEndpointABI, provider) + try { + // Fetch and decode for sendLib (both Executor and ULN Config) + const sendExecutorConfigBytes = await contract.getConfig( + addresses[srcChain].oappAddress, + addresses[srcChain].sendLibAddress, + endpointIds[dstChain], + executorConfigType + ) + const executorConfigAbi = ['tuple(uint32 maxMessageSize, address executorAddress)'] + const executorConfigArray = ethers.utils.defaultAbiCoder.decode(executorConfigAbi, sendExecutorConfigBytes) + console.log('Send Library Executor Config:') + console.log('maxMessageSize:', executorConfigArray[0][0]) + console.log('executorAddress:', executorConfigArray[0][1], '\n') - // Fetch and decode for receiveLib (only ULN Config) - const receiveUlnConfigBytes = await contract.getConfig( - addresses[srcChain].oappAddress, - addresses[srcChain].receiveLibAddress, - endpointIds[dstChain], - ulnConfigType, - ); - const receiveUlnConfigArray = ethers.utils.defaultAbiCoder.decode( - ulnConfigStructType, - receiveUlnConfigBytes, - ); - console.log('Receive Library ULN Config:'); - console.log(`confirmations: ${parseInt(receiveUlnConfigArray[0][0])} (${receiveUlnConfigArray[0][0]._hex})`); - console.log('requiredDVNCount:', receiveUlnConfigArray[0][1]); - console.log('optionalDVNCount:', receiveUlnConfigArray[0][2]); - console.log('optionalDVNThreshold:', receiveUlnConfigArray[0][3]); - console.log('requiredDVNs:', receiveUlnConfigArray[0][4]); - console.log('optionalDVNs:', receiveUlnConfigArray[0][5], '\n'); + const sendUlnConfigBytes = await contract.getConfig( + addresses[srcChain].oappAddress, + addresses[srcChain].sendLibAddress, + endpointIds[dstChain], + ulnConfigType + ) + const ulnConfigStructType = [ + 'tuple(uint64 confirmations, uint8 requiredDVNCount, uint8 optionalDVNCount, uint8 optionalDVNThreshold, address[] requiredDVNs, address[] optionalDVNs)', + ] + const sendUlnConfigArray = ethers.utils.defaultAbiCoder.decode(ulnConfigStructType, sendUlnConfigBytes) + console.log('Send Library ULN Config:') + console.log(`confirmations: ${parseInt(sendUlnConfigArray[0][0])} (${sendUlnConfigArray[0][0]._hex})`) + console.log('requiredDVNCount:', sendUlnConfigArray[0][1]) + console.log('optionalDVNCount:', sendUlnConfigArray[0][2]) + console.log('optionalDVNThreshold:', sendUlnConfigArray[0][3]) + console.log('requiredDVNs:', sendUlnConfigArray[0][4]) + console.log('optionalDVNs:', sendUlnConfigArray[0][5], '\n') - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + // Fetch and decode for receiveLib (only ULN Config) + const receiveUlnConfigBytes = await contract.getConfig( + addresses[srcChain].oappAddress, + addresses[srcChain].receiveLibAddress, + endpointIds[dstChain], + ulnConfigType + ) + const receiveUlnConfigArray = ethers.utils.defaultAbiCoder.decode(ulnConfigStructType, receiveUlnConfigBytes) + console.log('Receive Library ULN Config:') + console.log(`confirmations: ${parseInt(receiveUlnConfigArray[0][0])} (${receiveUlnConfigArray[0][0]._hex})`) + console.log('requiredDVNCount:', receiveUlnConfigArray[0][1]) + console.log('optionalDVNCount:', receiveUlnConfigArray[0][2]) + console.log('optionalDVNThreshold:', receiveUlnConfigArray[0][3]) + console.log('requiredDVNs:', receiveUlnConfigArray[0][4]) + console.log('optionalDVNs:', receiveUlnConfigArray[0][5], '\n') + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } // Execute the function getConfigAndDecode() - .then(() => { - // console.log("Script completed successfully."); - }) - .catch((error) => { - console.error("Error occurred:", error); - }); \ No newline at end of file + .then(() => { + // console.log("Script completed successfully."); + }) + .catch((error) => { + console.error('Error occurred:', error) + }) diff --git a/scripts/getEnforcedOptions.ts b/scripts/getEnforcedOptions.ts index 25e801e..dc0733d 100644 --- a/scripts/getEnforcedOptions.ts +++ b/scripts/getEnforcedOptions.ts @@ -1,53 +1,56 @@ -import { ethers, network } from 'hardhat'; -import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json'; -import sepoliaTokens from '../deployments/sepolia.json'; -import bscTestnetTokens from '../deployments/bscTestnet.json'; -import etherlinkTokens from '../deployments/etherlink.json'; -import mainnetTokens from '../deployments/mainnet.json'; -import arbitrumOneTokens from '../deployments/arbitrumOne.json'; -import baseTokens from '../deployments/base.json'; -import bscTokens from '../deployments/bsc.json'; +import { ethers, network } from 'hardhat' + +import arbitrumOneTokens from '../deployments/arbitrumOne.json' +import baseTokens from '../deployments/base.json' +import bscTokens from '../deployments/bsc.json' +import bscTestnetTokens from '../deployments/bscTestnet.json' +import etherlinkTokens from '../deployments/etherlink.json' +import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json' +import mainnetTokens from '../deployments/mainnet.json' +import sepoliaTokens from '../deployments/sepolia.json' const endpointIds: { [key: string]: string } = { - sepolia: '40161', - bscTestnet: '40102', - avalancheFujiTestnet: '40106', - polygonMumbai: '40109', - arbitrumSepolia: '40231', - optimismSepolia: '40232', - etherlinkTestnet: '40239', - etherlink: '30292', - mainnet: '30101', - arbitrumOne: '30110', - base: '30184', - bsc: '30102', - avalanche: '30106', - polygon: '30109', - optimisticEthereum: '30111', -}; + sepolia: '40161', + bscTestnet: '40102', + avalancheFujiTestnet: '40106', + polygonMumbai: '40109', + arbitrumSepolia: '40231', + optimismSepolia: '40232', + etherlinkTestnet: '40239', + etherlink: '30292', + mainnet: '30101', + arbitrumOne: '30110', + base: '30184', + bsc: '30102', + avalanche: '30106', + polygon: '30109', + optimisticEthereum: '30111', +} async function main() { - // current network - const networkName = network.name; - // targeted network - const targetNetworkName = process.env.targetNetworkName || ""; - const WXTZDeployed: { [key: string]: string } = { - etherlinkTestnet: etherlinkTestnetTokens.WXTZ, - sepolia: sepoliaTokens.WXTZ, - bscTestnet: bscTestnetTokens.WXTZ, - etherlink: etherlinkTokens.WXTZ, - mainnet: mainnetTokens.WXTZ, - arbitrumOne: arbitrumOneTokens.WXTZ, - base: baseTokens.WXTZ, - bsc: bscTokens.WXTZ - }; - const WXTZFactory = await ethers.getContractFactory('WXTZ'); - const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]); + // current network + const networkName = network.name + // targeted network + const targetNetworkName = process.env.targetNetworkName || '' + const WXTZDeployed: { [key: string]: string } = { + etherlinkTestnet: etherlinkTestnetTokens.WXTZ, + sepolia: sepoliaTokens.WXTZ, + bscTestnet: bscTestnetTokens.WXTZ, + etherlink: etherlinkTokens.WXTZ, + mainnet: mainnetTokens.WXTZ, + arbitrumOne: arbitrumOneTokens.WXTZ, + base: baseTokens.WXTZ, + bsc: bscTokens.WXTZ, + } + const WXTZFactory = await ethers.getContractFactory('WXTZ') + const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]) - console.log(`Enforced option from ${networkName} to ${targetNetworkName} is set to ${await WXTZ.enforcedOptions(endpointIds[targetNetworkName], 1)}`) + console.log( + `Enforced option from ${networkName} to ${targetNetworkName} is set to ${await WXTZ.enforcedOptions(endpointIds[targetNetworkName], 1)}` + ) } main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); \ No newline at end of file + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/getFullConfig.ts b/scripts/getFullConfig.ts index 3d5bf67..ae79078 100644 --- a/scripts/getFullConfig.ts +++ b/scripts/getFullConfig.ts @@ -1,524 +1,492 @@ -import { providers, ethers } from 'ethers'; +import { ethers, providers } from 'ethers' // Contract ABI const ABILzGetConfig = [ - 'function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config)', -]; + 'function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config)', +] const ABILzIsDefaultSendLibrary = [ - 'function isDefaultSendLibrary(address _sender, uint32 _dstEid) external view returns (bool)', -]; + 'function isDefaultSendLibrary(address _sender, uint32 _dstEid) external view returns (bool)', +] const ABILzGetReceiveLibrary = [ - 'function getReceiveLibrary(address _receiver, uint32 _srcEid) external view returns (address lib, bool isDefault)', -]; + 'function getReceiveLibrary(address _receiver, uint32 _srcEid) external view returns (address lib, bool isDefault)', +] -const ABILzDelegate = [ - 'function delegates(address oapp) external view returns (address delegate)', -]; +const ABILzDelegate = ['function delegates(address oapp) external view returns (address delegate)'] const ABILzGetSendLibrary = [ - 'function getSendLibrary(address _sender, uint32 _dstEid) external view returns (address lib)', -]; + 'function getSendLibrary(address _sender, uint32 _dstEid) external view returns (address lib)', +] -const ABIOappEndPoint = [ - 'function endpoint() view returns (address)', -]; +const ABIOappEndPoint = ['function endpoint() view returns (address)'] -const ABIOappPeers = [ - 'function peers(uint32 eid) view returns (bytes32 peer)', -]; +const ABIOappPeers = ['function peers(uint32 eid) view returns (bytes32 peer)'] const ABIOappEnforcedOptions = [ - 'function enforcedOptions(uint32 eid, uint16 msgType) view returns (bytes enforcedOption)', -]; + 'function enforcedOptions(uint32 eid, uint16 msgType) view returns (bytes enforcedOption)', +] const ABILibGetAppUlnConfig = [ - { - "inputs": [ - { - "name": "_oapp", - "type": "address" - }, - { - "name": "_remoteEid", - "type": "uint32" - } - ], - "name": "getAppUlnConfig", - "outputs": [ - { - "components": [ - { - "name": "confirmations", - "type": "uint64" - }, - { - "name": "requiredDVNCount", - "type": "uint8" - }, - { - "name": "optionalDVNCount", - "type": "uint8" - }, - { - "name": "optionalDVNThreshold", - "type": "uint8" - }, - { - "name": "requiredDVNs", - "type": "address[]" - }, - { - "name": "optionalDVNs", - "type": "address[]" - } + { + inputs: [ + { + name: '_oapp', + type: 'address', + }, + { + name: '_remoteEid', + type: 'uint32', + }, ], - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - } -]; + name: 'getAppUlnConfig', + outputs: [ + { + components: [ + { + name: 'confirmations', + type: 'uint64', + }, + { + name: 'requiredDVNCount', + type: 'uint8', + }, + { + name: 'optionalDVNCount', + type: 'uint8', + }, + { + name: 'optionalDVNThreshold', + type: 'uint8', + }, + { + name: 'requiredDVNs', + type: 'address[]', + }, + { + name: 'optionalDVNs', + type: 'address[]', + }, + ], + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] const ABILibGetExecutorConfig = [ - { - "inputs": [ - { - "internalType": "address", - "name": "_oapp", - "type": "address" - }, - { - "internalType": "uint32", - "name": "_remoteEid", - "type": "uint32" - } - ], - "name": "getExecutorConfig", - "outputs": [ - { - "components": [ - { - "internalType": "uint32", - "name": "maxMessageSize", - "type": "uint32" - }, - { - "internalType": "address", - "name": "executor", - "type": "address" - } + { + inputs: [ + { + internalType: 'address', + name: '_oapp', + type: 'address', + }, + { + internalType: 'uint32', + name: '_remoteEid', + type: 'uint32', + }, + ], + name: 'getExecutorConfig', + outputs: [ + { + components: [ + { + internalType: 'uint32', + name: 'maxMessageSize', + type: 'uint32', + }, + { + internalType: 'address', + name: 'executor', + type: 'address', + }, + ], + internalType: 'struct ExecutorConfig', + name: 'rtnConfig', + type: 'tuple', + }, ], - "internalType": "struct ExecutorConfig", - "name": "rtnConfig", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - } -]; - -const ABIOwner = [ - 'function owner() view returns (address)', -]; + stateMutability: 'view', + type: 'function', + }, +] + +const ABIOwner = ['function owner() view returns (address)'] // Providers for each network const providerList: { [key: string]: providers.JsonRpcProvider } = { - mainnet: new providers.JsonRpcProvider('https://eth-pokt.nodies.app'), - arbitrumOne: new providers.JsonRpcProvider('https://arbitrum.drpc.org'), - bsc: new providers.JsonRpcProvider('https://bsc.drpc.org'), - base: new providers.JsonRpcProvider('https://mainnet.base.org'), - etherlink: new providers.JsonRpcProvider('https://node.mainnet.etherlink.com'), -}; + mainnet: new providers.JsonRpcProvider('https://eth-pokt.nodies.app'), + arbitrumOne: new providers.JsonRpcProvider('https://arbitrum.drpc.org'), + bsc: new providers.JsonRpcProvider('https://bsc.drpc.org'), + base: new providers.JsonRpcProvider('https://mainnet.base.org'), + etherlink: new providers.JsonRpcProvider('https://node.mainnet.etherlink.com'), +} // Define the addresses and parameters const addresses: { [key: string]: { oappAddress: string } } = { - etherlink: { - oappAddress: '0xc9b53ab2679f573e480d01e0f49e2b5cfb7a3eab' - }, - mainnet: { - oappAddress: '0xc9b53ab2679f573e480d01e0f49e2b5cfb7a3eab' - }, - arbitrumOne: { - oappAddress: '0x7424f00845777A06E21F0bd8873f814A8A814B2D' - }, - bsc: { - oappAddress: '0x91F9cc2649ac70a071602cadE9b0C1A5868af51D' - }, - base: { - oappAddress: '0x91F9cc2649ac70a071602cadE9b0C1A5868af51D' - } + etherlink: { + oappAddress: '0xc9b53ab2679f573e480d01e0f49e2b5cfb7a3eab', + }, + mainnet: { + oappAddress: '0xc9b53ab2679f573e480d01e0f49e2b5cfb7a3eab', + }, + arbitrumOne: { + oappAddress: '0x7424f00845777A06E21F0bd8873f814A8A814B2D', + }, + bsc: { + oappAddress: '0x91F9cc2649ac70a071602cadE9b0C1A5868af51D', + }, + base: { + oappAddress: '0x91F9cc2649ac70a071602cadE9b0C1A5868af51D', + }, } // LayerZero endpoints ids const endpointIds: { [key: string]: number } = { - mainnet: 30101, - arbitrumOne: 30110, - bsc: 30102, - base: 30184, - etherlink: 30292, + mainnet: 30101, + arbitrumOne: 30110, + bsc: 30102, + base: 30184, + etherlink: 30292, } -const executorConfigType = 1; // 1 for executor -const ulnConfigType = 2; // 2 for UlnConfig +const executorConfigType = 1 // 1 for executor +const ulnConfigType = 2 // 2 for UlnConfig -const METADATA_URL = 'https://metadata.layerzero-api.com/v1/metadata'; +const METADATA_URL = 'https://metadata.layerzero-api.com/v1/metadata' function getEndpointIdDeployment(eid: number, metadata: any) { - const srcEidString = eid.toString(); - for (const objectKey in metadata) { - const entry = metadata[objectKey]; - - if (typeof entry.deployments !== 'undefined') { - for (const deployment of entry.deployments) { - if (srcEidString === deployment.eid) { - return deployment; + const srcEidString = eid.toString() + for (const objectKey in metadata) { + const entry = metadata[objectKey] + + if (typeof entry.deployments !== 'undefined') { + for (const deployment of entry.deployments) { + if (srcEidString === deployment.eid) { + return deployment + } + } } - } } - } - throw new Error(`Can't find endpoint with eid: "${eid}",`); + throw new Error(`Can't find endpoint with eid: "${eid}",`) } async function analyzeOApp() { - - // Rotate through each chain - - for (const chain in addresses) { - console.log('*******************************************************************************'); - console.log(`Configuration on ${chain} (${endpointIds[chain]})`); - console.log('*******************************************************************************\n'); - - console.log("OFT address:", addresses[chain].oappAddress); - - // Get the owner of the OApp - await getOappOwner(chain); - - // Extract the LZ endpoint from the OApp & - // check whether the LZ endpoint is the official one. - await checkOappEndpoint(chain); - - // Get the registered delegate for the OApp at the LZ endpoint - await getDelegate(chain); - - for (const remote_chain in addresses) { - if (remote_chain != chain) { - console.log('******************************************'); - console.log(`Remote chain: ${remote_chain} (${endpointIds[remote_chain]})`); - console.log('******************************************\n'); - - // Get configured peer and check whether peer is the correct one - await getCheckPeer(chain, remote_chain); - - console.log(`Analyse SEND library:`); - // Get configured SEND library - const sendLib = await getSendLibrary(chain, remote_chain) - // Check whether used SEND library is the configured default one: - await isDefaultSendLibrary(chain, remote_chain) - - if (sendLib) { - await getAppUlnConfigInLib(chain, remote_chain, sendLib); // will show 0 if not set - await getConfig(chain, remote_chain, sendLib); - await getExecutorConfigInLib(chain, remote_chain, sendLib); + // Rotate through each chain + + for (const chain in addresses) { + console.log('*******************************************************************************') + console.log(`Configuration on ${chain} (${endpointIds[chain]})`) + console.log('*******************************************************************************\n') + + console.log('OFT address:', addresses[chain].oappAddress) + + // Get the owner of the OApp + await getOappOwner(chain) + + // Extract the LZ endpoint from the OApp & + // check whether the LZ endpoint is the official one. + await checkOappEndpoint(chain) + + // Get the registered delegate for the OApp at the LZ endpoint + await getDelegate(chain) + + for (const remote_chain in addresses) { + if (remote_chain != chain) { + console.log('******************************************') + console.log(`Remote chain: ${remote_chain} (${endpointIds[remote_chain]})`) + console.log('******************************************\n') + + // Get configured peer and check whether peer is the correct one + await getCheckPeer(chain, remote_chain) + + console.log(`Analyse SEND library:`) + // Get configured SEND library + const sendLib = await getSendLibrary(chain, remote_chain) + // Check whether used SEND library is the configured default one: + await isDefaultSendLibrary(chain, remote_chain) + + if (sendLib) { + await getAppUlnConfigInLib(chain, remote_chain, sendLib) // will show 0 if not set + await getConfig(chain, remote_chain, sendLib) + await getExecutorConfigInLib(chain, remote_chain, sendLib) + } + console.log(`Analyse SEND library finished.\n`) + + console.log(`Analyse RECEIVE library:`) + // Get configured RECEIVE library & + // Check whether used RECEIVE library is the configured default one: + const receiveLib = await getReceiveLibrary(chain, remote_chain) + if (receiveLib) { + await getAppUlnConfigInLib(chain, remote_chain, receiveLib) // will show 0 if not set + await getConfig(chain, remote_chain, receiveLib) + } + console.log(`Analyse RECEIVE library finished.\n`) + + // Enforced Options + await enforcedOptionsOnOapp(chain, remote_chain) + } } - console.log(`Analyse SEND library finished.\n`); - - console.log(`Analyse RECEIVE library:`); - // Get configured RECEIVE library & - // Check whether used RECEIVE library is the configured default one: - const receiveLib = await getReceiveLibrary(chain, remote_chain) - if (receiveLib) { - await getAppUlnConfigInLib(chain, remote_chain, receiveLib); // will show 0 if not set - await getConfig(chain, remote_chain, receiveLib); - } - console.log(`Analyse RECEIVE library finished.\n`); - - // Enforced Options - await enforcedOptionsOnOapp(chain, remote_chain); - } + // break; // remove } - // break; // remove - } } async function getOappOwner(chain: string) { - const oappAddress = addresses[chain].oappAddress; - const provider = providerList[chain]; - - try { - const owner = await callContractMethod( - oappAddress, - ABIOwner, - provider, - 'owner'); - console.log(`Configured owner for OApp is: ${owner}\n`); - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + const oappAddress = addresses[chain].oappAddress + const provider = providerList[chain] + + try { + const owner = await callContractMethod(oappAddress, ABIOwner, provider, 'owner') + console.log(`Configured owner for OApp is: ${owner}\n`) + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } - async function getCheckPeer(chain: string, remote_chain: string) { - const oappAddress = addresses[chain].oappAddress; - const provider = providerList[chain]; - - try { - const peer = await callContractMethod( - oappAddress, - ABIOappPeers, - provider, - 'peers', - endpointIds[remote_chain] - ); - console.log(`Configured peer in OApp is: ${peer}\n`); - - const peer_hex = ethers.utils.getAddress("0x" + peer.slice(26)); - - if (peer_hex === ethers.utils.getAddress(addresses[remote_chain].oappAddress)) { - console.log(`Ok. Peer address is the correct one.\n`); - } else { - console.log(`Not ok. Peer address should be the OApp address ${addresses[remote_chain].oappAddress}\n`); - } + const oappAddress = addresses[chain].oappAddress + const provider = providerList[chain] + + try { + const peer = await callContractMethod(oappAddress, ABIOappPeers, provider, 'peers', endpointIds[remote_chain]) + console.log(`Configured peer in OApp is: ${peer}\n`) + + const peer_hex = ethers.utils.getAddress('0x' + peer.slice(26)) - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + if (peer_hex === ethers.utils.getAddress(addresses[remote_chain].oappAddress)) { + console.log(`Ok. Peer address is the correct one.\n`) + } else { + console.log(`Not ok. Peer address should be the OApp address ${addresses[remote_chain].oappAddress}\n`) + } + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } async function getDelegate(chain: string) { - const response = await fetch(METADATA_URL); - const metadata = await response.json(); - - const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata); - const endpointAddress = lzDeployment.endpointV2.address; - const provider = providerList[chain]; - - try { - const delegate = await callContractMethod( - endpointAddress, - ABILzDelegate, - provider, - 'delegates', - addresses[chain].oappAddress - ); - console.log(`Registered delegate in endpoint for OApp is: ${delegate}\n`); - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + const response = await fetch(METADATA_URL) + const metadata = await response.json() + + const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata) + const endpointAddress = lzDeployment.endpointV2.address + const provider = providerList[chain] + + try { + const delegate = await callContractMethod( + endpointAddress, + ABILzDelegate, + provider, + 'delegates', + addresses[chain].oappAddress + ) + console.log(`Registered delegate in endpoint for OApp is: ${delegate}\n`) + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } async function checkOappEndpoint(chain: string) { - try { - // Get the metadata and deployment information for the specified chain - const response = await fetch(METADATA_URL); - const metadata = await response.json(); - - const oappAddress = addresses[chain].oappAddress; - const provider = providerList[chain]; - - // Call the `endpoint` method of the Oapp contract - const endpoint = await callContractMethod( - oappAddress, - ABIOappEndPoint, - provider, - 'endpoint' - ); - - console.log(`Configured endpoint address for OApp is: ${endpoint}`); - - // Fetch the official endpoint address from metadata for verification - const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata); - const officialEndpointAddress = lzDeployment.endpointV2.address; - - if (areAddressesEqual(officialEndpointAddress, endpoint)) { - console.log("Ok, the configured OApp address is the official one.\n"); - } else { - console.log(`Not ok, the configured OApp address is NOT the official one (${officialEndpointAddress})\n.`); - } + try { + // Get the metadata and deployment information for the specified chain + const response = await fetch(METADATA_URL) + const metadata = await response.json() + + const oappAddress = addresses[chain].oappAddress + const provider = providerList[chain] + + // Call the `endpoint` method of the Oapp contract + const endpoint = await callContractMethod(oappAddress, ABIOappEndPoint, provider, 'endpoint') - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + console.log(`Configured endpoint address for OApp is: ${endpoint}`) + + // Fetch the official endpoint address from metadata for verification + const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata) + const officialEndpointAddress = lzDeployment.endpointV2.address + + if (areAddressesEqual(officialEndpointAddress, endpoint)) { + console.log('Ok, the configured OApp address is the official one.\n') + } else { + console.log(`Not ok, the configured OApp address is NOT the official one (${officialEndpointAddress})\n.`) + } + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } async function getSendLibrary(chain: string, remote_chain: string): Promise { - const response = await fetch(METADATA_URL); - const metadata = await response.json(); - - const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata); - const endpointAddress = lzDeployment.endpointV2.address; - const provider = providerList[chain]; - - try { - const sendLib = await callContractMethod( - endpointAddress, - ABILzGetSendLibrary, - provider, - 'getSendLibrary', - addresses[chain].oappAddress, - endpointIds[remote_chain] - ); - console.log(`Configured send library is: ${sendLib}`); - return sendLib - } catch (error) { - console.error('Error fetching or decoding config:', error); - return undefined; - } + const response = await fetch(METADATA_URL) + const metadata = await response.json() + + const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata) + const endpointAddress = lzDeployment.endpointV2.address + const provider = providerList[chain] + + try { + const sendLib = await callContractMethod( + endpointAddress, + ABILzGetSendLibrary, + provider, + 'getSendLibrary', + addresses[chain].oappAddress, + endpointIds[remote_chain] + ) + console.log(`Configured send library is: ${sendLib}`) + return sendLib + } catch (error) { + console.error('Error fetching or decoding config:', error) + return undefined + } } async function isDefaultSendLibrary(chain: string, remote_chain: string) { - const response = await fetch(METADATA_URL); - const metadata = await response.json(); - // Get endpoint for src id - const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata); - // Get address and libraries from endpoint - const endpointAddress = lzDeployment.endpointV2.address; - const provider = providerList[chain]; - - try { - const isDefaultSendLibrary = await callContractMethod( - endpointAddress, - ABILzIsDefaultSendLibrary, - provider, - 'isDefaultSendLibrary', - addresses[chain].oappAddress, - endpointIds[remote_chain] - ); - console.log("Is default send library used?", isDefaultSendLibrary); - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + const response = await fetch(METADATA_URL) + const metadata = await response.json() + // Get endpoint for src id + const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata) + // Get address and libraries from endpoint + const endpointAddress = lzDeployment.endpointV2.address + const provider = providerList[chain] + + try { + const isDefaultSendLibrary = await callContractMethod( + endpointAddress, + ABILzIsDefaultSendLibrary, + provider, + 'isDefaultSendLibrary', + addresses[chain].oappAddress, + endpointIds[remote_chain] + ) + console.log('Is default send library used?', isDefaultSendLibrary) + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } async function getReceiveLibrary(chain: string, remote_chain: string): Promise { - const response = await fetch(METADATA_URL); - const metadata = await response.json(); - - // Get endpoint for dst id - const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata); - const endpointAddress = lzDeployment.endpointV2.address; - const provider = providerList[chain]; - - try { - const receiveLibrary = await callContractMethod( - endpointAddress, - ABILzGetReceiveLibrary, - provider, - 'getReceiveLibrary', - addresses[chain].oappAddress, - endpointIds[remote_chain] - ); - - console.log(`Configured receive library is: ${receiveLibrary.lib}`); - console.log("Is default receive library used?", receiveLibrary.isDefault); - return receiveLibrary.lib - } catch (error) { - console.error('Error fetching or decoding config:', error); - return undefined; - } + const response = await fetch(METADATA_URL) + const metadata = await response.json() + + // Get endpoint for dst id + const lzDeployment = getEndpointIdDeployment(endpointIds[chain], metadata) + const endpointAddress = lzDeployment.endpointV2.address + const provider = providerList[chain] + + try { + const receiveLibrary = await callContractMethod( + endpointAddress, + ABILzGetReceiveLibrary, + provider, + 'getReceiveLibrary', + addresses[chain].oappAddress, + endpointIds[remote_chain] + ) + + console.log(`Configured receive library is: ${receiveLibrary.lib}`) + console.log('Is default receive library used?', receiveLibrary.isDefault) + return receiveLibrary.lib + } catch (error) { + console.error('Error fetching or decoding config:', error) + return undefined + } } - // Returns the OApp's config for the library. If there is no config the default config is used. async function getAppUlnConfigInLib(chain: string, remote_chain: string, library: string) { - const provider = providerList[chain]; - - try { - const config = await callContractMethod( - library, - ABILibGetAppUlnConfig, - provider, - 'getAppUlnConfig', - addresses[chain].oappAddress, - endpointIds[remote_chain] - ); - console.log("OApp specific library configuration (Security Stack):"); - console.log("- Confirmations:", parseInt(config.confirmations)); - console.log("- Required DVN Count:", config.requiredDVNCount); - console.log("- Optional DVN Count:", config.optionalDVNCount); - console.log("- Optional DVN Threshold:", config.optionalDVNThreshold); - console.log("- Required DVNs:", config.requiredDVNs); - console.log("- Optional DVNs:", config.optionalDVNs); - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + const provider = providerList[chain] + + try { + const config = await callContractMethod( + library, + ABILibGetAppUlnConfig, + provider, + 'getAppUlnConfig', + addresses[chain].oappAddress, + endpointIds[remote_chain] + ) + console.log('OApp specific library configuration (Security Stack):') + console.log('- Confirmations:', parseInt(config.confirmations)) + console.log('- Required DVN Count:', config.requiredDVNCount) + console.log('- Optional DVN Count:', config.optionalDVNCount) + console.log('- Optional DVN Threshold:', config.optionalDVNThreshold) + console.log('- Required DVNs:', config.requiredDVNs) + console.log('- Optional DVNs:', config.optionalDVNs) + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } // Returns the OApp's executor config for the send library. If there is no config the default config is used. async function getExecutorConfigInLib(chain: string, remote_chain: string, sendLibrary: string) { - const provider = providerList[chain]; - - try { - const config = await callContractMethod( - sendLibrary, - ABILibGetExecutorConfig, - provider, - 'getExecutorConfig', - addresses[chain].oappAddress, - endpointIds[remote_chain] - ); - console.log("OApp specific send library configuration (Executor):"); - console.log("- Max message size:", parseInt(config.maxMessageSize)); - console.log("- Executor address:", config.executor); - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + const provider = providerList[chain] + + try { + const config = await callContractMethod( + sendLibrary, + ABILibGetExecutorConfig, + provider, + 'getExecutorConfig', + addresses[chain].oappAddress, + endpointIds[remote_chain] + ) + console.log('OApp specific send library configuration (Executor):') + console.log('- Max message size:', parseInt(config.maxMessageSize)) + console.log('- Executor address:', config.executor) + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } async function getConfig(chain: string, remote_chain: string, library: string) { - const provider = providerList[chain]; - - try { - const config = await callContractMethod( - library, - ABILzGetConfig, - provider, - 'getConfig', - endpointIds[remote_chain], - addresses[chain].oappAddress, - ulnConfigType - ); - const ulnConfigStructType = [ - 'tuple(uint64 confirmations, uint8 requiredDVNCount, uint8 optionalDVNCount, uint8 optionalDVNThreshold, address[] requiredDVNs, address[] optionalDVNs)', - ]; - const receiveUlnConfigArray = ethers.utils.defaultAbiCoder.decode( - ulnConfigStructType, - config, - ); - console.log(`OApp library configuration:`); - console.log(`- Confirmations: ${parseInt(receiveUlnConfigArray[0][0])} (${receiveUlnConfigArray[0][0]._hex})`); - console.log('- Required DVN Count:', receiveUlnConfigArray[0][1]); - console.log('- Optional DVN Count:', receiveUlnConfigArray[0][2]); - console.log('- Optional DVN Threshold:', receiveUlnConfigArray[0][3]); - console.log('- Required DVNs:', receiveUlnConfigArray[0][4]); - console.log('- Optional DVNs:', receiveUlnConfigArray[0][5], '\n'); - - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + const provider = providerList[chain] + + try { + const config = await callContractMethod( + library, + ABILzGetConfig, + provider, + 'getConfig', + endpointIds[remote_chain], + addresses[chain].oappAddress, + ulnConfigType + ) + const ulnConfigStructType = [ + 'tuple(uint64 confirmations, uint8 requiredDVNCount, uint8 optionalDVNCount, uint8 optionalDVNThreshold, address[] requiredDVNs, address[] optionalDVNs)', + ] + const receiveUlnConfigArray = ethers.utils.defaultAbiCoder.decode(ulnConfigStructType, config) + console.log(`OApp library configuration:`) + console.log(`- Confirmations: ${parseInt(receiveUlnConfigArray[0][0])} (${receiveUlnConfigArray[0][0]._hex})`) + console.log('- Required DVN Count:', receiveUlnConfigArray[0][1]) + console.log('- Optional DVN Count:', receiveUlnConfigArray[0][2]) + console.log('- Optional DVN Threshold:', receiveUlnConfigArray[0][3]) + console.log('- Required DVNs:', receiveUlnConfigArray[0][4]) + console.log('- Optional DVNs:', receiveUlnConfigArray[0][5], '\n') + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } async function enforcedOptionsOnOapp(chain: string, remote_chain: string) { - const provider = providerList[chain]; - - try { - const enforcedOption = await callContractMethod( - addresses[chain].oappAddress, - ABIOappEnforcedOptions, - provider, - 'enforcedOptions', - endpointIds[remote_chain], - 1 - ); - console.log(`Enforced option from ${chain} to ${remote_chain} is set to ${enforcedOption}\n`); - } catch (error) { - console.error('Error fetching enforced option:', error); - } + const provider = providerList[chain] + + try { + const enforcedOption = await callContractMethod( + addresses[chain].oappAddress, + ABIOappEnforcedOptions, + provider, + 'enforcedOptions', + endpointIds[remote_chain], + 1 + ) + console.log(`Enforced option from ${chain} to ${remote_chain} is set to ${enforcedOption}\n`) + } catch (error) { + console.error('Error fetching enforced option:', error) + } } // *********************** @@ -526,41 +494,40 @@ async function enforcedOptionsOnOapp(chain: string, remote_chain: string) { // *********************** async function callContractMethod( - contractAddress: string, - abi: any, - provider: ethers.providers.Provider, - methodName: string, - ...args: any[] + contractAddress: string, + abi: any, + provider: ethers.providers.Provider, + methodName: string, + ...args: any[] ) { - const contract = new ethers.Contract(contractAddress, abi, provider); - try { - const result = await contract[methodName](...args); - return result; - } catch (error) { - console.error(`Error calling ${methodName}:`, error); - throw error; - } + const contract = new ethers.Contract(contractAddress, abi, provider) + try { + const result = await contract[methodName](...args) + return result + } catch (error) { + console.error(`Error calling ${methodName}:`, error) + throw error + } } function areAddressesEqual(address1: string, address2: string): boolean { - try { - // Normalize addresses to their checksummed versions and compare - return ethers.utils.getAddress(address1) === ethers.utils.getAddress(address2); - } catch (error) { - console.error("Invalid address provided:", error); - return false; // Return false if either address is invalid - } + try { + // Normalize addresses to their checksummed versions and compare + return ethers.utils.getAddress(address1) === ethers.utils.getAddress(address2) + } catch (error) { + console.error('Invalid address provided:', error) + return false // Return false if either address is invalid + } } - // *********************** // Execute the function // *********************** analyzeOApp() - .then(() => { - console.log("Script completed successfully."); - }) - .catch((error) => { - console.error("Error occurred:", error); - }); \ No newline at end of file + .then(() => { + console.log('Script completed successfully.') + }) + .catch((error) => { + console.error('Error occurred:', error) + }) diff --git a/scripts/getPeer.ts b/scripts/getPeer.ts index 8a0ce99..d80694e 100644 --- a/scripts/getPeer.ts +++ b/scripts/getPeer.ts @@ -1,55 +1,59 @@ -import { ethers, network } from 'hardhat'; -import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json'; -import sepoliaTokens from '../deployments/sepolia.json'; -import bscTestnetTokens from '../deployments/bscTestnet.json'; -import etherlinkTokens from '../deployments/etherlink.json'; -import mainnetTokens from '../deployments/mainnet.json'; -import arbitrumOneTokens from '../deployments/arbitrumOne.json'; -import baseTokens from '../deployments/base.json'; -import bscTokens from '../deployments/bsc.json'; -import { customFormatBytes32String } from './utils'; +import { ethers, network } from 'hardhat' + +import { customFormatBytes32String } from './utils' +import arbitrumOneTokens from '../deployments/arbitrumOne.json' +import baseTokens from '../deployments/base.json' +import bscTokens from '../deployments/bsc.json' +import bscTestnetTokens from '../deployments/bscTestnet.json' +import etherlinkTokens from '../deployments/etherlink.json' +import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json' +import mainnetTokens from '../deployments/mainnet.json' +import sepoliaTokens from '../deployments/sepolia.json' const endpointIds: { [key: string]: string } = { - sepolia: '40161', - bscTestnet: '40102', - avalancheFujiTestnet: '40106', - polygonMumbai: '40109', - arbitrumSepolia: '40231', - optimismSepolia: '40232', - etherlinkTestnet: '40239', - etherlink: '30292', - mainnet: '30101', - arbitrumOne: '30110', - base: '30184', - bsc: '30102', - avalanche: '30106', - polygon: '30109', - optimisticEthereum: '30111', -}; + sepolia: '40161', + bscTestnet: '40102', + avalancheFujiTestnet: '40106', + polygonMumbai: '40109', + arbitrumSepolia: '40231', + optimismSepolia: '40232', + etherlinkTestnet: '40239', + etherlink: '30292', + mainnet: '30101', + arbitrumOne: '30110', + base: '30184', + bsc: '30102', + avalanche: '30106', + polygon: '30109', + optimisticEthereum: '30111', +} async function main() { - // current network - const networkName = network.name; - // targeted network - const targetNetworkName = process.env.targetNetworkName || ""; - const WXTZDeployed: { [key: string]: string } = { - etherlinkTestnet: etherlinkTestnetTokens.WXTZ, - sepolia: sepoliaTokens.WXTZ, - bscTestnet: bscTestnetTokens.WXTZ, - etherlink: etherlinkTokens.WXTZ, - mainnet: mainnetTokens.WXTZ, - arbitrumOne: arbitrumOneTokens.WXTZ, - base: baseTokens.WXTZ, - bsc: bscTokens.WXTZ - }; - const WXTZFactory = await ethers.getContractFactory('WXTZ'); - const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]); + // current network + const networkName = network.name + // targeted network + const targetNetworkName = process.env.targetNetworkName || '' + const WXTZDeployed: { [key: string]: string } = { + etherlinkTestnet: etherlinkTestnetTokens.WXTZ, + sepolia: sepoliaTokens.WXTZ, + bscTestnet: bscTestnetTokens.WXTZ, + etherlink: etherlinkTokens.WXTZ, + mainnet: mainnetTokens.WXTZ, + arbitrumOne: arbitrumOneTokens.WXTZ, + base: baseTokens.WXTZ, + bsc: bscTokens.WXTZ, + } + const WXTZFactory = await ethers.getContractFactory('WXTZ') + const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]) - const isPeer = await WXTZ.isPeer(endpointIds[targetNetworkName], customFormatBytes32String(WXTZDeployed[targetNetworkName])) - console.log(`${networkName} <> ${targetNetworkName} is peer: ${isPeer}\n`); + const isPeer = await WXTZ.isPeer( + endpointIds[targetNetworkName], + customFormatBytes32String(WXTZDeployed[targetNetworkName]) + ) + console.log(`${networkName} <> ${targetNetworkName} is peer: ${isPeer}\n`) } main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); \ No newline at end of file + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/sendToken.ts b/scripts/sendToken.ts index 9968bcb..14aa2c5 100644 --- a/scripts/sendToken.ts +++ b/scripts/sendToken.ts @@ -1,115 +1,117 @@ -import { ethers, network } from 'hardhat'; -import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json'; -import sepoliaTokens from '../deployments/sepolia.json'; -import bscTestnetTokens from '../deployments/bscTestnet.json'; -import etherlinkTokens from '../deployments/etherlink.json'; -import mainnetTokens from '../deployments/mainnet.json'; -import arbitrumOneTokens from '../deployments/arbitrumOne.json'; -import baseTokens from '../deployments/base.json'; -import bscTokens from '../deployments/bsc.json'; -import { customFormatBytes32String } from './utils'; -import { Options } from '@layerzerolabs/lz-v2-utilities'; +import { ethers, network } from 'hardhat' + +import { customFormatBytes32String } from './utils' +import arbitrumOneTokens from '../deployments/arbitrumOne.json' +import baseTokens from '../deployments/base.json' +import bscTokens from '../deployments/bsc.json' +import bscTestnetTokens from '../deployments/bscTestnet.json' +import etherlinkTokens from '../deployments/etherlink.json' +import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json' +import mainnetTokens from '../deployments/mainnet.json' +import sepoliaTokens from '../deployments/sepolia.json' const endpointIds: { [key: string]: string } = { - sepolia: '40161', - bscTestnet: '40102', - avalancheFujiTestnet: '40106', - polygonMumbai: '40109', - arbitrumSepolia: '40231', - optimismSepolia: '40232', - etherlinkTestnet: '40239', - etherlink: '30292', - mainnet: '30101', - arbitrumOne: '30110', - base: '30184', - bsc: '30102', - avalanche: '30106', - polygon: '30109', - optimisticEthereum: '30111', -}; + sepolia: '40161', + bscTestnet: '40102', + avalancheFujiTestnet: '40106', + polygonMumbai: '40109', + arbitrumSepolia: '40231', + optimismSepolia: '40232', + etherlinkTestnet: '40239', + etherlink: '30292', + mainnet: '30101', + arbitrumOne: '30110', + base: '30184', + bsc: '30102', + avalanche: '30106', + polygon: '30109', + optimisticEthereum: '30111', +} async function main() { - const [owner] = await ethers.getSigners(); - // current network - const networkName = network.name; - // targeted network - const targetNetworkName = process.env.targetNetworkName || ""; - const WXTZDeployed: { [key: string]: string } = { - etherlinkTestnet: etherlinkTestnetTokens.WXTZ, - sepolia: sepoliaTokens.WXTZ, - bscTestnet: bscTestnetTokens.WXTZ, - etherlink: etherlinkTokens.WXTZ, - mainnet: mainnetTokens.WXTZ, - arbitrumOne: arbitrumOneTokens.WXTZ, - base: baseTokens.WXTZ, - bsc: bscTokens.WXTZ - }; - const WXTZFactory = await ethers.getContractFactory('WXTZ'); - const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]); + const [owner] = await ethers.getSigners() + // current network + const networkName = network.name + // targeted network + const targetNetworkName = process.env.targetNetworkName || '' + const WXTZDeployed: { [key: string]: string } = { + etherlinkTestnet: etherlinkTestnetTokens.WXTZ, + sepolia: sepoliaTokens.WXTZ, + bscTestnet: bscTestnetTokens.WXTZ, + etherlink: etherlinkTokens.WXTZ, + mainnet: mainnetTokens.WXTZ, + arbitrumOne: arbitrumOneTokens.WXTZ, + base: baseTokens.WXTZ, + bsc: bscTokens.WXTZ, + } + const WXTZFactory = await ethers.getContractFactory('WXTZ') + const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]) - const amount = ethers.utils.parseEther('0.01'); + const amount = ethers.utils.parseEther('0.01') - // If this is Etherlink deposit token - if (networkName == 'etherlinkTestnet' || networkName == 'etherlink') { - console.log(`Deposit ${ethers.utils.formatEther(amount)} XTZ in the WXTZ...`); - const tx = await WXTZ.deposit({ value: amount }); - await tx.wait(); - console.log('WXTZ received.'); - } + // If this is Etherlink deposit token + if (networkName == 'etherlinkTestnet' || networkName == 'etherlink') { + console.log(`Deposit ${ethers.utils.formatEther(amount)} XTZ in the WXTZ...`) + const tx = await WXTZ.deposit({ value: amount }) + await tx.wait() + console.log('WXTZ received.') + } - // Calculate options - let extraOptions = "0x"; - // Enforced so no need to set it as an extra option anymore - // /!\ WARNING /!\ - if the quote fails it can come from that! (check the option on the pathway) - // if (targetNetworkName == "etherlinkTestnet" || targetNetworkName == "etherlink") { - // // If etherlink, use a lot of gas - // const option = Options.newOptions().addExecutorLzReceiveOption(ethers.utils.formatUnits(41000000, "wei"), 0); - // extraOptions = option.toHex(); - // } else { - // // If classic EVM, use 200000 wei (recommended by documentation) - // extraOptions = "0x00030100110100000000000000000000000000030d40"; - // } + // Calculate options + const extraOptions = '0x' + // Enforced so no need to set it as an extra option anymore + // /!\ WARNING /!\ - if the quote fails it can come from that! (check the option on the pathway) + // if (targetNetworkName == "etherlinkTestnet" || targetNetworkName == "etherlink") { + // // If etherlink, use a lot of gas + // const option = Options.newOptions().addExecutorLzReceiveOption(ethers.utils.formatUnits(41000000, "wei"), 0); + // extraOptions = option.toHex(); + // } else { + // // If classic EVM, use 200000 wei (recommended by documentation) + // extraOptions = "0x00030100110100000000000000000000000000030d40"; + // } - console.log("quoteSend parameters:"); - console.log("dstEid", endpointIds[targetNetworkName]); - console.log("to", customFormatBytes32String(owner.address)); - console.log("amountLD", amount); - console.log("minAmountLD", 0); - console.log("extraOptions", extraOptions); - console.log("composeMsg", "0x"); - console.log("oftCmd", "0x"); + console.log('quoteSend parameters:') + console.log('dstEid', endpointIds[targetNetworkName]) + console.log('to', customFormatBytes32String(owner.address)) + console.log('amountLD', amount) + console.log('minAmountLD', 0) + console.log('extraOptions', extraOptions) + console.log('composeMsg', '0x') + console.log('oftCmd', '0x') - // Estimate gas - const sendParam = { - dstEid: endpointIds[targetNetworkName], // Destination endpoint ID. - to: customFormatBytes32String(owner.address), // Recipient address. - amountLD: amount, // Amount to send in local decimals. - minAmountLD: 0, // Minimum amount to send in local decimals. - extraOptions: extraOptions, // Additional options supplied by the caller to be used in the LayerZero message. - composeMsg: "0x", // The composed message for the send() operation. - oftCmd: "0x", // The OFT command to be executed, unused in default OFT implementations. - }; - const estimatedGas = await WXTZ.quoteSend( - sendParam, - false // do we want to pay in lz token - ); + // Estimate gas + const sendParam = { + dstEid: endpointIds[targetNetworkName], // Destination endpoint ID. + to: customFormatBytes32String(owner.address), // Recipient address. + amountLD: amount, // Amount to send in local decimals. + minAmountLD: 0, // Minimum amount to send in local decimals. + extraOptions: extraOptions, // Additional options supplied by the caller to be used in the LayerZero message. + composeMsg: '0x', // The composed message for the send() operation. + oftCmd: '0x', // The OFT command to be executed, unused in default OFT implementations. + } + const estimatedGas = await WXTZ.quoteSend( + sendParam, + false // do we want to pay in lz token + ) - console.log(`Your send is estimated at ${estimatedGas.nativeFee} gas amount in native gas token and ${estimatedGas.lzTokenFee} gas amount in ZRO token.`); + console.log( + `Your send is estimated at ${estimatedGas.nativeFee} gas amount in native gas token and ${estimatedGas.lzTokenFee} gas amount in ZRO token.` + ) - console.log(`Sending the tokens from ${networkName} to ${targetNetworkName}...`); + console.log(`Sending the tokens from ${networkName} to ${targetNetworkName}...`) - // Send tokens - const tx = await WXTZ.send( - sendParam, - estimatedGas, // messaging fee - owner.address, // refund address - { value: estimatedGas.nativeFee } - ); - await tx.wait(); - console.log(`See the token transfer here: https://layerzeroscan.com/tx/${tx.hash}`) + // Send tokens + const tx = await WXTZ.send( + sendParam, + estimatedGas, // messaging fee + owner.address, // refund address + { value: estimatedGas.nativeFee } + ) + await tx.wait() + console.log(`See the token transfer here: https://layerzeroscan.com/tx/${tx.hash}`) } main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); \ No newline at end of file + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/setEnforcedOption.ts b/scripts/setEnforcedOption.ts index d9b205e..5ab9397 100644 --- a/scripts/setEnforcedOption.ts +++ b/scripts/setEnforcedOption.ts @@ -1,78 +1,81 @@ -import { ethers, network } from 'hardhat'; -import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json'; -import sepoliaTokens from '../deployments/sepolia.json'; -import bscTestnetTokens from '../deployments/bscTestnet.json'; -import etherlinkTokens from '../deployments/etherlink.json'; -import mainnetTokens from '../deployments/mainnet.json'; -import arbitrumOneTokens from '../deployments/arbitrumOne.json'; -import baseTokens from '../deployments/base.json'; -import bscTokens from '../deployments/bsc.json'; -import { customFormatBytes32String } from './utils'; -import { Options } from '@layerzerolabs/lz-v2-utilities'; +import { ethers, network } from 'hardhat' + +import { Options } from '@layerzerolabs/lz-v2-utilities' + +import arbitrumOneTokens from '../deployments/arbitrumOne.json' +import baseTokens from '../deployments/base.json' +import bscTokens from '../deployments/bsc.json' +import bscTestnetTokens from '../deployments/bscTestnet.json' +import etherlinkTokens from '../deployments/etherlink.json' +import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json' +import mainnetTokens from '../deployments/mainnet.json' +import sepoliaTokens from '../deployments/sepolia.json' const endpointIds: { [key: string]: string } = { - sepolia: '40161', - bscTestnet: '40102', - avalancheFujiTestnet: '40106', - polygonMumbai: '40109', - arbitrumSepolia: '40231', - optimismSepolia: '40232', - etherlinkTestnet: '40239', - etherlink: '30292', - mainnet: '30101', - arbitrumOne: '30110', - base: '30184', - bsc: '30102', - avalanche: '30106', - polygon: '30109', - optimisticEthereum: '30111', -}; + sepolia: '40161', + bscTestnet: '40102', + avalancheFujiTestnet: '40106', + polygonMumbai: '40109', + arbitrumSepolia: '40231', + optimismSepolia: '40232', + etherlinkTestnet: '40239', + etherlink: '30292', + mainnet: '30101', + arbitrumOne: '30110', + base: '30184', + bsc: '30102', + avalanche: '30106', + polygon: '30109', + optimisticEthereum: '30111', +} async function main() { - const [owner] = await ethers.getSigners(); - // current network - const networkName = network.name; - // targeted network - const targetNetworkName = process.env.targetNetworkName || ""; - const WXTZDeployed: { [key: string]: string } = { - etherlinkTestnet: etherlinkTestnetTokens.WXTZ, - sepolia: sepoliaTokens.WXTZ, - bscTestnet: bscTestnetTokens.WXTZ, - etherlink: etherlinkTokens.WXTZ, - mainnet: mainnetTokens.WXTZ, - arbitrumOne: arbitrumOneTokens.WXTZ, - base: baseTokens.WXTZ, - bsc: bscTokens.WXTZ - }; - const WXTZFactory = await ethers.getContractFactory('WXTZ'); - const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]); + const [owner] = await ethers.getSigners() + // current network + const networkName = network.name + // targeted network + const targetNetworkName = process.env.targetNetworkName || '' + const WXTZDeployed: { [key: string]: string } = { + etherlinkTestnet: etherlinkTestnetTokens.WXTZ, + sepolia: sepoliaTokens.WXTZ, + bscTestnet: bscTestnetTokens.WXTZ, + etherlink: etherlinkTokens.WXTZ, + mainnet: mainnetTokens.WXTZ, + arbitrumOne: arbitrumOneTokens.WXTZ, + base: baseTokens.WXTZ, + bsc: bscTokens.WXTZ, + } + const WXTZFactory = await ethers.getContractFactory('WXTZ') + const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]) - // Set enforced options - let extraOptions; - if (targetNetworkName == "etherlinkTestnet" || targetNetworkName == "etherlink") { - // If etherlink, use a lot of gas - const option = Options.newOptions().addExecutorLzReceiveOption(ethers.utils.formatUnits(41000000, "wei"), 0); - extraOptions = option.toHex(); - } else { - // If classic EVM, use 200000 wei (recommended by documentation) - extraOptions = "0x00030100110100000000000000000000000000030d40"; - } + // Set enforced options + let extraOptions + if (targetNetworkName == 'etherlinkTestnet' || targetNetworkName == 'etherlink') { + // If etherlink, use a lot of gas + const option = Options.newOptions().addExecutorLzReceiveOption(ethers.utils.formatUnits(41000000, 'wei'), 0) + extraOptions = option.toHex() + } else { + // If classic EVM, use 200000 wei (recommended by documentation) + extraOptions = '0x00030100110100000000000000000000000000030d40' + } - let enforcedOptions = [ - { - eid: endpointIds[targetNetworkName], // destination Endpoint ID - msgType: 1, - options: extraOptions, - }, - ]; + const enforcedOptions = [ + { + eid: endpointIds[targetNetworkName], // destination Endpoint ID + msgType: 1, + options: extraOptions, + }, + ] - // Send tokens - const tx = await WXTZ.setEnforcedOptions(enforcedOptions); - await tx.wait(); - console.log(`Enforced option from ${networkName} to ${targetNetworkName} is set to ${await WXTZ.enforcedOptions(endpointIds[targetNetworkName], 1)}`) + // Send tokens + const tx = await WXTZ.setEnforcedOptions(enforcedOptions) + await tx.wait() + console.log( + `Enforced option from ${networkName} to ${targetNetworkName} is set to ${await WXTZ.enforcedOptions(endpointIds[targetNetworkName], 1)}` + ) } main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); \ No newline at end of file + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/setLibs.ts b/scripts/setLibs.ts index 0584b2d..142188f 100644 --- a/scripts/setLibs.ts +++ b/scripts/setLibs.ts @@ -1,125 +1,138 @@ -import { providers, Contract, utils, Wallet, } from 'ethers'; -import { network, ethers } from 'hardhat'; -import etherlinkTokens from '../deployments/etherlink.json'; -import mainnetTokens from '../deployments/mainnet.json'; -import arbitrumOneTokens from '../deployments/arbitrumOne.json'; -import baseTokens from '../deployments/base.json'; -import bscTokens from '../deployments/bsc.json'; +import { ethers, network } from 'hardhat' + +import arbitrumOneTokens from '../deployments/arbitrumOne.json' +import baseTokens from '../deployments/base.json' +import bscTokens from '../deployments/bsc.json' +import etherlinkTokens from '../deployments/etherlink.json' +import mainnetTokens from '../deployments/mainnet.json' // Supported networks -const SUPPORTED_NETWORKS = ['mainnet', 'arbitrumOne', 'bsc', 'base', 'etherlink']; +const SUPPORTED_NETWORKS = ['mainnet', 'arbitrumOne', 'bsc', 'base', 'etherlink'] // Contract ABI const LzEndpointABI = [ - 'function setSendLibrary(address oapp, uint32 eid, address sendLib) external', - 'function setReceiveLibrary(address oapp, uint32 eid, address receiveLib, uint256 gracePeriod) external', -]; + 'function setSendLibrary(address oapp, uint32 eid, address sendLib) external', + 'function setReceiveLibrary(address oapp, uint32 eid, address receiveLib, uint256 gracePeriod) external', +] // Define the addresses and parameters -const addresses: { [key: string]: { oappAddress: string, sendLibAddress: string, receiveLibAddress: string } } = { - mainnet: { - oappAddress: mainnetTokens.WXTZ, - sendLibAddress: '0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1', - receiveLibAddress: '0xc02Ab410f0734EFa3F14628780e6e695156024C2' - }, - arbitrumOne: { - oappAddress: arbitrumOneTokens.WXTZ, - sendLibAddress: '0x975bcD720be66659e3EB3C0e4F1866a3020E493A', - receiveLibAddress: '0x7B9E184e07a6EE1aC23eAe0fe8D6Be2f663f05e6' - }, - bsc: { - oappAddress: bscTokens.WXTZ, - sendLibAddress: '0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE', - receiveLibAddress: '0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1' - }, - base: { - oappAddress: baseTokens.WXTZ, - sendLibAddress: '0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda2', - receiveLibAddress: '0xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf' - }, - etherlink: { - oappAddress: etherlinkTokens.WXTZ, - sendLibAddress: '0xc1B621b18187F74c8F6D52a6F709Dd2780C09821', - receiveLibAddress: '0x377530cdA84DFb2673bF4d145DCF0C4D7fdcB5b6' - } +const addresses: { [key: string]: { oappAddress: string; sendLibAddress: string; receiveLibAddress: string } } = { + mainnet: { + oappAddress: mainnetTokens.WXTZ, + sendLibAddress: '0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1', + receiveLibAddress: '0xc02Ab410f0734EFa3F14628780e6e695156024C2', + }, + arbitrumOne: { + oappAddress: arbitrumOneTokens.WXTZ, + sendLibAddress: '0x975bcD720be66659e3EB3C0e4F1866a3020E493A', + receiveLibAddress: '0x7B9E184e07a6EE1aC23eAe0fe8D6Be2f663f05e6', + }, + bsc: { + oappAddress: bscTokens.WXTZ, + sendLibAddress: '0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE', + receiveLibAddress: '0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1', + }, + base: { + oappAddress: baseTokens.WXTZ, + sendLibAddress: '0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda2', + receiveLibAddress: '0xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf', + }, + etherlink: { + oappAddress: etherlinkTokens.WXTZ, + sendLibAddress: '0xc1B621b18187F74c8F6D52a6F709Dd2780C09821', + receiveLibAddress: '0x377530cdA84DFb2673bF4d145DCF0C4D7fdcB5b6', + }, } // LayerZero endpoints ids const endpointIds: { [key: string]: number } = { - mainnet: 30101, - arbitrumOne: 30110, - bsc: 30102, - base: 30184, - etherlink: 30292, + mainnet: 30101, + arbitrumOne: 30110, + bsc: 30102, + base: 30184, + etherlink: 30292, } -const METADATA_URL = 'https://metadata.layerzero-api.com/v1/metadata'; +const METADATA_URL = 'https://metadata.layerzero-api.com/v1/metadata' function getEndpointIdDeployment(eid: number, metadata: any) { - const srcEidString = eid.toString(); - for (const objectKey in metadata) { - const entry = metadata[objectKey]; + const srcEidString = eid.toString() + for (const objectKey in metadata) { + const entry = metadata[objectKey] - if (typeof entry.deployments !== 'undefined') { - for (const deployment of entry.deployments) { - if (srcEidString === deployment.eid) { - return deployment; + if (typeof entry.deployments !== 'undefined') { + for (const deployment of entry.deployments) { + if (srcEidString === deployment.eid) { + return deployment + } + } } - } } - } - throw new Error(`Can't find endpoint with eid: "${eid}",`); + throw new Error(`Can't find endpoint with eid: "${eid}",`) } async function setLibs() { - // Get the src & dst chains - const dstChain = process.env.targetNetworkName || ""; - const srcChain = network.name; - if (!SUPPORTED_NETWORKS.includes(dstChain) || !SUPPORTED_NETWORKS.includes(srcChain)) { - console.error("Unsupported chain provided, the chains supported are:", SUPPORTED_NETWORKS.join(', ')); - throw new Error('Unsupported chain'); - } - console.log(`WXTZ set receive and send libs on ${srcChain} targeting ${dstChain}:`); - // Get metadata from url - const response = await fetch(METADATA_URL); - const metadata = await response.json(); - // Get endpoint for src id - const lzDeployment = getEndpointIdDeployment(endpointIds[srcChain], metadata); - // Get address and libraries from endpoint - const endpointAddress = lzDeployment.endpointV2.address; - const sendUln302Address = lzDeployment.sendUln302.address; - const receiveUln302Address = lzDeployment.receiveUln302.address; - const [signer] = await ethers.getSigners(); - const contract = new ethers.Contract(endpointAddress, LzEndpointABI, signer); - const WXTZDeployed: { [key: string]: string } = { - etherlink: etherlinkTokens.WXTZ, - mainnet: mainnetTokens.WXTZ, - arbitrumOne: arbitrumOneTokens.WXTZ, - base: baseTokens.WXTZ, - bsc: bscTokens.WXTZ - }; + // Get the src & dst chains + const dstChain = process.env.targetNetworkName || '' + const srcChain = network.name + if (!SUPPORTED_NETWORKS.includes(dstChain) || !SUPPORTED_NETWORKS.includes(srcChain)) { + console.error('Unsupported chain provided, the chains supported are:', SUPPORTED_NETWORKS.join(', ')) + throw new Error('Unsupported chain') + } + console.log(`WXTZ set receive and send libs on ${srcChain} targeting ${dstChain}:`) + // Get metadata from url + const response = await fetch(METADATA_URL) + const metadata = await response.json() + // Get endpoint for src id + const lzDeployment = getEndpointIdDeployment(endpointIds[srcChain], metadata) + // Get address and libraries from endpoint + const endpointAddress = lzDeployment.endpointV2.address + const sendUln302Address = lzDeployment.sendUln302.address + const receiveUln302Address = lzDeployment.receiveUln302.address + const [signer] = await ethers.getSigners() + const contract = new ethers.Contract(endpointAddress, LzEndpointABI, signer) + const WXTZDeployed: { [key: string]: string } = { + etherlink: etherlinkTokens.WXTZ, + mainnet: mainnetTokens.WXTZ, + arbitrumOne: arbitrumOneTokens.WXTZ, + base: baseTokens.WXTZ, + bsc: bscTokens.WXTZ, + } - try { - // Set receive and send libs - console.log(`Setting sendUln302 lib ${sendUln302Address} on wxtz ${WXTZDeployed[srcChain]} with dstEid ${endpointIds[dstChain]}...`); - const sendLibTx = await contract.setSendLibrary(WXTZDeployed[srcChain], endpointIds[dstChain], sendUln302Address); - await sendLibTx.wait(); - console.log("sendUln302 lib set.") - console.log(`Setting receiveUln302 lib ${receiveUln302Address} on wxtz ${WXTZDeployed[srcChain]} with dstEid ${endpointIds[dstChain]}...`); - const receiveLibTx = await contract.setReceiveLibrary(WXTZDeployed[srcChain], endpointIds[dstChain], receiveUln302Address, 0); - await receiveLibTx.wait(); - console.log("receiveUln302 lib set.\n") - } catch (error) { - console.error('Error fetching or decoding config:', error); - } + try { + // Set receive and send libs + console.log( + `Setting sendUln302 lib ${sendUln302Address} on wxtz ${WXTZDeployed[srcChain]} with dstEid ${endpointIds[dstChain]}...` + ) + const sendLibTx = await contract.setSendLibrary( + WXTZDeployed[srcChain], + endpointIds[dstChain], + sendUln302Address + ) + await sendLibTx.wait() + console.log('sendUln302 lib set.') + console.log( + `Setting receiveUln302 lib ${receiveUln302Address} on wxtz ${WXTZDeployed[srcChain]} with dstEid ${endpointIds[dstChain]}...` + ) + const receiveLibTx = await contract.setReceiveLibrary( + WXTZDeployed[srcChain], + endpointIds[dstChain], + receiveUln302Address, + 0 + ) + await receiveLibTx.wait() + console.log('receiveUln302 lib set.\n') + } catch (error) { + console.error('Error fetching or decoding config:', error) + } } // Execute the function setLibs() - .then(() => { - console.log("Script completed successfully."); - }) - .catch((error) => { - console.error("Error occurred:", error); - }); \ No newline at end of file + .then(() => { + console.log('Script completed successfully.') + }) + .catch((error) => { + console.error('Error occurred:', error) + }) diff --git a/scripts/setPeer.ts b/scripts/setPeer.ts index 0a7dcc6..08554ba 100644 --- a/scripts/setPeer.ts +++ b/scripts/setPeer.ts @@ -1,72 +1,70 @@ -import { ethers, network } from 'hardhat'; -import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json'; -import sepoliaTokens from '../deployments/sepolia.json'; -import bscTestnetTokens from '../deployments/bscTestnet.json'; -import etherlinkTokens from '../deployments/etherlink.json'; -import mainnetTokens from '../deployments/mainnet.json'; -import arbitrumOneTokens from '../deployments/arbitrumOne.json'; -import baseTokens from '../deployments/base.json'; -import bscTokens from '../deployments/bsc.json'; -import { customFormatBytes32String } from './utils'; -import { error } from 'console'; +import { error } from 'console' + +import { ethers, network } from 'hardhat' + +import { customFormatBytes32String } from './utils' +import arbitrumOneTokens from '../deployments/arbitrumOne.json' +import baseTokens from '../deployments/base.json' +import bscTokens from '../deployments/bsc.json' +import bscTestnetTokens from '../deployments/bscTestnet.json' +import etherlinkTokens from '../deployments/etherlink.json' +import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json' +import mainnetTokens from '../deployments/mainnet.json' +import sepoliaTokens from '../deployments/sepolia.json' const endpointIds: { [key: string]: string } = { - sepolia: '40161', - bscTestnet: '40102', - avalancheFujiTestnet: '40106', - polygonMumbai: '40109', - arbitrumSepolia: '40231', - optimismSepolia: '40232', - etherlinkTestnet: '40239', - etherlink: '30292', - mainnet: '30101', - arbitrumOne: '30110', - base: '30184', - bsc: '30102', - avalanche: '30106', - polygon: '30109', - optimisticEthereum: '30111', -}; + sepolia: '40161', + bscTestnet: '40102', + avalancheFujiTestnet: '40106', + polygonMumbai: '40109', + arbitrumSepolia: '40231', + optimismSepolia: '40232', + etherlinkTestnet: '40239', + etherlink: '30292', + mainnet: '30101', + arbitrumOne: '30110', + base: '30184', + bsc: '30102', + avalanche: '30106', + polygon: '30109', + optimisticEthereum: '30111', +} async function main() { - const [ owner ] = await ethers.getSigners(); - // current network - const networkName = network.name; - // targeted network - const targetNetworkName = process.env.targetNetworkName || ""; - const WXTZDeployed: { [key: string]: string } = { - etherlinkTestnet: etherlinkTestnetTokens.WXTZ, - sepolia: sepoliaTokens.WXTZ, - bscTestnet: bscTestnetTokens.WXTZ, - etherlink: etherlinkTokens.WXTZ, - mainnet: mainnetTokens.WXTZ, - arbitrumOne: arbitrumOneTokens.WXTZ, - base: baseTokens.WXTZ, - bsc: bscTokens.WXTZ - }; - const WXTZFactory = await ethers.getContractFactory('WXTZ'); - const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]); + const [owner] = await ethers.getSigners() + // current network + const networkName = network.name + // targeted network + const targetNetworkName = process.env.targetNetworkName || '' + const WXTZDeployed: { [key: string]: string } = { + etherlinkTestnet: etherlinkTestnetTokens.WXTZ, + sepolia: sepoliaTokens.WXTZ, + bscTestnet: bscTestnetTokens.WXTZ, + etherlink: etherlinkTokens.WXTZ, + mainnet: mainnetTokens.WXTZ, + arbitrumOne: arbitrumOneTokens.WXTZ, + base: baseTokens.WXTZ, + bsc: bscTokens.WXTZ, + } + const WXTZFactory = await ethers.getContractFactory('WXTZ') + const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]) - // set peer - console.log( - `Calling setPeer on your ${networkName} OFT for ${targetNetworkName} network.` - ); + // set peer + console.log(`Calling setPeer on your ${networkName} OFT for ${targetNetworkName} network.`) - const tx = await WXTZ.setPeer( - endpointIds[targetNetworkName], - customFormatBytes32String(WXTZDeployed[targetNetworkName]) - ); - await tx.wait(); - if (await WXTZ.isPeer(endpointIds[targetNetworkName], customFormatBytes32String(WXTZDeployed[targetNetworkName]))) { - console.log( - `Your OFT on ${targetNetworkName} has been set as a peer on the ${networkName} OFT.` - ); - } else { - throw error(`Failed to set your OFT on ${targetNetworkName} as a peer on the ${networkName} OFT.`); - } + const tx = await WXTZ.setPeer( + endpointIds[targetNetworkName], + customFormatBytes32String(WXTZDeployed[targetNetworkName]) + ) + await tx.wait() + if (await WXTZ.isPeer(endpointIds[targetNetworkName], customFormatBytes32String(WXTZDeployed[targetNetworkName]))) { + console.log(`Your OFT on ${targetNetworkName} has been set as a peer on the ${networkName} OFT.`) + } else { + throw error(`Failed to set your OFT on ${targetNetworkName} as a peer on the ${networkName} OFT.`) + } } main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); \ No newline at end of file + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/utils.ts b/scripts/utils.ts index c8838e0..86385f1 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -1,53 +1,51 @@ -import { existsSync, readFileSync, writeFileSync } from 'fs'; -import { network, run } from "hardhat"; -import { BaseContract } from "ethers"; +import { existsSync, readFileSync, writeFileSync } from 'fs' + +import { network, run } from 'hardhat' export function formatBytes32String(bytes20String: string) { - const bytes32String = - '0x' + '0'.repeat(24) + bytes20String.substring(2, bytes20String.length); - return bytes32String; + const bytes32String = '0x' + '0'.repeat(24) + bytes20String.substring(2, bytes20String.length) + return bytes32String } export function updateDeploymentFile(networkName: string, newEntries: object) { - const filePath = `./deployments/${networkName}.json`; - let data = {}; + const filePath = `./deployments/${networkName}.json` + let data = {} - // Check if the file exists - if (existsSync(filePath)) { - // Read the current contents of the file - const fileContents = readFileSync(filePath, 'utf-8'); - data = JSON.parse(fileContents); - } + // Check if the file exists + if (existsSync(filePath)) { + // Read the current contents of the file + const fileContents = readFileSync(filePath, 'utf-8') + data = JSON.parse(fileContents) + } - // Merge the new entries with the existing data - const updatedData = { ...data, ...newEntries }; + // Merge the new entries with the existing data + const updatedData = { ...data, ...newEntries } - // Write the merged data back to the file - writeFileSync(filePath, JSON.stringify(updatedData, null, 2)); + // Write the merged data back to the file + writeFileSync(filePath, JSON.stringify(updatedData, null, 2)) } // Remember to wait the deployment of the contract before calling that export async function verifyContract(contract: string, args: any[]) { - const developmentChains = ["hardhat", "localhost"]; - if (!developmentChains.includes(network.name)) { - console.log("Verifying contract..."); - try { - await run("verify:verify", { - address: contract, - constructorArguments: args, - }); - } catch (e: any) { - if (e.message.toLowerCase().includes("already verified")) { - // console.log("Already verified!"); - } else { - console.log(e); - } + const developmentChains = ['hardhat', 'localhost'] + if (!developmentChains.includes(network.name)) { + console.log('Verifying contract...') + try { + await run('verify:verify', { + address: contract, + constructorArguments: args, + }) + } catch (e: any) { + if (e.message.toLowerCase().includes('already verified')) { + // console.log("Already verified!"); + } else { + console.log(e) + } + } } - } } export function customFormatBytes32String(bytes20String: string) { - const bytes32String = - '0x' + '0'.repeat(24) + bytes20String.substring(2, bytes20String.length); - return bytes32String; -} \ No newline at end of file + const bytes32String = '0x' + '0'.repeat(24) + bytes20String.substring(2, bytes20String.length) + return bytes32String +} diff --git a/scripts/withdrawWXTZ.ts b/scripts/withdrawWXTZ.ts index 071bdbd..87f250a 100644 --- a/scripts/withdrawWXTZ.ts +++ b/scripts/withdrawWXTZ.ts @@ -1,26 +1,27 @@ -import { ethers, network } from 'hardhat'; -import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json'; -import etherlinkTokens from '../deployments/etherlink.json'; +import { ethers, network } from 'hardhat' + +import etherlinkTokens from '../deployments/etherlink.json' +import etherlinkTestnetTokens from '../deployments/etherlinkTestnet.json' // Note: you can only deposit and withdraw on Etherlink async function main() { - const [ owner ] = await ethers.getSigners(); - // current network - const networkName = network.name; - const WXTZDeployed: { [key: string]: string } = { - etherlinkTestnet: etherlinkTestnetTokens.WXTZ, - etherlink: etherlinkTokens.WXTZ, - }; - const WXTZFactory = await ethers.getContractFactory('WXTZ'); - const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]); + const [owner] = await ethers.getSigners() + // current network + const networkName = network.name + const WXTZDeployed: { [key: string]: string } = { + etherlinkTestnet: etherlinkTestnetTokens.WXTZ, + etherlink: etherlinkTokens.WXTZ, + } + const WXTZFactory = await ethers.getContractFactory('WXTZ') + const WXTZ = WXTZFactory.attach(WXTZDeployed[networkName]) - console.log(`Your balance before withdraw: ${await WXTZ.balanceOf(owner.address)}.`); - const tx = await WXTZ.withdraw(await WXTZ.balanceOf(owner.address)); - await tx.wait(); - console.log(`Your balance after withdraw: ${await WXTZ.balanceOf(owner.address)}.`); + console.log(`Your balance before withdraw: ${await WXTZ.balanceOf(owner.address)}.`) + const tx = await WXTZ.withdraw(await WXTZ.balanceOf(owner.address)) + await tx.wait() + console.log(`Your balance after withdraw: ${await WXTZ.balanceOf(owner.address)}.`) } main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); \ No newline at end of file + console.error(error) + process.exitCode = 1 +}) diff --git a/test/WXTZ.test.ts b/test/WXTZ.test.ts index a3fe107..6c61ff8 100644 --- a/test/WXTZ.test.ts +++ b/test/WXTZ.test.ts @@ -1,196 +1,197 @@ +import { time } from '@nomicfoundation/hardhat-network-helpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { expect } from 'chai' import { Contract, ContractFactory } from 'ethers' -import { formatBytes32String } from '../scripts/utils' import { deployments, ethers } from 'hardhat' -import { time } from "@nomicfoundation/hardhat-network-helpers"; + +import { formatBytes32String } from '../scripts/utils' describe('WXTZ Test', function () { - // Constant representing a mock Endpoint ID for testing purposes - const eidA = 1 - const eidB = 2 - // Declaration of variables to be used in the test suite - let WXTZFactory: ContractFactory - let EndpointV2Mock: ContractFactory - let ownerA: SignerWithAddress - let ownerB: SignerWithAddress - let endpointOwner: SignerWithAddress - let wxtzA: Contract - let wxtzB: Contract - let mockEndpointV2A: Contract - let mockEndpointV2B: Contract - - // Before hook for setup that runs once before all tests in the block - before(async function () { - // Contract factory for our tested contract - // - // We are using a derived contract that exposes a mint() function for testing purposes - WXTZFactory = await ethers.getContractFactory('WXTZ') - - // Fetching the first three signers (accounts) from Hardhat's local Ethereum network - const signers = await ethers.getSigners() - - ownerA = signers.at(0)! - ownerB = signers.at(1)! - endpointOwner = signers.at(2)! - - // The EndpointV2Mock contract comes from @layerzerolabs/test-devtools-evm-hardhat package - // and its artifacts are connected as external artifacts to this project - // - // Unfortunately, hardhat itself does not yet provide a way of connecting external artifacts, - // so we rely on hardhat-deploy to create a ContractFactory for EndpointV2Mock - // - // See https://github.com/NomicFoundation/hardhat/issues/1040 - const EndpointV2MockArtifact = await deployments.getArtifact('EndpointV2Mock') - EndpointV2Mock = new ContractFactory(EndpointV2MockArtifact.abi, EndpointV2MockArtifact.bytecode, endpointOwner) - }) - - // beforeEach hook for setup that runs before each test in the block - beforeEach(async function () { - // Deploying a mock LZEndpoint with the given Endpoint ID - mockEndpointV2A = await EndpointV2Mock.deploy(eidA) - mockEndpointV2B = await EndpointV2Mock.deploy(eidB) - - // Deploying two instances of MyOFT contract with different identifiers and linking them to the mock LZEndpoint - wxtzA = await WXTZFactory.connect(ownerA).deploy(128123, mockEndpointV2A.address, ownerA.address) - wxtzB = await WXTZFactory.connect(ownerB).deploy(128123, mockEndpointV2B.address, ownerB.address) - - // Setting destination endpoints in the LZEndpoint mock for each MyOFT instance - await mockEndpointV2A.setDestLzEndpoint(wxtzB.address, mockEndpointV2B.address) - await mockEndpointV2B.setDestLzEndpoint(wxtzA.address, mockEndpointV2A.address) - - // Set the timelock for `setPeer()` on first call - await wxtzA.connect(ownerA).setPeer(eidB, ethers.utils.zeroPad(wxtzB.address, 32)); - await wxtzB.connect(ownerB).setPeer(eidA, ethers.utils.zeroPad(wxtzA.address, 32)); - - // advance time by 3 days and mine a new block - await time.increase(259200); - - // Setting each MyOFT instance as a peer of the other in the mock LZEndpoint - await wxtzA.connect(ownerA).setPeer(eidB, ethers.utils.zeroPad(wxtzB.address, 32)) - await wxtzB.connect(ownerB).setPeer(eidA, ethers.utils.zeroPad(wxtzA.address, 32)) - }) - - // Test that the constructor set the correct owner even if it is deployed but someone else - it('Should be the correct owner', async function () { - // Test already deployed ones - const wxtzAOwner = await wxtzA.owner(); - const wxtzBOwner = await wxtzB.owner(); - expect(wxtzAOwner).to.equal(ownerA.address); - expect(wxtzBOwner).to.equal(ownerB.address); - - // Test deployed by user A but owner should be new user - const newUser = ethers.Wallet.createRandom(); - let newOFT = await WXTZFactory.connect(ownerA).deploy(128123, mockEndpointV2A.address, newUser.address); - const OFTOwner = await newOFT.owner(); - expect(OFTOwner).to.equal(newUser.address); - }); - - // A test case to verify token deposit and withdraw functionality - // WARNING: you can only deposit and withdraw on Etherlink testnet and mainnet - it('Should be able to exchange XTZ for WXTZ 1:1', async function () { - // Check initial balances - const initialXTZ = await ethers.provider.getBalance(ownerA.address); - const initialWXTZ = await wxtzA.balanceOf(ownerA.address); - const initialTotalSupply = await wxtzA.totalSupply() - - expect(initialXTZ.toString()).eq("10000000000000000000000") - expect(initialWXTZ.toString()).eq("0") - expect(initialTotalSupply.toString()).eq("0") - - // Check deposit with non-owner account leads to correct exchange of XTZ for WXTZ - await wxtzA.connect(ownerB).deposit({ value: ethers.utils.parseEther("1") }); - - const depositXTZ = await ethers.provider.getBalance(ownerB.address); - const depositWXTZ = await wxtzA.balanceOf(ownerB.address); - - expect(depositXTZ.toString()).eq("9999000000000000000000") - expect(depositWXTZ.toString()).eq("1000000000000000000") - - // Check withdraw with non-owner account leads to correct exchange of WXTZ for XTZ - await wxtzA.connect(ownerB).withdraw(BigInt("1000000000000000000")); - - const withdrawXTZ = await ethers.provider.getBalance(ownerB.address); - const withdrawWXTZ = await wxtzA.balanceOf(ownerB.address); - - expect(withdrawXTZ.toString()).eq("10000000000000000000000") - expect(withdrawWXTZ.toString()).eq("0") - }) - - // Test that you can't withdraw if you don't have WXTZ even after an empty deposit - it("Shouldn't withdraw XTZ without depositing WXTZ", async function () { - // Check initial balances - const initialXTZ = await ethers.provider.getBalance(ownerA.address); - const initialWXTZ = await wxtzA.balanceOf(ownerA.address); - expect(initialWXTZ.toString()).to.equal('0'); - - // Try to retrieve XTZ without having WXTZ - await expect(wxtzA.withdraw(ethers.utils.parseEther("1"))).to.be.reverted; - let afterWithdrawXTZ = await ethers.provider.getBalance(ownerA.address); - expect(afterWithdrawXTZ.toString()).to.equal(initialXTZ.toString()); - - // Test fake deposit before retrying - wxtzA.deposit({ value: 0 }); - await expect(wxtzA.withdraw(ethers.utils.parseEther("1"))).to.be.reverted; - afterWithdrawXTZ = await ethers.provider.getBalance(ownerA.address); - expect(afterWithdrawXTZ.toString()).to.equal(initialXTZ.toString()); - }); - - // Test that the deposit without giving XTZ doesn't give you WXTZ - it("Shouldn't withdraw WXTZ without depositing XTZ", async function () { - // Check initial balances - const initialXTZ = await ethers.provider.getBalance(ownerA.address); - const initialWXTZ = await wxtzA.balanceOf(ownerA.address); - - // call deposit but without tokens - wxtzA.deposit({ value: 0 }); - - // Checks balances after the call - const afterDepositXTZ = await ethers.provider.getBalance(ownerA.address); - const afterDepositWXTZ = await wxtzA.balanceOf(ownerA.address); - // Balances should be the same after - expect(afterDepositXTZ.toBigInt()).to.equal(initialXTZ.toBigInt()); - expect(afterDepositWXTZ.toBigInt()).to.equal(initialWXTZ.toBigInt()); - }); - - // Test that you can't receive WXTZ on other chain if you don't own some - it("Shouldn't send WXTZ cross chain if no WXTZ owned", async function () { - // Check initial balances on token A and B are 0 - const initialWXTZOnA = await wxtzA.balanceOf(ownerA.address); - const initialWXTZOnB = await wxtzB.balanceOf(ownerA.address); - expect(initialWXTZOnA.toString()).to.equal('0'); - expect(initialWXTZOnB.toString()).to.equal('0'); - - // send token cross chain - const extraOptions = "0x00030100110100000000000000000000000000030d40"; - const sendParam = { - dstEid: eidB, // Destination endpoint ID. - to: formatBytes32String(ownerA.address), // Recipient address. - amountLD: ethers.utils.parseEther("1"), // Amount to send in local decimals. - minAmountLD: 0, // Minimum amount to send in local decimals. - extraOptions: extraOptions, // Additional options supplied by the caller to be used in the LayerZero message. - composeMsg: "0x", // The composed message for the send() operation. - oftCmd: "0x", // The OFT command to be executed, unused in default OFT implementations. - }; - const estimatedGas = await wxtzA.quoteSend( - sendParam, - false // do we want to pay in lz token - ); - - // Should revert - await expect( - wxtzA.send( - sendParam, - estimatedGas, // messaging fee - ownerA.address, // refund address - { value: estimatedGas.nativeFee } // pay for the destination chain - ) - ).to.be.reverted; - - // Check balances after the send on token A and B are 0 - const afterSendWXTZOnA = await wxtzA.balanceOf(ownerA.address); - const afterSendWXTZOnB = await wxtzB.balanceOf(ownerA.address); - expect(afterSendWXTZOnA.toString()).to.equal('0'); - expect(afterSendWXTZOnB.toString()).to.equal('0'); - }); + // Constant representing a mock Endpoint ID for testing purposes + const eidA = 1 + const eidB = 2 + // Declaration of variables to be used in the test suite + let WXTZFactory: ContractFactory + let EndpointV2Mock: ContractFactory + let ownerA: SignerWithAddress + let ownerB: SignerWithAddress + let endpointOwner: SignerWithAddress + let wxtzA: Contract + let wxtzB: Contract + let mockEndpointV2A: Contract + let mockEndpointV2B: Contract + + // Before hook for setup that runs once before all tests in the block + before(async function () { + // Contract factory for our tested contract + // + // We are using a derived contract that exposes a mint() function for testing purposes + WXTZFactory = await ethers.getContractFactory('WXTZ') + + // Fetching the first three signers (accounts) from Hardhat's local Ethereum network + const signers = await ethers.getSigners() + + ownerA = signers.at(0)! + ownerB = signers.at(1)! + endpointOwner = signers.at(2)! + + // The EndpointV2Mock contract comes from @layerzerolabs/test-devtools-evm-hardhat package + // and its artifacts are connected as external artifacts to this project + // + // Unfortunately, hardhat itself does not yet provide a way of connecting external artifacts, + // so we rely on hardhat-deploy to create a ContractFactory for EndpointV2Mock + // + // See https://github.com/NomicFoundation/hardhat/issues/1040 + const EndpointV2MockArtifact = await deployments.getArtifact('EndpointV2Mock') + EndpointV2Mock = new ContractFactory(EndpointV2MockArtifact.abi, EndpointV2MockArtifact.bytecode, endpointOwner) + }) + + // beforeEach hook for setup that runs before each test in the block + beforeEach(async function () { + // Deploying a mock LZEndpoint with the given Endpoint ID + mockEndpointV2A = await EndpointV2Mock.deploy(eidA) + mockEndpointV2B = await EndpointV2Mock.deploy(eidB) + + // Deploying two instances of MyOFT contract with different identifiers and linking them to the mock LZEndpoint + wxtzA = await WXTZFactory.connect(ownerA).deploy(128123, mockEndpointV2A.address, ownerA.address) + wxtzB = await WXTZFactory.connect(ownerB).deploy(128123, mockEndpointV2B.address, ownerB.address) + + // Setting destination endpoints in the LZEndpoint mock for each MyOFT instance + await mockEndpointV2A.setDestLzEndpoint(wxtzB.address, mockEndpointV2B.address) + await mockEndpointV2B.setDestLzEndpoint(wxtzA.address, mockEndpointV2A.address) + + // Set the timelock for `setPeer()` on first call + await wxtzA.connect(ownerA).setPeer(eidB, ethers.utils.zeroPad(wxtzB.address, 32)) + await wxtzB.connect(ownerB).setPeer(eidA, ethers.utils.zeroPad(wxtzA.address, 32)) + + // advance time by 3 days and mine a new block + await time.increase(259200) + + // Setting each MyOFT instance as a peer of the other in the mock LZEndpoint + await wxtzA.connect(ownerA).setPeer(eidB, ethers.utils.zeroPad(wxtzB.address, 32)) + await wxtzB.connect(ownerB).setPeer(eidA, ethers.utils.zeroPad(wxtzA.address, 32)) + }) + + // Test that the constructor set the correct owner even if it is deployed but someone else + it('Should be the correct owner', async function () { + // Test already deployed ones + const wxtzAOwner = await wxtzA.owner() + const wxtzBOwner = await wxtzB.owner() + expect(wxtzAOwner).to.equal(ownerA.address) + expect(wxtzBOwner).to.equal(ownerB.address) + + // Test deployed by user A but owner should be new user + const newUser = ethers.Wallet.createRandom() + const newOFT = await WXTZFactory.connect(ownerA).deploy(128123, mockEndpointV2A.address, newUser.address) + const OFTOwner = await newOFT.owner() + expect(OFTOwner).to.equal(newUser.address) + }) + + // A test case to verify token deposit and withdraw functionality + // WARNING: you can only deposit and withdraw on Etherlink testnet and mainnet + it('Should be able to exchange XTZ for WXTZ 1:1', async function () { + // Check initial balances + const initialXTZ = await ethers.provider.getBalance(ownerA.address) + const initialWXTZ = await wxtzA.balanceOf(ownerA.address) + const initialTotalSupply = await wxtzA.totalSupply() + + expect(initialXTZ.toString()).eq('10000000000000000000000') + expect(initialWXTZ.toString()).eq('0') + expect(initialTotalSupply.toString()).eq('0') + + // Check deposit with non-owner account leads to correct exchange of XTZ for WXTZ + await wxtzA.connect(ownerB).deposit({ value: ethers.utils.parseEther('1') }) + + const depositXTZ = await ethers.provider.getBalance(ownerB.address) + const depositWXTZ = await wxtzA.balanceOf(ownerB.address) + + expect(depositXTZ.toString()).eq('9999000000000000000000') + expect(depositWXTZ.toString()).eq('1000000000000000000') + + // Check withdraw with non-owner account leads to correct exchange of WXTZ for XTZ + await wxtzA.connect(ownerB).withdraw(BigInt('1000000000000000000')) + + const withdrawXTZ = await ethers.provider.getBalance(ownerB.address) + const withdrawWXTZ = await wxtzA.balanceOf(ownerB.address) + + expect(withdrawXTZ.toString()).eq('10000000000000000000000') + expect(withdrawWXTZ.toString()).eq('0') + }) + + // Test that you can't withdraw if you don't have WXTZ even after an empty deposit + it("Shouldn't withdraw XTZ without depositing WXTZ", async function () { + // Check initial balances + const initialXTZ = await ethers.provider.getBalance(ownerA.address) + const initialWXTZ = await wxtzA.balanceOf(ownerA.address) + expect(initialWXTZ.toString()).to.equal('0') + + // Try to retrieve XTZ without having WXTZ + await expect(wxtzA.withdraw(ethers.utils.parseEther('1'))).to.be.reverted + let afterWithdrawXTZ = await ethers.provider.getBalance(ownerA.address) + expect(afterWithdrawXTZ.toString()).to.equal(initialXTZ.toString()) + + // Test fake deposit before retrying + wxtzA.deposit({ value: 0 }) + await expect(wxtzA.withdraw(ethers.utils.parseEther('1'))).to.be.reverted + afterWithdrawXTZ = await ethers.provider.getBalance(ownerA.address) + expect(afterWithdrawXTZ.toString()).to.equal(initialXTZ.toString()) + }) + + // Test that the deposit without giving XTZ doesn't give you WXTZ + it("Shouldn't withdraw WXTZ without depositing XTZ", async function () { + // Check initial balances + const initialXTZ = await ethers.provider.getBalance(ownerA.address) + const initialWXTZ = await wxtzA.balanceOf(ownerA.address) + + // call deposit but without tokens + wxtzA.deposit({ value: 0 }) + + // Checks balances after the call + const afterDepositXTZ = await ethers.provider.getBalance(ownerA.address) + const afterDepositWXTZ = await wxtzA.balanceOf(ownerA.address) + // Balances should be the same after + expect(afterDepositXTZ.toBigInt()).to.equal(initialXTZ.toBigInt()) + expect(afterDepositWXTZ.toBigInt()).to.equal(initialWXTZ.toBigInt()) + }) + + // Test that you can't receive WXTZ on other chain if you don't own some + it("Shouldn't send WXTZ cross chain if no WXTZ owned", async function () { + // Check initial balances on token A and B are 0 + const initialWXTZOnA = await wxtzA.balanceOf(ownerA.address) + const initialWXTZOnB = await wxtzB.balanceOf(ownerA.address) + expect(initialWXTZOnA.toString()).to.equal('0') + expect(initialWXTZOnB.toString()).to.equal('0') + + // send token cross chain + const extraOptions = '0x00030100110100000000000000000000000000030d40' + const sendParam = { + dstEid: eidB, // Destination endpoint ID. + to: formatBytes32String(ownerA.address), // Recipient address. + amountLD: ethers.utils.parseEther('1'), // Amount to send in local decimals. + minAmountLD: 0, // Minimum amount to send in local decimals. + extraOptions: extraOptions, // Additional options supplied by the caller to be used in the LayerZero message. + composeMsg: '0x', // The composed message for the send() operation. + oftCmd: '0x', // The OFT command to be executed, unused in default OFT implementations. + } + const estimatedGas = await wxtzA.quoteSend( + sendParam, + false // do we want to pay in lz token + ) + + // Should revert + await expect( + wxtzA.send( + sendParam, + estimatedGas, // messaging fee + ownerA.address, // refund address + { value: estimatedGas.nativeFee } // pay for the destination chain + ) + ).to.be.reverted + + // Check balances after the send on token A and B are 0 + const afterSendWXTZOnA = await wxtzA.balanceOf(ownerA.address) + const afterSendWXTZOnB = await wxtzB.balanceOf(ownerA.address) + expect(afterSendWXTZOnA.toString()).to.equal('0') + expect(afterSendWXTZOnB.toString()).to.equal('0') + }) })