Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: NFT and FT upgradeable using OpenZeppelin's UUPS #6

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
560abc1
upgradeable wip
fadeev Dec 2, 2024
9cbb94d
wip
fadeev Dec 3, 2024
6b8e43c
wip
fadeev Dec 3, 2024
76b927b
wip
fadeev Dec 3, 2024
76ecec6
wip
fadeev Dec 3, 2024
7b75695
NFT works
fadeev Dec 11, 2024
e56a27b
yarn lock
fadeev Dec 11, 2024
3299510
Remove initialize task
fadeev Dec 12, 2024
c10a0c4
token contract now upgradeable
fadeev Dec 12, 2024
2703523
solidity version
fadeev Dec 12, 2024
2d20568
remove comment
fadeev Dec 13, 2024
510ad53
gitignore
fadeev Dec 13, 2024
41115ea
feat: use ZETA for gas when sending from ZetaChain (#10)
fadeev Dec 17, 2024
9ca161f
rename ft contracts
fadeev Dec 17, 2024
a3f58ba
FT: pay with ZETA when transferring tokens from ZetaChain
fadeev Dec 17, 2024
fd3d640
Merge branch 'main' into upgradeable
fadeev Dec 18, 2024
fcab2fa
fix: remove fallback functions (#26)
fadeev Dec 19, 2024
76b5c25
fix: added setGasLimit (#25)
fadeev Dec 19, 2024
326c16b
burnable init
fadeev Dec 19, 2024
b5767bb
ZeroMsgValue
fadeev Dec 19, 2024
dcae400
nft: TokenTransfer event
fadeev Dec 19, 2024
a2fd412
mint: throw errors if token not found
fadeev Dec 19, 2024
1d52473
UniversalTokenEvents
fadeev Dec 19, 2024
9d9834e
Universal Events
fadeev Dec 19, 2024
f62a780
fix mint task
fadeev Dec 19, 2024
f61fb4f
token: fix mint
fadeev Dec 19, 2024
3b9a093
fix: validate token URI (#29)
fadeev Dec 19, 2024
0b208f1
fix: address validation (#28)
fadeev Dec 20, 2024
565a6fe
rename tasks
fadeev Dec 20, 2024
99d2b08
token: rename tasks
fadeev Dec 20, 2024
ae1b1d8
rename
fadeev Dec 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
102 changes: 73 additions & 29 deletions contracts/nft/contracts/evm/UniversalNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,78 @@
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 "../shared/UniversalNFTEvents.sol";

contract UniversalNFT is
Initializable,
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721URIStorageUpgradeable,
ERC721BurnableUpgradeable,
OwnableUpgradeable,
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also call init for ERC721BurnableUpgradeable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also added burnable to universal token contract.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general ownable is ok but AccessControl.sol gives more granular roles and overall better, worth checking for furhter improvements imo

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#33

__ERC721Burnable_init();
__UUPSUpgradeable_init();
if (gatewayAddress == address(0)) revert InvalidAddress();
if (gas == 0) revert InvalidGasLimit();
gasLimitAmount = gas;
gateway = GatewayEVM(gatewayAddress);
}

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 onlyOwner {
uint256 hash = uint256(
keccak256(
Expand Down Expand Up @@ -134,30 +163,35 @@ 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)
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 +200,19 @@ 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 {}

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 {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious why we need this empty contract? we have evm/UniversalNFT and zetachain/UniversalNFT, so they are already different paths?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThirdWeb uses contract names as identifiers, so they should be unique (not the path, names).

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
Loading