From f0e314d0872dc6d8a52fbfdc213bb30385425c98 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Mon, 23 Dec 2024 16:31:19 +0300 Subject: [PATCH] nft: refactor to extract transferCrossChain into an abstract contract --- contracts/nft/contracts/evm/UniversalNFT.sol | 58 +++++---------- .../evm/UniversalNFTTransferrable.sol | 70 +++++++++++++++++++ 2 files changed, 86 insertions(+), 42 deletions(-) create mode 100644 contracts/nft/contracts/evm/UniversalNFTTransferrable.sol diff --git a/contracts/nft/contracts/evm/UniversalNFT.sol b/contracts/nft/contracts/evm/UniversalNFT.sol index 9fa8868..19cf834 100644 --- a/contracts/nft/contracts/evm/UniversalNFT.sol +++ b/contracts/nft/contracts/evm/UniversalNFT.sol @@ -14,6 +14,7 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U import {ERC721PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol"; import "../shared/UniversalNFTEvents.sol"; +import "./UniversalNFTTransferrable.sol"; contract UniversalNFT is Initializable, @@ -24,14 +25,14 @@ contract UniversalNFT is OwnableUpgradeable, ERC721BurnableUpgradeable, UUPSUpgradeable, - UniversalNFTEvents + UniversalNFTEvents, + UniversalNFTTransferrable { - GatewayEVM public gateway; uint256 private _nextTokenId; + GatewayEVM public gateway; address public universal; uint256 public gasLimitAmount; - error InvalidAddress(); error Unauthorized(); error InvalidGasLimit(); error GasTokenTransferFailed(); @@ -65,6 +66,18 @@ contract UniversalNFT is gateway = GatewayEVM(gatewayAddress); } + function getGateway() public view override returns (GatewayEVM) { + return gateway; + } + + function getUniversal() public view override returns (address) { + return universal; + } + + function getGasLimitAmount() public view override returns (uint256) { + return gasLimitAmount; + } + function setGasLimit(uint256 gas) external onlyOwner { if (gas == 0) revert InvalidGasLimit(); gasLimitAmount = gas; @@ -93,45 +106,6 @@ contract UniversalNFT is emit TokenMinted(to, tokenId, uri); } - function transferCrossChain( - uint256 tokenId, - address receiver, - address destination - ) external payable whenNotPaused { - if (receiver == address(0)) revert InvalidAddress(); - - string memory uri = tokenURI(tokenId); - _burn(tokenId); - bytes memory message = abi.encode( - destination, - receiver, - tokenId, - uri, - msg.sender - ); - if (destination == address(0)) { - gateway.call( - universal, - message, - RevertOptions(address(this), false, address(0), message, 0) - ); - } else { - gateway.depositAndCall{value: msg.value}( - universal, - message, - RevertOptions( - address(this), - true, - address(0), - abi.encode(receiver, tokenId, uri, msg.sender), - gasLimitAmount - ) - ); - } - - emit TokenTransfer(destination, receiver, tokenId, uri); - } - function onCall( MessageContext calldata context, bytes calldata message diff --git a/contracts/nft/contracts/evm/UniversalNFTTransferrable.sol b/contracts/nft/contracts/evm/UniversalNFTTransferrable.sol new file mode 100644 index 0000000..1ab523a --- /dev/null +++ b/contracts/nft/contracts/evm/UniversalNFTTransferrable.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import "@zetachain/protocol-contracts/contracts/evm/GatewayEVM.sol"; +import {RevertOptions} from "@zetachain/protocol-contracts/contracts/evm/GatewayEVM.sol"; +import "../shared/UniversalNFTEvents.sol"; +import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; + +abstract contract UniversalNFTTransferrable is + ERC721Upgradeable, + UniversalNFTEvents +{ + error InvalidAddress(); + + function getGateway() public view virtual returns (GatewayEVM); + + function getUniversal() public view virtual returns (address); + + function getGasLimitAmount() public view virtual returns (uint256); + + /** + * @notice Transfers an NFT to another chain. + * @dev Burns the NFT locally, then sends an encoded message to the + * Gateway to recreate it on the destination chain (or revert if needed). + * @param tokenId The ID of the NFT to transfer. + * @param receiver The address on the destination chain that will receive the NFT. + * @param destination The contract address on the destination chain (or address(0) if same chain). + */ + function transferCrossChain( + uint256 tokenId, + address receiver, + address destination + ) external payable virtual { + if (receiver == address(0)) revert InvalidAddress(); + + string memory uri = tokenURI(tokenId); + + _burn(tokenId); + + bytes memory message = abi.encode( + destination, + receiver, + tokenId, + uri, + msg.sender + ); + + if (destination == address(0)) { + getGateway().call( + getUniversal(), + message, + RevertOptions(address(this), false, address(0), message, 0) + ); + } else { + getGateway().depositAndCall{value: msg.value}( + getUniversal(), + message, + RevertOptions( + address(this), + true, + address(0), + abi.encode(receiver, tokenId, uri, msg.sender), + getGasLimitAmount() + ) + ); + } + + emit TokenTransfer(destination, receiver, tokenId, uri); + } +}