-
Notifications
You must be signed in to change notification settings - Fork 363
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from LayerZero-Labs/riley/oft
chore: add updated OFT contracts and tests
- Loading branch information
Showing
25 changed files
with
1,729 additions
and
22 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
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
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
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
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
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
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
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 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import { ILayerZeroReceiver, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol"; | ||
|
||
interface IOAppReceiver is ILayerZeroReceiver { | ||
/** | ||
* @notice Retrieves the address responsible for 'sending' composeMsg's to the Endpoint. | ||
* @return sender The address responsible for 'sending' composeMsg's to the Endpoint. | ||
* | ||
* @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer. | ||
* @dev The default sender IS the OApp implementer. | ||
*/ | ||
function composeMsgSender() external view returns (address sender); | ||
} |
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,100 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import { IOFT, OFTCore } from "./OFTCore.sol"; | ||
|
||
/** | ||
* @title OFT Contract | ||
* @dev OFT is an ERC-20 token that extends the functionality of the OFTCore contract. | ||
*/ | ||
abstract contract OFT is OFTCore, ERC20 { | ||
/** | ||
* @dev Constructor for the OFT contract. | ||
* @param _name The name of the OFT. | ||
* @param _symbol The symbol of the OFT. | ||
* @param _lzEndpoint The LayerZero endpoint address. | ||
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint. | ||
*/ | ||
constructor( | ||
string memory _name, | ||
string memory _symbol, | ||
address _lzEndpoint, | ||
address _delegate | ||
) ERC20(_name, _symbol) OFTCore(decimals(), _lzEndpoint, _delegate) {} | ||
|
||
/** | ||
* @notice Retrieves interfaceID and the version of the OFT. | ||
* @return interfaceId The interface ID. | ||
* @return version The version. | ||
* | ||
* @dev interfaceId: This specific interface ID is '0x02e49c2c'. | ||
* @dev version: Indicates a cross-chain compatible msg encoding with other OFTs. | ||
* @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented. | ||
* ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1) | ||
*/ | ||
function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) { | ||
return (type(IOFT).interfaceId, 1); | ||
} | ||
|
||
/** | ||
* @dev Retrieves the address of the underlying ERC20 implementation. | ||
* @return The address of the OFT token. | ||
* | ||
* @dev In the case of OFT, address(this) and erc20 are the same contract. | ||
*/ | ||
function token() external view returns (address) { | ||
return address(this); | ||
} | ||
|
||
/** | ||
* @notice Indicates whether the OFT contract requires approval of the 'token()' to send. | ||
* @return requiresApproval Needs approval of the underlying token implementation. | ||
* | ||
* @dev In the case of OFT where the contract IS the token, approval is NOT required. | ||
*/ | ||
function approvalRequired() external pure virtual returns (bool) { | ||
return false; | ||
} | ||
|
||
/** | ||
* @dev Burns tokens from the sender's specified balance. | ||
* @param _amountLD The amount of tokens to send in local decimals. | ||
* @param _minAmountLD The minimum amount to send in local decimals. | ||
* @param _dstEid The destination chain ID. | ||
* @return amountSentLD The amount sent in local decimals. | ||
* @return amountReceivedLD The amount received in local decimals on the remote. | ||
*/ | ||
function _debit( | ||
uint256 _amountLD, | ||
uint256 _minAmountLD, | ||
uint32 _dstEid | ||
) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) { | ||
(amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid); | ||
|
||
// @dev In NON-default OFT, amountSentLD could be 100, with a 10% fee, the amountReceivedLD amount is 90, | ||
// therefore amountSentLD CAN differ from amountReceivedLD. | ||
|
||
// @dev Default OFT burns on src. | ||
_burn(msg.sender, amountSentLD); | ||
} | ||
|
||
/** | ||
* @dev Credits tokens to the specified address. | ||
* @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 returns (uint256 amountReceivedLD) { | ||
// @dev Default OFT mints on dst. | ||
_mint(_to, _amountLD); | ||
// @dev In the case of NON-default OFT, the _amountLD MIGHT not be == amountReceivedLD. | ||
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,118 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
import { IERC20Metadata, IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | ||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import { IOFT, OFTCore } from "./OFTCore.sol"; | ||
|
||
/** | ||
* @title OFTAdapter Contract | ||
* @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality. | ||
* | ||
* @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility. | ||
* @dev WARNING: ONLY 1 of these should exist for a given global mesh, | ||
* unless you make a NON-default implementation of OFT and needs to be done very carefully. | ||
* @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. | ||
*/ | ||
abstract contract OFTAdapter is OFTCore { | ||
using SafeERC20 for IERC20; | ||
|
||
IERC20 internal immutable innerToken; | ||
|
||
/** | ||
* @dev Constructor for the OFTAdapter contract. | ||
* @param _token The address of the ERC-20 token to be adapted. | ||
* @param _lzEndpoint The LayerZero endpoint address. | ||
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint. | ||
*/ | ||
constructor( | ||
address _token, | ||
address _lzEndpoint, | ||
address _delegate | ||
) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) { | ||
innerToken = IERC20(_token); | ||
} | ||
|
||
/** | ||
* @notice Retrieves interfaceID and the version of the OFT. | ||
* @return interfaceId The interface ID. | ||
* @return version The version. | ||
* | ||
* @dev interfaceId: This specific interface ID is '0x02e49c2c'. | ||
* @dev version: Indicates a cross-chain compatible msg encoding with other OFTs. | ||
* @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented. | ||
* ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1) | ||
*/ | ||
function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) { | ||
return (type(IOFT).interfaceId, 1); | ||
} | ||
|
||
/** | ||
* @dev Retrieves the address of the underlying ERC20 implementation. | ||
* @return The address of the adapted ERC-20 token. | ||
* | ||
* @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract. | ||
*/ | ||
function token() external view returns (address) { | ||
return address(innerToken); | ||
} | ||
|
||
/** | ||
* @notice Indicates whether the OFT contract requires approval of the 'token()' to send. | ||
* @return requiresApproval Needs approval of the underlying token implementation. | ||
* | ||
* @dev In the case of default OFTAdapter, approval is required. | ||
* @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval. | ||
*/ | ||
function approvalRequired() external pure virtual returns (bool) { | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev Burns tokens from the sender's specified balance, ie. pull method. | ||
* @param _amountLD The amount of tokens to send in local decimals. | ||
* @param _minAmountLD The minimum amount to send in local decimals. | ||
* @param _dstEid The destination chain ID. | ||
* @return amountSentLD The amount sent in local decimals. | ||
* @return amountReceivedLD The amount received in local decimals on the remote. | ||
* | ||
* @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract. | ||
* @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 amountReceivedLD. | ||
*/ | ||
function _debit( | ||
uint256 _amountLD, | ||
uint256 _minAmountLD, | ||
uint32 _dstEid | ||
) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) { | ||
(amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid); | ||
// @dev Lock tokens by moving them into this contract from the caller. | ||
innerToken.safeTransferFrom(msg.sender, address(this), amountSentLD); | ||
} | ||
|
||
/** | ||
* @dev Credits tokens to the specified address. | ||
* @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. | ||
* | ||
* @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 amountReceivedLD. | ||
*/ | ||
function _credit( | ||
address _to, | ||
uint256 _amountLD, | ||
uint32 /*_srcEid*/ | ||
) internal virtual override returns (uint256 amountReceivedLD) { | ||
// @dev Unlock the tokens and transfer to the recipient. | ||
innerToken.safeTransfer(_to, _amountLD); | ||
// @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD. | ||
return _amountLD; | ||
} | ||
} |
Oops, something went wrong.