-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Layerzero OFT #2
Open
andreanistico
wants to merge
3
commits into
dev
Choose a base branch
from
HZN-2116/layerzero-oft-zen
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,22 @@ | ||
# general parameters | ||
PRIVATE_KEY= | ||
|
||
OTHER_CHAIN_PRIVATE_KEY= | ||
OTHER_CHAIN_RPC=https://sepolia-rollup.arbitrum.io/rpc | ||
|
||
CHAIN_LZ_ID=40306 | ||
OTHER_CHAIN_LZ_ID=40231 | ||
|
||
# deploy multichain parameters | ||
CHAIN_TOKEN_NAME=ZEN | ||
CHAIN_TOKEN_SYMBOL=ZEN | ||
CHAIN_LZ_ENDPOINT=0x6C7Ab2202C98C4227C5c46f1417D81144DA716Ff | ||
OTHER_CHAIN_LZ_ENDPOINT=0x6EDCE65403992e310A62460808c4b910D972f10f | ||
|
||
OTHER_CHAIN_TOKEN_NAME=LZZEN | ||
OTHER_CHAIN_TOKEN_SYMBOL=LZZEN | ||
|
||
# demo transfer parameters | ||
CHAIN_TOKEN_ADDRESS=0x742ae08ab454b3022C6E5f90c53073c2B8741feB | ||
OTHER_CHAIN_TOKEN_ADDRESS=0x916352E3b55D517bB9Dd74F7734761E9292eAC6c | ||
AMOUNT=100 |
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,17 @@ | ||
node_modules | ||
.env | ||
|
||
# Hardhat files | ||
/cache | ||
/artifacts | ||
|
||
# TypeChain files | ||
/typechain | ||
/typechain-types | ||
|
||
# solidity-coverage files | ||
/coverage | ||
/coverage.json | ||
|
||
# Hardhat Ignition default folder for deployments against a local node | ||
ignition/deployments/chain-31337 |
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,19 @@ | ||
# ZEN OFT | ||
|
||
LayerZero V2 files (`contracts/oapp`, `contracts/oft` and `contracts/precrime`) are downloaded from the [official GitHub repository](https://github.com/LayerZero-Labs/LayerZero-v2), branch **main**, commit **943ce4a**, on 2024-11-29 10:44 CET | ||
|
||
## Build | ||
`npm install` | ||
`npx hardhat compile` | ||
|
||
## Deploy on two chains | ||
Config *.env* file with required parameters, then launch | ||
|
||
`npx hardhat run ./scripts/deploy-multichain.ts` | ||
|
||
|
||
## Demo transfer | ||
Config *.env* file with required parameters, then launch | ||
|
||
`npx hardhat run ./scripts/demo-transfer.ts` | ||
|
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,37 @@ | ||
// 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 OtherSideOFT is OFT { | ||
|
||
error FailedUnwrap(); | ||
error NotEnoughMsgValue(); | ||
|
||
constructor( | ||
string memory _name, | ||
string memory _symbol, | ||
address _lzEndpoint, | ||
address _delegate | ||
) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {} | ||
|
||
function decimals() public view virtual override returns (uint8) { | ||
return 18; | ||
} | ||
|
||
/** | ||
* @dev Retrieves the shared decimals of the OFT. | ||
* @return The shared decimals of the OFT. | ||
* | ||
* @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap | ||
* Lowest common decimal denominator between chains. | ||
* Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64). | ||
* For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller. | ||
* ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615 | ||
*/ | ||
function sharedDecimals() public view virtual override returns (uint8) { | ||
return 18; | ||
} | ||
} |
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,171 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.22; | ||
|
||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; | ||
import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol"; | ||
import { SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; | ||
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; | ||
|
||
|
||
contract ZenNativeOFT is OFT, ReentrancyGuard { | ||
|
||
uint256 immutable public CONVERSION_RATE; | ||
|
||
error FailedUnwrap(); | ||
error NotEnoughMsgValue(); | ||
|
||
constructor( | ||
string memory _name, | ||
string memory _symbol, | ||
address _lzEndpoint, | ||
address _delegate | ||
) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) { | ||
CONVERSION_RATE = 10**(18-decimals()); | ||
} | ||
|
||
function decimals() public view virtual override returns (uint8) { | ||
return 18; | ||
} | ||
|
||
/** | ||
* @dev Retrieves the shared decimals of the OFT. | ||
* @return The shared decimals of the OFT. | ||
* | ||
* @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap | ||
* Lowest common decimal denominator between chains. | ||
* Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64). | ||
* For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller. | ||
* ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615 | ||
*/ | ||
function sharedDecimals() public view virtual override returns (uint8) { | ||
return 18; | ||
} | ||
|
||
/** | ||
* @dev Executes the send operation. | ||
* @param _sendParam The parameters for the send operation. | ||
* @param _fee The calculated fee for the send() operation. | ||
* - nativeFee: The native fee. | ||
* - lzTokenFee: The lzToken fee. | ||
* @param _refundAddress The address to receive any excess funds. | ||
* @return msgReceipt The receipt for the send operation. | ||
* @return oftReceipt The OFT receipt information. | ||
* | ||
* @dev MessagingReceipt: LayerZero msg receipt | ||
* - guid: The unique identifier for the sent message. | ||
* - nonce: The nonce of the sent message. | ||
* - fee: The LayerZero fee incurred for the message. | ||
*/ | ||
|
||
function send( | ||
SendParam calldata _sendParam, | ||
MessagingFee calldata _fee, | ||
address _refundAddress | ||
) external payable virtual override returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) { | ||
// - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender. | ||
// - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance. | ||
(uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid); | ||
|
||
// @dev Builds the options and OFT message to quote in the endpoint. | ||
(bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD); | ||
|
||
// @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt. | ||
msgReceipt = _lzSendWithAmount(_sendParam.dstEid, amountSentLD, message, options, _fee, _refundAddress); | ||
// @dev Formulate the OFT receipt. | ||
oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD); | ||
|
||
emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD); | ||
} | ||
|
||
/** | ||
* @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message. | ||
* @param _dstEid The destination endpoint ID. | ||
* @param _message The message payload. | ||
* @param _options Additional options for the message. | ||
* @param _fee The calculated LayerZero fee for the message. | ||
* - nativeFee: The native fee. | ||
* - lzTokenFee: The lzToken fee. | ||
* @param _refundAddress The address to receive any excess fee values sent to the endpoint. | ||
* @return receipt The receipt for the sent message. | ||
* - guid: The unique identifier for the sent message. | ||
* - nonce: The nonce of the sent message. | ||
* - fee: The LayerZero fee incurred for the message. | ||
*/ | ||
function _lzSendWithAmount( | ||
uint32 _dstEid, | ||
uint256 _amount, | ||
bytes memory _message, | ||
bytes memory _options, | ||
MessagingFee memory _fee, | ||
address _refundAddress | ||
) internal virtual returns (MessagingReceipt memory receipt) { | ||
// @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint. | ||
uint256 messageValue = _payNative(_amount, _fee.nativeFee); | ||
if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee); | ||
|
||
return | ||
// solhint-disable-next-line check-send-result | ||
endpoint.send{ value: messageValue }( | ||
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0), | ||
_refundAddress | ||
); | ||
} | ||
|
||
/** | ||
* @dev Internal function to pay the native fee associated with the message. | ||
* @param _nativeFee The native fee to be paid. | ||
* @return nativeFee The amount of native currency paid. | ||
* | ||
* @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction, | ||
* this will need to be overridden because msg.value would contain multiple lzFees. | ||
* @dev Should be overridden in the event the LayerZero endpoint requires a different native currency. | ||
* @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees. | ||
* @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time. | ||
*/ | ||
function _payNative(uint256 amount, uint256 _nativeFee) internal virtual returns (uint256 nativeFee) { | ||
if (msg.value != amount + _nativeFee) revert NotEnoughMsgValue(); | ||
return _nativeFee; | ||
} | ||
|
||
/** | ||
* @dev Internal function to mock the amount mutation from a OFT debit() operation. | ||
* @param _amountLD The amount to send in local decimals. | ||
* @param _minAmountLD The minimum amount to send in local decimals. | ||
* @dev _dstEid The destination endpoint ID. | ||
* @return amountSentLD The amount sent, in local decimals. | ||
* @return amountReceivedLD The amount to be received on the remote chain, in local decimals. | ||
* | ||
* @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote. | ||
*/ | ||
function _debitView( | ||
uint256 _amountLD, | ||
uint256 _minAmountLD, | ||
uint32 /*_dstEid*/ | ||
) internal view virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) { | ||
if (_amountLD < _minAmountLD) { | ||
revert SlippageExceeded(_amountLD, _minAmountLD); | ||
} | ||
return (_amountLD, _amountLD); | ||
} | ||
|
||
|
||
// ----------------------------- RECEIVER PART | ||
/** | ||
* @dev Unwrap tokens | ||
* @param _to The address to credit the tokens to. | ||
* @param _amountLD The amount of tokens to credit in local decimals. | ||
* @dev _srcEid The source chain ID. | ||
* @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals. | ||
*/ | ||
function _credit( | ||
address _to, | ||
uint256 _amountLD, | ||
uint32 /*_srcEid*/ | ||
) internal virtual override nonReentrant returns (uint256 amountReceivedLD) { | ||
(bool success, ) = _to.call{value: _amountLD * CONVERSION_RATE}(""); | ||
if(!success) revert FailedUnwrap(); | ||
|
||
return _amountLD; | ||
} | ||
} |
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,55 @@ | ||
import { HardhatUserConfig } from "hardhat/config"; | ||
import "@nomiclabs/hardhat-ethers"; | ||
import * as dotenv from "dotenv"; | ||
|
||
dotenv.config(); | ||
|
||
const account = [process.env.PRIVATE_KEY!] | ||
|
||
const config: HardhatUserConfig = { | ||
solidity: { | ||
compilers: [ | ||
{ | ||
version: "0.8.24", | ||
settings: { | ||
optimizer: { | ||
enabled: true, | ||
runs: 1, | ||
} | ||
} | ||
}, | ||
] | ||
}, | ||
defaultNetwork: "hardhat", | ||
networks: { | ||
hardhat: { | ||
gasPrice: 0, | ||
initialBaseFeePerGas: 0, | ||
mining: { | ||
auto: true | ||
} | ||
}, | ||
pregobi: { | ||
url: "http://evm-tn-pre-gobi-test-1.de.horizenlabs.io/ethv1", | ||
accounts: account | ||
}, | ||
gobi: { | ||
url: "https://rpc.ankr.com/horizen_gobi_testnet", | ||
accounts: account | ||
}, | ||
eon: { | ||
url: "https://eon-rpc.horizenlabs.io/ethv1", | ||
accounts: account | ||
}, | ||
amoy: { | ||
url: "https://rpc-amoy.polygon.technology", | ||
accounts: account | ||
}, | ||
curtis: { | ||
url: "https://rpc.curtis.apechain.com", | ||
accounts: account | ||
} | ||
} | ||
}; | ||
|
||
export default config; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just curious why we are overriding some of these functions if they are inherited from LayerZero's OFT contract
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, it's because we invoke the custom
_lzSendWithAmount
instead of_lsZend
I can't simply override the
_lzSend`` because I had to add the parameter amount, that it was not explicitely inserted before. Same for
_payNative, the original one doesn't check that
msg.value` is amount + fee because the original isn't developed for auto-wrapping native tokens.Similar for
_credit
, it is overriden since it has to send native token instead of minting erc20 tokenThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense. just curious why we need to pass the
amount
explicitly and not usemsg.sender
?