Skip to content

Commit

Permalink
Merge branch 'upgradeable' into audit-gas-refund-logic
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev committed Dec 23, 2024
2 parents 9ed7830 + 5dbab72 commit d4583b3
Show file tree
Hide file tree
Showing 26 changed files with 343 additions and 94 deletions.
43 changes: 32 additions & 11 deletions contracts/nft/contracts/evm/UniversalNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,20 @@ import {ERC721URIStorageUpgradeable} from "@openzeppelin/contracts-upgradeable/t
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/Events.sol";
import "../shared/UniversalNFTEvents.sol";

contract UniversalNFT is
Initializable,
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721URIStorageUpgradeable,
ERC721BurnableUpgradeable,
ERC721EnumerableUpgradeable,
ERC721PausableUpgradeable,
OwnableUpgradeable,
ERC721BurnableUpgradeable,
UUPSUpgradeable,
Events
UniversalNFTEvents
{
GatewayEVM public gateway;
uint256 private _nextTokenId;
Expand Down Expand Up @@ -54,20 +56,29 @@ contract UniversalNFT is
__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 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 {
function safeMint(
address to,
string memory uri
) public whenNotPaused onlyOwner {
uint256 hash = uint256(
keccak256(
abi.encodePacked(address(this), block.number, _nextTokenId++)
Expand All @@ -85,7 +96,7 @@ 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 @@ -156,10 +167,6 @@ contract UniversalNFT is
emit TokenTransferReverted(sender, tokenId, uri);
}

receive() external payable {}

fallback() external payable {}

// The following functions are overrides required by Solidity.

function _update(
Expand All @@ -168,7 +175,11 @@ contract UniversalNFT is
address auth
)
internal
override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
override(
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721PausableUpgradeable
)
returns (address)
{
return super._update(to, tokenId, auth);
Expand Down Expand Up @@ -210,4 +221,14 @@ contract UniversalNFT is
function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}

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

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

receive() external payable {}
}
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
46 changes: 36 additions & 10 deletions contracts/nft/contracts/zetachain/UniversalNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,28 @@ import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IWZETA.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 {ERC721BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import {ERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import {ERC721PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";
import {ERC721URIStorageUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import {ERC721BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

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

contract UniversalNFT is
Initializable,
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721URIStorageUpgradeable,
ERC721BurnableUpgradeable,
ERC721EnumerableUpgradeable,
ERC721PausableUpgradeable,
OwnableUpgradeable,
ERC721BurnableUpgradeable,
UniversalContract,
UUPSUpgradeable,
Events
UniversalNFTEvents
{
GatewayZEVM public gateway;
address public uniswapRouter;
Expand All @@ -40,6 +42,7 @@ contract UniversalNFT is
error InvalidAddress();
error InvalidGasLimit();
error ApproveFailed();
error ZeroMsgValue();

mapping(address => address) public connected;

Expand All @@ -64,8 +67,8 @@ contract UniversalNFT is
__ERC721_init(name, symbol);
__ERC721Enumerable_init();
__ERC721URIStorage_init();
__ERC721Burnable_init();
__Ownable_init(initialOwner);
__ERC721Burnable_init();
__UUPSUpgradeable_init();
if (gatewayAddress == address(0) || uniswapRouterAddress == address(0))
revert InvalidAddress();
Expand All @@ -75,6 +78,11 @@ contract UniversalNFT is
gasLimitAmount = gas;
}

function setGasLimit(uint256 gas) external onlyOwner {
if (gas == 0) revert InvalidGasLimit();
gasLimitAmount = gas;
}

function setConnected(
address zrc20,
address contractAddress
Expand All @@ -87,7 +95,8 @@ contract UniversalNFT is
uint256 tokenId,
address receiver,
address destination
) public payable {
) public payable whenNotPaused {
if (msg.value == 0) revert ZeroMsgValue();
if (receiver == address(0)) revert InvalidAddress();
string memory uri = tokenURI(tokenId);
_burn(tokenId);
Expand Down Expand Up @@ -142,9 +151,14 @@ contract UniversalNFT is
callOptions,
revertOptions
);

emit TokenTransfer(receiver, destination, tokenId, uri);
}

function safeMint(address to, string memory uri) public onlyOwner {
function safeMint(
address to,
string memory uri
) public onlyOwner whenNotPaused {
uint256 hash = uint256(
keccak256(
abi.encodePacked(address(this), block.number, _nextTokenId++)
Expand Down Expand Up @@ -230,7 +244,11 @@ contract UniversalNFT is
address auth
)
internal
override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
override(
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721PausableUpgradeable
)
returns (address)
{
return super._update(to, tokenId, auth);
Expand Down Expand Up @@ -273,5 +291,13 @@ contract UniversalNFT is
address newImplementation
) internal override onlyOwner {}

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

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

receive() external payable {}
}
6 changes: 1 addition & 5 deletions contracts/nft/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import "./tasks/deploy";
import "./tasks/mint";
import "./tasks/transfer";
import "./tasks/universalSetConnected";
import "./tasks/connectedSetUniversal";
import "./tasks";
import "@zetachain/localnet/tasks";
import "@nomicfoundation/hardhat-toolbox";
import "@zetachain/toolkit/tasks";
Expand Down
6 changes: 4 additions & 2 deletions contracts/nft/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@types/chai": "^4.2.0",
"@types/mocha": ">=9.1.0",
"@types/node": ">=12.0.0",
"@types/validator": "^13.12.2",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"@zetachain/localnet": "4.0.0-rc6",
Expand Down Expand Up @@ -61,6 +62,7 @@
"@solana-developers/helpers": "^2.4.0",
"@solana/spl-memo": "^0.2.5",
"@solana/web3.js": "^1.95.2",
"@zetachain/protocol-contracts": "11.0.0-rc3"
"@zetachain/protocol-contracts": "11.0.0-rc3",
"validator": "^13.12.0"
}
}
}
22 changes: 11 additions & 11 deletions contracts/nft/scripts/localnet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,46 +29,46 @@ GATEWAY_BNB=$(jq -r '.addresses[] | select(.type=="gatewayEVM" and .chain=="bnb"
UNISWAP_ROUTER=$(jq -r '.addresses[] | select(.type=="uniswapRouterInstance" and .chain=="zetachain") | .address' localnet.json)
SENDER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

CONTRACT_ZETACHAIN=$(npx hardhat deploy --network localhost --name ZetaChainUniversalNFT --gateway "$GATEWAY_ZETACHAIN" --uniswap-router "$UNISWAP_ROUTER" --json | jq -r '.contractAddress')
CONTRACT_ZETACHAIN=$(npx hardhat nft:deploy --network localhost --name ZetaChainUniversalNFT --gateway "$GATEWAY_ZETACHAIN" --uniswap-router "$UNISWAP_ROUTER" --json | jq -r '.contractAddress')
echo -e "\n🚀 Deployed NFT contract on ZetaChain: $CONTRACT_ZETACHAIN"

CONTRACT_ETHEREUM=$(npx hardhat deploy --name EVMUniversalNFT --json --network localhost --gateway "$GATEWAY_ETHEREUM" | jq -r '.contractAddress')
CONTRACT_ETHEREUM=$(npx hardhat nft:deploy --name EVMUniversalNFT --json --network localhost --gateway "$GATEWAY_ETHEREUM" | jq -r '.contractAddress')
echo -e "🚀 Deployed NFT contract on Ethereum: $CONTRACT_ETHEREUM"

CONTRACT_BNB=$(npx hardhat deploy --name EVMUniversalNFT --json --network localhost --gas-limit 1000000 --gateway "$GATEWAY_BNB" | jq -r '.contractAddress')
CONTRACT_BNB=$(npx hardhat nft:deploy --name EVMUniversalNFT --json --network localhost --gas-limit 1000000 --gateway "$GATEWAY_BNB" | jq -r '.contractAddress')
echo -e "🚀 Deployed NFT contract on BNB chain: $CONTRACT_BNB"

echo -e "\n📮 User Address: $SENDER"

echo -e "\n🔗 Setting universal and connected contracts..."
npx hardhat connected-set-universal --network localhost --contract "$CONTRACT_ETHEREUM" --universal "$CONTRACT_ZETACHAIN" --json
npx hardhat connected-set-universal --network localhost --contract "$CONTRACT_BNB" --universal "$CONTRACT_ZETACHAIN" --json &>/dev/null
npx hardhat universal-set-connected --network localhost --contract "$CONTRACT_ZETACHAIN" --connected "$CONTRACT_ETHEREUM" --zrc20 "$ZRC20_ETHEREUM" --json &>/dev/null
npx hardhat universal-set-connected --network localhost --contract "$CONTRACT_ZETACHAIN" --connected "$CONTRACT_BNB" --zrc20 "$ZRC20_BNB" --json &>/dev/null
npx hardhat nft:set-universal --network localhost --contract "$CONTRACT_ETHEREUM" --universal "$CONTRACT_ZETACHAIN" --json
npx hardhat nft:set-universal --network localhost --contract "$CONTRACT_BNB" --universal "$CONTRACT_ZETACHAIN" --json &>/dev/null
npx hardhat nft:set-connected --network localhost --contract "$CONTRACT_ZETACHAIN" --connected "$CONTRACT_ETHEREUM" --zrc20 "$ZRC20_ETHEREUM" --json &>/dev/null
npx hardhat nft:set-connected --network localhost --contract "$CONTRACT_ZETACHAIN" --connected "$CONTRACT_BNB" --zrc20 "$ZRC20_BNB" --json &>/dev/null

npx hardhat localnet-check
balance

NFT_ID=$(npx hardhat mint --network localhost --json --contract "$CONTRACT_ZETACHAIN" --token-uri https://example.com/nft/metadata/1 | jq -r '.tokenId')
NFT_ID=$(npx hardhat nft:mint --network localhost --json --contract "$CONTRACT_ZETACHAIN" --token-uri https://example.com/nft/metadata/1 | jq -r '.tokenId')
echo -e "\nMinted NFT with ID: $NFT_ID on ZetaChain."

npx hardhat localnet-check
balance

echo -e "\nTransferring NFT: ZetaChain → Ethereum..."
npx hardhat transfer --network localhost --json --token-id "$NFT_ID" --from "$CONTRACT_ZETACHAIN" --to "$ZRC20_ETHEREUM" --gas-amount 1
npx hardhat nft:transfer --network localhost --json --token-id "$NFT_ID" --from "$CONTRACT_ZETACHAIN" --to "$ZRC20_ETHEREUM" --gas-amount 1

npx hardhat localnet-check
balance

echo -e "\nTransferring NFT: Ethereum → BNB..."
npx hardhat transfer --network localhost --json --token-id "$NFT_ID" --from "$CONTRACT_ETHEREUM" --to "$ZRC20_BNB" --gas-amount 1
npx hardhat nft:transfer --network localhost --json --token-id "$NFT_ID" --from "$CONTRACT_ETHEREUM" --to "$ZRC20_BNB" --gas-amount 1

npx hardhat localnet-check
balance

echo -e "\nTransferring NFT: BNB → ZetaChain..."
npx hardhat transfer --network localhost --json --token-id "$NFT_ID" --from "$CONTRACT_BNB"
npx hardhat nft:transfer --network localhost --json --token-id "$NFT_ID" --from "$CONTRACT_BNB"

npx hardhat localnet-check
balance
Expand Down
10 changes: 9 additions & 1 deletion contracts/nft/tasks/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { task, types } from "hardhat/config";
import { HardhatRuntimeEnvironment } from "hardhat/types";

const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
const { isAddress } = hre.ethers.utils;
const network = hre.network.name;

const [signer] = await hre.ethers.getSigners();
Expand All @@ -11,6 +12,13 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
);
}

if (
!isAddress(args.gateway) ||
(args.uniswapRouter && !isAddress(args.uniswapRouter))
) {
throw new Error("Invalid Ethereum address provided.");
}

const factory: any = await hre.ethers.getContractFactory(args.name);

const contract = await hre.upgrades.deployProxy(factory, [
Expand All @@ -37,7 +45,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
}
};

task("deploy", "Deploy the NFT contract", main)
export const nftDeploy = task("nft:deploy", "Deploy the NFT contract", main)
.addFlag("json", "Output the result in JSON format")
.addOptionalParam("tokenName", "NFT name", "Universal NFT")
.addOptionalParam("tokenSymbol", "NFT symbol", "UNFT")
Expand Down
5 changes: 5 additions & 0 deletions contracts/nft/tasks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { nftSetUniversal } from "./setUniversal";
export { nftMint } from "./mint";
export { nftTransfer } from "./transfer";
export { nftSetConnected } from "./setConnected";
export { nftDeploy } from "./deploy";
Loading

0 comments on commit d4583b3

Please sign in to comment.