Skip to content

Commit

Permalink
feat: NFT and FT upgradeable using OpenZeppelin's UUPS (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev authored Dec 23, 2024
1 parent a0e6a9d commit 785f106
Show file tree
Hide file tree
Showing 36 changed files with 1,177 additions and 352 deletions.
2 changes: 2 additions & 0 deletions contracts/nft/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ cache_forge
access_token

localnet.json

.openzeppelin
123 changes: 92 additions & 31 deletions contracts/nft/contracts/evm/UniversalNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,84 @@
pragma solidity 0.8.26;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@zetachain/protocol-contracts/contracts/evm/GatewayEVM.sol";
import {RevertContext} from "@zetachain/protocol-contracts/contracts/Revert.sol";
import "../shared/Events.sol";

abstract contract UniversalNFT is
ERC721,
ERC721Enumerable,
ERC721URIStorage,
Ownable2Step,
Events
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import {ERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import {ERC721URIStorageUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import {ERC721BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {ERC721PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";

import "../shared/UniversalNFTEvents.sol";

contract UniversalNFT is
Initializable,
ERC721Upgradeable,
ERC721URIStorageUpgradeable,
ERC721EnumerableUpgradeable,
ERC721PausableUpgradeable,
OwnableUpgradeable,
ERC721BurnableUpgradeable,
UUPSUpgradeable,
UniversalNFTEvents
{
GatewayEVM public immutable gateway;
GatewayEVM public gateway;
uint256 private _nextTokenId;
address public universal;
uint256 public immutable gasLimitAmount;
uint256 public gasLimitAmount;

error InvalidAddress();
error Unauthorized();
error InvalidGasLimit();
error GasTokenTransferFailed();

function setUniversal(address contractAddress) external onlyOwner {
if (contractAddress == address(0)) revert InvalidAddress();
universal = contractAddress;
emit SetUniversal(contractAddress);
}

modifier onlyGateway() {
if (msg.sender != address(gateway)) revert Unauthorized();
_;
}

constructor(address payable gatewayAddress, uint256 gas) {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(
address initialOwner,
string memory name,
string memory symbol,
address payable gatewayAddress,
uint256 gas
) public initializer {
__ERC721_init(name, symbol);
__ERC721Enumerable_init();
__ERC721URIStorage_init();
__Ownable_init(initialOwner);
__ERC721Burnable_init();
__UUPSUpgradeable_init();
if (gatewayAddress == address(0)) revert InvalidAddress();
if (gas == 0) revert InvalidGasLimit();
gasLimitAmount = gas;
gateway = GatewayEVM(gatewayAddress);
}

function safeMint(address to, string memory uri) public onlyOwner {
function setGasLimit(uint256 gas) external onlyOwner {
if (gas == 0) revert InvalidGasLimit();
gasLimitAmount = gas;
}

function setUniversal(address contractAddress) external onlyOwner {
if (contractAddress == address(0)) revert InvalidAddress();
universal = contractAddress;
emit SetUniversal(contractAddress);
}

function safeMint(
address to,
string memory uri
) public whenNotPaused onlyOwner {
uint256 hash = uint256(
keccak256(
abi.encodePacked(address(this), block.number, _nextTokenId++)
Expand All @@ -63,7 +97,7 @@ abstract contract UniversalNFT is
uint256 tokenId,
address receiver,
address destination
) external payable {
) external payable whenNotPaused {
if (receiver == address(0)) revert InvalidAddress();

string memory uri = tokenURI(tokenId);
Expand Down Expand Up @@ -134,30 +168,39 @@ abstract contract UniversalNFT is
emit TokenTransferReverted(sender, tokenId, uri);
}

receive() external payable {}

fallback() external payable {}

// The following functions are overrides required by Solidity.

function _update(
address to,
uint256 tokenId,
address auth
) internal override(ERC721, ERC721Enumerable) returns (address) {
)
internal
override(
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721PausableUpgradeable
)
returns (address)
{
return super._update(to, tokenId, auth);
}

function _increaseBalance(
address account,
uint128 value
) internal override(ERC721, ERC721Enumerable) {
) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {
super._increaseBalance(account, value);
}

function tokenURI(
uint256 tokenId
) public view override(ERC721, ERC721URIStorage) returns (string memory) {
)
public
view
override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
returns (string memory)
{
return super.tokenURI(tokenId);
}

Expand All @@ -166,9 +209,27 @@ abstract contract UniversalNFT is
)
public
view
override(ERC721, ERC721Enumerable, ERC721URIStorage)
override(
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721URIStorageUpgradeable
)
returns (bool)
{
return super.supportsInterface(interfaceId);
}

function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}

function pause() public onlyOwner {
_pause();
}

function unpause() public onlyOwner {
_unpause();
}

receive() external payable {}
}
18 changes: 0 additions & 18 deletions contracts/nft/contracts/example/Connected.sol

This file was deleted.

6 changes: 6 additions & 0 deletions contracts/nft/contracts/example/EVMUniversalNFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "../evm/UniversalNFT.sol";

contract EVMUniversalNFT is UniversalNFT {}
19 changes: 0 additions & 19 deletions contracts/nft/contracts/example/Universal.sol

This file was deleted.

6 changes: 6 additions & 0 deletions contracts/nft/contracts/example/ZetaChainUniversalNFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import "../zetachain/UniversalNFT.sol";

contract ZetaChainUniversalNFT is UniversalNFT {}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract Events {
contract UniversalNFTEvents {
event SetUniversal(address indexed universalAddress);
event SetConnected(address indexed zrc20, address contractAddress);
event TokenMinted(address indexed to, uint256 indexed tokenId, string uri);
Expand Down
Loading

0 comments on commit 785f106

Please sign in to comment.