-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: main
Are you sure you want to change the base?
Changes from 3 commits
560abc1
9cbb94d
6b8e43c
76b927b
76ecec6
7b75695
e56a27b
3299510
c10a0c4
2703523
2d20568
510ad53
41115ea
9ca161f
a3f58ba
fd3d640
fcab2fa
76b5c25
326c16b
b5767bb
dcae400
a2fd412
1d52473
9d9834e
f62a780
f61fb4f
3b9a093
0b208f1
565a6fe
99d2b08
ae1b1d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,49 +2,69 @@ | |
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 {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 "../shared/Events.sol"; | ||
|
||
abstract contract UniversalNFT is | ||
ERC721, | ||
ERC721Enumerable, | ||
ERC721URIStorage, | ||
Ownable2Step, | ||
Initializable, | ||
ERC721Upgradeable, | ||
ERC721EnumerableUpgradeable, | ||
ERC721URIStorageUpgradeable, | ||
ERC721BurnableUpgradeable, | ||
OwnableUpgradeable, | ||
Events | ||
{ | ||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
if (gatewayAddress == address(0)) revert InvalidAddress(); | ||
if (gas == 0) revert InvalidGasLimit(); | ||
gasLimitAmount = gas; | ||
gateway = GatewayEVM(gatewayAddress); | ||
} | ||
|
||
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( | ||
|
@@ -144,20 +164,29 @@ abstract contract UniversalNFT is | |
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); | ||
} | ||
|
||
|
@@ -166,7 +195,11 @@ abstract contract UniversalNFT is | |
) | ||
public | ||
view | ||
override(ERC721, ERC721Enumerable, ERC721URIStorage) | ||
override( | ||
ERC721Upgradeable, | ||
ERC721EnumerableUpgradeable, | ||
ERC721URIStorageUpgradeable | ||
) | ||
returns (bool) | ||
{ | ||
return super.supportsInterface(interfaceId); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,7 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.26; | ||
// Compatible with OpenZeppelin Contracts ^5.0.0 | ||
pragma solidity ^0.8.22; | ||
|
||
|
||
import "../zetachain/UniversalNFT.sol"; | ||
|
||
contract Universal is UniversalNFT { | ||
constructor( | ||
address payable gatewayAddress, | ||
address owner, | ||
string memory name, | ||
string memory symbol, | ||
uint256 gasLimit, | ||
address uniswapRouter | ||
) | ||
UniversalNFT(gatewayAddress, gasLimit, uniswapRouter) | ||
Ownable(owner) | ||
ERC721(name, symbol) | ||
{} | ||
} | ||
contract Universal is UniversalNFT {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,36 @@ | ||
// SPDX-License-Identifier: MIT | ||
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 {RevertContext, RevertOptions} from "@zetachain/protocol-contracts/contracts/Revert.sol"; | ||
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/UniversalContract.sol"; | ||
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IGatewayZEVM.sol"; | ||
import "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol"; | ||
import {SwapHelperLib} from "@zetachain/toolkit/contracts/SwapHelperLib.sol"; | ||
import {SystemContract} from "@zetachain/toolkit/contracts/SystemContract.sol"; | ||
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 "../shared/Events.sol"; | ||
|
||
abstract contract UniversalNFT is | ||
ERC721, | ||
ERC721Enumerable, | ||
ERC721URIStorage, | ||
Ownable2Step, | ||
contract UniversalNFT is | ||
Initializable, | ||
ERC721Upgradeable, | ||
ERC721EnumerableUpgradeable, | ||
ERC721URIStorageUpgradeable, | ||
ERC721BurnableUpgradeable, | ||
OwnableUpgradeable, | ||
UniversalContract, | ||
Events | ||
{ | ||
GatewayZEVM public immutable gateway; | ||
address public immutable uniswapRouter; | ||
GatewayZEVM public gateway; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would say gateway and uniswapRouter should not change address so they can remain immutable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The contract is upgradeable, so I believe state variables have to be mutable, so they can be initialized in the |
||
address public uniswapRouter; | ||
uint256 private _nextTokenId; | ||
bool public constant isUniversal = true; | ||
uint256 public immutable gasLimitAmount; | ||
uint256 public gasLimitAmount; | ||
|
||
error TransferFailed(); | ||
error Unauthorized(); | ||
|
@@ -41,11 +45,24 @@ abstract contract UniversalNFT is | |
_; | ||
} | ||
|
||
constructor( | ||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
function initialize( | ||
address initialOwner, | ||
string memory name, | ||
string memory symbol, | ||
address payable gatewayAddress, | ||
uint256 gas, | ||
address uniswapRouterAddress | ||
) { | ||
) public initializer { | ||
__ERC721_init(name, symbol); | ||
__ERC721Enumerable_init(); | ||
__ERC721URIStorage_init(); | ||
__ERC721Burnable_init(); | ||
__Ownable_init(initialOwner); | ||
if (gatewayAddress == address(0) || uniswapRouterAddress == address(0)) | ||
revert InvalidAddress(); | ||
if (gas == 0) revert InvalidGasLimit(); | ||
|
@@ -193,20 +210,29 @@ abstract contract UniversalNFT is | |
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); | ||
} | ||
|
||
|
@@ -215,7 +241,11 @@ abstract contract UniversalNFT is | |
) | ||
public | ||
view | ||
override(ERC721, ERC721Enumerable, ERC721URIStorage) | ||
override( | ||
ERC721Upgradeable, | ||
ERC721EnumerableUpgradeable, | ||
ERC721URIStorageUpgradeable | ||
) | ||
returns (bool) | ||
{ | ||
return super.supportsInterface(interfaceId); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,11 +15,14 @@ | |
"@ethersproject/abi": "^5.4.7", | ||
"@ethersproject/providers": "^5.4.7", | ||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.0", | ||
"@nomicfoundation/hardhat-ethers": "^3.0.8", | ||
"@nomicfoundation/hardhat-foundry": "^1.1.2", | ||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0", | ||
"@nomicfoundation/hardhat-toolbox": "^2.0.0", | ||
"@nomiclabs/hardhat-ethers": "^2.0.0", | ||
"@nomicfoundation/hardhat-verify": "^2.0.12", | ||
"@nomiclabs/hardhat-ethers": "^2.2.3", | ||
"@nomiclabs/hardhat-etherscan": "^3.0.0", | ||
"@openzeppelin/hardhat-upgrades": "^3.6.0", | ||
"@typechain/ethers-v5": "^10.1.0", | ||
"@typechain/hardhat": "^6.1.2", | ||
"@types/chai": "^4.2.0", | ||
|
@@ -41,7 +44,7 @@ | |
"eslint-plugin-simple-import-sort": "^10.0.0", | ||
"eslint-plugin-sort-keys-fix": "^1.1.2", | ||
"eslint-plugin-typescript-sort-keys": "^2.3.0", | ||
"ethers": "^5.4.7", | ||
"ethers": "^6.13.4", | ||
"hardhat": "^2.17.2", | ||
"hardhat-gas-reporter": "^1.0.8", | ||
"prettier": "^2.8.8", | ||
|
@@ -53,6 +56,8 @@ | |
"packageManager": "[email protected]+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72", | ||
"dependencies": { | ||
"@coral-xyz/anchor": "0.30.0", | ||
"@openzeppelin/contracts": "^5.1.0", | ||
"@openzeppelin/contracts-upgradeable": "^5.1.0", | ||
"@solana-developers/helpers": "^2.4.0", | ||
"@solana/spl-memo": "^0.2.5", | ||
"@solana/web3.js": "^1.95.2", | ||
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
326c16b
There was a problem hiding this comment.
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.