-
Notifications
You must be signed in to change notification settings - Fork 230
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d911826
commit fb40350
Showing
2 changed files
with
10 additions
and
155 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,12 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; | ||
import "@openzeppelin/contracts/utils/Strings.sol"; | ||
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; | ||
import "./xpNFT.sol"; | ||
|
||
contract ZetaXP_V2 is ERC721Upgradeable, Ownable2StepUpgradeable, EIP712Upgradeable { | ||
bytes32 private constant MINTORUPDATE_TYPEHASH = | ||
keccak256("MintOrUpdateNFT(address to,uint256 signatureExpiration,uint256 sigTimestamp,bytes32 tag)"); | ||
|
||
bytes32 private constant SETLEVEL_TYPEHASH = | ||
contract ZetaXP_V2 is ZetaXP { | ||
bytes32 internal constant SETLEVEL_TYPEHASH = | ||
keccak256("SetLevel(uint256 tokenId,uint256 signatureExpiration,uint256 sigTimestamp,uint256 level)"); | ||
|
||
struct UpdateData { | ||
address to; | ||
bytes signature; | ||
uint256 signatureExpiration; | ||
uint256 sigTimestamp; | ||
bytes32 tag; | ||
} | ||
|
||
mapping(uint256 => uint256) public lastUpdateTimestampByTokenId; | ||
mapping(uint256 => bytes32) public tagByTokenId; | ||
mapping(address => mapping(bytes32 => uint256)) public tokenByUserTag; | ||
|
||
// Base URL for NFT images | ||
string public baseTokenURI; | ||
address public signerAddress; | ||
|
||
// Counter for the next token ID | ||
uint256 private _currentTokenId; | ||
|
||
struct SetLevelData { | ||
uint256 tokenId; | ||
bytes signature; | ||
|
@@ -43,137 +17,18 @@ contract ZetaXP_V2 is ERC721Upgradeable, Ownable2StepUpgradeable, EIP712Upgradea | |
|
||
mapping(uint256 => uint256) public levelByTokenId; | ||
|
||
// Event for New Mint | ||
event NFTMinted(address indexed sender, uint256 indexed tokenId, bytes32 tag); | ||
// Event for NFT Update | ||
event NFTUpdated(address indexed sender, uint256 indexed tokenId, bytes32 tag); | ||
// Event for Signer Update | ||
event SignerUpdated(address indexed signerAddress); | ||
// Event for Base URI Update | ||
event BaseURIUpdated(string baseURI); | ||
// Event for Level Set | ||
event LevelSet(address indexed sender, uint256 indexed tokenId, uint256 level); | ||
|
||
error InvalidSigner(); | ||
error SignatureExpired(); | ||
error InvalidAddress(); | ||
error LengthMismatch(); | ||
error TransferNotAllowed(); | ||
error OutdatedSignature(); | ||
error TagAlreadyHoldByUser(); | ||
|
||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
function initialize( | ||
string memory name, | ||
string memory symbol, | ||
string memory baseTokenURI_, | ||
address signerAddress_, | ||
address owner | ||
) public initializer { | ||
if (signerAddress_ == address(0)) revert InvalidAddress(); | ||
__EIP712_init("ZetaXP", "1"); | ||
__ERC721_init(name, symbol); | ||
__Ownable_init(); | ||
transferOwnership(owner); | ||
baseTokenURI = baseTokenURI_; | ||
signerAddress = signerAddress_; | ||
_currentTokenId = 1; // Start token IDs from 1 | ||
} | ||
|
||
function version() public pure returns (string memory) { | ||
function version() public pure override returns (string memory) { | ||
return "2.0.0"; | ||
} | ||
|
||
// Internal function to set the signer address | ||
function setSignerAddress(address signerAddress_) external onlyOwner { | ||
if (signerAddress_ == address(0)) revert InvalidAddress(); | ||
signerAddress = signerAddress_; | ||
emit SignerUpdated(signerAddress_); | ||
} | ||
|
||
// Set the base URI for tokens | ||
function setBaseURI(string calldata _uri) external onlyOwner { | ||
baseTokenURI = _uri; | ||
emit BaseURIUpdated(_uri); | ||
} | ||
|
||
// The following functions are overrides required by Solidity. | ||
function tokenURI(uint256 tokenId) public view override(ERC721Upgradeable) returns (string memory) { | ||
_requireMinted(tokenId); | ||
|
||
return string(abi.encodePacked(baseTokenURI, Strings.toString(tokenId))); | ||
} | ||
|
||
function supportsInterface(bytes4 interfaceId) public view override(ERC721Upgradeable) returns (bool) { | ||
return super.supportsInterface(interfaceId); | ||
} | ||
|
||
function _verifyUpdateNFTSignature(uint256 tokenId, UpdateData memory updateData) private view { | ||
bytes32 structHash = keccak256( | ||
abi.encode( | ||
MINTORUPDATE_TYPEHASH, | ||
updateData.to, | ||
updateData.signatureExpiration, | ||
updateData.sigTimestamp, | ||
updateData.tag | ||
) | ||
); | ||
bytes32 constructedHash = _hashTypedDataV4(structHash); | ||
|
||
if (!SignatureChecker.isValidSignatureNow(signerAddress, constructedHash, updateData.signature)) { | ||
revert InvalidSigner(); | ||
} | ||
|
||
if (block.timestamp > updateData.signatureExpiration) revert SignatureExpired(); | ||
if (updateData.sigTimestamp <= lastUpdateTimestampByTokenId[tokenId]) revert OutdatedSignature(); | ||
} | ||
|
||
function _updateNFT(uint256 tokenId, UpdateData memory updateData) internal { | ||
_verifyUpdateNFTSignature(tokenId, updateData); | ||
lastUpdateTimestampByTokenId[tokenId] = updateData.sigTimestamp; | ||
tagByTokenId[tokenId] = updateData.tag; | ||
tokenByUserTag[updateData.to][updateData.tag] = tokenId; | ||
} | ||
|
||
// External mint function with auto-incremented token ID | ||
function mintNFT(UpdateData memory mintData) external { | ||
uint256 newTokenId = _currentTokenId; | ||
_mint(mintData.to, newTokenId); | ||
|
||
if (tokenByUserTag[mintData.to][mintData.tag] != 0) revert TagAlreadyHoldByUser(); | ||
_updateNFT(newTokenId, mintData); | ||
|
||
emit NFTMinted(mintData.to, newTokenId, mintData.tag); | ||
|
||
_currentTokenId++; // Increment the token ID for the next mint | ||
} | ||
|
||
// External update function | ||
function updateNFT(uint256 tokenId, UpdateData memory updateData) external { | ||
address owner = ownerOf(tokenId); | ||
updateData.to = owner; | ||
bool willUpdateTag = tagByTokenId[tokenId] != updateData.tag; | ||
|
||
if (willUpdateTag) { | ||
if (tokenByUserTag[owner][updateData.tag] != 0) revert TagAlreadyHoldByUser(); | ||
tokenByUserTag[owner][tagByTokenId[tokenId]] = 0; | ||
} | ||
|
||
_updateNFT(tokenId, updateData); | ||
|
||
emit NFTUpdated(owner, tokenId, updateData.tag); | ||
} | ||
|
||
function _transfer(address from, address to, uint256 tokenId) internal override { | ||
revert TransferNotAllowed(); | ||
function _verifyUpdateNFTSignature(uint256 tokenId, UpdateData memory updateData) internal view { | ||
_verify(tokenId, updateData); | ||
} | ||
Check warning Code scanning / Slither Dead-code Warning
ZetaXP_V2._verifyUpdateNFTSignature(uint256,ZetaXP.UpdateData) is never used and should be removed
|
||
|
||
// V2 Methods | ||
function _verifySetLevelSignature(SetLevelData memory data) private view { | ||
function _verifySetLevelSignature(SetLevelData memory data) internal view { | ||
bytes32 structHash = keccak256( | ||
abi.encode(SETLEVEL_TYPEHASH, data.tokenId, data.signatureExpiration, data.sigTimestamp, data.level) | ||
); | ||
|