123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- // SPDX-License-Identifier: BUSL-1.1
- pragma solidity 0.7.6;
- import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
- import "@openzeppelin/contracts/access/Ownable.sol";
- import "./interfaces/ILayerZeroReceiver.sol";
- import "./interfaces/ILayerZeroEndpoint.sol";
- import "./interfaces/ILayerZeroUserApplicationConfig.sol";
- //---------------------------------------------------------------------------
- // THIS CONTRACT IS OF BUSINESS LICENSE. CONTACT US BEFORE YOU USE IT.
- //
- // LayerZero is pushing now a new cross-chain token standard with permissive license soon
- //
- // Stay tuned for maximum cross-chain compatability of your token
- //---------------------------------------------------------------------------
- contract OmnichainFungibleToken is ERC20, Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig {
- ILayerZeroEndpoint immutable public endpoint;
- mapping(uint16 => bytes) public dstContractLookup; // a map of the connected contracts
- bool public paused; // indicates cross chain transfers are paused
- bool public isMain; // indicates this contract is on the main chain
- event Paused(bool isPaused);
- event SendToChain(uint16 srcChainId, bytes toAddress, uint256 qty, uint64 nonce);
- event ReceiveFromChain(uint16 srcChainId, address toAddress, uint256 qty, uint64 nonce);
- constructor(
- string memory _name,
- string memory _symbol,
- address _endpoint,
- uint16 _mainChainId,
- uint256 _initialSupplyOnMainEndpoint
- ) ERC20(_name, _symbol) {
- // only mint the total supply on the main chain
- if (ILayerZeroEndpoint(_endpoint).getChainId() == _mainChainId) {
- _mint(msg.sender, _initialSupplyOnMainEndpoint);
- isMain = true;
- }
- // set the LayerZero endpoint
- endpoint = ILayerZeroEndpoint(_endpoint);
- }
- function pauseSendTokens(bool _pause) external onlyOwner {
- paused = _pause;
- emit Paused(_pause);
- }
- function setDestination(uint16 _dstChainId, bytes calldata _destinationContractAddress) public onlyOwner {
- dstContractLookup[_dstChainId] = _destinationContractAddress;
- }
- function chainId() external view returns (uint16){
- return endpoint.getChainId();
- }
- function sendTokens(
- uint16 _dstChainId, // send tokens to this chainId
- bytes calldata _to, // where to deliver the tokens on the destination chain
- uint256 _qty, // how many tokens to send
- address _zroPaymentAddress, // ZRO payment address
- bytes calldata _adapterParam // txParameters
- ) public payable {
- require(!paused, "OFT: sendTokens() is currently paused");
- // lock by transferring to this contract if leaving the main chain, otherwise burn
- if (isMain) {
- _transfer(msg.sender, address(this), _qty);
- } else {
- _burn(msg.sender, _qty);
- }
- // abi.encode() the payload with the values to send
- bytes memory payload = abi.encode(_to, _qty);
- // send LayerZero message
- endpoint.send{value: msg.value}(
- _dstChainId, // destination chainId
- dstContractLookup[_dstChainId], // destination UA address
- payload, // abi.encode()'ed bytes
- msg.sender, // refund address (LayerZero will refund any extra gas back to msg.sender
- _zroPaymentAddress, // 'zroPaymentAddress'
- _adapterParam // 'adapterParameters'
- );
- uint64 nonce = endpoint.getOutboundNonce(_dstChainId, address(this));
- emit SendToChain(_dstChainId, _to, _qty, nonce);
- }
- function lzReceive(
- uint16 _srcChainId,
- bytes memory _fromAddress,
- uint64 _nonce,
- bytes memory _payload
- ) external override {
- require(msg.sender == address(endpoint)); // lzReceive must only be called by the endpoint
- require(
- _fromAddress.length == dstContractLookup[_srcChainId].length && keccak256(_fromAddress) == keccak256(dstContractLookup[_srcChainId]),
- "OFT: invalid source sending contract"
- );
- // decode and load the toAddress
- (bytes memory _to, uint256 _qty) = abi.decode(_payload, (bytes, uint256));
- address toAddress;
- assembly { toAddress := mload(add(_to, 20)) }
- // if the toAddress is 0x0, burn it
- if (toAddress == address(0x0)) toAddress == address(0xdEaD);
- // on the main chain unlock via transfer, otherwise _mint
- if (isMain) {
- _transfer(address(this), toAddress, _qty);
- } else {
- _mint(toAddress, _qty);
- }
- emit ReceiveFromChain(_srcChainId, toAddress, _qty, _nonce);
- }
- function estimateSendTokensFee(uint16 _dstChainId, bytes calldata _toAddress, bool _useZro, bytes calldata _txParameters) external view returns (uint256 nativeFee, uint256 zroFee) {
- // mock the payload for sendTokens()
- bytes memory payload = abi.encode(_toAddress, 1);
- return endpoint.estimateFees(_dstChainId, address(this), payload, _useZro, _txParameters);
- }
- //---------------------------DAO CALL----------------------------------------
- // generic config for user Application
- function setConfig(
- uint16 _version,
- uint16 _chainId,
- uint256 _configType,
- bytes calldata _config
- ) external override onlyOwner {
- endpoint.setConfig(_version, _chainId, _configType, _config);
- }
- function setSendVersion(uint16 _version) external override onlyOwner {
- endpoint.setSendVersion(_version);
- }
- function setReceiveVersion(uint16 _version) external override onlyOwner {
- endpoint.setReceiveVersion(_version);
- }
- function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner {
- endpoint.forceResumeReceive(_srcChainId, _srcAddress);
- }
- function renounceOwnership() public override onlyOwner {}
- }
|