-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🗞️ Add mint and burn oft adapter example (#1120)
- Loading branch information
Showing
33 changed files
with
1,577 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
artifacts | ||
cache | ||
dist | ||
node_modules | ||
out | ||
*.log | ||
*.sol | ||
*.yaml | ||
*.lock | ||
package-lock.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
v18.18.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
artifacts/ | ||
cache/ | ||
dist/ | ||
node_modules/ | ||
out/ | ||
*.log | ||
*ignore | ||
*.yaml | ||
*.lock | ||
package-lock.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
...require('@layerzerolabs/prettier-config-next'), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
<p align="center"> | ||
<a href="https://layerzero.network"> | ||
<img alt="LayerZero" style="max-width: 500px" src="https://d3a2dpnnrypp5h.cloudfront.net/bridge-app/lz.png"/> | ||
</a> | ||
</p> | ||
<h1 align="center">MintBurnOFTAdapter Example</h1> | ||
|
||
<p align="center"> | ||
<a href="https://docs.layerzero.network/contracts/oft" style="color: #a77dff">Quickstart</a> | <a href="https://docs.layerzero.network/contracts/oapp-configuration" style="color: #a77dff">Configuration</a> | <a href="https://docs.layerzero.network/contracts/options" style="color: #a77dff">Message Execution Options</a> | <a href="https://docs.layerzero.network/contracts/endpoint-addresses" style="color: #a77dff">Endpoint Addresses</a> | ||
</p> | ||
|
||
<p align="center">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.</p> | ||
|
||
<p align="center"> | ||
<a href="https://layerzero.network" style="color: #a77dff">Homepage</a> | <a href="https://docs.layerzero.network/" style="color: #a77dff">Docs</a> | <a href="https://layerzero.network/developers" style="color: #a77dff">Developers</a> | ||
</p> | ||
|
||
--- | ||
|
||
- [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. | ||
|
||
<br></br> | ||
|
||
<p align="center"> | ||
Join our community on <a href="https://discord-layerzero.netlify.app/discord" style="color: #a77dff">Discord</a> | Follow us on <a href="https://twitter.com/LayerZero_Labs" style="color: #a77dff">Twitter</a> | ||
</p> | ||
|
||
## 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`. |
25 changes: 25 additions & 0 deletions
25
examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) {} | ||
} |
48 changes: 48 additions & 0 deletions
48
examples/mint-burn-oft-adapter/contracts/mocks/MintBurnERC20Mock.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
examples/mint-burn-oft-adapter/contracts/mocks/MyMintBurnOFTAdapterMock.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) {} | ||
} |
Oops, something went wrong.