-
Notifications
You must be signed in to change notification settings - Fork 60
NFT: improvements to make production-ready #217
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
Changes from all commits
57bc55a
a674ace
3c638dc
d54d7ca
14dd3e2
73347ae
edca64c
bddf609
f191e37
bed8230
c779848
b77396f
6ba78a0
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 |
|---|---|---|
|
|
@@ -4,45 +4,55 @@ 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/access/Ownable.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 "./shared/Events.sol"; | ||
|
|
||
| contract Universal is | ||
| ERC721, | ||
| ERC721Enumerable, | ||
| ERC721URIStorage, | ||
| Ownable, | ||
| UniversalContract | ||
| Ownable2Step, | ||
| UniversalContract, | ||
| Events | ||
| { | ||
| GatewayZEVM public immutable gateway; | ||
| SystemContract public immutable systemContract = | ||
| SystemContract(0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9); | ||
| uint256 private _nextTokenId; | ||
| bool public isUniversal = true; | ||
| uint256 public gasLimit = 700000; | ||
| uint256 public gasLimit; | ||
fadeev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| error TransferFailed(); | ||
| error Unauthorized(); | ||
| error InvalidAddress(); | ||
| error InvalidGasLimit(); | ||
|
|
||
| mapping(address => bytes) public counterparty; | ||
|
|
||
| event CounterpartySet(address indexed zrc20, bytes indexed contractAddress); | ||
|
|
||
| modifier onlyGateway() { | ||
| require(msg.sender == address(gateway), "Caller is not the gateway"); | ||
| if (msg.sender != address(gateway)) revert Unauthorized(); | ||
| _; | ||
| } | ||
|
|
||
| constructor( | ||
| address payable gatewayAddress, | ||
| address initialOwner | ||
| ) ERC721("MyToken", "MTK") Ownable(initialOwner) { | ||
| address owner, | ||
| string memory name, | ||
| string memory symbol, | ||
| uint256 gas | ||
| ) ERC721(name, symbol) Ownable(owner) { | ||
| if (gatewayAddress == address(0) || owner == address(0)) | ||
| revert InvalidAddress(); | ||
| if (gas == 0) revert InvalidGasLimit(); | ||
| gateway = GatewayZEVM(gatewayAddress); | ||
| gasLimit = gas; | ||
| } | ||
|
|
||
| function setCounterparty( | ||
|
|
@@ -58,6 +68,7 @@ contract Universal is | |
| address receiver, | ||
| address destination | ||
| ) public { | ||
| if (receiver == address(0)) revert InvalidAddress(); | ||
| string memory uri = tokenURI(tokenId); | ||
| _burn(tokenId); | ||
|
|
||
|
|
@@ -68,25 +79,27 @@ contract Universal is | |
| !IZRC20(destination).transferFrom(msg.sender, address(this), gasFee) | ||
| ) revert TransferFailed(); | ||
| IZRC20(destination).approve(address(gateway), gasFee); | ||
| bytes memory encodedData = abi.encode(tokenId, receiver, uri); | ||
| bytes memory message = abi.encode(tokenId, receiver, uri); | ||
|
|
||
| CallOptions memory callOptions = CallOptions(gasLimit, false); | ||
|
|
||
| RevertOptions memory revertOptions = RevertOptions( | ||
| address(this), | ||
| true, | ||
| address(0), | ||
| encodedData, | ||
| message, | ||
| gasLimit | ||
| ); | ||
|
|
||
| gateway.call( | ||
| counterparty[destination], | ||
| destination, | ||
| encodedData, | ||
| message, | ||
| callOptions, | ||
| revertOptions | ||
| ); | ||
|
|
||
| emit TokenTransfer(tokenId, receiver, destination, uri); | ||
| } | ||
|
|
||
| function safeMint(address to, string memory uri) public onlyOwner { | ||
|
|
@@ -121,9 +134,10 @@ contract Universal is | |
| if (destination == address(0)) { | ||
| _safeMint(sender, tokenId); | ||
| _setTokenURI(tokenId, uri); | ||
| emit TokenTransferReceived(tokenId, sender, uri); | ||
|
Contributor
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. 💡 Codebase verification Reentrancy protection is recommended for external calls The
Example implementation: import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Universal is ..., ReentrancyGuard {
function onCall(...) external override onlyGateway nonReentrant {
// existing implementation
}
}🔗 Analysis chainConsider reentrancy protection for external calls. The function makes external calls to Also applies to: 140-140, 156-156, 159-159 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check for ReentrancyGuard usage in the codebase
ast-grep --pattern 'contract $_ is $$$, ReentrancyGuard, $$$ {'
Length of output: 65 Script: #!/bin/bash
# Let's check for ReentrancyGuard imports and usage with different patterns
rg -i "reentrancy|ReentrancyGuard" -A 3
# Check the specific onCall function implementation
ast-grep --pattern 'function onCall($$$) {
$$$
}'
# Check for nonReentrant modifier usage
ast-grep --pattern 'nonReentrant'
# Look for the gateway.call pattern to understand the external call context
rg "gateway.*call" -A 3
Length of output: 1514 Script: #!/bin/bash
# Let's examine the full context of the onCall function to understand the control flow
rg "function onCall" -A 20
# Check for existing modifiers on the contract and function
ast-grep --pattern 'function onCall($$$) $_ {
$$$
}'
# Look for existing checks-effects-interactions pattern
rg "require\(.*\)" -A 2 examples/nft/contracts/Universal.sol
Length of output: 7522 |
||
| } else { | ||
| (, uint256 gasFee) = IZRC20(destination).withdrawGasFeeWithGasLimit( | ||
| 700000 | ||
| gasLimit | ||
| ); | ||
|
|
||
| SwapHelperLib.swapExactTokensForTokens( | ||
|
|
@@ -139,9 +153,10 @@ contract Universal is | |
| counterparty[destination], | ||
| destination, | ||
| abi.encode(tokenId, sender, uri), | ||
| CallOptions(700000, false), | ||
| CallOptions(gasLimit, false), | ||
| RevertOptions(address(0), false, address(0), "", 0) | ||
| ); | ||
| emit TokenTransferToDestination(tokenId, sender, destination, uri); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -153,6 +168,7 @@ contract Universal is | |
|
|
||
| _safeMint(sender, tokenId); | ||
| _setTokenURI(tokenId, uri); | ||
| emit TokenTransferReverted(tokenId, sender, uri); | ||
| } | ||
|
|
||
| // The following functions are overrides required by Solidity. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.26; | ||
|
|
||
| contract Events { | ||
| event SetCounterparty(address indexed newCounterparty); | ||
| event TokenMinted(address indexed to, uint256 indexed tokenId, string uri); | ||
| event TokenTransfer( | ||
| uint256 indexed tokenId, | ||
| address indexed receiver, | ||
| address indexed destination, | ||
| string uri | ||
| ); | ||
| event TokenTransferReceived( | ||
| uint256 indexed tokenId, | ||
| address indexed receiver, | ||
| string uri | ||
| ); | ||
| event TokenTransferReverted( | ||
| uint256 indexed tokenId, | ||
| address indexed sender, | ||
| string uri | ||
| ); | ||
| event CounterpartySet(address indexed zrc20, bytes indexed contractAddress); | ||
| event TokenTransferToDestination( | ||
| uint256 indexed tokenId, | ||
| address indexed sender, | ||
| address indexed destination, | ||
| string uri | ||
| ); | ||
| } | ||
fadeev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Uh oh!
There was an error while loading. Please reload this page.