OmnichainFungibleToken.sol 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // SPDX-License-Identifier: BUSL-1.1
  2. pragma solidity 0.7.6;
  3. import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
  4. import "@openzeppelin/contracts/access/Ownable.sol";
  5. import "@layerzerolabs/contracts/contracts/interfaces/ILayerZeroEndpoint.sol";
  6. import "@layerzerolabs/contracts/contracts/interfaces/ILayerZeroReceiver.sol";
  7. import "@layerzerolabs/contracts/contracts/interfaces/ILayerZeroUserApplicationConfig.sol";
  8. contract OmnichainFungibleToken is ERC20, Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig {
  9. // the only endpointId these tokens will ever be minted on
  10. // required: the LayerZero endpoint which is passed in the constructor
  11. ILayerZeroEndpoint immutable public endpoint;
  12. // a map of our connected contracts
  13. mapping(uint16 => bytes) public dstContractLookup;
  14. // pause the sendTokens()
  15. bool public paused;
  16. bool public isMain;
  17. event Paused(bool isPaused);
  18. event SendToChain(uint16 dstChainId, bytes to, uint256 qty);
  19. event ReceiveFromChain(uint16 srcChainId, uint64 nonce, uint256 qty);
  20. constructor(
  21. string memory _name,
  22. string memory _symbol,
  23. address _endpoint,
  24. uint16 _mainChainId,
  25. uint256 initialSupplyOnMainEndpoint
  26. ) ERC20(_name, _symbol) {
  27. if (ILayerZeroEndpoint(_endpoint).getChainId() == _mainChainId) {
  28. _mint(msg.sender, initialSupplyOnMainEndpoint);
  29. isMain = true;
  30. }
  31. // set the LayerZero endpoint
  32. endpoint = ILayerZeroEndpoint(_endpoint);
  33. }
  34. function pauseSendTokens(bool _pause) external onlyOwner {
  35. paused = _pause;
  36. emit Paused(_pause);
  37. }
  38. function setDestination(uint16 _dstChainId, bytes calldata _destinationContractAddress) public onlyOwner {
  39. dstContractLookup[_dstChainId] = _destinationContractAddress;
  40. }
  41. function chainId() external view returns (uint16){
  42. return endpoint.getChainId();
  43. }
  44. function sendTokens(
  45. uint16 _dstChainId, // send tokens to this chainId
  46. bytes calldata _to, // where to deliver the tokens on the destination chain
  47. uint256 _qty, // how many tokens to send
  48. address zroPaymentAddress, // ZRO payment address
  49. bytes calldata adapterParam // txParameters
  50. ) public payable {
  51. require(!paused, "OFT: sendTokens() is currently paused");
  52. // lock if leaving the safe chain, otherwise burn
  53. if (isMain) {
  54. // ... transferFrom the tokens to this contract for locking purposes
  55. _transfer(msg.sender, address(this), _qty);
  56. } else {
  57. _burn(msg.sender, _qty);
  58. }
  59. // abi.encode() the payload with the values to send
  60. bytes memory payload = abi.encode(_to, _qty);
  61. // send LayerZero message
  62. endpoint.send{value: msg.value}(
  63. _dstChainId, // destination chainId
  64. dstContractLookup[_dstChainId], // destination UA address
  65. payload, // abi.encode()'ed bytes
  66. msg.sender, // refund address (LayerZero will refund any extra gas back to caller of send()
  67. zroPaymentAddress, // 'zroPaymentAddress' unused for this mock/example
  68. adapterParam // 'adapterParameters' unused for this mock/example
  69. );
  70. emit SendToChain(_dstChainId, _to, _qty);
  71. }
  72. function lzReceive(
  73. uint16 _srcChainId,
  74. bytes memory _fromAddress,
  75. uint64 nonce,
  76. bytes memory _payload
  77. ) external override {
  78. require(msg.sender == address(endpoint)); // boilerplate! lzReceive must be called by the endpoint for security
  79. require(
  80. _fromAddress.length == dstContractLookup[_srcChainId].length && keccak256(_fromAddress) == keccak256(dstContractLookup[_srcChainId]),
  81. "OFT: invalid source sending contract"
  82. );
  83. // decode
  84. (bytes memory _to, uint256 _qty) = abi.decode(_payload, (bytes, uint256));
  85. address toAddress;
  86. // load the toAddress from the bytes
  87. assembly {
  88. toAddress := mload(add(_to, 20))
  89. }
  90. // mint the tokens back into existence, to the receiving address
  91. if (isMain) {
  92. _transfer(address(this), toAddress, _qty);
  93. } else {
  94. _mint(toAddress, _qty);
  95. }
  96. emit ReceiveFromChain(_srcChainId, nonce, _qty);
  97. }
  98. function estimateSendTokensFee(uint16 _dstChainId, bool _useZro, bytes calldata txParameters) external view returns (uint256 nativeFee, uint256 zroFee) {
  99. return endpoint.estimateFees(_dstChainId, address(this), bytes(""), _useZro, txParameters);
  100. }
  101. //---------------------------DAO CALL----------------------------------------
  102. // generic config for user Application
  103. function setConfig(
  104. uint16 _version,
  105. uint16 _chainId,
  106. uint256 _configType,
  107. bytes calldata _config
  108. ) external override onlyOwner {
  109. endpoint.setConfig(_version, _chainId, _configType, _config);
  110. }
  111. function setSendVersion(uint16 version) external override onlyOwner {
  112. endpoint.setSendVersion(version);
  113. }
  114. function setReceiveVersion(uint16 version) external override onlyOwner {
  115. endpoint.setReceiveVersion(version);
  116. }
  117. function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner {
  118. endpoint.forceResumeReceive(_srcChainId, _srcAddress);
  119. }
  120. function renounceOwnership() public override onlyOwner {}
  121. }