8000 Solidity SDK ERC1155 by nkrishang · Pull Request #206 · thirdweb-dev/contracts · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Solidity SDK ERC1155 #206

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

Merged
merged 27 commits into from
Aug 12, 2022
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
277 changes: 277 additions & 0 deletions contracts/base/ERC1155Base.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import { ERC1155 } from "../eip/ERC1155.sol";

import "../extension/ContractMetadata.sol";
import "../extension/Multicall.sol";
import "../extension/Ownable.sol";
import "../extension/Royalty.sol";
import "../extension/BatchMintMetadata.sol";

import "../lib/TWStrings.sol";

/**
* The `ERC1155Base` smart contract implements the ERC1155 NFT standard.
* It includes the following additions to standard ERC1155 logic:
*
* - Ability to mint NFTs via the provided `mintTo` and `batchMintTo` functions.
*
* - Contract metadata for royalty support on platforms such as OpenSea that use
* off-chain information to distribute roaylties.
*
* - Ownership of the contract, with the ability to restrict certain functions to
* only be called by the contract's owner.
*
* - Multicall capability to perform multiple actions atomically
*
* - EIP 2981 compliance for royalty support on NFT marketplaces.
*/

contract ERC1155Base is ERC1155, ContractMetadata, Ownable, Royalty, Multicall, BatchMintMetadata {
using TWStrings for uint256;

/*//////////////////////////////////////////////////////////////
State variables
//////////////////////////////////////////////////////////////*/

/// @dev The tokenId of the next NFT to mint.
uint256 internal nextTokenIdToMint_;

Check warning

Code scanning / Slither

Unused state variable

ERC1155Base.nextTokenIdToMint_ (contracts/base/ERC1155Base.sol#39) is never used in ERC1155DelayedReveal (contracts/base/ERC1155DelayedReveal.sol#25-114)

/*//////////////////////////////////////////////////////////////
Mappings
//////////////////////////////////////////////////////////////*/

/**
* @notice Returns the total supply of NFTs of a given tokenId
* @dev Mapping from tokenId => total circulating supply of NFTs of that tokenId.
*/
mapping(uint256 => uint256) public totalSupply;

/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/

constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC1155(_name, _symbol) {
_setupOwner(msg.sender);
_setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
}

/*//////////////////////////////////////////////////////////////
Overriden metadata logic
//////////////////////////////////////////////////////////////*/

/// @notice Returns the metadata URI for the given tokenId.
function uri(uint256 _tokenId) public view virtual override returns (string memory) {
string memory uriForToken = _uri[_tokenId];
if (bytes(uriForToken).length > 0) {
return uriForToken;
}

string memory batchUri = getBaseURI(_tokenId);
return string(abi.encodePacked(batchUri, _tokenId.toString()));
}

/*//////////////////////////////////////////////////////////////
Mint / burn logic
//////////////////////////////////////////////////////////////*/

/**
* @notice Lets an authorized address mint NFTs to a recipient.
* @dev - The logic in the `_canMint` function determines whether the caller is authorized to mint NFTs.
* - If `_tokenId == type(uint256).max` a new NFT at tokenId `nextTokenIdToMint` is minted. If the given
* `tokenId < nextTokenIdToMint`, then additional supply of an existing NFT is being minted.
*
* @param _to The recipient of the NFTs to mint.
* @param _tokenId The tokenId of the NFT to mint.
* @param _tokenURI The full metadata URI for the NFTs minted (if a new NFT is being minted).
* @param _amount The amount of the same NFT to mint.
*/
function mintTo(
address _to,
uint256 _tokenId,
string memory _tokenURI,
uint256 _amount
) public virtual {
require(_canMint(), "Not authorized to mint.");

uint256 tokenIdToMint;
uint256 nextIdToMint = nextTokenIdToMint();

if (_tokenId == type(uint256).max) {
tokenIdToMint = nextIdToMint;
nextTokenIdToMint_ += 1;
_setTokenURI(nextIdToMint, _tokenURI);
} else {
require(_tokenId < nextIdToMint, "invalid id");
tokenIdToMint = _tokenId;
}

_mint(_to, tokenIdToMint, _amount, "");
}

/**
* @notice Lets an authorized address mint multiple NEW NFTs at once to a recipient.
* @dev The logic in the `_canMint` function determines whether the caller is authorized to mint NFTs.
* If `_tokenIds[i] == type(uint256).max` a new NFT at tokenId `nextTokenIdToMint` is minted. If the given
* `tokenIds[i] < nextTokenIdToMint`, then additional supply of an existing NFT is minted.
* The metadata for each new NFT is stored at `baseURI/{tokenID of NFT}`
*
* @param _to The recipient of the NFT to mint.
* @param _tokenIds The tokenIds of the NFTs to mint.
* @param _amounts The amounts of each NFT to mint.
* @param _baseURI The baseURI for the `n` number of NFTs minted. The metadata for each NFT is `baseURI/tokenId`
*/
function batchMintTo(
address _to,
uint256[] memory _tokenIds,
uint256[] memory _amounts,
string memory _baseURI
) public virtual {
require(_canMint(), "Not authorized to mint.");
require(_amounts.length > 0, "Minting zero tokens.");
require(_tokenIds.length == _amounts.length, "Length mismatch.");

uint256 nextIdToMint = nextTokenIdToMint();
uint256 startNextIdToMint = nextIdToMint;

uint256 numOfNewNFTs;

Check warning

Code scanning / Slither

Uninitialized local variables

ERC1155Base.batchMintTo(address,uint256[],uint256[],string).numOfNewNFTs (contracts/base/ERC1155Base.sol#143) is a local variable never initialized

for (uint256 i = 0; i < _tokenIds.length; i += 1) {
if (_tokenIds[i] == type(uint256).max) {
_tokenIds[i] = nextIdToMint;

nextIdToMint += 1;
numOfNewNFTs += 1;
} else {
require(_tokenIds[i] < nextIdToMint, "invalid id");
}
}

if (numOfNewNFTs > 0) {
_batchMintMetadata(startNextIdToMint, numOfNewNFTs, _baseURI);
}

nextTokenIdToMint_ = nextIdToMint;
_mintBatch(_to, _tokenIds, _amounts, "");
}

/**
* @notice Lets an owner or approved operator burn NFTs of the given tokenId.
*
* @param _owner The owner of the NFT to burn.
* @param _tokenId The tokenId of the NFT to burn.
* @param _amount The amount of the NFT to burn.
*/
function burn(
address _owner,

Check notice

Code scanning / Slither

Local variable shadowing

ERC1155Base.burn(address,uint256,uint256)._owner (contracts/base/ERC1155Base.sol#172) shadows: - Ownable._owner (contracts/extension/Ownable.sol#15) (state variable)
uint256 _tokenId,
uint256 _amount
) external virtual {
address caller = msg.sender;

require(caller == _owner || isApprovedForAll[_owner][caller], "Unapproved caller");
require(balanceOf[_owner][_tokenId] >= _amount, "Not enough tokens owned");

_burn(_owner, _tokenId, _amount);
}

/**
* @notice Lets an owner or approved operator burn NFTs of the given tokenIds.
*
* @param _owner The owner of the NFTs to burn.
* @param _tokenIds The tokenIds of the NFTs to burn.
* @param _amounts The amounts of the NFTs to burn.
*/
function burnBatch(
address _owner,

Check notice

Code scanning / Slither

Local variable shadowing

ERC1155Base.burnBatch(address,uint256[],uint256[])._owner (contracts/base/ERC1155Base.sol#192) shadows: - Ownable._owner (contracts/extension/Ownable.sol#15) (state variable)
uint256[] memory _tokenIds,
uint256[] memory _amounts
) external virtual {
address caller = msg.sender;

require(caller == _owner || isApprovedForAll[_owner][caller], "Unapproved caller");
require(_tokenIds.length == _amounts.length, "Length mismatch");

for (uint256 i = 0; i < _tokenIds.length; i += 1) {
require(balanceOf[_owner][_tokenIds[i]] >= _amounts[i], "Not enough tokens owned");
}

_burnBatch(_owner, _tokenIds, _amounts);
}

/*//////////////////////////////////////////////////////////////
ERC165 Logic
//////////////////////////////////////////////////////////////*/

/// @notice Returns whether this contract supports the given interface.
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155, IERC165) returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
interfaceId == 0x0e89341c || // ERC165 Interface ID for ERC1155MetadataURI
interfaceId == type(IERC2981).interfaceId; // ERC165 ID for ERC2981
}

/*//////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/

/// @notice The tokenId assigned to the next new NFT to be minted.
function nextTokenIdToMint() public view virtual returns (uint256) {
return nextTokenIdToMint_;
}

/*//////////////////////////////////////////////////////////////
Internal (overrideable) functions
//////////////////////////////////////////////////////////////*/

/// @dev Returns whether contract metadata can be set in the given execution context.
function _canSetContractURI() internal view virtual override returns (bool) {
return msg.sender == owner();
}

/// @dev Returns whether a token can be minted in the given execution context.
function _canMint() internal view virtual returns (bool) {
return msg.sender == owner();
}

/// @dev Returns whether owner can be set in the given execution context.
function _canSetOwner() internal view virtual override returns (bool) {
return msg.sender == owner();
}

/// @dev Returns whether royalty info can be set in the given execution context.
function _canSetRoyaltyInfo() internal view virtual override returns (bool) {
return msg.sender == owner();
}

/// @dev Runs before every token transfer / mint / burn.
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual override {
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

if (from == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
totalSupply[ids[i]] += amounts[i];
}
}

if (to == address(0)) {
for (uint256 i = 0; i < ids.length; ++i) {
totalSupply[ids[i]] -= amounts[i];
}
}
}
}
114 changes: 114 additions & 0 deletions contracts/base/ERC1155DelayedReveal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "./ERC1155LazyMint.sol";
import "../extension/DelayedReveal.sol";

/**
* BASE: ERC1155Base
* EXTENSION: LazyMint, DelayedReveal
*
* The `ERC1155DelayedReveal` contract uses the `ERC1155Base` contract, along with the `LazyMint` and `DelayedReveal` extension.
*
* 'Lazy minting' means defining the metadata of NFTs without minting it to an address. Regular 'minting'
* of NFTs means actually assigning an owner to an NFT.
*
* As a contract admin, this lets you prepare the metadata for NFTs that will be minted by an external party,
* without paying the gas cost for actually minting the NFTs.
*
* 'Delayed reveal' is a mechanism by which you can distribute NFTs to your audience and reveal the metadata of the distributed
* NFTs, after the fact.
*
* You can read more about how the `DelayedReveal` extension works, here: https://blog.thirdweb.com/delayed-reveal-nfts
*/

contract ERC1155DelayedReveal is ERC1155LazyMint, DelayedReveal {
using TWStrings for uint256;

/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/

constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC1155LazyMint(_name, _symbol, _royaltyRecipient, _royaltyBps) {}

/*//////////////////////////////////////////////////////////////
Overriden Metadata logic
//////////////////////////////////////////////////////////////*/

/**
* @notice Returns the metadata URI for an NFT.
* @dev See `BatchMintMetadata` for handling of metadata in this contract.
*
* @param _tokenId The tokenId of an NFT.
*/
function uri(uint256 _tokenId) public view override returns (string memory) {
(uint256 batchId, ) = getBatchId(_tokenId);
string memory batchUri = getBaseURI(_tokenId);

if (isEncryptedBatch(batchId)) {
return string(abi.encodePacked(batchUri, "0"));
} else {
return string(abi.encodePacked(batchUri, _tokenId.toString()));
}
}

/*//////////////////////////////////////////////////////////////
Lazy minting logic
//////////////////////////////////////////////////////////////*/

/**
* @notice Lets an authorized address lazy mint a given amount of NFTs.
*
* @param _amount The number of NFTs to lazy mint.
* @param _baseURIForTokens The placeholder base URI for the 'n' number of NFTs being lazy minted, where the
* metadata for each of those NFTs is `${baseURIForTokens}/${tokenId}`.
* @param _data The encrypted base URI + provenance hash for the batch of NFTs being lazy minted.
* @return batchId A unique integer identifier for the batch of NFTs lazy minted together.
*/
function lazyMint(
uint256 _amount,
string calldata _baseURIForTokens,
bytes calldata _data
) public virtual override returns (uint256 batchId) {
if (_data.length > 0) {
(bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(_data, (bytes, bytes32));
if (encryptedURI.length != 0 && provenanceHash != "") {
_setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
}
}

return super.lazyMint(_amount, _baseURIForTokens, _data);
}

/*//////////////////////////////////////////////////////////////
Delayed reveal logic
//////////////////////////////////////////////////////////////*/

/**
* @notice Lets an authorized address reveal a batch of delayed reveal NFTs.
*
* @param _index The ID for the batch of delayed-reveal NFTs to reveal.
* @param _key The key with which the base URI for the relevant batch of NFTs was encrypted.
*/
function reveal(uint256 _index, bytes calldata _key) external virtual override returns (string memory revealedURI) {
require(_canReveal(), "Not authorized");

uint256 batchId = getBatchIdAtIndex(_index);
revealedURI = getRevealURI(batchId, _key);

_setEncryptedData(batchId, "");
_setBaseURI(batchId, revealedURI);

emit TokenURIRevealed(_index, revealedURI);
}

/// @dev Checks whether NFTs can be revealed in the given execution context.
function _canReveal() internal view virtual returns (bool) {
return msg.sender == owner();
}
}
Loading
0