From 3d619299884692b83a06fe1f4ffb7394c04079f4 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Tue, 10 Dec 2024 19:37:54 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=9E=EF=B8=8F=20Add=20mint=20and=20burn?= =?UTF-8?q?=20oft=20adapter=20example=20(#1120)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/mint-burn-oft-adapter/.env.example | 15 + examples/mint-burn-oft-adapter/.eslintignore | 10 + examples/mint-burn-oft-adapter/.eslintrc.js | 10 + examples/mint-burn-oft-adapter/.gitignore | 24 ++ examples/mint-burn-oft-adapter/.nvmrc | 1 + .../mint-burn-oft-adapter/.prettierignore | 10 + examples/mint-burn-oft-adapter/.prettierrc.js | 3 + examples/mint-burn-oft-adapter/README.md | 195 ++++++++++ .../contracts/MyMintBurnOFTAdapter.sol | 25 ++ .../mint-burn-oft-adapter/contracts/MyOFT.sol | 14 + .../contracts/mocks/MintBurnERC20Mock.sol | 48 +++ .../mocks/MyMintBurnOFTAdapterMock.sol | 16 + .../contracts/mocks/MyOFTMock.sol | 19 + .../deploy/MyMintBurnOFTAdapter.ts | 62 +++ .../mint-burn-oft-adapter/deploy/MyOFT.ts | 60 +++ examples/mint-burn-oft-adapter/foundry.toml | 31 ++ .../mint-burn-oft-adapter/hardhat.config.ts | 87 +++++ .../mint-burn-oft-adapter/layerzero.config.ts | 75 ++++ examples/mint-burn-oft-adapter/package.json | 75 ++++ .../mint-burn-oft-adapter/solhint.config.js | 1 + .../test/foundry/MyMintBurnOFTAdapter.t.sol | 180 +++++++++ .../test/hardhat/MyMintBurnOFTAdapter.test.ts | 117 ++++++ .../test/mocks/MintBurnERC20Mock.sol | 48 +++ .../test/mocks/MintBurnOFTAdapterMock.sol | 40 ++ .../test/mocks/OFTComposerMock.sol | 27 ++ .../test/mocks/OFTMock.sol | 59 +++ examples/mint-burn-oft-adapter/tsconfig.json | 13 + .../mint-burn-oft-adapter/type-extensions.ts | 23 ++ package.json | 6 +- packages/create-lz-oapp/src/config.ts | 12 + pnpm-lock.yaml | 356 ++++++++++++------ tests-user/tests/create-lz-oapp.bats | 33 ++ turbo.json | 1 + 33 files changed, 1577 insertions(+), 119 deletions(-) create mode 100644 examples/mint-burn-oft-adapter/.env.example create mode 100644 examples/mint-burn-oft-adapter/.eslintignore create mode 100644 examples/mint-burn-oft-adapter/.eslintrc.js create mode 100644 examples/mint-burn-oft-adapter/.gitignore create mode 100644 examples/mint-burn-oft-adapter/.nvmrc create mode 100644 examples/mint-burn-oft-adapter/.prettierignore create mode 100644 examples/mint-burn-oft-adapter/.prettierrc.js create mode 100644 examples/mint-burn-oft-adapter/README.md create mode 100644 examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol create mode 100644 examples/mint-burn-oft-adapter/contracts/MyOFT.sol create mode 100644 examples/mint-burn-oft-adapter/contracts/mocks/MintBurnERC20Mock.sol create mode 100644 examples/mint-burn-oft-adapter/contracts/mocks/MyMintBurnOFTAdapterMock.sol create mode 100644 examples/mint-burn-oft-adapter/contracts/mocks/MyOFTMock.sol create mode 100644 examples/mint-burn-oft-adapter/deploy/MyMintBurnOFTAdapter.ts create mode 100644 examples/mint-burn-oft-adapter/deploy/MyOFT.ts create mode 100644 examples/mint-burn-oft-adapter/foundry.toml create mode 100644 examples/mint-burn-oft-adapter/hardhat.config.ts create mode 100644 examples/mint-burn-oft-adapter/layerzero.config.ts create mode 100644 examples/mint-burn-oft-adapter/package.json create mode 100644 examples/mint-burn-oft-adapter/solhint.config.js create mode 100644 examples/mint-burn-oft-adapter/test/foundry/MyMintBurnOFTAdapter.t.sol create mode 100644 examples/mint-burn-oft-adapter/test/hardhat/MyMintBurnOFTAdapter.test.ts create mode 100644 examples/mint-burn-oft-adapter/test/mocks/MintBurnERC20Mock.sol create mode 100644 examples/mint-burn-oft-adapter/test/mocks/MintBurnOFTAdapterMock.sol create mode 100644 examples/mint-burn-oft-adapter/test/mocks/OFTComposerMock.sol create mode 100644 examples/mint-burn-oft-adapter/test/mocks/OFTMock.sol create mode 100644 examples/mint-burn-oft-adapter/tsconfig.json create mode 100644 examples/mint-burn-oft-adapter/type-extensions.ts diff --git a/examples/mint-burn-oft-adapter/.env.example b/examples/mint-burn-oft-adapter/.env.example new file mode 100644 index 000000000..197ba1d67 --- /dev/null +++ b/examples/mint-burn-oft-adapter/.env.example @@ -0,0 +1,15 @@ +# .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.- +# / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ +# `-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' +# +# Example environment configuration +# +# .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.- +# / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ +# `-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' + +# By default, the examples support both mnemonic-based and private key-based authentication +# +# You don't need to set both of these values, just pick the one that you prefer and set that one +MNEMONIC= +PRIVATE_KEY= \ No newline at end of file diff --git a/examples/mint-burn-oft-adapter/.eslintignore b/examples/mint-burn-oft-adapter/.eslintignore new file mode 100644 index 000000000..ee9f768fd --- /dev/null +++ b/examples/mint-burn-oft-adapter/.eslintignore @@ -0,0 +1,10 @@ +artifacts +cache +dist +node_modules +out +*.log +*.sol +*.yaml +*.lock +package-lock.json \ No newline at end of file diff --git a/examples/mint-burn-oft-adapter/.eslintrc.js b/examples/mint-burn-oft-adapter/.eslintrc.js new file mode 100644 index 000000000..f0ea891fd --- /dev/null +++ b/examples/mint-burn-oft-adapter/.eslintrc.js @@ -0,0 +1,10 @@ +require('@rushstack/eslint-patch/modern-module-resolution'); + +module.exports = { + extends: ['@layerzerolabs/eslint-config-next/recommended'], + rules: { + // @layerzerolabs/eslint-config-next defines rules for turborepo-based projects + // that are not relevant for this particular project + 'turbo/no-undeclared-env-vars': 'off', + }, +}; diff --git a/examples/mint-burn-oft-adapter/.gitignore b/examples/mint-burn-oft-adapter/.gitignore new file mode 100644 index 000000000..e2face954 --- /dev/null +++ b/examples/mint-burn-oft-adapter/.gitignore @@ -0,0 +1,24 @@ +node_modules +.env +coverage +coverage.json +typechain +typechain-types + +# Hardhat files +cache +artifacts + + +# LayerZero specific files +.layerzero + +# foundry test compilation files +out + +# pnpm +pnpm-error.log + +# Editor and OS files +.DS_Store +.idea diff --git a/examples/mint-burn-oft-adapter/.nvmrc b/examples/mint-burn-oft-adapter/.nvmrc new file mode 100644 index 000000000..b714151ef --- /dev/null +++ b/examples/mint-burn-oft-adapter/.nvmrc @@ -0,0 +1 @@ +v18.18.0 \ No newline at end of file diff --git a/examples/mint-burn-oft-adapter/.prettierignore b/examples/mint-burn-oft-adapter/.prettierignore new file mode 100644 index 000000000..6e8232f5a --- /dev/null +++ b/examples/mint-burn-oft-adapter/.prettierignore @@ -0,0 +1,10 @@ +artifacts/ +cache/ +dist/ +node_modules/ +out/ +*.log +*ignore +*.yaml +*.lock +package-lock.json \ No newline at end of file diff --git a/examples/mint-burn-oft-adapter/.prettierrc.js b/examples/mint-burn-oft-adapter/.prettierrc.js new file mode 100644 index 000000000..6f55b4019 --- /dev/null +++ b/examples/mint-burn-oft-adapter/.prettierrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('@layerzerolabs/prettier-config-next'), +}; diff --git a/examples/mint-burn-oft-adapter/README.md b/examples/mint-burn-oft-adapter/README.md new file mode 100644 index 000000000..e9cde1ed6 --- /dev/null +++ b/examples/mint-burn-oft-adapter/README.md @@ -0,0 +1,195 @@ +

+ + LayerZero + +

+

MintBurnOFTAdapter Example

+ +

+ Quickstart | Configuration | Message Execution Options | Endpoint Addresses +

+ +

This repository contains an example implementation of the MintBurnOFTAdapter, a variant of the OFTAdapter.sol standard from LayerZero. The purpose of this contract is to enable the deployment of more than one OFTAdapter within the mesh network, by utilziing an already deployed ERC20 token's external mint and burn methods on each chain.

+ +

+ Homepage | Docs | Developers +

+ +--- + +- [Usage](#usage) + - [Developing Contracts](#developing-contracts) + - [Installing dependencies](#installing-dependencies) + - [Compiling your contracts](#compiling-your-contracts) + - [Running tests](#running-tests) + - [Deploying Contracts](#deploying-contracts) +- [What is an OFT Adapter?](#what-is-an-oft-adapter) +- [Key Features](#key-features) +- [Deployment Requirements](#deployment-requirements) +- [MintBurnOFTAdapter](#mintburnoftadapter) +- [Requirement](#requirement) +- [Contracts Structure](#contracts-structure) + - [`MinterBurner`](#minterburner) + - [`MintBurnOFTAdapter.sol`](#mintburnoftadaptersol) + - [Variables](#variables-1) + - [Functions](#functions-1) + +## Usage + +### OFTAdapter additional setup: + +- In your `hardhat.config.ts` file, add the following configuration to the network you want to deploy the OFTAdapter to: + ```typescript + // Replace `0x0` with the address of the ERC20 token you want to adapt to the OFT functionality. + oftAdapter: { + tokenAddress: '0x0', + } + ``` + +### Developing Contracts + +#### Installing dependencies + +We recommend using `pnpm` as a package manager (but you can of course use a package manager of your choice): + +```bash +pnpm install +``` + +#### Compiling your contracts + +This project supports both `hardhat` and `forge` compilation. By default, the `compile` command will execute both: + +```bash +pnpm compile +``` + +If you prefer one over the other, you can use the tooling-specific commands: + +```bash +pnpm compile:forge +pnpm compile:hardhat +``` + +Or adjust the `package.json` to for example remove `forge` build: + +```diff +- "compile": "$npm_execpath run compile:forge && $npm_execpath run compile:hardhat", +- "compile:forge": "forge build", +- "compile:hardhat": "hardhat compile", ++ "compile": "hardhat compile" +``` + +#### Running tests + +Similarly to the contract compilation, we support both `hardhat` and `forge` tests. By default, the `test` command will execute both: + +```bash +pnpm test +``` + +If you prefer one over the other, you can use the tooling-specific commands: + +```bash +pnpm test:forge +pnpm test:hardhat +``` + +Or adjust the `package.json` to for example remove `hardhat` tests: + +```diff +- "test": "$npm_execpath test:forge && $npm_execpath test:hardhat", +- "test:forge": "forge test", +- "test:hardhat": "$npm_execpath hardhat test" ++ "test": "forge test" +``` + +### Deploying Contracts + +Set up deployer wallet/account: + +- Rename `.env.example` -> `.env` +- Choose your preferred means of setting up your deployer wallet/account: + +``` +MNEMONIC="test test test test test test test test test test test junk" +or... +PRIVATE_KEY="0xabc...def" +``` + +- Fund this address with the corresponding chain's native tokens you want to deploy to. + +To deploy your contracts to your desired blockchains, run the following command in your project's folder: + +```bash +npx hardhat lz:deploy +``` + +More information about available CLI arguments can be found using the `--help` flag: + +```bash +npx hardhat lz:deploy --help +``` + +By following these steps, you can focus more on creating innovative omnichain solutions and less on the complexities of cross-chain communication. + +

+ +

+ Join our community on Discord | Follow us on Twitter +

+ +## What is an OFT Adapter? + +OFT Adapter allows an existing token to expand to any supported chain as a native token with a unified global supply, inheriting all the features of the OFT Standard. This works as an intermediary contract that handles sending and receiving tokens that have already been deployed. Read more [here](https://docs.layerzero.network/v2/developers/evm/oft/adapter). + +Ideally, when you want to convert an existing ERC20 token with its current fixed supply into an Omnichain token, you can use the OFTAdapter as a wrapper around that ERC20. + +There are several ways to go about it since the base code of OFTAdapter keeps contract logic implementation up to the developer. Eg., the Adapter could be implemented in such a way that the original ERC20 is locked inside the Adapter on chain A and the OFT is minted on chain B. + +## Key Features + +- **Mint and Burn Access**: Enables the MintBurnOFTAdapter to interact with ERC20 tokens that have minting and burning capabilities. This is crucial for maintaining unified token supply across different blockchain networks in a decentralized manner. + +- **Access Control Integration**: Ensures that only authorized entities (deployers or specific contracts) have the permissions to mint and burn tokens. This is managed through an access control or allowlist mechanism. + +- **Multiple Adapter Deployments**: Supports the deployment of multiple instances of the MintBurnOFTAdapter, each configured with different token contracts and LayerZero endpoints, thus enhancing flexibility in cross-chain operations. + +## Deployment Requirements + +1. **ERC20 Token Access**: The deployer must ensure that the ERC20 token contract allows the MintBurnOFTAdapter to access its mint and burn methods. This typically requires configuring the ERC20 token's access control mechanisms to include the adapter's address in an allowlist. + +2. **Adapter Deployment and Configuration**: + Deploy the MintBurnOFTAdapter with references to the ERC20 token, the LayerZero endpoint, and any relevant delegate addresses. + Add the address of the newly deployed MintBurnOFTAdapter to the ERC20 token's allowlist to enable minting and burning. + +## MintBurnOFTAdapter + +[`MyMintBurnOFTAdapter`](./contracts/MyMintBurnOFTAdapter.sol) is a variant of OFT Adapter that can use a token's external permissions to **burn** on chain A (source chain), as opposed to **lock**, and mint on chain B (destination chain). + +## Requirement + +The only requirement is that the base ERC20 must have an external or public `burn` and a `mint` function, and implement the `IMintableBurnable.sol` interface found in `./devtools/packages/oft-evm/interfaces/IMintableBurnable.sol`. + +## Contracts Structure + +### `MinterBurner` + +This is a periphery contract for minting or burning tokens and executing arbitrary calls on the underlying ERC20. + +### `MintBurnOFTAdapter.sol` + +#### Variables + +This is the actual OFT Adapter contract that maintains two constants: `innerToken` and `minterBurner` + +- `innerToken`: underlying ERC20 implementation +- `minterBurner`: reference to the `IMintableBurnable` implementation that has the implementation of `burn` and `mint` functions + +#### Functions + +- `_debit`: Calls `burn` on `minterBurner` effectively burning tokens from sender's balance from source chain. +- `_credit`: Calls `mint` on `minterBurner`, effectively minting tokens to sender's balance on destination chain. + +> [!IMPORTANT] +> The default `OFTAdapter` implementation assumes **lossless** transfers, ie. 1 token in = 1 token out. If the underlying ERC20 applies something like a transfer fee, the default will **not** work. A pre/post balance check will need to be added to calculate the `amountReceivedLD`. diff --git a/examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol b/examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol new file mode 100644 index 000000000..b03d501dd --- /dev/null +++ b/examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.22; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { MintBurnOFTAdapter } from "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol"; +import { IMintableBurnable } from "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol"; + +/** + * @title MintBurnOFTAdapter Contract + * @dev MintBurnOFTAdapter is a contract that adapts an ERC-20 token with external mint and burn logic to the OFT functionality. + * + * @dev For existing ERC20 tokens with exposed mint and burn permissions, this can be used to convert the token to crosschain compatibility. + * @dev Unlike the vanilla OFT Adapter, multiple of these can exist for a given global mesh. + * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out. + * IF the 'innerToken' applies something like a transfer fee, the default will NOT work... + * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD. + */ +contract MyMintBurnOFTAdapter is MintBurnOFTAdapter { + constructor( + address _token, + IMintableBurnable _minterBurner, + address _lzEndpoint, + address _delegate + ) MintBurnOFTAdapter(_token, _minterBurner, _lzEndpoint, _delegate) Ownable(_delegate) {} +} diff --git a/examples/mint-burn-oft-adapter/contracts/MyOFT.sol b/examples/mint-burn-oft-adapter/contracts/MyOFT.sol new file mode 100644 index 000000000..f8bc7b47f --- /dev/null +++ b/examples/mint-burn-oft-adapter/contracts/MyOFT.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.22; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol"; + +contract MyOFT is OFT { + constructor( + string memory _name, + string memory _symbol, + address _lzEndpoint, + address _delegate + ) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {} +} diff --git a/examples/mint-burn-oft-adapter/contracts/mocks/MintBurnERC20Mock.sol b/examples/mint-burn-oft-adapter/contracts/mocks/MintBurnERC20Mock.sol new file mode 100644 index 000000000..ba2fc5227 --- /dev/null +++ b/examples/mint-burn-oft-adapter/contracts/mocks/MintBurnERC20Mock.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +import { IMintableBurnable } from "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol"; + +/** + * @title MintBurnERC20Mock + * + * @dev WARNING: This contract is for testing purposes only. + * In a production scenario, the `mint` and `burn` methods + * should be guarded by appropriate access control mechanisms. + */ +contract MintBurnERC20Mock is ERC20, IMintableBurnable { + /// @notice Constructor to initialize the ERC20 token with a name and symbol. + constructor(string memory name, string memory symbol) ERC20(name, symbol) {} + + /** + * @notice Burns a specific amount of tokens from a given address. + * + * @dev WARNING: In production, this function should have access control. + * + * @param _from The address from which tokens will be burned. + * @param _amount The amount of tokens to burn. + * + * @return A boolean indicating the success of the burn operation. + */ + function burn(address _from, uint256 _amount) external returns (bool) { + _burn(_from, _amount); + return true; + } + + /** + * @notice Mints a specific amount of tokens to a given address. + * + * @dev WARNING: In production, this function should have access control. + * + * @param _to The address to which tokens will be minted. + * @param _amount The amount of tokens to mint. + * + * @return A boolean indicating the success of the mint operation. + */ + function mint(address _to, uint256 _amount) external returns (bool) { + _mint(_to, _amount); + return true; + } +} diff --git a/examples/mint-burn-oft-adapter/contracts/mocks/MyMintBurnOFTAdapterMock.sol b/examples/mint-burn-oft-adapter/contracts/mocks/MyMintBurnOFTAdapterMock.sol new file mode 100644 index 000000000..888b7e78b --- /dev/null +++ b/examples/mint-burn-oft-adapter/contracts/mocks/MyMintBurnOFTAdapterMock.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.22; + +import { MyMintBurnOFTAdapter } from "../MyMintBurnOFTAdapter.sol"; + +import { IMintableBurnable } from "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol"; + +// @dev WARNING: This is for testing purposes only +contract MyMintBurnOFTAdapterMock is MyMintBurnOFTAdapter { + constructor( + address _token, + IMintableBurnable _minterBurner, + address _lzEndpoint, + address _delegate + ) MyMintBurnOFTAdapter(_token, _minterBurner, _lzEndpoint, _delegate) {} +} diff --git a/examples/mint-burn-oft-adapter/contracts/mocks/MyOFTMock.sol b/examples/mint-burn-oft-adapter/contracts/mocks/MyOFTMock.sol new file mode 100644 index 000000000..71ee1d841 --- /dev/null +++ b/examples/mint-burn-oft-adapter/contracts/mocks/MyOFTMock.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.22; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol"; + +// @dev WARNING: This is for testing purposes only +contract MyOFTMock is OFT { + constructor( + string memory _name, + string memory _symbol, + address _lzEndpoint, + address _delegate + ) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {} + + function mint(address _to, uint256 _amount) public { + _mint(_to, _amount); + } +} diff --git a/examples/mint-burn-oft-adapter/deploy/MyMintBurnOFTAdapter.ts b/examples/mint-burn-oft-adapter/deploy/MyMintBurnOFTAdapter.ts new file mode 100644 index 000000000..6fec9ed5f --- /dev/null +++ b/examples/mint-burn-oft-adapter/deploy/MyMintBurnOFTAdapter.ts @@ -0,0 +1,62 @@ +import assert from 'assert' + +import { type DeployFunction } from 'hardhat-deploy/types' + +const contractName = 'MyMintBurnOFTAdapter' + +const deploy: DeployFunction = async (hre) => { + const { getNamedAccounts, deployments } = hre + + const { deploy } = deployments + const { deployer } = await getNamedAccounts() + + assert(deployer, 'Missing named deployer account') + + console.log(`Network: ${hre.network.name}`) + console.log(`Deployer: ${deployer}`) + + // This is an external deployment pulled in from @layerzerolabs/lz-evm-sdk-v2 + // + // @layerzerolabs/toolbox-hardhat takes care of plugging in the external deployments + // from @layerzerolabs packages based on the configuration in your hardhat config + // + // For this to work correctly, your network config must define an eid property + // set to `EndpointId` as defined in @layerzerolabs/lz-definitions + // + // For example: + // + // networks: { + // fuji: { + // ... + // eid: EndpointId.AVALANCHE_V2_TESTNET + // } + // } + const endpointV2Deployment = await hre.deployments.get('EndpointV2') + const minterBurnerAddress = '' + + // The token address must be defined in hardhat.config.ts + // If the token address is not defined, the deployment will log a warning and skip the deployment + if (hre.network.config.oftAdapter == null) { + console.warn(`oftAdapter not configured on network config, skipping OFTWrapper deployment`) + + return + } + + const { address } = await deploy(contractName, { + from: deployer, + args: [ + hre.network.config.oftAdapter.tokenAddress, // token address + minterBurnerAddress, // token address implementing IMintableBurnable + endpointV2Deployment.address, // LayerZero's EndpointV2 address + deployer, // owner + ], + log: true, + skipIfAlreadyDeployed: false, + }) + + console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${address}`) +} + +deploy.tags = [contractName] + +export default deploy diff --git a/examples/mint-burn-oft-adapter/deploy/MyOFT.ts b/examples/mint-burn-oft-adapter/deploy/MyOFT.ts new file mode 100644 index 000000000..5db617116 --- /dev/null +++ b/examples/mint-burn-oft-adapter/deploy/MyOFT.ts @@ -0,0 +1,60 @@ +import assert from 'assert' + +import { type DeployFunction } from 'hardhat-deploy/types' + +const contractName = 'MyOFT' + +const deploy: DeployFunction = async (hre) => { + const { getNamedAccounts, deployments } = hre + + const { deploy } = deployments + const { deployer } = await getNamedAccounts() + + assert(deployer, 'Missing named deployer account') + + console.log(`Network: ${hre.network.name}`) + console.log(`Deployer: ${deployer}`) + + // This is an external deployment pulled in from @layerzerolabs/lz-evm-sdk-v2 + // + // @layerzerolabs/toolbox-hardhat takes care of plugging in the external deployments + // from @layerzerolabs packages based on the configuration in your hardhat config + // + // For this to work correctly, your network config must define an eid property + // set to `EndpointId` as defined in @layerzerolabs/lz-definitions + // + // For example: + // + // networks: { + // fuji: { + // ... + // eid: EndpointId.AVALANCHE_V2_TESTNET + // } + // } + const endpointV2Deployment = await hre.deployments.get('EndpointV2') + + // If the oftAdapter configuration is defined on a network that is deploying an OFT, + // the deployment will log a warning and skip the deployment + if (hre.network.config.oftAdapter != null) { + console.warn(`oftAdapter configuration found on OFT deployment, skipping OFT deployment`) + return + } + + const { address } = await deploy(contractName, { + from: deployer, + args: [ + 'MyOFT', // name + 'MOFT', // symbol + endpointV2Deployment.address, // LayerZero's EndpointV2 address + deployer, // owner + ], + log: true, + skipIfAlreadyDeployed: false, + }) + + console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${address}`) +} + +deploy.tags = [contractName] + +export default deploy diff --git a/examples/mint-burn-oft-adapter/foundry.toml b/examples/mint-burn-oft-adapter/foundry.toml new file mode 100644 index 000000000..2b46113c9 --- /dev/null +++ b/examples/mint-burn-oft-adapter/foundry.toml @@ -0,0 +1,31 @@ +[profile.default] +solc-version = '0.8.22' +src = 'contracts' +out = 'out' +test = 'test/foundry' +cache_path = 'cache/foundry' +verbosity = 3 +libs = [ + # We provide a set of useful contract utilities + # in the lib directory of @layerzerolabs/toolbox-foundry: + # + # - forge-std + # - ds-test + # - solidity-bytes-utils + 'node_modules/@layerzerolabs/toolbox-foundry/lib', + 'node_modules', +] + +remappings = [ + # Due to a misconfiguration of solidity-bytes-utils, an outdated version + # of forge-std is being dragged in + # + # To remedy this, we'll remap the ds-test and forge-std imports to our own versions + 'ds-test/=node_modules/@layerzerolabs/toolbox-foundry/lib/ds-test', + 'forge-std/=node_modules/@layerzerolabs/toolbox-foundry/lib/forge-std', + '@layerzerolabs/=node_modules/@layerzerolabs/', + '@openzeppelin/=node_modules/@openzeppelin/', +] + +[fuzz] +runs = 1000 diff --git a/examples/mint-burn-oft-adapter/hardhat.config.ts b/examples/mint-burn-oft-adapter/hardhat.config.ts new file mode 100644 index 000000000..b1fbe86f8 --- /dev/null +++ b/examples/mint-burn-oft-adapter/hardhat.config.ts @@ -0,0 +1,87 @@ +// Get the environment configuration from .env file +// +// To make use of automatic environment setup: +// - Duplicate .env.example file and name it .env +// - Fill in the environment variables +import 'dotenv/config' + +import 'hardhat-deploy' +import 'hardhat-contract-sizer' +import '@nomiclabs/hardhat-ethers' +import '@layerzerolabs/toolbox-hardhat' +import { HardhatUserConfig, HttpNetworkAccountsUserConfig } from 'hardhat/types' + +import { EndpointId } from '@layerzerolabs/lz-definitions' + +import './type-extensions' + +// Set your preferred authentication method +// +// If you prefer using a mnemonic, set a MNEMONIC environment variable +// to a valid mnemonic +const MNEMONIC = process.env.MNEMONIC + +// If you prefer to be authenticated using a private key, set a PRIVATE_KEY environment variable +const PRIVATE_KEY = process.env.PRIVATE_KEY + +const accounts: HttpNetworkAccountsUserConfig | undefined = MNEMONIC + ? { 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.' + ) +} + +const config: HardhatUserConfig = { + paths: { + cache: 'cache/hardhat', + }, + solidity: { + compilers: [ + { + version: '0.8.22', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + ], + }, + networks: { + 'sepolia-testnet': { + eid: EndpointId.SEPOLIA_V2_TESTNET, + url: process.env.RPC_URL_SEPOLIA || 'https://rpc.sepolia.org/', + accounts, + oftAdapter: { + tokenAddress: '0x0', // Set the token address for the OFT adapter + }, + }, + 'avalanche-testnet': { + eid: EndpointId.AVALANCHE_V2_TESTNET, + url: process.env.RPC_URL_FUJI || 'https://rpc.ankr.com/avalanche_fuji', + accounts, + }, + 'amoy-testnet': { + eid: EndpointId.AMOY_V2_TESTNET, + url: process.env.RPC_URL_AMOY || 'https://polygon-amoy-bor-rpc.publicnode.com', + accounts, + }, + hardhat: { + // Need this for testing because TestHelperOz5.sol is exceeding the compiled contract size limit + allowUnlimitedContractSize: true, + }, + }, + namedAccounts: { + deployer: { + default: 0, // wallet address of index[0], of the mnemonic in .env + }, + }, +} + +export default config diff --git a/examples/mint-burn-oft-adapter/layerzero.config.ts b/examples/mint-burn-oft-adapter/layerzero.config.ts new file mode 100644 index 000000000..96e2a6969 --- /dev/null +++ b/examples/mint-burn-oft-adapter/layerzero.config.ts @@ -0,0 +1,75 @@ +import { EndpointId } from '@layerzerolabs/lz-definitions' + +import type { OAppOmniGraphHardhat, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat' + +/** + * WARNING: ONLY 1 OFTAdapter should exist for a given global mesh. + * The token address for the adapter should be defined in hardhat.config. This will be used in deployment. + * + * for example: + * + * sepolia: { + * eid: EndpointId.SEPOLIA_V2_TESTNET, + * url: process.env.RPC_URL_SEPOLIA || 'https://rpc.sepolia.org/', + * accounts, + * oft-adapter: { + * tokenAddress: '0x0', // Set the token address for the OFT adapter + * }, + * }, + */ +const sepoliaContract: OmniPointHardhat = { + eid: EndpointId.SEPOLIA_V2_TESTNET, + contractName: 'MyMintBurnOFTAdapter', +} + +const fujiContract: OmniPointHardhat = { + eid: EndpointId.AVALANCHE_V2_TESTNET, + contractName: 'MyOFT', +} + +const amoyContract: OmniPointHardhat = { + eid: EndpointId.AMOY_V2_TESTNET, + contractName: 'MyOFT', +} + +const config: OAppOmniGraphHardhat = { + contracts: [ + { + contract: fujiContract, + }, + { + contract: sepoliaContract, + }, + { + contract: amoyContract, + }, + ], + connections: [ + { + from: fujiContract, + to: sepoliaContract, + }, + { + from: fujiContract, + to: amoyContract, + }, + { + from: sepoliaContract, + to: fujiContract, + }, + { + from: sepoliaContract, + to: amoyContract, + }, + { + from: amoyContract, + to: sepoliaContract, + }, + { + from: amoyContract, + to: fujiContract, + }, + ], +} + +export default config diff --git a/examples/mint-burn-oft-adapter/package.json b/examples/mint-burn-oft-adapter/package.json new file mode 100644 index 000000000..c0e207272 --- /dev/null +++ b/examples/mint-burn-oft-adapter/package.json @@ -0,0 +1,75 @@ +{ + "name": "@layerzerolabs/mint-burn-oft-adapter-example", + "version": "0.0.1", + "private": true, + "license": "MIT", + "scripts": { + "clean": "rm -rf artifacts cache out", + "compile": "concurrently -c auto --names forge,hardhat '$npm_execpath run compile:forge' '$npm_execpath run compile:hardhat'", + "compile:forge": "forge build", + "compile:hardhat": "hardhat compile", + "lint": "$npm_execpath run lint:js && $npm_execpath run lint:sol", + "lint:fix": "eslint --fix '**/*.{js,ts,json}' && prettier --write . && solhint 'contracts/**/*.sol' --fix --noPrompt", + "lint:js": "eslint '**/*.{js,ts,json}' && prettier --check .", + "lint:sol": "solhint 'contracts/**/*.sol'", + "test": "$npm_execpath run test:forge && $npm_execpath run test:hardhat", + "test:forge": "forge test", + "test:hardhat": "hardhat test" + }, + "resolutions": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + }, + "devDependencies": { + "@babel/core": "^7.23.9", + "@layerzerolabs/eslint-config-next": "~2.3.39", + "@layerzerolabs/lz-definitions": "^3.0.21", + "@layerzerolabs/lz-evm-messagelib-v2": "^3.0.12", + "@layerzerolabs/lz-evm-protocol-v2": "^3.0.12", + "@layerzerolabs/lz-evm-v1-0.7": "^3.0.12", + "@layerzerolabs/lz-v2-utilities": "^3.0.12", + "@layerzerolabs/oapp-evm": "^0.3.0", + "@layerzerolabs/oft-evm": "^3.0.0", + "@layerzerolabs/prettier-config-next": "^2.3.39", + "@layerzerolabs/solhint-config": "^3.0.12", + "@layerzerolabs/test-devtools-evm-foundry": "~5.1.0", + "@layerzerolabs/toolbox-foundry": "~0.1.9", + "@layerzerolabs/toolbox-hardhat": "~0.6.1", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomiclabs/hardhat-ethers": "^2.2.3", + "@openzeppelin/contracts": "^5.0.2", + "@openzeppelin/contracts-upgradeable": "^5.0.2", + "@rushstack/eslint-patch": "^1.7.0", + "@types/chai": "^4.3.11", + "@types/mocha": "^10.0.6", + "@types/node": "~18.18.14", + "chai": "^4.4.1", + "concurrently": "~9.1.0", + "dotenv": "^16.4.1", + "eslint": "^8.55.0", + "eslint-plugin-jest-extended": "~2.0.0", + "ethers": "^5.7.2", + "hardhat": "^2.22.10", + "hardhat-contract-sizer": "^2.10.0", + "hardhat-deploy": "^0.12.1", + "mocha": "^10.2.0", + "prettier": "^3.2.5", + "solhint": "^4.1.1", + "solidity-bytes-utils": "^0.8.2", + "ts-node": "^10.9.2", + "typescript": "^5.4.4" + }, + "engines": { + "node": ">=18.16.0" + }, + "pnpm": { + "overrides": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + } + }, + "overrides": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + } +} diff --git a/examples/mint-burn-oft-adapter/solhint.config.js b/examples/mint-burn-oft-adapter/solhint.config.js new file mode 100644 index 000000000..52efe629c --- /dev/null +++ b/examples/mint-burn-oft-adapter/solhint.config.js @@ -0,0 +1 @@ +module.exports = require('@layerzerolabs/solhint-config'); diff --git a/examples/mint-burn-oft-adapter/test/foundry/MyMintBurnOFTAdapter.t.sol b/examples/mint-burn-oft-adapter/test/foundry/MyMintBurnOFTAdapter.t.sol new file mode 100644 index 000000000..a00532ef1 --- /dev/null +++ b/examples/mint-burn-oft-adapter/test/foundry/MyMintBurnOFTAdapter.t.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +// Mock imports +import { OFTMock } from "../mocks/OFTMock.sol"; +import { MintBurnOFTAdapterMock } from "../mocks/MintBurnOFTAdapterMock.sol"; +import { MintBurnERC20Mock } from "../mocks/MintBurnERC20Mock.sol"; +import { OFTComposerMock } from "../mocks/OFTComposerMock.sol"; + +// OApp imports +import { IOAppOptionsType3, EnforcedOptionParam } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; + +// OFT imports +import { IMintableBurnable } from "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol"; +import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/oft-evm/contracts/OFTCore.sol"; +import { OFTMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol"; +import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol"; + +// OZ imports +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +// Forge imports +import "forge-std/console.sol"; + +// DevTools imports +import { TestHelperOz5 } from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; + +contract MyMintBurnOFTAdapterTest is TestHelperOz5 { + using OptionsBuilder for bytes; + + uint32 private aEid = 1; + uint32 private bEid = 2; + + MintBurnERC20Mock private aMintBurnToken; + MintBurnOFTAdapterMock private aMintBurnOFTAdapter; + OFTMock private bOFT; + + address private userA = address(0x1); + address private userB = address(0x2); + uint256 private initialBalance = 100 ether; + + function setUp() public virtual override { + vm.deal(userA, 1000 ether); + vm.deal(userB, 1000 ether); + + super.setUp(); + setUpEndpoints(2, LibraryType.UltraLightNode); + + aMintBurnToken = MintBurnERC20Mock( + _deployOApp(type(MintBurnERC20Mock).creationCode, abi.encode("Token", "TOKEN")) + ); + + aMintBurnOFTAdapter = MintBurnOFTAdapterMock( + _deployOApp( + type(MintBurnOFTAdapterMock).creationCode, + abi.encode( + address(aMintBurnToken), + IMintableBurnable(aMintBurnToken), + address(endpoints[aEid]), + address(this) + ) + ) + ); + + bOFT = OFTMock( + _deployOApp( + type(OFTMock).creationCode, + abi.encode("Token", "TOKEN", address(endpoints[bEid]), address(this)) + ) + ); + + // config and wire the ofts + address[] memory ofts = new address[](2); + ofts[0] = address(aMintBurnOFTAdapter); + ofts[1] = address(bOFT); + this.wireOApps(ofts); + + // mint tokens + aMintBurnToken.mint(userA, initialBalance); + } + + function test_constructor() public { + assertEq(aMintBurnOFTAdapter.owner(), address(this)); + assertEq(bOFT.owner(), address(this)); + + assertEq(aMintBurnToken.balanceOf(userA), initialBalance); + assertEq(aMintBurnToken.balanceOf(address(aMintBurnOFTAdapter)), 0); + assertEq(bOFT.balanceOf(userB), 0); + + assertEq(aMintBurnOFTAdapter.token(), address(aMintBurnToken)); + assertEq(bOFT.token(), address(bOFT)); + } + + function test_send_mint_burn_oft_adapter() public { + uint256 tokensToSend = 1 ether; + bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 0); + SendParam memory sendParam = SendParam( + bEid, + addressToBytes32(userB), + tokensToSend, + tokensToSend, + options, + "", + "" + ); + MessagingFee memory fee = aMintBurnOFTAdapter.quoteSend(sendParam, false); + + assertEq(aMintBurnToken.balanceOf(userA), initialBalance); + assertEq(aMintBurnToken.balanceOf(address(aMintBurnOFTAdapter)), 0); + assertEq(bOFT.balanceOf(userB), 0); + + vm.prank(userA); + aMintBurnOFTAdapter.send{ value: fee.nativeFee }(sendParam, fee, payable(address(this))); + verifyPackets(bEid, addressToBytes32(address(bOFT))); + + assertEq(aMintBurnToken.balanceOf(userA), initialBalance - tokensToSend); + assertEq(aMintBurnToken.balanceOf(address(aMintBurnOFTAdapter)), 0); + assertEq(bOFT.balanceOf(userB), tokensToSend); + } + + function test_send_oft_adapter_compose_msg() public { + uint256 tokensToSend = 1 ether; + + OFTComposerMock composer = new OFTComposerMock(); + + bytes memory options = OptionsBuilder + .newOptions() + .addExecutorLzReceiveOption(200000, 0) + .addExecutorLzComposeOption(0, 500000, 0); + bytes memory composeMsg = hex"1234"; + SendParam memory sendParam = SendParam( + bEid, + addressToBytes32(address(composer)), + tokensToSend, + tokensToSend, + options, + composeMsg, + "" + ); + MessagingFee memory fee = aMintBurnOFTAdapter.quoteSend(sendParam, false); + + assertEq(aMintBurnToken.balanceOf(userA), initialBalance); + assertEq(aMintBurnToken.balanceOf(address(aMintBurnOFTAdapter)), 0); + assertEq(bOFT.balanceOf(userB), 0); + + vm.prank(userA); + (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) = aMintBurnOFTAdapter.send{ + value: fee.nativeFee + }(sendParam, fee, payable(address(this))); + verifyPackets(bEid, addressToBytes32(address(bOFT))); + + // lzCompose params + uint32 dstEid_ = bEid; + address from_ = address(bOFT); + bytes memory options_ = options; + bytes32 guid_ = msgReceipt.guid; + address to_ = address(composer); + bytes memory composerMsg_ = OFTComposeMsgCodec.encode( + msgReceipt.nonce, + aEid, + oftReceipt.amountReceivedLD, + abi.encodePacked(addressToBytes32(userA), composeMsg) + ); + this.lzCompose(dstEid_, from_, options_, guid_, to_, composerMsg_); + + assertEq(aMintBurnToken.balanceOf(userA), initialBalance - tokensToSend); + assertEq(aMintBurnToken.balanceOf(address(aMintBurnOFTAdapter)), 0); + assertEq(bOFT.balanceOf(address(composer)), tokensToSend); + + assertEq(composer.from(), from_); + assertEq(composer.guid(), guid_); + assertEq(composer.message(), composerMsg_); + assertEq(composer.executor(), address(this)); + assertEq(composer.extraData(), composerMsg_); // default to setting the extraData to the message as well to test + } + + // TODO import the rest of oft tests? +} diff --git a/examples/mint-burn-oft-adapter/test/hardhat/MyMintBurnOFTAdapter.test.ts b/examples/mint-burn-oft-adapter/test/hardhat/MyMintBurnOFTAdapter.test.ts new file mode 100644 index 000000000..798cbdc20 --- /dev/null +++ b/examples/mint-burn-oft-adapter/test/hardhat/MyMintBurnOFTAdapter.test.ts @@ -0,0 +1,117 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { expect } from 'chai' +import { Contract, ContractFactory } from 'ethers' +import { deployments, ethers } from 'hardhat' + +import { Options } from '@layerzerolabs/lz-v2-utilities' + +describe('MyMintBurnOFTAdapter 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 MyMintBurnOFTAdapter: ContractFactory + let MyOFT: ContractFactory + let MintBurnERC20Mock: ContractFactory + let EndpointV2Mock: ContractFactory + let ownerA: SignerWithAddress + let ownerB: SignerWithAddress + let endpointOwner: SignerWithAddress + let token: Contract + let myMintBurnOFTAdapterA: Contract + let myOFTB: 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 + MyMintBurnOFTAdapter = await ethers.getContractFactory('MyMintBurnOFTAdapterMock') + + MyOFT = await ethers.getContractFactory('MyOFTMock') + + MintBurnERC20Mock = await ethers.getContractFactory('MintBurnERC20Mock') + + // Fetching the first three signers (accounts) from Hardhat's local Ethereum network + const signers = await ethers.getSigners() + + ;[ownerA, ownerB, endpointOwner] = signers + + // 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) + + token = await MintBurnERC20Mock.deploy('Token', 'TOKEN') + + // Deploying two instances of MyOFT contract with different identifiers and linking them to the mock LZEndpoint + myMintBurnOFTAdapterA = await MyMintBurnOFTAdapter.deploy( + token.address, + token.address, + mockEndpointV2A.address, + ownerA.address + ) + myOFTB = await MyOFT.deploy('bOFT', 'bOFT', mockEndpointV2B.address, ownerB.address) + + // Setting destination endpoints in the LZEndpoint mock for each MyOFT instance + await mockEndpointV2A.setDestLzEndpoint(myOFTB.address, mockEndpointV2B.address) + await mockEndpointV2B.setDestLzEndpoint(myMintBurnOFTAdapterA.address, mockEndpointV2A.address) + + // Setting each MyOFT instance as a peer of the other in the mock LZEndpoint + await myMintBurnOFTAdapterA.connect(ownerA).setPeer(eidB, ethers.utils.zeroPad(myOFTB.address, 32)) + await myOFTB.connect(ownerB).setPeer(eidA, ethers.utils.zeroPad(myMintBurnOFTAdapterA.address, 32)) + }) + + // A test case to verify token transfer functionality + it('should send a token from A address to B address via OFTAdapter/OFT', async function () { + // Minting an initial amount of tokens to ownerA's address in the myOFTA contract + const initialAmount = ethers.utils.parseEther('100') + await token.mint(ownerA.address, initialAmount) + + // Defining the amount of tokens to send and constructing the parameters for the send operation + const tokensToSend = ethers.utils.parseEther('1') + + // Defining extra message execution options for the send operation + const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toString() + + const sendParam = [ + eidB, + ethers.utils.zeroPad(ownerB.address, 32), + tokensToSend, + tokensToSend, + options, + '0x', + '0x', + ] + + // Fetching the native fee for the token send operation + const [nativeFee] = await myMintBurnOFTAdapterA.quoteSend(sendParam, false) + + // Executing the send operation from myOFTA contract + await myMintBurnOFTAdapterA.send(sendParam, [nativeFee, 0], ownerA.address, { value: nativeFee }) + + // Fetching the final token balances of ownerA and ownerB + const finalBalanceA = await token.balanceOf(ownerA.address) + const finalBalanceAdapter = await token.balanceOf(myMintBurnOFTAdapterA.address) + const finalBalanceB = await myOFTB.balanceOf(ownerB.address) + + // Asserting that the final balances are as expected after the send operation + expect(finalBalanceA).eql(initialAmount.sub(tokensToSend)) + expect(finalBalanceAdapter).eql(ethers.utils.parseEther('0')) + expect(finalBalanceB).eql(tokensToSend) + }) +}) diff --git a/examples/mint-burn-oft-adapter/test/mocks/MintBurnERC20Mock.sol b/examples/mint-burn-oft-adapter/test/mocks/MintBurnERC20Mock.sol new file mode 100644 index 000000000..ba2fc5227 --- /dev/null +++ b/examples/mint-burn-oft-adapter/test/mocks/MintBurnERC20Mock.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +import { IMintableBurnable } from "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol"; + +/** + * @title MintBurnERC20Mock + * + * @dev WARNING: This contract is for testing purposes only. + * In a production scenario, the `mint` and `burn` methods + * should be guarded by appropriate access control mechanisms. + */ +contract MintBurnERC20Mock is ERC20, IMintableBurnable { + /// @notice Constructor to initialize the ERC20 token with a name and symbol. + constructor(string memory name, string memory symbol) ERC20(name, symbol) {} + + /** + * @notice Burns a specific amount of tokens from a given address. + * + * @dev WARNING: In production, this function should have access control. + * + * @param _from The address from which tokens will be burned. + * @param _amount The amount of tokens to burn. + * + * @return A boolean indicating the success of the burn operation. + */ + function burn(address _from, uint256 _amount) external returns (bool) { + _burn(_from, _amount); + return true; + } + + /** + * @notice Mints a specific amount of tokens to a given address. + * + * @dev WARNING: In production, this function should have access control. + * + * @param _to The address to which tokens will be minted. + * @param _amount The amount of tokens to mint. + * + * @return A boolean indicating the success of the mint operation. + */ + function mint(address _to, uint256 _amount) external returns (bool) { + _mint(_to, _amount); + return true; + } +} diff --git a/examples/mint-burn-oft-adapter/test/mocks/MintBurnOFTAdapterMock.sol b/examples/mint-burn-oft-adapter/test/mocks/MintBurnOFTAdapterMock.sol new file mode 100644 index 000000000..216aff336 --- /dev/null +++ b/examples/mint-burn-oft-adapter/test/mocks/MintBurnOFTAdapterMock.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { MintBurnOFTAdapter } from "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol"; +import { IMintableBurnable } from "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol"; + +contract MintBurnOFTAdapterMock is MintBurnOFTAdapter { + constructor( + address _token, + IMintableBurnable _minterBurner, + address _lzEndpoint, + address _delegate + ) MintBurnOFTAdapter(_token, _minterBurner, _lzEndpoint, _delegate) Ownable(_delegate) {} + + // @dev expose internal functions for testing purposes + function debit( + uint256 _amountToSendLD, + uint256 _minAmountToCreditLD, + uint32 _dstEid + ) public returns (uint256 amountDebitedLD, uint256 amountToCreditLD) { + return _debit(msg.sender, _amountToSendLD, _minAmountToCreditLD, _dstEid); + } + + function debitView( + uint256 _amountToSendLD, + uint256 _minAmountToCreditLD, + uint32 _dstEid + ) public view returns (uint256 amountDebitedLD, uint256 amountToCreditLD) { + return _debitView(_amountToSendLD, _minAmountToCreditLD, _dstEid); + } + + function credit(address _to, uint256 _amountToCreditLD, uint32 _srcEid) public returns (uint256 amountReceivedLD) { + return _credit(_to, _amountToCreditLD, _srcEid); + } + + function removeDust(uint256 _amountLD) public view returns (uint256 amountLD) { + return _removeDust(_amountLD); + } +} diff --git a/examples/mint-burn-oft-adapter/test/mocks/OFTComposerMock.sol b/examples/mint-burn-oft-adapter/test/mocks/OFTComposerMock.sol new file mode 100644 index 000000000..fdd5c2426 --- /dev/null +++ b/examples/mint-burn-oft-adapter/test/mocks/OFTComposerMock.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { IOAppComposer } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppComposer.sol"; + +contract OFTComposerMock is IOAppComposer { + // default empty values for testing a lzCompose received message + address public from; + bytes32 public guid; + bytes public message; + address public executor; + bytes public extraData; + + function lzCompose( + address _from, + bytes32 _guid, + bytes calldata _message, + address _executor, + bytes calldata /*_extraData*/ + ) external payable { + from = _from; + guid = _guid; + message = _message; + executor = _executor; + extraData = _message; + } +} diff --git a/examples/mint-burn-oft-adapter/test/mocks/OFTMock.sol b/examples/mint-burn-oft-adapter/test/mocks/OFTMock.sol new file mode 100644 index 000000000..cef8770f5 --- /dev/null +++ b/examples/mint-burn-oft-adapter/test/mocks/OFTMock.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol"; +import { SendParam } from "@layerzerolabs/oft-evm/contracts/OFTCore.sol"; + +contract OFTMock is OFT { + constructor( + string memory _name, + string memory _symbol, + address _lzEndpoint, + address _delegate + ) Ownable(_delegate) OFT(_name, _symbol, _lzEndpoint, _delegate) {} + + function mint(address _to, uint256 _amount) public { + _mint(_to, _amount); + } + + // @dev expose internal functions for testing purposes + function debit( + uint256 _amountToSendLD, + uint256 _minAmountToCreditLD, + uint32 _dstEid + ) public returns (uint256 amountDebitedLD, uint256 amountToCreditLD) { + return _debit(msg.sender, _amountToSendLD, _minAmountToCreditLD, _dstEid); + } + + function debitView( + uint256 _amountToSendLD, + uint256 _minAmountToCreditLD, + uint32 _dstEid + ) public view returns (uint256 amountDebitedLD, uint256 amountToCreditLD) { + return _debitView(_amountToSendLD, _minAmountToCreditLD, _dstEid); + } + + function removeDust(uint256 _amountLD) public view returns (uint256 amountLD) { + return _removeDust(_amountLD); + } + + function toLD(uint64 _amountSD) public view returns (uint256 amountLD) { + return _toLD(_amountSD); + } + + function toSD(uint256 _amountLD) public view returns (uint64 amountSD) { + return _toSD(_amountLD); + } + + function credit(address _to, uint256 _amountToCreditLD, uint32 _srcEid) public returns (uint256 amountReceivedLD) { + return _credit(_to, _amountToCreditLD, _srcEid); + } + + function buildMsgAndOptions( + SendParam calldata _sendParam, + uint256 _amountToCreditLD + ) public view returns (bytes memory message, bytes memory options) { + return _buildMsgAndOptions(_sendParam, _amountToCreditLD); + } +} diff --git a/examples/mint-burn-oft-adapter/tsconfig.json b/examples/mint-burn-oft-adapter/tsconfig.json new file mode 100644 index 000000000..027ad0f3f --- /dev/null +++ b/examples/mint-burn-oft-adapter/tsconfig.json @@ -0,0 +1,13 @@ +{ + "exclude": ["node_modules"], + "include": ["deploy", "tasks", "test", "hardhat.config.ts"], + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true + } +} diff --git a/examples/mint-burn-oft-adapter/type-extensions.ts b/examples/mint-burn-oft-adapter/type-extensions.ts new file mode 100644 index 000000000..ffc2b722e --- /dev/null +++ b/examples/mint-burn-oft-adapter/type-extensions.ts @@ -0,0 +1,23 @@ +import 'hardhat/types/config' + +interface OftAdapterConfig { + tokenAddress: string +} + +declare module 'hardhat/types/config' { + interface HardhatNetworkUserConfig { + oftAdapter?: never + } + + interface HardhatNetworkConfig { + oftAdapter?: never + } + + interface HttpNetworkUserConfig { + oftAdapter?: OftAdapterConfig + } + + interface HttpNetworkConfig { + oftAdapter?: OftAdapterConfig + } +} diff --git a/package.json b/package.json index c18dc5710..54bf2c186 100644 --- a/package.json +++ b/package.json @@ -40,9 +40,9 @@ "@changesets/cli": "^2.27.1", "@layerzerolabs/prettier-config-next": "^2.3.39", "@types/node": "~18.18.14", - "@typescript-eslint/eslint-plugin": "^7.5.0", - "@typescript-eslint/parser": "^7.5.0", - "eslint": "^8.55.0", + "@typescript-eslint/eslint-plugin": "^7.7.1", + "@typescript-eslint/parser": "^7.7.1", + "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", diff --git a/packages/create-lz-oapp/src/config.ts b/packages/create-lz-oapp/src/config.ts index 981f4c9a1..a0676eee4 100644 --- a/packages/create-lz-oapp/src/config.ts +++ b/packages/create-lz-oapp/src/config.ts @@ -74,6 +74,18 @@ export const getExamples = (): Example[] => { }, ] : []), + // Mint Burn OFT Adapter example is feature flagged for the time being + ...(process.env.LZ_ENABLE_MINTBURN_EXAMPLE + ? [ + { + id: 'mint-burn-oft-adapter', + label: 'MintBurnOFTAdapter', + repository, + directory: 'examples/mint-burn-oft-adapter', + ref, + }, + ] + : []), // OApp Read examples are feature flagged for the time being ...(process.env.LZ_ENABLE_READ_EXAMPLE ? [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0eb4151e..b5a4edb7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,41 +24,41 @@ importers: specifier: ~18.18.14 version: 18.18.14 '@typescript-eslint/eslint-plugin': - specifier: ^7.5.0 - version: 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0)(typescript@5.5.3) + specifier: ^7.7.1 + version: 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1)(typescript@5.5.3) '@typescript-eslint/parser': - specifier: ^7.5.0 - version: 7.7.1(eslint@8.57.0)(typescript@5.5.3) + specifier: ^7.7.1 + version: 7.7.1(eslint@8.57.1)(typescript@5.5.3) eslint: - specifier: ^8.55.0 - version: 8.57.0 + specifier: ^8.57.1 + version: 8.57.1 eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) + version: 9.1.0(eslint@8.57.1) eslint-config-standard: specifier: ^17.1.0 - version: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0) + version: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.1) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) eslint-plugin-jest: specifier: ^27.6.3 - version: 27.6.3(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.0)(typescript@5.5.3) + version: 27.6.3(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.1)(typescript@5.5.3) eslint-plugin-n: specifier: ^16.6.2 - version: 16.6.2(eslint@8.57.0) + version: 16.6.2(eslint@8.57.1) eslint-plugin-node: specifier: ^11.1.0 - version: 11.1.0(eslint@8.57.0) + version: 11.1.0(eslint@8.57.1) eslint-plugin-prettier: specifier: ^5.1.3 - version: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5) + version: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.1)(prettier@3.2.5) eslint-plugin-promise: specifier: ^6.1.1 - version: 6.1.1(eslint@8.57.0) + version: 6.1.1(eslint@8.57.1) eslint-plugin-turbo: specifier: ^1.12.3 - version: 1.12.3(eslint@8.57.0) + version: 1.12.3(eslint@8.57.1) husky: specifier: ^8.0.3 version: 8.0.3 @@ -75,6 +75,120 @@ importers: specifier: 1.11.0 version: 1.11.0 + examples/mint-burn-oft-adapter: + devDependencies: + '@babel/core': + specifier: ^7.23.9 + version: 7.23.9 + '@layerzerolabs/eslint-config-next': + specifier: ~2.3.39 + version: 2.3.44(typescript@5.5.3) + '@layerzerolabs/lz-definitions': + specifier: ^3.0.21 + version: 3.0.21 + '@layerzerolabs/lz-evm-messagelib-v2': + specifier: ^3.0.12 + version: 3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-protocol-v2': + specifier: ^3.0.12 + version: 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': + specifier: ^3.0.12 + version: 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) + '@layerzerolabs/lz-v2-utilities': + specifier: ^3.0.12 + version: 3.0.12 + '@layerzerolabs/oapp-evm': + specifier: ^0.3.0 + version: link:../../packages/oapp-evm + '@layerzerolabs/oft-evm': + specifier: ^3.0.0 + version: link:../../packages/oft-evm + '@layerzerolabs/prettier-config-next': + specifier: ^2.3.39 + version: 2.3.44 + '@layerzerolabs/solhint-config': + specifier: ^3.0.12 + version: 3.0.12(typescript@5.5.3) + '@layerzerolabs/test-devtools-evm-foundry': + specifier: ~5.1.0 + version: link:../../packages/test-devtools-evm-foundry + '@layerzerolabs/toolbox-foundry': + specifier: ~0.1.9 + version: link:../../packages/toolbox-foundry + '@layerzerolabs/toolbox-hardhat': + specifier: ~0.6.1 + version: link:../../packages/toolbox-hardhat + '@nomicfoundation/hardhat-ethers': + specifier: ^3.0.5 + version: 3.0.5(ethers@5.7.2)(hardhat@2.22.12) + '@nomiclabs/hardhat-ethers': + specifier: ^2.2.3 + version: 2.2.3(ethers@5.7.2)(hardhat@2.22.12) + '@openzeppelin/contracts': + specifier: ^5.0.2 + version: 5.1.0 + '@openzeppelin/contracts-upgradeable': + specifier: ^5.0.2 + version: 5.1.0(@openzeppelin/contracts@5.1.0) + '@rushstack/eslint-patch': + specifier: ^1.7.0 + version: 1.7.0 + '@types/chai': + specifier: ^4.3.11 + version: 4.3.20 + '@types/mocha': + specifier: ^10.0.6 + version: 10.0.6 + '@types/node': + specifier: ~18.18.14 + version: 18.18.14 + chai: + specifier: ^4.4.1 + version: 4.5.0 + concurrently: + specifier: ~9.1.0 + version: 9.1.0 + dotenv: + specifier: ^16.4.1 + version: 16.4.5 + eslint: + specifier: ^8.55.0 + version: 8.57.1 + eslint-plugin-jest-extended: + specifier: ~2.0.0 + version: 2.0.0(eslint@8.57.1)(typescript@5.5.3) + ethers: + specifier: ^5.7.2 + version: 5.7.2 + hardhat: + specifier: ^2.22.10 + version: 2.22.12(ts-node@10.9.2)(typescript@5.5.3) + hardhat-contract-sizer: + specifier: ^2.10.0 + version: 2.10.0(hardhat@2.22.12) + hardhat-deploy: + specifier: ^0.12.1 + version: 0.12.4 + mocha: + specifier: ^10.2.0 + version: 10.2.0 + prettier: + specifier: ^3.2.5 + version: 3.2.5 + solhint: + specifier: ^4.1.1 + version: 4.1.1(typescript@5.5.3) + solidity-bytes-utils: + specifier: ^0.8.2 + version: 0.8.2 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + typescript: + specifier: ^5.4.4 + version: 5.5.3 + examples/native-oft-adapter: devDependencies: '@babel/core': @@ -4164,7 +4278,7 @@ packages: '@babel/traverse': 7.23.9 '@babel/types': 7.23.9 convert-source-map: 2.0.0 - debug: 4.3.5 + debug: 4.3.7 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5224,6 +5338,16 @@ packages: eslint-visitor-keys: 3.4.3 dev: true + /@eslint-community/eslint-utils@4.4.1(eslint@8.57.0): + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + dev: true + /@eslint-community/eslint-utils@4.4.1(eslint@8.57.1): resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6175,16 +6299,16 @@ packages: /@layerzerolabs/eslint-config-next@2.3.44(typescript@5.5.3): resolution: {integrity: sha512-WlBSy47LGPILdrNgzPiRtQf/hAY62IN37ncUsQwcr8T7cyX1HZREx2qljuXpvduLDAKn5otsm0XIqHuCRUHEFg==} dependencies: - '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0)(typescript@5.5.3) - '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) - eslint: 8.57.0 - eslint-config-prettier: 9.1.0(eslint@8.57.0) - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-autofix: 2.2.0(eslint@8.57.0) - eslint-plugin-compat: 4.2.0(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5) - eslint-plugin-unused-imports: 3.2.0(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.0) + '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1)(typescript@5.5.3) + '@typescript-eslint/parser': 7.7.1(eslint@8.57.1)(typescript@5.5.3) + eslint: 8.57.1 + eslint-config-prettier: 9.1.0(eslint@8.57.1) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.1) + eslint-plugin-autofix: 2.2.0(eslint@8.57.1) + eslint-plugin-compat: 4.2.0(eslint@8.57.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.1)(prettier@3.2.5) + eslint-plugin-unused-imports: 3.2.0(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.1) prettier: 3.2.5 transitivePeerDependencies: - '@types/eslint' @@ -7455,7 +7579,7 @@ packages: ethers: ^5.7.2 hardhat: ^2.0.0 dependencies: - debug: 4.3.5 + debug: 4.3.7 ethers: 5.7.2 hardhat: 2.22.12(ts-node@10.9.2)(typescript@5.5.3) lodash.isequal: 4.5.0 @@ -9093,7 +9217,7 @@ packages: /@types/yoga-layout@1.9.2: resolution: {integrity: sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==} - /@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0)(typescript@5.5.3): + /@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1)(typescript@5.5.3): resolution: {integrity: sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: @@ -9104,16 +9228,16 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 7.7.1(eslint@8.57.1)(typescript@5.5.3) '@typescript-eslint/scope-manager': 7.7.1 - '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.5.3) - '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.1)(typescript@5.5.3) + '@typescript-eslint/utils': 7.7.1(eslint@8.57.1)(typescript@5.5.3) '@typescript-eslint/visitor-keys': 7.7.1 - debug: 4.3.5 - eslint: 8.57.0 + debug: 4.3.7 + eslint: 8.57.1 graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 natural-compare: 1.4.0 semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.5.3) @@ -9122,7 +9246,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.3): + /@typescript-eslint/parser@7.7.1(eslint@8.57.1)(typescript@5.5.3): resolution: {integrity: sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: @@ -9136,8 +9260,8 @@ packages: '@typescript-eslint/types': 7.7.1 '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.3) '@typescript-eslint/visitor-keys': 7.7.1 - debug: 4.3.5 - eslint: 8.57.0 + debug: 4.3.7 + eslint: 8.57.1 typescript: 5.5.3 transitivePeerDependencies: - supports-color @@ -9159,7 +9283,7 @@ packages: '@typescript-eslint/visitor-keys': 7.7.1 dev: true - /@typescript-eslint/type-utils@7.7.1(eslint@8.57.0)(typescript@5.5.3): + /@typescript-eslint/type-utils@7.7.1(eslint@8.57.1)(typescript@5.5.3): resolution: {integrity: sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: @@ -9170,9 +9294,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.3) - '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/utils': 7.7.1(eslint@8.57.1)(typescript@5.5.3) debug: 4.3.7 - eslint: 8.57.0 + eslint: 8.57.1 ts-api-utils: 1.3.0(typescript@5.5.3) typescript: 5.5.3 transitivePeerDependencies: @@ -9238,15 +9362,15 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.0) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.3) eslint: 8.57.0 eslint-scope: 5.1.1 - semver: 7.5.4 + semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript @@ -9258,33 +9382,33 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.3) eslint: 8.57.1 eslint-scope: 5.1.1 - semver: 7.5.4 + semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@7.7.1(eslint@8.57.0)(typescript@5.5.3): + /@typescript-eslint/utils@7.7.1(eslint@8.57.1)(typescript@5.5.3): resolution: {integrity: sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 7.7.1 '@typescript-eslint/types': 7.7.1 '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.3) - eslint: 8.57.0 + eslint: 8.57.1 semver: 7.6.2 transitivePeerDependencies: - supports-color @@ -9771,7 +9895,7 @@ packages: /axios@0.21.4(debug@4.3.4): resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: - follow-redirects: 1.15.6(debug@4.3.4) + follow-redirects: 1.15.9(debug@4.3.4) transitivePeerDependencies: - debug dev: true @@ -9779,7 +9903,7 @@ packages: /axios@0.21.4(debug@4.3.5): resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: - follow-redirects: 1.15.6(debug@4.3.5) + follow-redirects: 1.15.9(debug@4.3.5) transitivePeerDependencies: - debug dev: true @@ -9787,7 +9911,7 @@ packages: /axios@0.25.0: resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==} dependencies: - follow-redirects: 1.15.6(debug@4.3.4) + follow-redirects: 1.15.6(debug@4.3.5) transitivePeerDependencies: - debug dev: true @@ -11622,25 +11746,25 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - /eslint-compat-utils@0.1.2(eslint@8.57.0): + /eslint-compat-utils@0.1.2(eslint@8.57.1): resolution: {integrity: sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==} engines: {node: '>=12'} peerDependencies: eslint: '>=6.0.0' dependencies: - eslint: 8.57.0 + eslint: 8.57.1 dev: true - /eslint-config-prettier@9.1.0(eslint@8.57.0): + /eslint-config-prettier@9.1.0(eslint@8.57.1): resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.57.0 + eslint: 8.57.1 dev: true - /eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0): + /eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.1): resolution: {integrity: sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==} engines: {node: '>=12.0.0'} peerDependencies: @@ -11649,10 +11773,10 @@ packages: eslint-plugin-n: '^15.0.0 || ^16.0.0 ' eslint-plugin-promise: ^6.0.0 dependencies: - eslint: 8.57.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-n: 16.6.2(eslint@8.57.0) - eslint-plugin-promise: 6.1.1(eslint@8.57.0) + eslint: 8.57.1 + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-n: 16.6.2(eslint@8.57.1) + eslint-plugin-promise: 6.1.1(eslint@8.57.1) dev: true /eslint-import-resolver-node@0.3.9: @@ -11665,18 +11789,18 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.1): resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' eslint-plugin-import: '*' dependencies: - debug: 4.3.5 + debug: 4.3.7 enhanced-resolve: 5.16.0 - eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint: 8.57.1 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.7.3 is-core-module: 2.13.1 @@ -11688,7 +11812,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -11709,16 +11833,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/parser': 7.7.1(eslint@8.57.1)(typescript@5.5.3) debug: 3.2.7 - eslint: 8.57.0 + eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} peerDependencies: @@ -11739,28 +11863,28 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/parser': 7.7.1(eslint@8.57.1)(typescript@5.5.3) debug: 3.2.7 - eslint: 8.57.0 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint: 8.57.1 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-autofix@2.2.0(eslint@8.57.0): + /eslint-plugin-autofix@2.2.0(eslint@8.57.1): resolution: {integrity: sha512-lu8+0r+utyTroROqXIL+a8sUpICi6za22hIzlpb0+x0tQGRnOjhOKU7v8mC/NS/faDoVsw6xW3vUpc+Mcz5NWA==} engines: {node: '>=18'} peerDependencies: eslint: '>=8' dependencies: - eslint: 8.57.0 + eslint: 8.57.1 eslint-rule-composer: 0.3.0 espree: 9.6.1 esutils: 2.0.3 string-similarity: 4.0.4 dev: true - /eslint-plugin-compat@4.2.0(eslint@8.57.0): + /eslint-plugin-compat@4.2.0(eslint@8.57.1): resolution: {integrity: sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w==} engines: {node: '>=14.x'} peerDependencies: @@ -11770,36 +11894,36 @@ packages: ast-metadata-inferer: 0.8.0 browserslist: 4.22.2 caniuse-lite: 1.0.30001579 - eslint: 8.57.0 + eslint: 8.57.1 find-up: 5.0.0 lodash.memoize: 4.1.2 semver: 7.6.2 dev: true - /eslint-plugin-es-x@7.5.0(eslint@8.57.0): + /eslint-plugin-es-x@7.5.0(eslint@8.57.1): resolution: {integrity: sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '>=8' dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) '@eslint-community/regexpp': 4.10.0 - eslint: 8.57.0 - eslint-compat-utils: 0.1.2(eslint@8.57.0) + eslint: 8.57.1 + eslint-compat-utils: 0.1.2(eslint@8.57.1) dev: true - /eslint-plugin-es@3.0.1(eslint@8.57.0): + /eslint-plugin-es@3.0.1(eslint@8.57.1): resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} engines: {node: '>=8.10.0'} peerDependencies: eslint: '>=4.19.1' dependencies: - eslint: 8.57.0 + eslint: 8.57.1 eslint-utils: 2.1.0 regexpp: 3.2.0 dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -11809,16 +11933,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/parser': 7.7.1(eslint@8.57.1)(typescript@5.5.3) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.57.0 + eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -11860,7 +11984,7 @@ packages: - typescript dev: true - /eslint-plugin-jest@27.6.3(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.0)(typescript@5.5.3): + /eslint-plugin-jest@27.6.3(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.1)(typescript@5.5.3): resolution: {integrity: sha512-+YsJFVH6R+tOiO3gCJon5oqn4KWc+mDq2leudk8mrp8RFubLOo9CVyi3cib4L7XMpxExmkmBZQTPDYVBzgpgOA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -11873,24 +11997,24 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0)(typescript@5.5.3) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.3) - eslint: 8.57.0 + '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1)(typescript@5.5.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.5.3) + eslint: 8.57.1 transitivePeerDependencies: - supports-color - typescript dev: true - /eslint-plugin-n@16.6.2(eslint@8.57.0): + /eslint-plugin-n@16.6.2(eslint@8.57.1): resolution: {integrity: sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==} engines: {node: '>=16.0.0'} peerDependencies: eslint: '>=7.0.0' dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) builtins: 5.0.1 - eslint: 8.57.0 - eslint-plugin-es-x: 7.5.0(eslint@8.57.0) + eslint: 8.57.1 + eslint-plugin-es-x: 7.5.0(eslint@8.57.1) get-tsconfig: 4.7.2 globals: 13.24.0 ignore: 5.3.0 @@ -11901,14 +12025,14 @@ packages: semver: 7.5.4 dev: true - /eslint-plugin-node@11.1.0(eslint@8.57.0): + /eslint-plugin-node@11.1.0(eslint@8.57.1): resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} engines: {node: '>=8.10.0'} peerDependencies: eslint: '>=5.16.0' dependencies: - eslint: 8.57.0 - eslint-plugin-es: 3.0.1(eslint@8.57.0) + eslint: 8.57.1 + eslint-plugin-es: 3.0.1(eslint@8.57.1) eslint-utils: 2.1.0 ignore: 5.3.0 minimatch: 3.1.2 @@ -11916,7 +12040,7 @@ packages: semver: 6.3.1 dev: true - /eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5): + /eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.1)(prettier@3.2.5): resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -11930,20 +12054,20 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.57.0 - eslint-config-prettier: 9.1.0(eslint@8.57.0) + eslint: 8.57.1 + eslint-config-prettier: 9.1.0(eslint@8.57.1) prettier: 3.2.5 prettier-linter-helpers: 1.0.0 synckit: 0.8.8 dev: true - /eslint-plugin-promise@6.1.1(eslint@8.57.0): + /eslint-plugin-promise@6.1.1(eslint@8.57.1): resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.57.0 + eslint: 8.57.1 dev: true /eslint-plugin-react-hooks@4.6.0(eslint@8.57.1): @@ -11980,16 +12104,16 @@ packages: string.prototype.matchall: 4.0.10 dev: true - /eslint-plugin-turbo@1.12.3(eslint@8.57.0): + /eslint-plugin-turbo@1.12.3(eslint@8.57.1): resolution: {integrity: sha512-7hEyxa+oP898EFNoxVenHlH8jtBwV1hbbIkdQWgqDcB0EmVNGVEZkYRo5Hm6BuMAjR433B+NISBJdj0bQo4/Lg==} peerDependencies: eslint: '>6.6.0' dependencies: dotenv: 16.0.3 - eslint: 8.57.0 + eslint: 8.57.1 dev: true - /eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.0): + /eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.1): resolution: {integrity: sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -11999,8 +12123,8 @@ packages: '@typescript-eslint/eslint-plugin': optional: true dependencies: - '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0)(typescript@5.5.3) - eslint: 8.57.0 + '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1)(typescript@5.5.3) + eslint: 8.57.1 eslint-rule-composer: 0.3.0 dev: true @@ -12697,7 +12821,7 @@ packages: /fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - /follow-redirects@1.15.6(debug@4.3.4): + /follow-redirects@1.15.6(debug@4.3.5): resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} peerDependencies: @@ -12706,11 +12830,10 @@ packages: debug: optional: true dependencies: - debug: 4.3.4(supports-color@8.1.1) - dev: true + debug: 4.3.5 - /follow-redirects@1.15.6(debug@4.3.5): - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + /follow-redirects@1.15.9(debug@4.3.4): + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -12718,7 +12841,8 @@ packages: debug: optional: true dependencies: - debug: 4.3.5 + debug: 4.3.4(supports-color@8.1.1) + dev: true /follow-redirects@1.15.9(debug@4.3.5): resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} diff --git a/tests-user/tests/create-lz-oapp.bats b/tests-user/tests/create-lz-oapp.bats index a5ade9c7e..4f9329310 100644 --- a/tests-user/tests/create-lz-oapp.bats +++ b/tests-user/tests/create-lz-oapp.bats @@ -169,6 +169,17 @@ teardown() { pnpm lint:fix } +@test "should work with pnpm & mint-burn-oft-adapter example in CI mode" { + local DESTINATION="$PROJECTS_DIRECTORY/pnpm-mint-burn-oft-adapter" + + LZ_ENABLE_MINTBURN_EXAMPLE=1 npx --yes create-lz-oapp --ci --example mint-burn-oft-adapter --destination $DESTINATION --package-manager pnpm + cd "$DESTINATION" + pnpm compile + pnpm test + pnpm lint + pnpm lint:fix +} + @test "should work with pnpm & oft solana example in CI mode" { local DESTINATION="$PROJECTS_DIRECTORY/pnpm-oft-solana" @@ -242,6 +253,17 @@ teardown() { yarn lint:fix } +@test "should work with yarn & mint-burn-oft-adapter example in CI mode" { + local DESTINATION="$PROJECTS_DIRECTORY/yarn-mint-burn-oft-adapter" + + YARN_CACHE_FOLDER="/tmp/.yarn-cache-mint-burn-oft-adapter-evm" LZ_ENABLE_MINTBURN_EXAMPLE=1 npx --yes create-lz-oapp --ci --example mint-burn-oft-adapter --destination $DESTINATION --package-manager yarn + cd "$DESTINATION" + yarn compile + yarn test + yarn lint + yarn lint:fix +} + @test "should work with yarn & oft solana example in CI mode" { local DESTINATION="$PROJECTS_DIRECTORY/yarn-oft-solana" @@ -315,6 +337,17 @@ teardown() { npm run lint:fix } +@test "should work with npm & mint-burn-oft-adapter example in CI mode" { + local DESTINATION="$PROJECTS_DIRECTORY/npm-mint-burn-oft-adapter" + + LZ_ENABLE_MINTBURN_EXAMPLE=1 npx --yes create-lz-oapp --ci --example mint-burn-oft-adapter --destination $DESTINATION --package-manager npm + cd "$DESTINATION" + npm run compile + npm run test + npm run lint + npm run lint:fix +} + @test "should work with npm & oft solana example in CI mode" { local DESTINATION="$PROJECTS_DIRECTORY/npm-oft-solana" diff --git a/turbo.json b/turbo.json index ba5d6233c..95af4caa5 100644 --- a/turbo.json +++ b/turbo.json @@ -51,6 +51,7 @@ "LZ_ENABLE_READ_EXAMPLE", "LZ_ENABLE_ZKSOLC_EXAMPLE", "LZ_ENABLE_NATIVE_EXAMPLE", + "LZ_ENABLE_MINTBURN_EXAMPLE", "LZ_ENABLE_UPGRADEABLE_EXAMPLE", "LAYERZERO_EXAMPLES_REPOSITORY_URL",