-
Notifications
You must be signed in to change notification settings - Fork 63
README
The architecture works as follows:
- ERC721CommunityImplementationFactory is a base contract that manages the creation of NFTs. It takes small gas fee (about 500k gas) to create a new NFT smart-contract.
- ERC721CommunityImplementation is an NFT sale contract. It can mint NFTs and allows other contracts to connect to it for minting. It includes public sale options by default.
- INFTExtension is an interface that is allowed to connect to ERC721CommunityImplementation and mint on their behalf
- ERC721CommunityBase is a standalone contract that can be deployed without Factory. It has all the features from ERC721CommunityImplementation, but allows to be extended and deployed separately.
- Deploy extension contract that conforms to
INFTExtension
interface. Optionally, useNFTExtension
as a base contract. - On that
ERC721CommunityImplementation
, calladdExtension(address _extension)
with the address of the extension. - Now you can startSale or use extension any other way to mint tokens from the
ERC721CommunityImplementation
.
This is a clone-able version of contracts/AvatarNFT.sol
. It's a fixed-supply ERC721 minter. You can set price and other misc params for the public sale.
It doesn't use ERC721Enumerable, saving 30-40% of gas on mint or transfer.
Note: While it says ERC721Upgradeable
, it's not upgradeable. We use this version of OpenZeppelin contracts, because we need to be able to Clone
the contract.
Features:
- low gas on mint
- gasless Opensea listing (no need to call approve)
- includes public sale options
- can be extended to include other features
Sometimes you need to override functionality. We published a ERC721CommunityBase
that can be used as a base for your own NFT smart-contract.
It's a copy of ERC721CommunityImplementation
, but uses non-upgradeable versions of ERC721 and Ownable.
Check that the code is identical:
colordiff contracts/ERC721CommunityBase.sol contracts/ERC721CommunityImplementation.sol
This one is a cherry on top of this architecture! It's a contract that can be connected to ERC721CommunityImplementation and mint on their behalf.
The main idea here is that INFTExtension
is a stateless contract. The state should be stored in the ERC721CommunityImplementation
contract.
This is meant to reduce deployment cost as much as possible. Extensions are meant to be deployed once and be available to use for every ERC721CommunityImplementation
instance who wants to connect them.
Examples of state stored in the original contract would be:
- Tier info
- Sale price
- LastTokenId counter
However, sometimes you might need additional data in the extension, say, on-chain art. Then you would store this data in extension contract, but mapped by original NFT collection address.
Example:
interface INFT is IERC721 {
function getArt(address _collectionAddress) public view returns (bytes32);
}
contract Extension is INFTExtension {
mapping (INFT => uint256) public ;
}
Basically there are three options to connect extension to ERC721CommunityImplementation.
NFTSale <=> ExtensionA <=> ExtensionB
- Send token data to ERC721CommunityImplementation directly. Store extension data in ERC721CommunityImplementation.
function mint(uint nTokens, bytes32[] data) {
for (uint i; i < nTokens; i++) {
tokenId = _tokenIdCounter + i;
_tokenData[tokenId] = data[i];
}
_tokenIdCounter += nTokens;
}
- no need to keep track in Extension, which tokenIds were minted
- can't control which tokenIds are being issued, only issued sequentially
- ERC721CommunityImplementation accepts tokenIds data from outside and doesn't store lastTokenId
function mint(uint[] tokenIds) {
for (uint i; i < tokenIds.length; i++) {
_safeMint(tokenIds[i]);
}
}
- control over tokenIds
- can issue different sequences of tokenId, e.g. 0-100 separate from 1000-1100
- extension needs to store tokenIdCounter for each collection
- ERC721CommunityImplementation accepts tokenIds data from outside, but also stores lastTokenId as a public value
function totalSupply () {
return _tokenIdCounter;
}
function mint(uint[] tokenIds) {
for (uint i; i < tokenIds.length; i++) {
_safeMint(tokenIds[i]);
}
}
Usecases:
- Sale 10k NFT avatars. Sell tokenIds sequentially, fixed price. Also allocate X tokens to owner (claimReserved)
- Sell 5-10 NFT on your website. Can be minted by owner, or bought at fixed price, different for each token. Can mint new tokens in the collection.
- Tiered 10k sale. Different amounts of different tokens, some can be minted by owner, others are sold publicly at the same price inside tier. 0-99: 1 ETH, 100-9999: 0.1 ETH, 1000-9999: 0.05 ETH;
- LazyMint – different price for different tokenIds, but signed by owner, not on-chain before minting.
- On-chain art. Replace URI with base64-encoded json and image.
- ERC1155 Factory?