generated from protokol/solidity-typescript-hardhat-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from protokol/feat/nftyhalloween
feat: nfty halloween contract implementation with tests, deployment and instructions
- Loading branch information
Showing
12 changed files
with
1,023 additions
and
488 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 |
---|---|---|
|
@@ -118,5 +118,4 @@ lint/outputs/ | |
lint/tmp/ | ||
# lint/reports/ | ||
|
||
node_modules | ||
node_modules | ||
abi |
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 |
---|---|---|
@@ -0,0 +1,175 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | ||
|
||
/** | ||
* @dev This implements an optional extension of {ERC721} defined in the EIP that adds | ||
* enumerability of all the token ids in the contract as well as all token ids owned by each | ||
* account. | ||
*/ | ||
abstract contract ERC721CustomEnumerable is ERC721 { | ||
// Mapping from owner to list of owned token IDs | ||
mapping(address => mapping(uint256 => uint256)) private _ownedTokens; | ||
|
||
// Mapping from token ID to index of the owner tokens list | ||
mapping(uint256 => uint256) private _ownedTokensIndex; | ||
|
||
// Array with all token ids, used for enumeration | ||
uint256[] private _allTokens; | ||
|
||
// Mapping from token id to position in the allTokens array | ||
mapping(uint256 => uint256) private _allTokensIndex; | ||
|
||
/** | ||
* @dev See {IERC165-supportsInterface}. | ||
*/ | ||
function supportsInterface(bytes4 interfaceId) | ||
public | ||
view | ||
virtual | ||
override | ||
returns (bool) | ||
{ | ||
return | ||
interfaceId == type(ERC721CustomEnumerable).interfaceId || | ||
super.supportsInterface(interfaceId); | ||
} | ||
|
||
/** | ||
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. | ||
*/ | ||
function tokenOfOwnerByIndex(address owner, uint256 index) | ||
public | ||
view | ||
virtual | ||
returns (uint256) | ||
{ | ||
require( | ||
index < ERC721.balanceOf(owner), | ||
"ERC721CustomEnumerable: owner index out of bounds" | ||
); | ||
return _ownedTokens[owner][index]; | ||
} | ||
|
||
/** | ||
* @dev See {IERC721Enumerable-tokenByIndex}. | ||
*/ | ||
function tokenByIndex(uint256 index) public view virtual returns (uint256) { | ||
require( | ||
index < _allTokens.length, | ||
"ERC721CustomEnumerable: global index out of bounds" | ||
); | ||
return _allTokens[index]; | ||
} | ||
|
||
/** | ||
* @dev Hook that is called before any token transfer. This includes minting | ||
* and burning. | ||
* | ||
* Calling conditions: | ||
* | ||
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be | ||
* transferred to `to`. | ||
* - When `from` is zero, `tokenId` will be minted for `to`. | ||
* - When `to` is zero, ``from``'s `tokenId` will be burned. | ||
* - `from` cannot be the zero address. | ||
* - `to` cannot be the zero address. | ||
* | ||
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. | ||
*/ | ||
function _beforeTokenTransfer( | ||
address from, | ||
address to, | ||
uint256 tokenId | ||
) internal virtual override { | ||
super._beforeTokenTransfer(from, to, tokenId); | ||
|
||
if (from == address(0)) { | ||
_addTokenToAllTokensEnumeration(tokenId); | ||
} else if (from != to) { | ||
_removeTokenFromOwnerEnumeration(from, tokenId); | ||
} | ||
if (to == address(0)) { | ||
_removeTokenFromAllTokensEnumeration(tokenId); | ||
} else if (to != from) { | ||
_addTokenToOwnerEnumeration(to, tokenId); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Private function to add a token to this extension's ownership-tracking data structures. | ||
* @param to address representing the new owner of the given token ID | ||
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address | ||
*/ | ||
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { | ||
uint256 length = ERC721.balanceOf(to); | ||
_ownedTokens[to][length] = tokenId; | ||
_ownedTokensIndex[tokenId] = length; | ||
} | ||
|
||
/** | ||
* @dev Private function to add a token to this extension's token tracking data structures. | ||
* @param tokenId uint256 ID of the token to be added to the tokens list | ||
*/ | ||
function _addTokenToAllTokensEnumeration(uint256 tokenId) private { | ||
_allTokensIndex[tokenId] = _allTokens.length; | ||
_allTokens.push(tokenId); | ||
} | ||
|
||
/** | ||
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that | ||
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for | ||
* gas optimizations e.g. when performing a transfer operation (avoiding double writes). | ||
* This has O(1) time complexity, but alters the order of the _ownedTokens array. | ||
* @param from address representing the previous owner of the given token ID | ||
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address | ||
*/ | ||
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) | ||
private | ||
{ | ||
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and | ||
// then delete the last slot (swap and pop). | ||
|
||
uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; | ||
uint256 tokenIndex = _ownedTokensIndex[tokenId]; | ||
|
||
// When the token to delete is the last token, the swap operation is unnecessary | ||
if (tokenIndex != lastTokenIndex) { | ||
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; | ||
|
||
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token | ||
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index | ||
} | ||
|
||
// This also deletes the contents at the last position of the array | ||
delete _ownedTokensIndex[tokenId]; | ||
delete _ownedTokens[from][lastTokenIndex]; | ||
} | ||
|
||
/** | ||
* @dev Private function to remove a token from this extension's token tracking data structures. | ||
* This has O(1) time complexity, but alters the order of the _allTokens array. | ||
* @param tokenId uint256 ID of the token to be removed from the tokens list | ||
*/ | ||
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { | ||
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and | ||
// then delete the last slot (swap and pop). | ||
|
||
uint256 lastTokenIndex = _allTokens.length - 1; | ||
uint256 tokenIndex = _allTokensIndex[tokenId]; | ||
|
||
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so | ||
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding | ||
// an 'if' statement (like in _removeTokenFromOwnerEnumeration) | ||
uint256 lastTokenId = _allTokens[lastTokenIndex]; | ||
|
||
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token | ||
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index | ||
|
||
// This also deletes the contents at the last position of the array | ||
delete _allTokensIndex[tokenId]; | ||
_allTokens.pop(); | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.4; | ||
|
||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | ||
import "@openzeppelin/contracts/security/Pausable.sol"; | ||
import "@openzeppelin/contracts/access/Ownable.sol"; | ||
import "@1001-digital/erc721-extensions/contracts/RandomlyAssigned.sol"; | ||
|
||
import "./ERC721CustomEnumerable.sol"; | ||
|
||
contract NftyHalloween is | ||
ERC721, | ||
Pausable, | ||
Ownable, | ||
RandomlyAssigned, | ||
ERC721CustomEnumerable | ||
{ | ||
uint256 public constant MAX_TOKENS = 9000; | ||
string private nftyBaseURI = ""; | ||
IERC721 public nftyPass; | ||
|
||
mapping(uint256 => address) private claimed; | ||
|
||
constructor( | ||
string memory _nftyBaseURI, | ||
address _nftyPass | ||
) ERC721("NftyHalloween", "NFTYH") | ||
RandomlyAssigned(MAX_TOKENS, 0) | ||
{ | ||
nftyBaseURI = _nftyBaseURI; | ||
nftyPass = IERC721(_nftyPass); | ||
} | ||
|
||
function mint(uint256 pass) external whenNotPaused { | ||
require(nftyPass.ownerOf(pass) == msg.sender, "Pass not owned by sender"); | ||
require(claimed[pass] == address(0), "Pass already used"); | ||
|
||
claimed[pass] = msg.sender; | ||
uint256 next = nextToken(); | ||
_safeMint(msg.sender, next); | ||
} | ||
|
||
function setBaseURI(string memory baseURI) external onlyOwner { | ||
nftyBaseURI = baseURI; | ||
} | ||
|
||
function pause() external onlyOwner { | ||
_pause(); | ||
} | ||
|
||
function unpause() external onlyOwner { | ||
_unpause(); | ||
} | ||
|
||
function _beforeTokenTransfer( | ||
address from, | ||
address to, | ||
uint256 tokenId | ||
) internal override(ERC721, ERC721CustomEnumerable) { | ||
super._beforeTokenTransfer(from, to, tokenId); | ||
} | ||
|
||
function claimedPass(uint256 pass) public view returns (address) { | ||
address claimedAddress = claimed[pass]; | ||
require(claimedAddress != address(0), "Pass not claimed"); | ||
|
||
return claimedAddress; | ||
} | ||
|
||
function _baseURI() internal view virtual override returns (string memory) { | ||
return nftyBaseURI; | ||
} | ||
|
||
function supportsInterface(bytes4 interfaceId) | ||
public | ||
view | ||
override(ERC721, ERC721CustomEnumerable) | ||
returns (bool) | ||
{ | ||
return super.supportsInterface(interfaceId); | ||
} | ||
} |
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
Oops, something went wrong.