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

Fix mint prevention issue #340

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity ^0.8.13;

import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import {ERC1155Supply} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
import {IERC1155MetadataURI} from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
Expand All @@ -18,7 +17,7 @@ struct MintEntry {

/// @dev A Paima Inverse Projection ERC1155 token where initialization is handled by the app-layer.
/// A standard ERC1155 that can be freely minted and stores an unique `<minter, userTokenId>` pair (used in `tokenURI`) when minted.
contract InverseAppProjected1155 is IInverseAppProjected1155, ERC1155Supply, Ownable {
contract InverseAppProjected1155 is IInverseAppProjected1155, ERC1155, Ownable {
using Strings for uint256;

string public name;
Expand All @@ -27,6 +26,7 @@ contract InverseAppProjected1155 is IInverseAppProjected1155, ERC1155Supply, Own
mapping(uint256 id => MintEntry) public tokenToMint;
mapping(address minter => uint256) public mintCount;
mapping(uint256 id => uint256) private _initialSupply;
mapping(uint256 id => uint256) private _totalSupply;

/// @dev The token ID that will be minted when calling the `mint` function.
uint256 public currentTokenId;
Expand Down Expand Up @@ -72,6 +72,11 @@ contract InverseAppProjected1155 is IInverseAppProjected1155, ERC1155Supply, Own
return _initialSupply[id];
}

/// @dev Returns the total amount of tokens with ID `id` that currently exists.
function totalSupply(uint256 id) public view virtual returns (uint256) {
return _totalSupply[id];
}

/// @dev Mints `value` of a new token to the transaction sender.
/// Increases the `currentTokenId`.
/// Reverts if transaction sender is a smart contract that does not implement IERC1155Receiver-onERC1155Received.
Expand Down Expand Up @@ -132,7 +137,7 @@ contract InverseAppProjected1155 is IInverseAppProjected1155, ERC1155Supply, Own
uint256 id,
string memory customBaseUri
) public view virtual returns (string memory) {
require(exists(id), "InverseAppProjected1155: URI query for nonexistent token");
require(_totalSupply[id] > 0, "InverseAppProjected1155: URI query for nonexistent token");
MintEntry memory entry = tokenToMint[id];
string memory URI = bytes(customBaseUri).length > 0
? string.concat(
Expand All @@ -144,7 +149,7 @@ contract InverseAppProjected1155 is IInverseAppProjected1155, ERC1155Supply, Own
"/",
entry.userTokenId.toString(),
"/",
initialSupply(id).toString()
_initialSupply[id].toString()
)
: "";
return string(abi.encodePacked(URI, baseExtension));
Expand Down Expand Up @@ -183,4 +188,30 @@ contract InverseAppProjected1155 is IInverseAppProjected1155, ERC1155Supply, Own
function updateMetadata(uint256 _tokenId) public virtual {
emit MetadataUpdate(_tokenId);
}

/// @dev See {ERC1155-_update}.
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory values
) internal virtual override {
super._update(from, to, ids, values);

if (from == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply[ids[i]] += values[i];
}
}

if (to == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
unchecked {
// Overflow not possible: values[i] <= balanceOf(from, ids[i]) <= totalSupply(ids[i])
_totalSupply[ids[i]] -= values[i];
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity ^0.8.13;

import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import {ERC1155Supply} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
import {IERC1155MetadataURI} from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
Expand All @@ -13,7 +12,7 @@ import {IUri} from "./IUri.sol";

/// @dev A Paima Inverse Projection ERC1155 token where initialization is handled by the base-layer.
/// A standard ERC1155 that accepts calldata in the mint function for any initialization data needed in a Paima dApp.
contract InverseBaseProjected1155 is IInverseBaseProjected1155, ERC1155Supply, Ownable {
contract InverseBaseProjected1155 is IInverseBaseProjected1155, ERC1155, Ownable {
using Strings for uint256;

string public name;
Expand All @@ -26,6 +25,8 @@ contract InverseBaseProjected1155 is IInverseBaseProjected1155, ERC1155Supply, O
/// @dev Base extension that is used in the `uri` function to form the end of the token URI.
string public baseExtension;

mapping(uint256 id => uint256) private _totalSupply;

/// @dev Sets the NFT's `name`, `symbol`, and transfers ownership to `owner`.
/// Also sets `currentTokenId` to 1.
constructor(
Expand All @@ -48,6 +49,11 @@ contract InverseBaseProjected1155 is IInverseBaseProjected1155, ERC1155Supply, O
super.supportsInterface(interfaceId);
}

/// @dev Returns the total amount of tokens with ID `id` that currently exists.
function totalSupply(uint256 id) public view virtual returns (uint256) {
return _totalSupply[id];
}

/// @dev Mints `value` of a new token to transaction sender, passing `initialData` to be emitted in the event.
/// Increases the `currentTokenId`.
/// Reverts if transaction sender is a smart contract that does not implement IERC1155Receiver-onERC1155Received.
Expand Down Expand Up @@ -94,7 +100,7 @@ contract InverseBaseProjected1155 is IInverseBaseProjected1155, ERC1155Supply, O
uint256 id,
string memory customBaseUri
) public view virtual returns (string memory) {
require(exists(id), "InverseBaseProjected1155: URI query for nonexistent token");
require(_totalSupply[id] > 0, "InverseBaseProjected1155: URI query for nonexistent token");
string memory URI = bytes(customBaseUri).length > 0
? string.concat(customBaseUri, "eip155:", block.chainid.toString(), "/", id.toString())
: "";
Expand Down Expand Up @@ -134,4 +140,30 @@ contract InverseBaseProjected1155 is IInverseBaseProjected1155, ERC1155Supply, O
function updateMetadata(uint256 _tokenId) public virtual {
emit MetadataUpdate(_tokenId);
}

/// @dev See {ERC1155-_update}.
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory values
) internal virtual override {
super._update(from, to, ids, values);

if (from == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply[ids[i]] += values[i];
}
}

if (to == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
unchecked {
// Overflow not possible: values[i] <= balanceOf(from, ids[i]) <= totalSupply(ids[i])
_totalSupply[ids[i]] -= values[i];
}
}
}
}
}