diff --git a/.solhint.json b/.solhint.json index 791f830..d474b62 100644 --- a/.solhint.json +++ b/.solhint.json @@ -20,6 +20,7 @@ "avoid-throw": "off", "avoid-suicide": "error", "avoid-sha3": "warn", - "no-global-import": "off" + "no-global-import": "off", + "quotes": "warn" } } diff --git a/contracts/auction/AuctionManager.sol b/contracts/auction/AuctionManager.sol index ab24822..d17d20d 100644 --- a/contracts/auction/AuctionManager.sol +++ b/contracts/auction/AuctionManager.sol @@ -6,6 +6,7 @@ import { IAuctionManager } from "./interfaces/IAuctionManager.sol"; import { IOwnable } from "./interfaces/IOwnable.sol"; import { IERC721GeneralMint } from "../erc721/interfaces/IERC721GeneralMint.sol"; import { IERC721EditionMint } from "../erc721/interfaces/IERC721EditionMint.sol"; +import { IERC721EditionsStartId } from "../erc721/interfaces/IERC721EditionsStartId.sol"; import "../utils/EIP712Upgradeable.sol"; import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; @@ -285,10 +286,7 @@ contract AuctionManager is IAuctionManager.EditionAuction memory editionAuction = _auctionEditions[claim.auctionId]; if (editionAuction.used == true) { - auction.tokenId = IERC721EditionMint(auction.collection).mintOneToRecipient( - editionAuction.editionId, - address(this) - ); + IERC721EditionMint(auction.collection).mintOneToRecipient(editionAuction.editionId, address(this)); } else { auction.tokenId = IERC721GeneralMint(auction.collection).mintOneToOneRecipient(address(this)); } @@ -319,14 +317,22 @@ contract AuctionManager is function fulfillAuction(bytes32 auctionId) external override auctionIsLiveOnChain(auctionId) nonReentrant { IAuctionManager.EnglishAuction memory auction = _auctions[auctionId]; IAuctionManager.HighestBidderData memory highestBidderData = _highestBidders[auctionId]; + IAuctionManager.EditionAuction memory editionData = _auctionEditions[auctionId]; require(block.timestamp > auction.endTime && auction.endTime != 0, "Auction hasn't ended"); // send nft to recipient as preferred by winning bidder + + // use edition to transfer (calculate tokenId on the fly) + uint256 tokenId = auction.tokenId; + if (editionData.used) { + tokenId = IERC721EditionsStartId(auction.collection).editionStartId(editionData.editionId); + } + try IERC721(auction.collection).safeTransferFrom( address(this), highestBidderData.preferredNFTRecipient, - auction.tokenId + tokenId ) {} catch { // encourage fulfiller to urge highest bidder to update their preferred nft recipient @@ -371,6 +377,34 @@ contract AuctionManager is _auctions[auctionId].state = AuctionState.FULFILLED; } + function cleanup() external { + bytes32 auc6Id = 0x3635643538626564343763383530306366376633383630310000000000000000; + bytes32 auc1Id = 0x3635643537656362626539653766323466383839393738340000000000000000; + + // handle sending of money of auc1 + IAuctionManager.EnglishAuction memory auction = _auctions[auc1Id]; + require(auction.state != AuctionState.FULFILLED, "a"); + IAuctionManager.HighestBidderData memory highestBidderData = _highestBidders[auc1Id]; + uint256 platformCut = highestBidderData.amount; + require(platformCut == 30000000000000000, "a.5"); + (bool sentToPlatform, bytes memory dataPlatform) = _platform.call{ value: platformCut }(""); + require(sentToPlatform, "Failed to send native gas token to platform"); + + // handle sending nft of auc6 + IAuctionManager.EnglishAuction memory auctionAuc6 = _auctions[auc6Id]; + require(auctionAuc6.state == AuctionState.FULFILLED, "b"); + IAuctionManager.HighestBidderData memory highestBidderDataAuc6 = _highestBidders[auc6Id]; + IAuctionManager.EditionAuction memory editionData = _auctionEditions[auc6Id]; + require(editionData.editionId == 6, "c"); + uint256 tokenId = IERC721EditionsStartId(auction.collection).editionStartId(editionData.editionId); + require(tokenId == 7, "d"); + IERC721(auction.collection).safeTransferFrom( + address(this), + highestBidderDataAuc6.preferredNFTRecipient, + tokenId + ); + } + /** * @notice See {IAuctionManager-cancelAuctionOnChain} */ diff --git a/contracts/erc721/ERC721Editions.sol b/contracts/erc721/ERC721Editions.sol index 52a252d..08625b9 100644 --- a/contracts/erc721/ERC721Editions.sol +++ b/contracts/erc721/ERC721Editions.sol @@ -177,10 +177,10 @@ contract ERC721Editions is uint48 tokenLimitPerTx, uint48 maxTotalClaimableViaVector, uint48 maxUserClaimableViaVector, - bytes32 allowlistRoot + address currency ) = abi.decode( mintVectorData, - (address, address, uint48, uint48, uint192, uint48, uint48, uint48, bytes32) + (address, address, uint48, uint48, uint192, uint48, uint48, uint48, address) ); IAbridgedMintVector(mintManager).createAbridgedVector( @@ -191,14 +191,14 @@ contract ERC721Editions is uint160(paymentRecipient), maxTotalClaimableViaVector, 0, - 0, + uint160(currency), tokenLimitPerTx, maxUserClaimableViaVector, pricePerToken, uint48(editionId), // cast down true, false, - allowlistRoot + 0 ) ); } diff --git a/contracts/erc721/ERC721EditionsDFS.sol b/contracts/erc721/ERC721EditionsDFS.sol index 362b8f6..cdf8822 100644 --- a/contracts/erc721/ERC721EditionsDFS.sol +++ b/contracts/erc721/ERC721EditionsDFS.sol @@ -172,10 +172,10 @@ contract ERC721EditionsDFS is uint48 tokenLimitPerTx, uint48 maxTotalClaimableViaVector, uint48 maxUserClaimableViaVector, - bytes32 allowlistRoot + address currency ) = abi.decode( mintVectorData, - (address, address, uint48, uint48, uint192, uint48, uint48, uint48, bytes32) + (address, address, uint48, uint48, uint192, uint48, uint48, uint48, address) ); IAbridgedMintVector(mintManager).createAbridgedVector( @@ -186,14 +186,14 @@ contract ERC721EditionsDFS is uint160(paymentRecipient), maxTotalClaimableViaVector, 0, - 0, + uint160(currency), tokenLimitPerTx, maxUserClaimableViaVector, pricePerToken, uint48(editionId), // cast down true, false, - allowlistRoot + 0 ) ); } diff --git a/contracts/erc721/ERC721GeneralSequence.sol b/contracts/erc721/ERC721GeneralSequence.sol index 8a82ab4..c580bcc 100644 --- a/contracts/erc721/ERC721GeneralSequence.sol +++ b/contracts/erc721/ERC721GeneralSequence.sol @@ -7,13 +7,14 @@ import "../tokenManager/interfaces/IPostTransfer.sol"; import "../tokenManager/interfaces/IPostBurn.sol"; import "./interfaces/IERC721GeneralMint.sol"; import "./ERC721GeneralSequenceBase.sol"; +import "./onchain/OnchainFileStorage.sol"; /** * @title Generalized ERC721 that expects tokenIds to increment in a monotonically increasing sequence * @author highlight.xyz * @notice Generalized NFT smart contract */ -contract ERC721GeneralSequence is MetadataEncryption, ERC721GeneralSequenceBase { +contract ERC721GeneralSequence is MetadataEncryption, ERC721GeneralSequenceBase, OnchainFileStorage { using EnumerableSet for EnumerableSet.AddressSet; /** @@ -125,6 +126,42 @@ contract ERC721GeneralSequence is MetadataEncryption, ERC721GeneralSequenceBase ); } + /** + * @notice Used for meta-transactions + */ + function _msgSender() + internal + view + virtual + override(ERC721GeneralSequenceBase, ContextUpgradeable) + returns (address sender) + { + return ERC721GeneralSequenceBase._msgSender(); + } + + /** + * @notice Used for meta-transactions + */ + function _msgData() + internal + view + virtual + override(ERC721GeneralSequenceBase, ContextUpgradeable) + returns (bytes calldata) + { + return ERC721GeneralSequenceBase._msgData(); + } + + /** + * @dev For more efficient reverts. + */ + function _revert(bytes4 errorSelector) internal pure override(ERC721GeneralSequenceBase, OnchainFileStorage) { + assembly { + mstore(0x00, errorSelector) + revert(0x00, 0x04) + } + } + /** * @notice Initialize the contract * @param creator Creator/owner of contract diff --git a/contracts/erc721/ERC721GeneralSequenceBase.sol b/contracts/erc721/ERC721GeneralSequenceBase.sol index 25a4b5f..cf620dd 100644 --- a/contracts/erc721/ERC721GeneralSequenceBase.sol +++ b/contracts/erc721/ERC721GeneralSequenceBase.sol @@ -7,7 +7,7 @@ import "../tokenManager/interfaces/IPostTransfer.sol"; import "../tokenManager/interfaces/IPostBurn.sol"; import "./interfaces/IERC721GeneralSequenceMint.sol"; import "./erc721a/ERC721AURIStorageUpgradeable.sol"; -import "./custom/interfaces/IHighlightRenderer.sol"; +import "./inchain-rendering/interfaces/IHLRenderer.sol"; /** * @title Generalized Base ERC721 @@ -96,7 +96,7 @@ abstract contract ERC721GeneralSequenceBase is ERC721Base, ERC721AURIStorageUpgr // process mint on custom renderer if present CustomRendererConfig memory _customRendererConfig = customRendererConfig; if (_customRendererConfig.processMintDataOnRenderer) { - IHighlightRenderer(_customRendererConfig.renderer).processOneRecipientMint(tempSupply, 1, recipient); + IHLRenderer(_customRendererConfig.renderer).processOneRecipientMint(tempSupply, 1, recipient); } return tempSupply; @@ -118,11 +118,7 @@ abstract contract ERC721GeneralSequenceBase is ERC721Base, ERC721AURIStorageUpgr // process mint on custom renderer if present CustomRendererConfig memory _customRendererConfig = customRendererConfig; if (_customRendererConfig.processMintDataOnRenderer) { - IHighlightRenderer(_customRendererConfig.renderer).processOneRecipientMint( - tempSupply + 1, - amount, - recipient - ); + IHLRenderer(_customRendererConfig.renderer).processOneRecipientMint(tempSupply + 1, amount, recipient); } } @@ -145,11 +141,7 @@ abstract contract ERC721GeneralSequenceBase is ERC721Base, ERC721AURIStorageUpgr // process mint on custom renderer if present CustomRendererConfig memory _customRendererConfig = customRendererConfig; if (_customRendererConfig.processMintDataOnRenderer) { - IHighlightRenderer(_customRendererConfig.renderer).processMultipleRecipientMint( - tempSupply + 1, - 1, - recipients - ); + IHLRenderer(_customRendererConfig.renderer).processMultipleRecipientMint(tempSupply + 1, 1, recipients); } } @@ -175,7 +167,7 @@ abstract contract ERC721GeneralSequenceBase is ERC721Base, ERC721AURIStorageUpgr // process mint on custom renderer if present CustomRendererConfig memory _customRendererConfig = customRendererConfig; if (_customRendererConfig.processMintDataOnRenderer) { - IHighlightRenderer(_customRendererConfig.renderer).processMultipleRecipientMint( + IHLRenderer(_customRendererConfig.renderer).processMultipleRecipientMint( tempSupply + 1, amount, recipients @@ -300,7 +292,7 @@ abstract contract ERC721GeneralSequenceBase is ERC721Base, ERC721AURIStorageUpgr */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { if (customRendererConfig.renderer != address(0)) { - return IHighlightRenderer(customRendererConfig.renderer).tokenURI(tokenId); + return IHLRenderer(customRendererConfig.renderer).tokenURI(tokenId); } return ERC721AURIStorageUpgradeable.tokenURI(tokenId); } diff --git a/contracts/erc721/custom/FiniSketch.sol b/contracts/erc721/custom/FiniSketch.sol new file mode 100644 index 0000000..1d538e7 --- /dev/null +++ b/contracts/erc721/custom/FiniSketch.sol @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-3.0 + +/** + + #@@@@ @@@@@ + @@@@@@@@@. @@@@@@@@@ + @@@@@@@@@. @@@@@@@@@ + @@@@@@@@@. @@@@@ %@@@@ @@@@@@@@@ + #@@@@ @@@@@ @@@@@ @@@@@ + @@@@@@@@@@@@ + ///////////// //////////// + ///////////////. ////////////////// + ///////////////// ////////////////// + ///////////////// //////////////// + ///////////// //////////// + +*/ + +/* solhint-disable max-line-length */ + +pragma solidity 0.8.10; + +import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol"; +import "./interfaces/IFiniOracle.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +// import "hardhat/console.sol"; + +contract FiniSketch { + using Strings for uint256; + using Strings for int256; + + address public finiOracleContract = 0xfA52EC029c47AfeD7Ff10DcF478149342441CEb4; + + // svgPart1 + // font + // svgPart2 + // color + // svgPart3 + // text part 1 (variable) + // svgPart4 + // text part 2 (variable) + // svgPart5 + // image1 + // svgPart6 + // image2 + // svgPart7 + // fini logo + // highlight logo + + string public svgPart1 = + '")); + } + + /** + * @notice Returns oracle address for a tokenId. + * @dev This will be used to determine which token is assigned to which oracle. + */ + function getOracleAddressForTokenId(uint256 tokenId) public view returns (address) { + uint256 index = pseudoRandom(tokenId) % oracleAddresses.length; + return oracleAddresses[index]; + } + + function getColorForTokenId(uint256 tokenId) public view returns (string memory) { + uint256 index = pseudoRandom(tokenId) % colors.length; + return colors[index]; + } + + /** + * @notice Get a pseudorandom idempotent value for each tokenId. + * @dev This will be used to determine which token is assigned to which oracle. + */ + function pseudoRandom(uint256 tokenId) public pure returns (uint) { + return uint(keccak256(abi.encodePacked(tokenId))); + } + + function intToStringWithDecimals(int256 value, uint8 decimals) public pure returns (string memory) { + // Check if the value is negative + bool negative = value < 0; + + // Get what should come before the decimal + uint256 intValue = negative ? uint256(-value) : uint256(value); + // string memory intPart = toString(intValue); + uint256 intPart = intValue / 10 ** decimals; + + // Get what should come after the decimals + uint256 fractionalPart = (intValue - (intPart * 10 ** decimals)); + + // Concatenate the integer and fractional parts + string memory result; + if (negative) { + result = string(abi.encodePacked("-", intPart.toString(), ".", fractionalPart.toString())); + } else { + result = string(abi.encodePacked(intPart.toString(), ".", fractionalPart.toString())); + } + + return result; + } +} diff --git a/contracts/erc721/custom/[untitled]/CuratedRendererV1.sol b/contracts/erc721/custom/[untitled]/CuratedRendererV1.sol new file mode 100644 index 0000000..0b7d2f0 --- /dev/null +++ b/contracts/erc721/custom/[untitled]/CuratedRendererV1.sol @@ -0,0 +1,484 @@ +//SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; + +import "../../inchain-rendering/interfaces/IHLRenderer.sol"; +import "../../interfaces/IERC721GeneralSupplyMetadata.sol"; +import "../interfaces/IHLFS.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/Base64.sol"; +import "../interfaces/IOwnable.sol"; + +/** + * @notice HL in-chain Renderer for curated hash based projects + * @dev Currently supports a fairly fixed configuratoin + * @author highlight.xyz + */ +contract CuratedRendererV1 { + /** + * @notice Throw when invalid input to process mint data + */ + error InvalidMintData(); + + /** + * @notice Throw when seed input for a token cannot be found + */ + error SeedInputNotFound(); + + /** + * @notice Throw when transaction sender isn't collection owner + */ + error NotCollectionOwner(); + + /** + * @notice Input that seeds token metadata + * @param previousBlockHash Hash of the block before the one the tokens were minted on + * @param blockTimestamp Timestamp of block that tokens were minted on + * @param startTokenId ID of first token of minted batch + */ + struct SeedInput { + bytes32 previousBlockHash; + uint48 blockTimestamp; + uint176 startTokenId; + uint32 numMinted; + } + + /** + * @notice Simple collection config (to be made more complex) + */ + struct CollectionConfig { + string name; + string previewsBaseUri; + string htmlLang; + bool htmlBodyExpected; + bool useCDN; + } + + uint256 private constant _32_BIT_MAX_MINUS_1 = 2 ** 32 - 1; + + /** + * @notice Store the seed inputs for each token batch (for each nft contract) + * @dev Assume startTokenIds are incrementing (up to implementer), assume first batch's startTokenId is 1 + */ + mapping(address => SeedInput[]) public collectionSeedInputs; + + /** + * @notice Store config per collection + */ + mapping(address => CollectionConfig) public collectionConfig; + + /** + * @notice Set a collection's config + */ + function setCollectionConfig(CollectionConfig calldata config, address collection) external { + if (IOwnable(collection).owner() == msg.sender || msg.sender == collection) { + collectionConfig[collection] = config; + } else { + _revert(NotCollectionOwner.selector); + } + } + + /** + * @notice See {IHlRenderer-processOneRecipientMint} + */ + function processOneRecipientMint(uint256 firstTokenId, uint256 numTokens, address recipient) external { + collectionSeedInputs[msg.sender].push( + SeedInput(blockhash(block.number - 1), uint48(block.timestamp), uint176(firstTokenId), uint32(numTokens)) + ); + } + + /** + * @notice See {IHLRenderer-tokenURI} + */ + function tokenURI(uint256 tokenId) public view virtual returns (string memory) { + address collection = msg.sender; + SeedInput memory _seedInput = getSeedInput(tokenId, collection); + bytes32 curatedHash = getCuratedHash(tokenId, collection); + + string memory tokenIdStr = Strings.toString(tokenId); + + bytes[] memory metadata = new bytes[](3); + bytes[] memory encodedJson = new bytes[](2); + + // inject values in JS + string memory injectedToken = string( + abi.encodePacked( + 'const injectedToken = {"blockHash": "', + Strings.toHexString(uint256(_seedInput.previousBlockHash)), + '", "', + 'tokenId": "', + tokenIdStr, + '", "', + 'timestamp": "', + Strings.toString(_seedInput.blockTimestamp), + '", "', + 'hash": "', + Strings.toHexString(uint256(curatedHash)), + '", "', + 'isCurated": "1"', + "};" + ) + ); + + metadata[0] = abi.encodePacked( + '{"name": "', + collectionConfig[collection].name, + " #", + tokenIdStr, + '", "', + 'description": "', + IHLFS(collection).fileContents("description.txt"), + '", "', + 'image": "', + collectionConfig[collection].previewsBaseUri, + "/", + Strings.toString(tokenId), + ".png", + '", "' + 'animation_url": "data:text/html;base64,' + ); + metadata[1] = bytes(Base64.encode(_generateHTML(collection, injectedToken, false))); + metadata[2] = bytes('"}'); + + encodedJson[0] = bytes("data:application/json;base64,"); + encodedJson[1] = bytes(Base64.encode(concat(metadata))); + return string(concat(encodedJson)); + } + + /** + * @notice See {IHLRenderer-tokenURI} + */ + function tokenURIWithCDN(uint256 tokenId, address collection) public view virtual returns (string memory) { + SeedInput memory _seedInput = getSeedInput(tokenId, collection); + bytes32 curatedHash = getCuratedHash(tokenId, collection); + + string memory tokenIdStr = Strings.toString(tokenId); + + bytes[] memory metadata = new bytes[](3); + bytes[] memory encodedJson = new bytes[](2); + + // inject values in JS + string memory injectedToken = string( + abi.encodePacked( + 'const injectedToken = {"blockHash": "', + Strings.toHexString(uint256(_seedInput.previousBlockHash)), + '", "', + 'tokenId": "', + tokenIdStr, + '", "', + 'timestamp": "', + Strings.toString(_seedInput.blockTimestamp), + '", "', + 'hash": "', + Strings.toHexString(uint256(curatedHash)), + '", "', + 'isCurated": "1"', + "};" + ) + ); + + metadata[0] = abi.encodePacked( + '{"name": "', + collectionConfig[collection].name, + " #", + tokenIdStr, + '", "', + 'description": "', + IHLFS(collection).fileContents("description.txt"), + '", "', + 'image": "', + collectionConfig[collection].previewsBaseUri, + "/", + Strings.toString(tokenId), + ".png", + '", "' + 'animation_url": "data:text/html;base64,' + ); + metadata[1] = bytes(Base64.encode(_generateHTML(collection, injectedToken, true))); + metadata[2] = bytes('"}'); + + encodedJson[0] = bytes("data:application/json;base64,"); + encodedJson[1] = bytes(Base64.encode(concat(metadata))); + return string(concat(encodedJson)); + } + + /** + * @notice Get a token's curated hash + */ + function getCuratedHash(uint256 tokenId, address collection) public view returns (bytes32) { + bytes32[] memory curatedHashes = _parseCuratedHashText( + IHLFS(collection).fileContents("curatedHashes.txt"), + IERC721GeneralSupplyMetadata(collection).limitSupply() + ); + uint256 initialCuratedHashesLength = curatedHashes.length; + bytes32 lastCuratedHash = bytes32(0); + + for (uint256 i = 0; i < tokenId; i++) { + SeedInput memory _seedInput = getSeedInput(i + 1, collection); + bytes32 seed = _getSeed(_seedInput, i + 1); + uint256 generatedIndex = _prngSeedInput( + seed, + _seedInput.previousBlockHash, + i + 1, + initialCuratedHashesLength - i + ); + uint256 virtualIndexPlusOne = 0; + + for (uint256 j = 0; j < initialCuratedHashesLength; j++) { + if (curatedHashes[j] != bytes32(0)) { + virtualIndexPlusOne += 1; + if (virtualIndexPlusOne == generatedIndex + 1) { + // curated hash found for final token id + if (i == tokenId - 1) { + lastCuratedHash = curatedHashes[j]; + } + curatedHashes[j] = bytes32(0); + break; + } + } + } + } + + return lastCuratedHash; + } + + /** + * Get a token's seed + */ + function getSeed(uint256 tokenId, address collection) public view returns (bytes32) { + SeedInput memory _seedInput = getSeedInput(tokenId, collection); + return _getSeed(_seedInput, tokenId); + } + + /** + * @notice Get a token's seed input + */ + function getSeedInput(uint256 tokenId, address collection) public view returns (SeedInput memory) { + SeedInput[] memory _seedInputs = collectionSeedInputs[collection]; + uint256 numInputs = _seedInputs.length; + if (numInputs == 0) { + _revert(SeedInputNotFound.selector); + } + for (uint256 i = numInputs - 1; i >= 0; i--) { + if (tokenId >= _seedInputs[i].startTokenId) { + // assume first batch's startTokenId is 1 + if (_seedInputs[i].startTokenId + _seedInputs[i].numMinted <= tokenId) { + _revert(SeedInputNotFound.selector); + } else { + return _seedInputs[i]; + } + } + } + } + + /** + * @notice Generate the project's HTML file + */ + function _generateHTML( + address collection, + string memory injectedToken, + bool useCDNOverride + ) public view returns (bytes memory) { + bytes[] memory html = new bytes[](4); + + bool useCDN = useCDNOverride ? true : collectionConfig[collection].useCDN; + string memory libScriptPrefix = useCDN ? ' src="' : ">"; + string memory libScriptSuffix = useCDN ? '">' : ""; + html[0] = abi.encodePacked( + '', + "
", + IHLFS(collection).fileContents("headPrefix.html"), + "", + "", + "" + ); + + string memory HTMLBody = ""; + if (collectionConfig[collection].htmlBodyExpected) { + HTMLBody = IHLFS(collection).fileContents("body.html"); + } + html[3] = abi.encodePacked("", HTMLBody, "", ""); + + return concat(html); + } + + /** + * @notice Base64 Encode a metadata JSON + */ + function _encodeMetadataJSON(bytes memory json) private pure returns (string memory) { + return string(abi.encodePacked("data:application/json;base64,", Base64.encode(json))); + } + + /** + * @notice Concatenate byte arrays + */ + function concat(bytes[] memory arrays) public pure returns (bytes memory) { + uint totalLength = 0; + for (uint i = 0; i < arrays.length; i++) { + totalLength += arrays[i].length; + } + + bytes memory result = new bytes(totalLength); + uint resultPtr; + assembly { + resultPtr := add(result, 0x20) + } + + for (uint i = 0; i < arrays.length; i++) { + bytes memory array = arrays[i]; + uint arrayLength = array.length; + + uint arrayPtr; + assembly { + arrayPtr := add(array, 0x20) + } + + // Efficiently copy memory block + for (uint j = 0; j < arrayLength; j += 32) { + assembly { + let chunk := mload(add(arrayPtr, j)) + mstore(add(resultPtr, j), chunk) + } + } + + resultPtr += arrayLength; + } + + return result; + } + + /** + * @notice Parse out all curated hashes for a mint + */ + function _parseCuratedHashText( + string memory curatedHashesText, + uint256 numLines + ) private pure returns (bytes32[] memory) { + bytes32[] memory curatedHashes = new bytes32[](numLines); + + uint256 arrayIndex = 0; + bytes memory stringBytes = bytes(curatedHashesText); + uint256 i = 0; + + while (i < stringBytes.length && arrayIndex < numLines) { + // Skip the "0x" prefix at the start of each line + if (i == 0 || stringBytes[i - 1] == "\n") { + i += 2; + } + + bytes32 line; + for (uint j = 0; j < 32; j++) { + // Convert two hex characters to one byte + bytes1 b1 = _parseHexChar(stringBytes[i]); + bytes1 b2 = _parseHexChar(stringBytes[i + 1]); + line |= bytes32((uint8(b1) * 16 + uint8(b2)) * 2 ** (8 * (31 - j))); + i += 2; + } + + curatedHashes[arrayIndex] = line; + arrayIndex++; + + // Skip the newline character + if (i < stringBytes.length && stringBytes[i] == "\n") { + i++; + } + } + + return curatedHashes; + } + + /** + * @notice Parse a byte value represented in string to the byte representation of the value + */ + function _parseHexChar(bytes1 char) internal pure returns (bytes1) { + if (uint8(char) >= 48 && uint8(char) <= 57) { + return bytes1(uint8(char) - 48); // 0-9 + } + if (uint8(char) >= 65 && uint8(char) <= 70) { + return bytes1(uint8(char) - 55); // A-F + } + if (uint8(char) >= 97 && uint8(char) <= 102) { + return bytes1(uint8(char) - 87); // a-f + } + revert("Invalid hex character"); + } + + /** + * @notice Generate an index via a prng, given seed input and a max value + */ + function _prngSeedInput( + bytes32 generalHash, + bytes32 blockHash, + uint256 tokenId, + uint256 max + ) private pure returns (uint256) { + uint256 seed = tokenId; + + // process each byte of generalHash and blockHash + for (uint256 i = 0; i < 32; i++) { + // extract and process high and low nibbles for generalHash + uint8 highNibbleTx = uint8(generalHash[i]) >> 4; + uint8 lowNibbleTx = uint8(generalHash[i]) & 0x0F; + if (highNibbleTx <= 9) { + seed += highNibbleTx; + } + if (lowNibbleTx <= 9) { + seed += lowNibbleTx; + } + + // extract and process high and low nibbles for blockHash + uint8 highNibbleBlock = uint8(blockHash[i]) >> 4; + uint8 lowNibbleBlock = uint8(blockHash[i]) & 0x0F; + if (highNibbleBlock <= 9) { + seed += highNibbleBlock; + } + if (lowNibbleBlock <= 9) { + seed += lowNibbleBlock; + } + } + + uint256 t = (seed + 0x6D2B79F5) & (_32_BIT_MAX_MINUS_1); + t = imul(t ^ (t >> 15), t | 1) & (_32_BIT_MAX_MINUS_1); + t ^= (t + imul(t ^ (t >> 7), t | 61)) & (_32_BIT_MAX_MINUS_1); + t = (t ^ (t >> 14)) & (_32_BIT_MAX_MINUS_1); + return t % max; + } + + /** + * @notice Replicate js Math.imul + */ + function imul(uint256 a, uint256 b) private pure returns (uint256) { + return (a * b) % (_32_BIT_MAX_MINUS_1 + 1); + } + + /** + * @notice Get a token's seed util + */ + function _getSeed(SeedInput memory _seedInput, uint256 tokenId) private view returns (bytes32) { + return keccak256(abi.encodePacked(_seedInput.previousBlockHash, tokenId, _seedInput.blockTimestamp)); + } + + /** + * @notice Efficient revert + */ + function _revert(bytes4 errorSelector) private pure { + assembly { + mstore(0x00, errorSelector) + revert(0x00, 0x04) + } + } +} diff --git a/contracts/erc721/custom/[untitled]/TestRead.sol b/contracts/erc721/custom/[untitled]/TestRead.sol new file mode 100644 index 0000000..6ff1b5b --- /dev/null +++ b/contracts/erc721/custom/[untitled]/TestRead.sol @@ -0,0 +1,154 @@ +//SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.10; + +library Bytecode { + error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end); + + /** + @notice Generate a creation code that results on a contract with `_code` as bytecode + @param _code The returning value of the resulting `creationCode` + @return creationCode (constructor) for new contract + */ + function creationCodeFor(bytes memory _code) internal pure returns (bytes memory) { + /* + 0x00 0x63 0x63XXXXXX PUSH4 _code.length size + 0x01 0x80 0x80 DUP1 size size + 0x02 0x60 0x600e PUSH1 14 14 size size + 0x03 0x60 0x6000 PUSH1 00 0 14 size size + 0x04 0x39 0x39 CODECOPY size + 0x05 0x60 0x6000 PUSH1 00 0 size + 0x06 0xf3 0xf3 RETURN +
+ */
+
+ return abi.encodePacked(hex"63", uint32(_code.length), hex"80_60_0E_60_00_39_60_00_F3", _code);
+ }
+
+ /**
+ @notice Returns the size of the code on a given address
+ @param _addr Address that may or may not contain code
+ @return size of the code on the given `_addr`
+ */
+ function codeSize(address _addr) internal view returns (uint256 size) {
+ assembly {
+ size := extcodesize(_addr)
+ }
+ }
+
+ /**
+ @notice Returns the code of a given address
+ @dev It will fail if `_end < _start`
+ @param _addr Address that may or may not contain code
+ @param _start number of bytes of code to skip on read
+ @param _end index before which to end extraction
+ @return oCode read from `_addr` deployed bytecode
+
+ Forked from: https://gist.github.com/KardanovIR/fe98661df9338c842b4a30306d507fbd
+ */
+ function codeAt(address _addr, uint256 _start, uint256 _end) internal view returns (bytes memory oCode) {
+ uint256 csize = codeSize(_addr);
+ if (csize == 0) return bytes("");
+
+ if (_start > csize) return bytes("");
+ if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end);
+
+ unchecked {
+ uint256 reqSize = _end - _start;
+ uint256 maxSize = csize - _start;
+
+ uint256 size = maxSize < reqSize ? maxSize : reqSize;
+
+ assembly {
+ // allocate output byte array - this could also be done without assembly
+ // by using o_code = new bytes(size)
+ oCode := mload(0x40)
+ // new "memory end" including padding
+ mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f))))
+ // store length in memory
+ mstore(oCode, size)
+ // actually retrieve the code, this needs assembly
+ extcodecopy(_addr, add(oCode, 0x20), _start, size)
+ }
+ }
+ }
+}
+
+contract TestRead {
+ address[] public chunks;
+
+ constructor(address[] memory _chunks) {
+ chunks = _chunks;
+ }
+
+ function readLibrary() external view returns (string memory lib) {
+ uint256 size;
+ uint ptr = 0x20;
+ address currentChunk;
+ unchecked {
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ lib := mload(0x40)
+ }
+
+ // Copy chunks from storage into memory
+ for (uint i = 0; i < chunks.length; i++) {
+ currentChunk = chunks[i];
+ size = Bytecode.codeSize(currentChunk) - 1;
+
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ extcodecopy(currentChunk, add(lib, ptr), 1, size)
+ }
+ ptr += size;
+ }
+
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ // allocate output byte array - this could also be done without assembly
+ // by using o_code = new bytes(size)
+ // new "memory end" including padding
+ mstore(0x40, add(lib, and(add(ptr, 0x1f), not(0x1f))))
+ // store length in memory
+ mstore(lib, sub(ptr, 0x20))
+ }
+ }
+ return lib;
+ }
+
+ function fileContents() external view returns (string memory) {
+ uint256 chunksLength = chunks.length;
+ string memory contents = "";
+
+ for (uint256 i = 0; i < chunksLength; i++) {
+ contents = string(
+ abi.encodePacked(contents, string(_readBytecode(chunks[i], 1, chunks[i].code.length - 1)))
+ );
+ }
+
+ return contents;
+ }
+
+ /**
+ * @notice Read bytecode at an address
+ * @ author SOLMATE
+ */
+ function _readBytecode(address pointer, uint256 start, uint256 size) private view returns (bytes memory data) {
+ /// @solidity memory-safe-assembly
+ assembly {
+ // Get a pointer to some free memory.
+ data := mload(0x40)
+
+ // Update the free memory pointer to prevent overriding our data.
+ // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
+ // Adding 31 to size and running the result through the logic above ensures
+ // the memory pointer remains word-aligned, following the Solidity convention.
+ mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))
+
+ // Store the size of the data in the first 32 byte chunk of free memory.
+ mstore(data, size)
+
+ // Copy the code into memory right after the 32 bytes we used to store the size.
+ extcodecopy(pointer, add(data, 32), start, size)
+ }
+ }
+}
diff --git a/contracts/erc721/custom/interfaces/IBaseURI.sol b/contracts/erc721/custom/interfaces/IBaseURI.sol
new file mode 100644
index 0000000..c007bda
--- /dev/null
+++ b/contracts/erc721/custom/interfaces/IBaseURI.sol
@@ -0,0 +1,12 @@
+//SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+/**
+ * @notice Interface for gen series base uri
+ */
+interface IBaseURI {
+ /**
+ * @notice Return base uri
+ */
+ function baseURI() external view returns (string memory);
+}
diff --git a/contracts/erc721/custom/interfaces/IFiniOracle.sol b/contracts/erc721/custom/interfaces/IFiniOracle.sol
new file mode 100644
index 0000000..79e4367
--- /dev/null
+++ b/contracts/erc721/custom/interfaces/IFiniOracle.sol
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-3.0
+
+/// @title IDescriptor interface
+
+pragma solidity 0.8.10;
+import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol";
+
+interface IFiniOracle {
+ function findRoundId(
+ uint256 targetTimestamp,
+ AggregatorV2V3Interface feed,
+ uint80 roundId,
+ uint16 counter,
+ uint16 jumpSize,
+ bool jumpDirection
+ ) external view returns (uint80);
+}
diff --git a/contracts/erc721/custom/interfaces/IHLFS.sol b/contracts/erc721/custom/interfaces/IHLFS.sol
new file mode 100644
index 0000000..37b2833
--- /dev/null
+++ b/contracts/erc721/custom/interfaces/IHLFS.sol
@@ -0,0 +1,9 @@
+//SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+interface IHLFS {
+ /**
+ * @notice Get contents of a file on a HL FS
+ */
+ function fileContents(string calldata fileName) external view returns (string memory);
+}
diff --git a/contracts/erc721/custom/interfaces/IOwnable.sol b/contracts/erc721/custom/interfaces/IOwnable.sol
new file mode 100644
index 0000000..db0ad2d
--- /dev/null
+++ b/contracts/erc721/custom/interfaces/IOwnable.sol
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: Unlicensed
+pragma solidity 0.8.10;
+
+/**
+ * @notice Simple interface to interact with EIP-173 implementing contracts
+ */
+interface IOwnable {
+ function owner() external view returns (address);
+}
diff --git a/contracts/erc721/custom/shloms404/Renderer404.sol b/contracts/erc721/custom/shloms404/Renderer404.sol
new file mode 100644
index 0000000..0c1e6d5
--- /dev/null
+++ b/contracts/erc721/custom/shloms404/Renderer404.sol
@@ -0,0 +1,147 @@
+//SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+import "../../inchain-rendering/interfaces/IHLRenderer.sol";
+import "../../interfaces/IERC721GeneralSupplyMetadata.sol";
+import "../interfaces/IHLFS.sol";
+import "@openzeppelin/contracts/utils/Strings.sol";
+import "@openzeppelin/contracts/utils/Base64.sol";
+
+/**
+ * @notice Custom HL renderer for 404 by Shl0ms
+ * @author highlight.xyz
+ */
+contract Renderer404 {
+ /**
+ * @notice Constant CIDs
+ */
+ string private constant _CREATOR_CID = "bafkreigki7ryypgtde7ykgy7zzkyabkwgu4nmqw6tyapfzmsbmknnnltau";
+ string private constant _FILE_FORMAT_CID = "bafkreih5tlzemmxra3p635ljecifcefcyooplayoc77o3m4gxipz6ycism";
+ string private constant _IMAGE_DIMENSIONS_CID = "bafkreigqorkwpylsg5mh4ve3ngw2mpm4ssk2nthbdljfl47oq7iqypqzve";
+
+ /**
+ * @notice See {IHLRenderer-tokenURI}
+ */
+ function tokenURI(uint256 tokenId) external view virtual returns (string memory) {
+ return _tokenURI(tokenId, msg.sender);
+ }
+
+ /**
+ * @notice tokenURI for easier inspection
+ */
+ function tokenURIInspection(uint256 tokenId, address collection) external view returns (string memory) {
+ return _tokenURI(tokenId, collection);
+ }
+
+ /**
+ * @notice Generate uri for a token
+ */
+ function _tokenURI(uint256 tokenId, address collection) private view returns (string memory) {
+ uint256 limitSupply = IERC721GeneralSupplyMetadata(collection).limitSupply();
+ if (limitSupply == 0 || tokenId > limitSupply || tokenId == 0) {
+ revert("Invalid");
+ }
+
+ string memory tokenIdStr = Strings.toString(tokenId);
+ if (tokenId < 10) {
+ tokenIdStr = string(abi.encodePacked("00", tokenIdStr));
+ } else if (tokenId < 100) {
+ tokenIdStr = string(abi.encodePacked("0", tokenIdStr));
+ }
+
+ bytes memory metadataPt1 = abi.encodePacked(
+ '{"name": "',
+ IHLFS(collection).fileContents("monospace404.txt"),
+ " // ",
+ _getTraitCID(tokenId, collection, limitSupply, "monospaceTokenIds.txt", 12),
+ '", "',
+ 'description": "',
+ IHLFS(collection).fileContents("description.txt"),
+ '", "',
+ 'image": "ipfs://',
+ _getTraitCID(tokenId, collection, limitSupply, "images.txt", 59),
+ '", "'
+ );
+ bytes memory metadataAttributesPt1 = abi.encodePacked(
+ 'attributes": ['
+ '{"trait_type": "FILE FORMAT", "value": "ipfs://',
+ _FILE_FORMAT_CID,
+ '"}, ',
+ '{"trait_type": "IMAGE DIMENSIONS", "value": "ipfs://',
+ _IMAGE_DIMENSIONS_CID,
+ '"}, ',
+ '{"trait_type": "COLOR SCHEME", "value": "ipfs://',
+ _getTraitCID(tokenId, collection, limitSupply, "colorSchemes.txt", 59),
+ '"}, '
+ );
+ bytes memory metadataAttributesPt2 = abi.encodePacked(
+ '{"trait_type": "MUSICAL ACCOMPANIMENT", "value": "ipfs://',
+ _getTraitCID(tokenId, collection, limitSupply, "musicalAccompaniments.txt", 59),
+ '"}, ',
+ '{"trait_type": "CREATOR", "value": "ipfs://',
+ _CREATOR_CID,
+ '"}, ',
+ '{"trait_type": "FILE NO.", "value": "',
+ tokenIdStr,
+ '"}]}'
+ );
+
+ return
+ string(
+ abi.encodePacked(
+ "data:application/json;base64,",
+ Base64.encode(abi.encodePacked(metadataPt1, metadataAttributesPt1, metadataAttributesPt2))
+ )
+ );
+ }
+
+ /**
+ * @notice Get the CID for a token trait (image or attribute)
+ */
+ function _getTraitCID(
+ uint256 tokenId,
+ address collection,
+ uint256 numTokens,
+ string memory fileName,
+ uint256 numBytesPerLine
+ ) private view returns (string memory) {
+ return _parseCIDsText(IHLFS(collection).fileContents(fileName), numTokens, numBytesPerLine)[tokenId - 1];
+ }
+
+ /**
+ * @notice Parse out all CIDs in a text file
+ */
+ function _parseCIDsText(
+ string memory cidsText,
+ uint256 numLines,
+ uint256 numBytesPerLine
+ ) private pure returns (string[] memory) {
+ // example CID, all lines expected to follow this format:
+ // bafkreiapwnok3zsifqvdvotlgv4z5hfdi247wdxtmxa4u7tzfxsbysn3pa
+ // 59 characters long
+ // Parse lines that are 60 characters long (CID + \n) (for images.txt for eg.)
+
+ string[] memory cids = new string[](numLines);
+
+ uint256 arrayIndex = 0;
+ bytes memory stringBytes = bytes(cidsText);
+ uint256 i = 0;
+
+ while (i < stringBytes.length && arrayIndex < numLines) {
+ bytes memory line = new bytes(numBytesPerLine);
+ for (uint j = 0; j < numBytesPerLine; j++) {
+ line[j] = stringBytes[i];
+ i++;
+ }
+ cids[arrayIndex] = string(line);
+ arrayIndex++;
+
+ // Skip the newline character
+ if (i < stringBytes.length && stringBytes[i] == "\n") {
+ i++;
+ }
+ }
+
+ return cids;
+ }
+}
diff --git a/contracts/erc721/inchain-rendering/TestHighlightRenderer.sol b/contracts/erc721/inchain-rendering/TestHighlightRenderer.sol
new file mode 100644
index 0000000..873feac
--- /dev/null
+++ b/contracts/erc721/inchain-rendering/TestHighlightRenderer.sol
@@ -0,0 +1,136 @@
+//SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+import "./interfaces/IHLRenderer.sol";
+import "../interfaces/IERC721GeneralSupplyMetadata.sol";
+import "@openzeppelin/contracts/utils/Strings.sol";
+
+/**
+ * @notice Mock implementation of IHLRenderer
+ */
+contract TestHighlightRenderer is IHLRenderer {
+ /**
+ * @notice Throw when mint details are queried for a token that hasn't been minted
+ */
+ error InvalidTokenId();
+
+ /**
+ * @notice Details that seed token metadata
+ */
+ struct SeedDetails {
+ bytes32 previousBlockHash;
+ uint256 blockTimestamp;
+ // etc.
+ }
+
+ /**
+ * @notice Store the seed details for each token batch (for each nft contract)
+ */
+ mapping(address => mapping(uint256 => SeedDetails)) private _startTokenIdToSeedDetails;
+
+ /**
+ * @notice Store the first token id of each minted batch (for each nft contract)
+ */
+ mapping(address => uint256[]) private _startTokenIds;
+
+ /**
+ * @notice See {IHLRenderer-processMultipleRecipientMint}
+ */
+ function processMultipleRecipientMint(
+ uint256 firstTokenId,
+ uint256 numTokensPerRecipient, // unused in this implementation
+ address[] calldata orderedRecipients // unused in this implementation
+ ) external {
+ _startTokenIdToSeedDetails[msg.sender][firstTokenId] = SeedDetails(
+ blockhash(block.number - 1),
+ block.timestamp
+ );
+ _startTokenIds[msg.sender].push(firstTokenId);
+ }
+
+ /**
+ * @notice See {IHLRenderer-processOneRecipientMint}
+ */
+ function processOneRecipientMint(
+ uint256 firstTokenId,
+ uint256 numTokens, // unused in this implementation
+ address recipient // unused in this implementation
+ ) external {
+ _startTokenIdToSeedDetails[msg.sender][firstTokenId] = SeedDetails(
+ blockhash(block.number - 1),
+ block.timestamp
+ );
+ _startTokenIds[msg.sender].push(firstTokenId);
+ }
+
+ /**
+ * @notice See {IHLRenderer-tokenURI}
+ */
+ function tokenURI(uint256 tokenId) external view returns (string memory) {
+ // typically return a base64-encoded json
+ // probably store a preview images base uri globally (stored via Highlight)
+ // for demonstration purposes, just return a simple string here:
+ uint256 numTokens = IERC721GeneralSupplyMetadata(msg.sender).supply();
+ return concatenateSeedDetails(getSeedDetails(tokenId, numTokens + 1, msg.sender), tokenId);
+ }
+
+ /**
+ * @notice Concatenate seed details into a fake uri
+ */
+ function concatenateSeedDetails(
+ SeedDetails memory _seedDetails,
+ uint256 tokenId
+ ) public view returns (string memory) {
+ return
+ string(
+ abi.encodePacked(
+ Strings.toString(uint256(_seedDetails.previousBlockHash)),
+ Strings.toString(_seedDetails.blockTimestamp),
+ Strings.toString(tokenId)
+ )
+ );
+ }
+
+ /**
+ * @notice Get a token's seed details
+ * @dev Assumes _startTokenIds are in ascending order
+ * @param tokenId ID of token to get seed details for
+ * @param nextTokenId ID of immediate token that hasn't been minted on NFT contract
+ * @param nftContract NFT contract
+ */
+ function getSeedDetails(
+ uint256 tokenId,
+ uint256 nextTokenId,
+ address nftContract
+ ) public view returns (SeedDetails memory) {
+ uint256[] memory tempStartTokenIds = _startTokenIds[nftContract];
+ uint256 numBatches = tempStartTokenIds.length;
+
+ if (numBatches == 0) {
+ revert InvalidTokenId();
+ }
+
+ uint256 previousStartTokenId = tempStartTokenIds[0];
+ if (numBatches == 1) {
+ if (tokenId >= previousStartTokenId && tokenId < nextTokenId) {
+ return _startTokenIdToSeedDetails[nftContract][previousStartTokenId];
+ } else {
+ revert InvalidTokenId();
+ }
+ }
+
+ for (uint256 i = 1; i < numBatches; i++) {
+ if (tokenId >= previousStartTokenId && tokenId < tempStartTokenIds[i]) {
+ return _startTokenIdToSeedDetails[nftContract][previousStartTokenId];
+ }
+
+ previousStartTokenId = tempStartTokenIds[i];
+ }
+
+ if (tokenId >= previousStartTokenId && tokenId < nextTokenId) {
+ return _startTokenIdToSeedDetails[nftContract][previousStartTokenId];
+ } else {
+ revert InvalidTokenId();
+ }
+ }
+}
diff --git a/contracts/erc721/inchain-rendering/interfaces/IHLRenderer.sol b/contracts/erc721/inchain-rendering/interfaces/IHLRenderer.sol
new file mode 100644
index 0000000..225ceb7
--- /dev/null
+++ b/contracts/erc721/inchain-rendering/interfaces/IHLRenderer.sol
@@ -0,0 +1,36 @@
+//SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+/**
+ * @notice Highlight's custom renderer interface for collections
+ */
+interface IHLRenderer {
+ /**
+ * @notice Process a mint to multiple recipients (likely store mint details)
+ * @dev Implementations should assume msg.sender to be the NFT contract
+ * @param firstTokenId ID of first token to be minted (next ones are minted sequentially)
+ * @param numTokensPerRecipient Number of tokens minted to each recipient
+ * @param orderedRecipients Recipients to mint tokens to, sequentially
+ */
+ function processMultipleRecipientMint(
+ uint256 firstTokenId,
+ uint256 numTokensPerRecipient,
+ address[] calldata orderedRecipients
+ ) external;
+
+ /**
+ * @notice Process a mint to one recipient (likely store mint details)
+ * @dev Implementations should assume msg.sender to be the NFT contract
+ * @param firstTokenId ID of first token to be minted (next ones are minted sequentially)
+ * @param numTokens Number of tokens minted
+ * @param recipient Recipient to mint to
+ */
+ function processOneRecipientMint(uint256 firstTokenId, uint256 numTokens, address recipient) external;
+
+ /**
+ * @notice Return token metadata for a token
+ * @dev Implementations should assume msg.sender to be the NFT contract
+ * @param tokenId ID of token to return metadata for
+ */
+ function tokenURI(uint256 tokenId) external view returns (string memory);
+}
diff --git a/contracts/erc721/instances/GenerativeSeries.sol b/contracts/erc721/instances/GenerativeSeries.sol
index d939062..2301113 100644
--- a/contracts/erc721/instances/GenerativeSeries.sol
+++ b/contracts/erc721/instances/GenerativeSeries.sol
@@ -37,7 +37,7 @@ contract GenerativeSeries is Proxy {
* @ param tokenLimitPerTx
* @ param maxTotalClaimableViaVector
* @ param maxUserClaimableViaVector
- * @ param allowlistRoot
+ * @ param currency
* @param mechanicVectorData Mechanic mint vector data
* @ param mechanicVectorId Global mechanic vector ID
* @ param mechanic Mechanic address
@@ -69,10 +69,10 @@ contract GenerativeSeries is Proxy {
uint48 tokenLimitPerTx,
uint48 maxTotalClaimableViaVector,
uint48 maxUserClaimableViaVector,
- bytes32 allowlistRoot
+ address currency
) = abi.decode(
mintVectorData,
- (address, address, uint48, uint48, uint192, uint48, uint48, uint48, bytes32)
+ (address, address, uint48, uint48, uint192, uint48, uint48, uint48, address)
);
IAbridgedMintVector(mintManager).createAbridgedVector(
@@ -83,14 +83,14 @@ contract GenerativeSeries is Proxy {
uint160(paymentRecipient),
maxTotalClaimableViaVector,
0,
- 0,
+ uint160(currency),
tokenLimitPerTx,
maxUserClaimableViaVector,
pricePerToken,
0,
false,
false,
- allowlistRoot
+ 0
)
);
}
diff --git a/contracts/erc721/instances/MultipleEditions.sol b/contracts/erc721/instances/MultipleEditions.sol
index ac53b49..bf52367 100644
--- a/contracts/erc721/instances/MultipleEditions.sol
+++ b/contracts/erc721/instances/MultipleEditions.sol
@@ -47,7 +47,7 @@ contract MultipleEditions is Proxy {
* @ param tokenLimitPerTx
* @ param maxTotalClaimableViaVector
* @ param maxUserClaimableViaVector
- * @ param allowlistRoot
+ * @ param currency
* @param mechanicVectorData Mechanic mint vector data
* @ param mechanicVectorId Global mechanic vector ID
* @ param mechanic Mechanic address
diff --git a/contracts/erc721/instances/MultipleEditionsDFS.sol b/contracts/erc721/instances/MultipleEditionsDFS.sol
index d39d46a..8287b99 100644
--- a/contracts/erc721/instances/MultipleEditionsDFS.sol
+++ b/contracts/erc721/instances/MultipleEditionsDFS.sol
@@ -45,7 +45,7 @@ contract MultipleEditionsDFS is Proxy {
* @ param tokenLimitPerTx
* @ param maxTotalClaimableViaVector
* @ param maxUserClaimableViaVector
- * @ param allowlistRoot
+ * @ param currency
* @param mechanicVectorData Mechanic mint vector data
* @ param mechanicVectorId Global mechanic vector ID
* @ param mechanic Mechanic address
diff --git a/contracts/erc721/instances/Series.sol b/contracts/erc721/instances/Series.sol
index 5efb784..893656c 100644
--- a/contracts/erc721/instances/Series.sol
+++ b/contracts/erc721/instances/Series.sol
@@ -37,7 +37,7 @@ contract Series is Proxy {
* @ param tokenLimitPerTx
* @ param maxTotalClaimableViaVector
* @ param maxUserClaimableViaVector
- * @ param allowlistRoot
+ * @ param currency
* @param mechanicVectorData Mechanic mint vector data
* @ param mechanicVectorId Global mechanic vector ID
* @ param mechanic Mechanic address
@@ -66,10 +66,10 @@ contract Series is Proxy {
uint48 tokenLimitPerTx,
uint48 maxTotalClaimableViaVector,
uint48 maxUserClaimableViaVector,
- bytes32 allowlistRoot
+ address currency
) = abi.decode(
mintVectorData,
- (address, address, uint48, uint48, uint192, uint48, uint48, uint48, bytes32)
+ (address, address, uint48, uint48, uint192, uint48, uint48, uint48, address)
);
IAbridgedMintVector(mintManager).createAbridgedVector(
@@ -80,14 +80,14 @@ contract Series is Proxy {
uint160(paymentRecipient),
maxTotalClaimableViaVector,
0,
- 0,
+ uint160(currency),
tokenLimitPerTx,
maxUserClaimableViaVector,
pricePerToken,
0,
false,
false,
- allowlistRoot
+ 0
)
);
}
diff --git a/contracts/erc721/instances/SingleEdition.sol b/contracts/erc721/instances/SingleEdition.sol
index 89e97b6..e0dd887 100644
--- a/contracts/erc721/instances/SingleEdition.sol
+++ b/contracts/erc721/instances/SingleEdition.sol
@@ -37,7 +37,7 @@ contract SingleEdition is Proxy {
* @ param tokenLimitPerTx
* @ param maxTotalClaimableViaVector
* @ param maxUserClaimableViaVector
- * @ param allowlistRoot
+ * @ param currency
* @param mechanicVectorData Mechanic mint vector data
* @ param mechanicVectorId Global mechanic vector ID
* @ param mechanic Mechanic address
@@ -69,10 +69,10 @@ contract SingleEdition is Proxy {
uint48 tokenLimitPerTx,
uint48 maxTotalClaimableViaVector,
uint48 maxUserClaimableViaVector,
- bytes32 allowlistRoot
+ address currency
) = abi.decode(
mintVectorData,
- (address, address, uint48, uint48, uint192, uint48, uint48, uint48, bytes32)
+ (address, address, uint48, uint48, uint192, uint48, uint48, uint48, address)
);
IAbridgedMintVector(mintManager).createAbridgedVector(
@@ -83,14 +83,14 @@ contract SingleEdition is Proxy {
uint160(paymentRecipient),
maxTotalClaimableViaVector,
0,
- 0,
+ uint160(currency),
tokenLimitPerTx,
maxUserClaimableViaVector,
pricePerToken,
0,
true,
false,
- allowlistRoot
+ 0
)
);
}
diff --git a/contracts/erc721/instances/SingleEditionDFS.sol b/contracts/erc721/instances/SingleEditionDFS.sol
index a79d353..bb2eec5 100644
--- a/contracts/erc721/instances/SingleEditionDFS.sol
+++ b/contracts/erc721/instances/SingleEditionDFS.sol
@@ -37,7 +37,7 @@ contract SingleEditionDFS is Proxy {
* @ param tokenLimitPerTx
* @ param maxTotalClaimableViaVector
* @ param maxUserClaimableViaVector
- * @ param allowlistRoot
+ * @ param currency
* @param mechanicVectorData Mechanic mint vector data
* @ param mechanicVectorId Global mechanic vector ID
* @ param mechanic Mechanic address
@@ -69,10 +69,10 @@ contract SingleEditionDFS is Proxy {
uint48 tokenLimitPerTx,
uint48 maxTotalClaimableViaVector,
uint48 maxUserClaimableViaVector,
- bytes32 allowlistRoot
+ address currency
) = abi.decode(
mintVectorData,
- (address, address, uint48, uint48, uint192, uint48, uint48, uint48, bytes32)
+ (address, address, uint48, uint48, uint192, uint48, uint48, uint48, address)
);
IAbridgedMintVector(mintManager).createAbridgedVector(
@@ -83,14 +83,14 @@ contract SingleEditionDFS is Proxy {
uint160(paymentRecipient),
maxTotalClaimableViaVector,
0,
- 0,
+ uint160(currency),
tokenLimitPerTx,
maxUserClaimableViaVector,
pricePerToken,
0,
true,
false,
- allowlistRoot
+ 0
)
);
}
diff --git a/contracts/erc721/interfaces/IERC721EditionsStartId.sol b/contracts/erc721/interfaces/IERC721EditionsStartId.sol
new file mode 100644
index 0000000..a41b282
--- /dev/null
+++ b/contracts/erc721/interfaces/IERC721EditionsStartId.sol
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+/**
+ * @author highlight.xyz
+ */
+interface IERC721EditionsStartId {
+ /**
+ * @notice Get an edition's start id
+ */
+ function editionStartId(uint256 editionId) external view returns (uint256);
+}
diff --git a/contracts/erc721/onchain/Bytecode.sol b/contracts/erc721/onchain/Bytecode.sol
new file mode 100644
index 0000000..a69a24f
--- /dev/null
+++ b/contracts/erc721/onchain/Bytecode.sol
@@ -0,0 +1,77 @@
+//SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+/**
+ * @notice From MathCastles deployment
+ */
+library Bytecode {
+ error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end);
+
+ /**
+ @notice Generate a creation code that results on a contract with `_code` as bytecode
+ @param _code The returning value of the resulting `creationCode`
+ @return creationCode (constructor) for new contract
+ */
+ function creationCodeFor(bytes memory _code) internal pure returns (bytes memory) {
+ /*
+ 0x00 0x63 0x63XXXXXX PUSH4 _code.length size
+ 0x01 0x80 0x80 DUP1 size size
+ 0x02 0x60 0x600e PUSH1 14 14 size size
+ 0x03 0x60 0x6000 PUSH1 00 0 14 size size
+ 0x04 0x39 0x39 CODECOPY size
+ 0x05 0x60 0x6000 PUSH1 00 0 size
+ 0x06 0xf3 0xf3 RETURN
+
+ */
+
+ return abi.encodePacked(hex"63", uint32(_code.length), hex"80_60_0E_60_00_39_60_00_F3", _code);
+ }
+
+ /**
+ @notice Returns the size of the code on a given address
+ @param _addr Address that may or may not contain code
+ @return size of the code on the given `_addr`
+ */
+ function codeSize(address _addr) internal view returns (uint256 size) {
+ assembly {
+ size := extcodesize(_addr)
+ }
+ }
+
+ /**
+ @notice Returns the code of a given address
+ @dev It will fail if `_end < _start`
+ @param _addr Address that may or may not contain code
+ @param _start number of bytes of code to skip on read
+ @param _end index before which to end extraction
+ @return oCode read from `_addr` deployed bytecode
+
+ Forked from: https://gist.github.com/KardanovIR/fe98661df9338c842b4a30306d507fbd
+ */
+ function codeAt(address _addr, uint256 _start, uint256 _end) internal view returns (bytes memory oCode) {
+ uint256 csize = codeSize(_addr);
+ if (csize == 0) return bytes("");
+
+ if (_start > csize) return bytes("");
+ if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end);
+
+ unchecked {
+ uint256 reqSize = _end - _start;
+ uint256 maxSize = csize - _start;
+
+ uint256 size = maxSize < reqSize ? maxSize : reqSize;
+
+ assembly {
+ // allocate output byte array - this could also be done without assembly
+ // by using o_code = new bytes(size)
+ oCode := mload(0x40)
+ // new "memory end" including padding
+ mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f))))
+ // store length in memory
+ mstore(oCode, size)
+ // actually retrieve the code, this needs assembly
+ extcodecopy(_addr, add(oCode, 0x20), _start, size)
+ }
+ }
+ }
+}
diff --git a/contracts/erc721/onchain/OnchainFileStorage.sol b/contracts/erc721/onchain/OnchainFileStorage.sol
index 2ecd0c4..6d6483f 100644
--- a/contracts/erc721/onchain/OnchainFileStorage.sol
+++ b/contracts/erc721/onchain/OnchainFileStorage.sol
@@ -1,6 +1,7 @@
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;
+import "./Bytecode.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
/**
@@ -105,40 +106,39 @@ abstract contract OnchainFileStorage is OwnableUpgradeable {
uint256 fileStorageAddressesLength = fileStorageAddresses.length;
string memory contents = "";
- for (uint256 i = 0; i < fileStorageAddressesLength; i++) {
- contents = string(
- abi.encodePacked(
- contents,
- string(_readBytecode(fileStorageAddresses[i], 1, fileStorageAddresses[i].code.length - 1))
- )
- );
- }
-
- return contents;
- }
-
- /**
- * @notice Read bytecode at an address
- * @ author SOLMATE
- */
- function _readBytecode(address pointer, uint256 start, uint256 size) private view returns (bytes memory data) {
- /// @solidity memory-safe-assembly
- assembly {
- // Get a pointer to some free memory.
- data := mload(0x40)
+ // @author of the following section: @xaltgeist (0x16cc845d144a283d1b0687fbac8b0601cc47a6c3 on Ethereum mainnet)
+ // edited with HL FS -like variable names
+ uint256 size;
+ uint ptr = 0x20;
+ address currentChunk;
+ unchecked {
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ contents := mload(0x40)
+ }
- // Update the free memory pointer to prevent overriding our data.
- // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
- // Adding 31 to size and running the result through the logic above ensures
- // the memory pointer remains word-aligned, following the Solidity convention.
- mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))
+ for (uint i = 0; i < fileStorageAddressesLength; i++) {
+ currentChunk = fileStorageAddresses[i];
+ size = Bytecode.codeSize(currentChunk) - 1;
- // Store the size of the data in the first 32 byte chunk of free memory.
- mstore(data, size)
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ extcodecopy(currentChunk, add(contents, ptr), 1, size)
+ }
+ ptr += size;
+ }
- // Copy the code into memory right after the 32 bytes we used to store the size.
- extcodecopy(pointer, add(data, 32), start, size)
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ // allocate output byte array - this could also be done without assembly
+ // by using o_code = new bytes(size)
+ // new "memory end" including padding
+ mstore(0x40, add(contents, and(add(ptr, 0x1f), not(0x1f))))
+ // store length in memory
+ mstore(contents, sub(ptr, 0x20))
+ }
}
+ return contents;
}
/**
diff --git a/contracts/erc721/onchain/interfaces/IHLFS.sol b/contracts/erc721/onchain/interfaces/IHLFS.sol
new file mode 100644
index 0000000..59aaaa8
--- /dev/null
+++ b/contracts/erc721/onchain/interfaces/IHLFS.sol
@@ -0,0 +1,19 @@
+//SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+interface IHLFS {
+ /**
+ * @notice Return registered file names
+ */
+ function files() external view returns (string[] memory);
+
+ /**
+ * @notice Return storage bytecode addresses for a file
+ */
+ function fileStorage(string calldata fileName) external view returns (address[] memory);
+
+ /**
+ * @notice Return file contents
+ */
+ function fileContents(string calldata fileName) external view returns (string memory);
+}
diff --git a/contracts/mint/MintFeeOracle.sol b/contracts/mint/MintFeeOracle.sol
new file mode 100644
index 0000000..4a5523f
--- /dev/null
+++ b/contracts/mint/MintFeeOracle.sol
@@ -0,0 +1,546 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity 0.8.10;
+
+import "./interfaces/IMintFeeOracle.sol";
+import "./interfaces/IAbridgedMintVector.sol";
+import "./mechanics/interfaces/IMechanicMintManagerView.sol";
+import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
+import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
+import "../utils/FullMath.sol";
+import "../utils/IUniswapV3PoolState.sol";
+import "./referrals/IReferralManagerView.sol";
+
+/**
+ * @title MintManager's mint fee oracle
+ * @author highlight.xyz
+ */
+contract MintFeeOracle is UUPSUpgradeable, OwnableUpgradeable {
+ /**
+ * @notice Throw when an action is unauthorized
+ */
+ error Unauthorized();
+
+ /**
+ * @notice Throw when an ERC20 is invalid
+ */
+ error InvalidERC20();
+
+ /**
+ * @notice Throw when an ERC20 config is invalid
+ */
+ error InvalidERC20Config();
+
+ /**
+ * @notice Throw when caller is not the MintManager
+ */
+ error NotMintManager();
+
+ /**
+ * @notice Throw when an invalid ether value is sent in when processing an ether mint fee cap
+ */
+ error InvalidEtherMintFeeCap();
+
+ /**
+ * @notice Throw when sending ether fails
+ */
+ error EtherSendFailed();
+
+ /**
+ * @notice Throw when a mint vector's expected type is false
+ */
+ error InvalidVectorType();
+
+ /**
+ * @notice Throw when resolved referrer is invalid
+ */
+ error InvalidReferrer();
+
+ /**
+ * @notice Config for allowlisted ERC20s
+ * @param baseMintFee Base fee fee amount per token (if price isn't real-time)
+ * @param realTimeOracle Address of real time oracle to query if price is real-time
+ */
+ struct ERC20Config {
+ uint96 baseMintFee;
+ address realTimeOracle;
+ }
+
+ /**
+ * @notice MintManager
+ */
+ address private _mintManager;
+
+ /**
+ * @notice Mint fee subsidized config (vector + user)
+ */
+ mapping(bytes32 => bool) private _subsidizedMintConfig;
+
+ /**
+ * @notice Gasless mechanic address
+ */
+ address private _gaslessMechanicAddress;
+
+ /**
+ * @notice Allowlisted ERC20s -> mint fee
+ */
+ mapping(address => ERC20Config) private _allowlistedERC20s;
+
+ /**
+ * @notice When true, creator rewards is enabled
+ */
+ bool private _creatorRewardsEnabled;
+
+ /**
+ * @notice Constants for uniswap price calculation
+ */
+ uint256 public constant ETH_WEI = 10 ** 18;
+ uint256 public constant FULL_MATH_SHIFT = 1 << 192;
+
+ /**
+ * @notice Backup referral manager
+ */
+ address private _backupReferralManager;
+
+ /**
+ * @notice Backup referral manager
+ */
+ address private _backupDiscreteDutchAuctionMechanic;
+
+ /**
+ * @notice Backup referral manager
+ */
+ address private _backupRankedAuctionMechanic;
+
+ /**
+ * @notice Emitted when a referrer is paid out a portion of the mint fee
+ * @param vectorId Vector ID
+ * @param referrer Referrer
+ * @param currency Currency
+ * @param referralPayout Amount paid out to referrer
+ */
+ event ReferralPayout(
+ bytes32 indexed vectorId,
+ address indexed referrer,
+ address indexed currency,
+ uint256 referralPayout
+ );
+
+ /**
+ * @notice Only let the mint manager call
+ */
+ modifier onlyMintManager() {
+ if (msg.sender != _mintManager) {
+ _revert(NotMintManager.selector);
+ }
+ _;
+ }
+
+ /* solhint-disable no-empty-blocks */
+ /**
+ * @notice Initialize contract
+ */
+ function initialize(
+ address mintManager,
+ address platform,
+ address gaslessMechanic,
+ address backupReferralManager,
+ address backupDiscreteDutchAuctionMechanic,
+ address backupRankedAuctionMechanic
+ ) external initializer {
+ __Ownable_init();
+ _transferOwnership(platform);
+ _mintManager = mintManager;
+ _gaslessMechanicAddress = gaslessMechanic;
+ _backupReferralManager = backupReferralManager;
+ _backupDiscreteDutchAuctionMechanic = backupDiscreteDutchAuctionMechanic;
+ _backupRankedAuctionMechanic = backupRankedAuctionMechanic;
+ }
+
+ /**
+ * @notice Set an allowlisted erc20 config
+ * @param erc20 ERC20 address
+ * @param config ERC20 config
+ */
+ function setAllowlistedERC20Config(address erc20, ERC20Config calldata config) external onlyOwner {
+ if (
+ !(config.baseMintFee != 0 && config.realTimeOracle == address(0)) &&
+ !(config.baseMintFee == 0 && config.realTimeOracle != address(0))
+ ) {
+ _revert(InvalidERC20Config.selector);
+ }
+ _allowlistedERC20s[erc20] = config;
+ }
+
+ /**
+ * @notice Delist an allowlisted erc20 config
+ * @param erc20 ERC20 address
+ */
+ function delistERC20(address erc20) external onlyOwner {
+ delete _allowlistedERC20s[erc20];
+ }
+
+ /**
+ * @notice Set mint manager
+ */
+ function setMintManager(address newMintManager) external onlyOwner {
+ _mintManager = newMintManager;
+ }
+
+ /**
+ * @notice Set backup referral manager
+ */
+ function setBackupReferralManager(address newBackupReferralManager) external onlyOwner {
+ _backupReferralManager = newBackupReferralManager;
+ }
+
+ /**
+ * @notice Set backup discrete dutch auction mechanic
+ */
+ function setBackupDiscreteDutchAuctionMechanic(address newBackupDiscreteDutchAuctionMechanic) external onlyOwner {
+ _backupDiscreteDutchAuctionMechanic = newBackupDiscreteDutchAuctionMechanic;
+ }
+
+ /**
+ * @notice Set backup ranked auction mechanic
+ */
+ function setBackupRankedAuctionMechanic(address newBackupRankedAuctionMechanic) external onlyOwner {
+ _backupRankedAuctionMechanic = newBackupRankedAuctionMechanic;
+ }
+
+ /**
+ * @notice Set gasless mechanic
+ */
+ function setGaslessMechanic(address newGaslessMechanic) external onlyOwner {
+ _gaslessMechanicAddress = newGaslessMechanic;
+ }
+
+ /**
+ * @notice Set creator rewards enabled
+ */
+ function setCreatorRewardsEnabled(bool creatorRewardsEnabled) external onlyOwner {
+ _creatorRewardsEnabled = creatorRewardsEnabled;
+ }
+
+ /**
+ * @notice Subsidize mint fee for a mint config (vector + sender)
+ */
+ function subsidizeMintConfig(bytes32 vectorId, address minter) external onlyOwner {
+ bytes32 mintConfig = _encodeMintConfig(vectorId, minter);
+ require(!_subsidizedMintConfig[mintConfig], "Already subsidized");
+ _subsidizedMintConfig[mintConfig] = true;
+ }
+
+ /**
+ * @notice Subsidize mint fee for a mint config (vector + sender)
+ */
+ function unsubsidizeMintVector(bytes32 vectorId, address minter) external onlyOwner {
+ bytes32 mintConfig = _encodeMintConfig(vectorId, minter);
+ require(_subsidizedMintConfig[mintConfig], "Not already subsidized");
+ _subsidizedMintConfig[mintConfig] = false;
+ }
+
+ /**
+ * @notice Withdraw native gas token owed to platform
+ */
+ function withdrawNativeGasToken(uint256 amountToWithdraw, address payable recipient) external onlyOwner {
+ (bool sentToPlatform, ) = recipient.call{ value: amountToWithdraw }("");
+ if (!sentToPlatform) {
+ _revert(EtherSendFailed.selector);
+ }
+ }
+
+ /**
+ * @notice Withdraw ERC20 owed to platform
+ */
+ function withdrawERC20(address currency, uint256 amountToWithdraw, address recipient) external onlyOwner {
+ IERC20(currency).transfer(recipient, amountToWithdraw);
+ }
+
+ /* solhint-disable code-complexity */
+ /**
+ * @notice See {IMintFeeOracle-processClassicVectorMintFeeCap}
+ */
+ function processClassicVectorMintFeeCap(
+ bytes32 vectorId,
+ bool payoutCreatorReward,
+ address vectorPaymentRecipient,
+ address currency,
+ uint256 amount,
+ address minter
+ ) external payable onlyMintManager returns (uint256) {
+ if (currency == address(0)) {
+ if (msg.value != amount) {
+ _revert(InvalidEtherMintFeeCap.selector);
+ }
+ }
+
+ address referralManager = _referralManager();
+ if (referralManager == minter) {
+ uint256 referralPayout = (amount * 10) / 100;
+ // get referrer via referral manager
+ address referrer = IReferralManagerView(referralManager).getCurrentReferrer(vectorId);
+ if (referrer == address(0)) {
+ _revert(InvalidReferrer.selector);
+ }
+
+ // only send referral if minter wasn't referrer
+ if (referrer != tx.origin) {
+ if (currency == address(0)) {
+ (bool sentToRecipient, ) = payable(referrer).call{ value: referralPayout }("");
+ if (!sentToRecipient) {
+ _revert(EtherSendFailed.selector);
+ }
+ } else {
+ IERC20(currency).transfer(referrer, referralPayout);
+ }
+
+ emit ReferralPayout(vectorId, referrer, currency, referralPayout);
+ }
+ }
+
+ if (payoutCreatorReward) {
+ uint256 creatorPayout = amount / 2;
+ if (currency == address(0)) {
+ (bool sentToRecipient, ) = vectorPaymentRecipient.call{ value: creatorPayout }("");
+ if (!sentToRecipient) {
+ _revert(EtherSendFailed.selector);
+ }
+ } else {
+ IERC20(currency).transfer(vectorPaymentRecipient, creatorPayout);
+ }
+
+ return creatorPayout;
+ }
+
+ return 0;
+ }
+
+ /* solhint-enable code-complexity */
+
+ /**
+ * @notice See {IMintFeeOracle-getClassicVectorMintFeeCap}
+ */
+ function getClassicVectorMintFeeCap(
+ bytes32 vectorId,
+ uint256 numToMint,
+ address minter,
+ address currency
+ ) external view returns (uint256) {
+ if (_isFeeSubsidized(vectorId, minter)) {
+ return 0;
+ }
+ if (currency == address(0)) {
+ return (block.chainid == 137 ? 2265000000000000000 : 800000000000000) * numToMint;
+ } else {
+ return _getClassicVectorERC20MintFeeCap(currency, numToMint);
+ }
+ }
+
+ /**
+ * @notice See {IMintFeeOracle-getMechanicMintFee}
+ */
+ function getMechanicMintFee(
+ bytes32 mechanicVectorId,
+ uint32 numToMint,
+ address mechanic,
+ address minter
+ ) external view returns (uint256) {
+ if (_isMintFeeWaivedMechanic(mechanic) || _isFeeSubsidized(mechanicVectorId, minter)) {
+ return 0;
+ } else {
+ return (block.chainid == 137 ? 2265000000000000000 : 800000000000000) * uint256(numToMint);
+ }
+ }
+
+ /**
+ * @notice Get public vector mint fee (optimized for offchain querying)
+ */
+ function getPublicVectorMintFee(
+ uint256 vectorId,
+ uint256 numToMint,
+ address minter
+ ) external view returns (uint256, address) {
+ if (_isFeeSubsidized(bytes32(vectorId), minter)) {
+ return (0, address(0));
+ }
+ IAbridgedMintVector.AbridgedVector memory _vector = IAbridgedMintVector(_mintManager).getAbridgedVector(
+ vectorId
+ );
+ if (_vector.contractAddress == address(0)) {
+ _revert(InvalidVectorType.selector);
+ }
+ if (_vector.currency != address(0)) {
+ return (_getClassicVectorERC20MintFeeCap(_vector.currency, numToMint), _vector.currency);
+ } else {
+ return ((block.chainid == 137 ? 2265000000000000000 : 800000000000000) * uint256(numToMint), address(0));
+ }
+ }
+
+ /**
+ * @notice Get gated vector mint fee (optimized for offchain querying)
+ */
+ function getGatedVectorMintFee(
+ bytes32 vectorId,
+ uint256 numToMint,
+ address minter,
+ address currency
+ ) external view returns (uint256, address) {
+ if (_isFeeSubsidized(vectorId, minter)) {
+ return (0, currency);
+ }
+ if (currency != address(0)) {
+ return (_getClassicVectorERC20MintFeeCap(currency, numToMint), currency);
+ }
+
+ return ((block.chainid == 137 ? 2265000000000000000 : 800000000000000) * uint256(numToMint), address(0));
+ }
+
+ /**
+ * @notice Get mechanic vector mint fee (optimized for offchain querying)
+ */
+ function getMechanicVectorMintFee(
+ bytes32 vectorId,
+ uint256 numToMint,
+ address minter
+ ) external view returns (uint256, address) {
+ IMechanicData.MechanicVectorMetadata memory _mechanicMetadata = IMechanicMintManagerView(_mintManager)
+ .mechanicVectorMetadata(vectorId);
+ if (_mechanicMetadata.contractAddress == address(0)) {
+ _revert(InvalidVectorType.selector);
+ }
+ if (_isMintFeeWaivedMechanic(_mechanicMetadata.mechanic) || _isFeeSubsidized(vectorId, minter)) {
+ return (0, address(0));
+ }
+
+ return ((block.chainid == 137 ? 2265000000000000000 : 800000000000000) * uint256(numToMint), address(0));
+ }
+
+ /**
+ * @notice Limit upgrades of contract to MintFeeOracle owner
+ * @param // New implementation address
+ */
+ function _authorizeUpgrade(address) internal override onlyOwner {}
+
+ /**
+ * @dev For more efficient reverts.
+ */
+ function _revert(bytes4 errorSelector) internal pure {
+ assembly {
+ mstore(0x00, errorSelector)
+ revert(0x00, 0x04)
+ }
+ }
+
+ /**
+ * @notice Return if mint fee is subsidized for a mint config
+ * @param vectorId ID of vector
+ * @param minter Original minter address
+ */
+ function _isFeeSubsidized(bytes32 vectorId, address minter) private view returns (bool) {
+ return _subsidizedMintConfig[_encodeMintConfig(vectorId, minter)];
+ }
+
+ /**
+ * @notice Encode a mint config
+ * @param vectorId ID of vector
+ * @param minter Original minter address
+ */
+ function _encodeMintConfig(bytes32 vectorId, address minter) private pure returns (bytes32) {
+ return keccak256(abi.encodePacked(vectorId, minter));
+ }
+
+ function _getClassicVectorERC20MintFeeCap(address currency, uint256 numToMint) private view returns (uint256) {
+ ERC20Config memory config = _allowlistedERC20s[currency];
+ if (config.baseMintFee != 0) {
+ return config.baseMintFee * numToMint;
+ } else if (config.realTimeOracle != address(0)) {
+ (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3PoolState(config.realTimeOracle).slot0();
+ return
+ (block.chainid == 137 ? 2265000000000000000 : 800000000000000) *
+ sqrtPriceX96ToUint(sqrtPriceX96) *
+ numToMint;
+ } else {
+ _revert(InvalidERC20.selector);
+ }
+ }
+
+ /* solhint-disable code-complexity */
+
+ function _isMintFeeWaivedMechanic(address mechanic) private view returns (bool) {
+ // RAM, DDAM
+ // TODO: add gasless mechanic
+ if (block.chainid == 1) {
+ return
+ mechanic == 0xDFEe0Ed4A217F37b3FA87624eE00fe5685bDc509 ||
+ mechanic == 0x94Fa6e7Fc2555aDA63eA56cfFF425558360F0074;
+ } else if (block.chainid == 8453) {
+ return
+ mechanic == 0x922E9f8cc491fACBd403afa143AA53ee9146474C ||
+ mechanic == 0xA748BE280C9a00edaF7d04076FE8A93c59e95B03;
+ } else if (block.chainid == 10) {
+ return
+ mechanic == 0xb207774Ac4E32eCE47771e64BDE5ec3894C1De6b ||
+ mechanic == 0x15753e20667961fB30d5aa92e2255B876568BE7e;
+ } else if (block.chainid == 42161) {
+ return
+ mechanic == 0x7f75358787f880506c5dc6100386F77be8DE0A30 ||
+ mechanic == 0x3a2aFe86E594540cbf3eA345dd29e09228f186D2;
+ } else if (block.chainid == 7777777) {
+ return
+ mechanic == 0x0AFB6566C836D1C4788cD2b54Bd9cA0158CC2D3D ||
+ mechanic == 0xf12A4018647DD2275072967Fd5F3ac5Fef7a0471;
+ } else if (block.chainid == 137) {
+ return
+ mechanic == 0x4CCB72E7E0Cd948aF50bC7Bf598Fc4E027b70f98 ||
+ mechanic == 0xAE22Cd8052D64e7C2aF6B5E3045Fab0a86C8334C;
+ } else if (block.chainid == 11155111) {
+ return
+ mechanic == 0xa2D14CA9985De170db128c8CB74Cecb35eEAF47E ||
+ mechanic == 0xceBc3B3134FbEF95ED13AEcdF997D4371d022385;
+ } else if (block.chainid == 84532) {
+ return
+ mechanic == 0x9958F83F383CA150BB2252B4275D3e3051be469F ||
+ mechanic == 0x4821B6e9aC0CCC590acCe2442bb6BB32388C1CB7;
+ }
+
+ return
+ mechanic == _backupDiscreteDutchAuctionMechanic ||
+ mechanic == _backupRankedAuctionMechanic ||
+ mechanic == _gaslessMechanicAddress;
+ }
+
+ /**
+ * @notice Get the referral manager
+ */
+ function _referralManager() private view returns (address) {
+ if (block.chainid == 1) {
+ return 0xD3C63951b2Ed18e8d92B5b251C3B636A45A547d0;
+ } else if (block.chainid == 8453) {
+ return 0xd9E58978808d17F99ccCEAb5195B052E972c0188;
+ } else if (block.chainid == 10) {
+ return 0x9CF5B12D2e2a88083647Ff2Fe0610F818b28eC77;
+ } else if (block.chainid == 7777777) {
+ return 0x7Cb2cecFCFFdccE0bf69366e52caec6BD719CD44;
+ } else if (block.chainid == 42161) {
+ return 0x617b2383D93909590fAC0b2aaa547EC5615d82eF;
+ } else if (block.chainid == 137) {
+ return 0x6fd07d4B5fd7093762Fb2f278769aa7e2511d45c;
+ } else if (block.chainid == 84532) {
+ return 0x4619b9673241eB41B642Dc04371100d238b73fFE;
+ } else if (block.chainid == 11155111) {
+ return 0xd33c1bE264bb98F86e18CD816D5fd44e97cb7163;
+ } else {
+ return _backupReferralManager;
+ }
+ }
+
+ /**
+ * @notice Convert uniswap sqrtX96 price
+ * @dev token0 always assumed to be ETH
+ */
+ function sqrtPriceX96ToUint(uint160 sqrtPriceX96) private pure returns (uint256) {
+ return FullMath.mulDiv(uint256(sqrtPriceX96) * uint256(sqrtPriceX96), ETH_WEI, FULL_MATH_SHIFT);
+ }
+}
diff --git a/contracts/mint/MintManager.sol b/contracts/mint/MintManager.sol
index 2bef0c1..9952823 100644
--- a/contracts/mint/MintManager.sol
+++ b/contracts/mint/MintManager.sol
@@ -9,6 +9,7 @@ import "./interfaces/INativeMetaTransaction.sol";
import "../utils/EIP712Upgradeable.sol";
import "../metatx/ERC2771ContextUpgradeable.sol";
import "./interfaces/IAbridgedMintVector.sol";
+import "./interfaces/IMintFeeOracle.sol";
import "./mechanics/interfaces/IMechanicMintManager.sol";
import "./mechanics/interfaces/IMechanic.sol";
@@ -357,6 +358,16 @@ contract MintManager is
*/
mapping(bytes32 => MechanicVectorMetadata) public mechanicVectorMetadata;
+ /**
+ * @notice Mint fee oracle
+ */
+ address private _mintFeeOracle;
+
+ /**
+ * @notice Gasless mechanic address
+ */
+ address private _gaslessMechanicAddress;
+
/**
* @notice Emitted when platform executor is added or removed
* @param executor Changed executor
@@ -503,6 +514,25 @@ contract MintManager is
*/
event MechanicVectorPauseSet(bytes32 indexed mechanicVectorId, bool indexed paused);
+ /**
+ * @notice Emitted when the platform mint fee is updated
+ * @param newPlatformMintFee New platform mint fee
+ */
+ event PlatformMintFeeUpdated(uint256 indexed newPlatformMintFee);
+
+ /**
+ * @notice Emit when creator is paid out
+ * @param currency Currency creator reward is in
+ * @param rewardRecipient Creator reward recipient
+ * @param amount Amount of payout
+ */
+ event CreatorRewardPayout(
+ bytes32 indexed vectorId,
+ address indexed currency,
+ address indexed rewardRecipient,
+ uint256 amount
+ );
+
/**
* @notice Restricts calls to platform
*/
@@ -618,8 +648,8 @@ contract MintManager is
if (updateConfig.updatePricePerToken > 0) {
_abridgedVectors[vectorId].pricePerToken = _newVector.pricePerToken;
}
- if (updateConfig.updateAllowlistRoot > 0) {
- _abridgedVectors[vectorId].allowlistRoot = _newVector.allowlistRoot;
+ if (updateConfig.updateCurrency > 0) {
+ _abridgedVectors[vectorId].currency = uint160(_newVector.currency);
}
if (updateConfig.updateRequireDirectEOA > 0) {
_abridgedVectors[vectorId].requireDirectEOA = _newVector.requireDirectEOA;
@@ -638,23 +668,6 @@ contract MintManager is
/* solhint-enable code-complexity */
- /**
- * @notice See {IAbridgedMintVector-deleteAbridgedVector}
- */
- function deleteAbridgedVector(uint256 vectorId) external {
- address contractAddress = address(_abridgedVectors[vectorId].contractAddress);
- address msgSender = _msgSender();
- // check .owner() first, more likely
- if (Ownable(contractAddress).owner() == msgSender || msgSender == contractAddress) {
- delete _abridgedVectors[vectorId];
- delete _abridgedVectorMetadata[vectorId];
-
- emit VectorDeleted(vectorId);
- } else {
- _revert(Unauthorized.selector);
- }
- }
-
/**
* @notice See {IAbridgedMintVector-setAbridgedVectorMetadata}
*/
@@ -732,6 +745,7 @@ contract MintManager is
emit MechanicVectorPauseSet(mechanicVectorId, pause);
}
+ /* solhint-disable code-complexity */
/**
* @notice See {IMechanicMintManager-mechanicMintNum}
*/
@@ -739,9 +753,10 @@ contract MintManager is
bytes32 mechanicVectorId,
address recipient,
uint32 numToMint,
- bytes calldata data
+ bytes memory data
) external payable {
MechanicVectorMetadata memory _mechanicVectorMetadata = mechanicVectorMetadata[mechanicVectorId];
+ address msgSender = _msgSender();
if (_mechanicVectorMetadata.paused) {
_revert(MechanicPaused.selector);
@@ -749,17 +764,35 @@ contract MintManager is
if (_mechanicVectorMetadata.isChoose) {
_revert(InvalidMechanic.selector);
}
- uint256 _platformFee = (numToMint * _platformMintFee);
- if (msg.value < _platformFee) {
- _revert(MintFeeTooLow.selector);
+
+ uint256 _platformFee = 0;
+ // constant gasless mechanic address
+ if (_mechanicVectorMetadata.mechanic == _gaslessMechanicAddress) {
+ uint256 fee;
+ address feeCollector;
+ assembly {
+ fee := calldataload(sub(calldatasize(), 32))
+ feeCollector := shr(96, calldataload(sub(calldatasize(), 72)))
+ }
+
+ data = abi.encode(fee, feeCollector, data);
+ } else {
+ _platformFee = IMintFeeOracle(_mintFeeOracle).getMechanicMintFee(
+ mechanicVectorId,
+ numToMint,
+ _mechanicVectorMetadata.mechanic,
+ msgSender
+ );
+ if (msg.value < _platformFee) {
+ _revert(MintFeeTooLow.selector);
+ }
}
- uint256 amountWithoutMintFee = msg.value - _platformFee;
- IMechanic(_mechanicVectorMetadata.mechanic).processNumMint{ value: amountWithoutMintFee }(
+ IMechanic(_mechanicVectorMetadata.mechanic).processNumMint{ value: msg.value - _platformFee }(
mechanicVectorId,
recipient,
numToMint,
- _msgSender(),
+ msgSender,
_mechanicVectorMetadata,
data
);
@@ -798,9 +831,10 @@ contract MintManager is
bytes32 mechanicVectorId,
address recipient,
uint256[] calldata tokenIds,
- bytes calldata data
+ bytes memory data
) external payable {
MechanicVectorMetadata memory _mechanicVectorMetadata = mechanicVectorMetadata[mechanicVectorId];
+ address msgSender = _msgSender();
if (_mechanicVectorMetadata.paused) {
_revert(MechanicPaused.selector);
@@ -809,7 +843,12 @@ contract MintManager is
_revert(InvalidMechanic.selector);
}
uint32 numToMint = uint32(tokenIds.length);
- uint256 _platformFee = (numToMint * _platformMintFee);
+ uint256 _platformFee = IMintFeeOracle(_mintFeeOracle).getMechanicMintFee(
+ mechanicVectorId,
+ numToMint,
+ _mechanicVectorMetadata.mechanic,
+ msgSender
+ );
if (msg.value < _platformFee) {
_revert(MintFeeTooLow.selector);
}
@@ -819,7 +858,7 @@ contract MintManager is
mechanicVectorId,
recipient,
tokenIds,
- _msgSender(),
+ msgSender,
_mechanicVectorMetadata,
data
);
@@ -868,11 +907,13 @@ contract MintManager is
}
if (Ownable(collection).owner() == msgSender || msgSender == collection) {
- // validate platform mint fee
+ // platform mint fee deprecated for creator reserves mints
+ /*
uint256 mintFeeAmount = _platformMintFee * numToMint;
if (mintFeeAmount > msg.value) {
_revert(InvalidPaymentAmount.selector);
}
+ */
if (isEditionBased) {
if (numToMint == 1) {
@@ -924,44 +965,77 @@ contract MintManager is
) external payable {
address msgSender = _msgSender();
uint256 numTokensToMint = tokenIds.length;
- _processGatedSeriesMintClaim(claim, claimSignature, numTokensToMint, msgSender);
-
- // mint NFT(s)
if (claim.claimer != msgSender && mintRecipient != claim.claimer) {
_revert(UnsafeMintRecipient.selector);
}
+
+ _verifyAndUpdateSeriesClaim(claim, claimSignature, numTokensToMint);
+ _processClassicVectorPayments(
+ claim.offchainVectorId,
+ numTokensToMint,
+ msgSender,
+ claim.currency,
+ claim.pricePerToken,
+ claim.paymentRecipient
+ );
+ emit ChooseTokenMint(claim.offchainVectorId, claim.contractAddress, false, tokenIds);
+
+ // mint NFT(s)
if (numTokensToMint == 1) {
IERC721GeneralMint(claim.contractAddress).mintSpecificTokenToOneRecipient(mintRecipient, tokenIds[0]);
} else {
IERC721GeneralMint(claim.contractAddress).mintSpecificTokensToOneRecipient(mintRecipient, tokenIds);
}
-
- emit ChooseTokenMint(claim.offchainVectorId, claim.contractAddress, false, tokenIds);
}
/**
- * @notice Mint on a Series collection with a valid claim
+ * @notice Mint on a collection with sequentially minted token IDs with a valid claim
* @param claim Claim
* @param claimSignature Signed + encoded claim
* @param mintRecipient Who to mint the NFT(s) to.
* Can't mint to different recipient if tx isn't sent by claim.claimer.
*/
- function gatedSeriesMint(
+ function gatedNumMint(
Claim calldata claim,
bytes calldata claimSignature,
- address mintRecipient
+ address mintRecipient,
+ bool isEditionBased
) external payable {
address msgSender = _msgSender();
- _processGatedMintClaim(claim, claimSignature, msgSender);
-
- // mint NFT(s)
if (claim.claimer != msgSender && mintRecipient != claim.claimer) {
_revert(UnsafeMintRecipient.selector);
}
- if (claim.numTokensToMint == 1) {
- IERC721GeneralMint(claim.contractAddress).mintOneToOneRecipient(mintRecipient);
+
+ _verifyAndUpdateClaim(claim, claimSignature);
+ _processClassicVectorPayments(
+ claim.offchainVectorId,
+ claim.numTokensToMint,
+ msgSender,
+ claim.currency,
+ claim.pricePerToken,
+ claim.paymentRecipient
+ );
+ emit NumTokenMint(claim.offchainVectorId, claim.contractAddress, false, claim.numTokensToMint);
+
+ if (isEditionBased) {
+ if (claim.numTokensToMint == 1) {
+ IERC721EditionMint(claim.contractAddress).mintOneToRecipient(claim.editionId, mintRecipient);
+ } else {
+ IERC721EditionMint(claim.contractAddress).mintAmountToRecipient(
+ claim.editionId,
+ mintRecipient,
+ claim.numTokensToMint
+ );
+ }
} else {
- IERC721GeneralMint(claim.contractAddress).mintAmountToOneRecipient(mintRecipient, claim.numTokensToMint);
+ if (claim.numTokensToMint == 1) {
+ IERC721GeneralMint(claim.contractAddress).mintOneToOneRecipient(mintRecipient);
+ } else {
+ IERC721GeneralMint(claim.contractAddress).mintAmountToOneRecipient(
+ mintRecipient,
+ claim.numTokensToMint
+ );
+ }
}
}
@@ -998,6 +1072,7 @@ contract MintManager is
_vector,
numTokensToMint,
mintRecipient,
+ msgSender,
newNumClaimedViaVector,
newNumClaimedForUser
);
@@ -1007,42 +1082,13 @@ contract MintManager is
_vector,
numTokensToMint,
mintRecipient,
+ msgSender,
newNumClaimedViaVector,
newNumClaimedForUser
);
}
}
- /**
- * @notice Mint on an ERC721Editions or ERC721SingleEdiion collection with a valid claim
- * @param _claim Claim
- * @param _signature Signed + encoded claim
- * @param _recipient Who to mint the NFT(s) to.
- * Can't mint to different recipient if tx isn't sent by claim.claimer.
- */
- function gatedMintEdition721(
- Claim calldata _claim,
- bytes calldata _signature,
- address _recipient
- ) external payable {
- address msgSender = _msgSender();
- _processGatedMintClaim(_claim, _signature, msgSender);
-
- // mint NFT(s)
- if (_claim.claimer != msgSender && _recipient != _claim.claimer) {
- _revert(UnsafeMintRecipient.selector);
- }
- if (_claim.numTokensToMint == 1) {
- IERC721EditionMint(_claim.contractAddress).mintOneToRecipient(_claim.editionId, _recipient);
- } else {
- IERC721EditionMint(_claim.contractAddress).mintAmountToRecipient(
- _claim.editionId,
- _recipient,
- _claim.numTokensToMint
- );
- }
- }
-
/**
* @notice Withdraw native gas token owed to platform
*/
@@ -1056,29 +1102,30 @@ contract MintManager is
/**
* @notice Update platform payment address
*/
- function updatePlatformAndMintFee(address payable newPlatform, uint256 newPlatformMintFee) external onlyOwner {
+ function updatePlatformAndMintFeeOracle(
+ address payable newPlatform,
+ address newOracle,
+ address gaslessMechanic
+ ) external onlyOwner {
if (newPlatform == address(0)) {
_revert(Unauthorized.selector);
}
_platform = newPlatform;
- _platformMintFee = newPlatformMintFee;
+ if (_mintFeeOracle != newOracle) {
+ _mintFeeOracle = newOracle;
+ }
+ if (_gaslessMechanicAddress != gaslessMechanic) {
+ _gaslessMechanicAddress = gaslessMechanic;
+ }
}
/**
- * @notice Returns platform executors
+ * @notice Returns if an address is a platform executor
*/
function isPlatformExecutor(address _executor) external view returns (bool) {
return _platformExecutors.contains(_executor);
}
- /**
- * @notice Returns claim ids used for an offchain vector
- * @param vectorId ID of offchain vector
- */
- function getClaimNoncesUsedForOffchainVector(bytes32 vectorId) external view returns (bytes32[] memory) {
- return _offchainVectorsToNoncesUsed[vectorId].values();
- }
-
/**
* @notice Returns number of NFTs minted by user on vector
* @param vectorId ID of offchain vector
@@ -1182,84 +1229,6 @@ contract MintManager is
return ERC2771ContextUpgradeable._msgData();
}
- /**
- * @notice Process, verify, and update the state of a gated mint claim
- * @param claim Claim
- * @param claimSignature Signed + encoded claim
- * @param msgSender Transaction sender
- */
- function _processGatedMintClaim(Claim calldata claim, bytes calldata claimSignature, address msgSender) private {
- _verifyAndUpdateClaim(claim, claimSignature);
-
- // calculate mint fee amount
- uint256 mintFeeAmount = _platformMintFee * claim.numTokensToMint;
-
- // make payments
- if (claim.currency == address(0) && claim.pricePerToken > 0) {
- // pay in native gas token
- uint256 amount = claim.numTokensToMint * claim.pricePerToken;
- _processNativeGasTokenPayment(amount, mintFeeAmount, claim.paymentRecipient, claim.offchainVectorId);
- } else if (claim.pricePerToken > 0) {
- // pay in ERC20
- uint256 amount = claim.numTokensToMint * claim.pricePerToken;
- _processERC20Payment(
- amount,
- mintFeeAmount,
- claim.paymentRecipient,
- msgSender,
- claim.currency,
- claim.offchainVectorId
- );
- } else {
- if (mintFeeAmount > msg.value) {
- _revert(MintFeeTooLow.selector);
- }
- }
-
- emit NumTokenMint(claim.offchainVectorId, claim.contractAddress, false, claim.numTokensToMint);
- }
-
- /**
- * @notice Process, verify, and update the state of a gated series mint claim
- * @param claim Series Claim
- * @param claimSignature Signed + encoded claim
- * @param numTokensToMint Number of tokens to mint on series
- * @param msgSender Transaction sender
- */
- function _processGatedSeriesMintClaim(
- SeriesClaim calldata claim,
- bytes calldata claimSignature,
- uint256 numTokensToMint,
- address msgSender
- ) private {
- _verifyAndUpdateSeriesClaim(claim, claimSignature, numTokensToMint);
-
- // calculate mint fee amount
- uint256 mintFeeAmount = _platformMintFee * numTokensToMint;
-
- // make payments
- if (claim.currency == address(0) && claim.pricePerToken > 0) {
- // pay in native gas token
- uint256 amount = numTokensToMint * claim.pricePerToken;
- _processNativeGasTokenPayment(amount, mintFeeAmount, claim.paymentRecipient, claim.offchainVectorId);
- } else if (claim.pricePerToken > 0) {
- // pay in ERC20
- uint256 amount = numTokensToMint * claim.pricePerToken;
- _processERC20Payment(
- amount,
- mintFeeAmount,
- claim.paymentRecipient,
- msgSender,
- claim.currency,
- claim.offchainVectorId
- );
- } else {
- if (mintFeeAmount > msg.value) {
- _revert(MintFeeTooLow.selector);
- }
- }
- }
-
/**
* @notice Verify, and update the state of a gated mint claim
* @param claim Claim
@@ -1313,7 +1282,7 @@ contract MintManager is
if (
!_platformExecutors.contains(signer) ||
- numTokensToMint > claim.maxPerTxn ||
+ (numTokensToMint > claim.maxPerTxn && claim.maxPerTxn != 0) ||
_offchainVectorsToNoncesUsed[claim.offchainVectorId].contains(claim.claimNonce) ||
block.timestamp > claim.claimExpiryTimestamp ||
(expectedNumClaimedViaVector > claim.maxClaimableViaVector && claim.maxClaimableViaVector != 0) ||
@@ -1335,13 +1304,15 @@ contract MintManager is
* @param numTokensToMint Number of NFTs to mint on vector
* @param newNumClaimedViaVector New number of NFTs minted via vector after this ones
* @param newNumClaimedForUser New number of NFTs minted by user via vector after this ones
+ * @param msgSender Minter
*/
function _processVectorMint(
uint256 _vectorId,
AbridgedVectorData memory _vector,
uint48 numTokensToMint,
uint48 newNumClaimedViaVector,
- uint48 newNumClaimedForUser
+ uint48 newNumClaimedForUser,
+ address msgSender
) private {
if (
(_vector.maxTotalClaimableViaVector < newNumClaimedViaVector && _vector.maxTotalClaimableViaVector != 0) ||
@@ -1358,34 +1329,14 @@ contract MintManager is
_revert(MintPaused.selector);
}
- // calculate mint fee amount
- uint256 mintFeeAmount = _platformMintFee * numTokensToMint;
-
- if (_vector.currency == 0 && _vector.pricePerToken > 0) {
- // pay in native gas token
- uint256 amount = numTokensToMint * _vector.pricePerToken;
- _processNativeGasTokenPayment(
- amount,
- mintFeeAmount,
- payable(address(_vector.paymentRecipient)),
- bytes32(_vectorId)
- );
- } else if (_vector.pricePerToken > 0) {
- // pay in ERC20
- uint256 amount = numTokensToMint * _vector.pricePerToken;
- _processERC20Payment(
- amount,
- mintFeeAmount,
- payable(address(_vector.paymentRecipient)),
- _msgSender(),
- address(_vector.currency),
- bytes32(_vectorId)
- );
- } else {
- if (mintFeeAmount > msg.value) {
- _revert(MintFeeTooLow.selector);
- }
- }
+ _processClassicVectorPayments(
+ bytes32(_vectorId),
+ uint256(numTokensToMint),
+ msgSender,
+ address(_vector.currency),
+ _vector.pricePerToken,
+ payable(address(_vector.paymentRecipient))
+ );
emit NumTokenMint(bytes32(_vectorId), address(_vector.contractAddress), true, numTokensToMint);
}
@@ -1396,6 +1347,7 @@ contract MintManager is
* @param _vector Vector being minted on
* @param numTokensToMint Number of tokens to mint
* @param mintRecipient Who to mint the NFT(s) to
+ * @param msgSender Minter
* @param newNumClaimedViaVector New number of NFTs minted via vector after this ones
* @param newNumClaimedForUser New number of NFTs minted by user via vector after this ones
*/
@@ -1404,10 +1356,18 @@ contract MintManager is
AbridgedVectorData memory _vector,
uint48 numTokensToMint,
address mintRecipient,
+ address msgSender,
uint48 newNumClaimedViaVector,
uint48 newNumClaimedForUser
) private {
- _processVectorMint(_vectorId, _vector, numTokensToMint, newNumClaimedViaVector, newNumClaimedForUser);
+ _processVectorMint(
+ _vectorId,
+ _vector,
+ numTokensToMint,
+ newNumClaimedViaVector,
+ newNumClaimedForUser,
+ msgSender
+ );
if (numTokensToMint == 1) {
IERC721GeneralMint(address(_vector.contractAddress)).mintOneToOneRecipient(mintRecipient);
} else {
@@ -1424,6 +1384,7 @@ contract MintManager is
* @param _vector Vector being minted on
* @param numTokensToMint Number of tokens to mint
* @param mintRecipient Who to mint the NFT(s) to
+ * @param msgSender Minter
* @param newNumClaimedViaVector New number of NFTs minted via vector after this ones
* @param newNumClaimedForUser New number of NFTs minted by user via vector after this ones
*/
@@ -1432,10 +1393,18 @@ contract MintManager is
AbridgedVectorData memory _vector,
uint48 numTokensToMint,
address mintRecipient,
+ address msgSender,
uint48 newNumClaimedViaVector,
uint48 newNumClaimedForUser
) private {
- _processVectorMint(_vectorId, _vector, numTokensToMint, newNumClaimedViaVector, newNumClaimedForUser);
+ _processVectorMint(
+ _vectorId,
+ _vector,
+ numTokensToMint,
+ newNumClaimedViaVector,
+ newNumClaimedForUser,
+ msgSender
+ );
if (numTokensToMint == 1) {
IERC721EditionMint(address(_vector.contractAddress)).mintOneToRecipient(_vector.editionId, mintRecipient);
} else {
@@ -1448,23 +1417,13 @@ contract MintManager is
}
/**
- * @notice Process payment in native gas token, sending to creator and platform
+ * @notice Process payment in native gas token
* @param totalAmount Total amount being paid
- * @param mintFeeAmount Amount to pay platform
* @param recipient Creator recipient of payment
* @param vectorId ID of vector (on-chain or off-chain)
*/
- function _processNativeGasTokenPayment(
- uint256 totalAmount,
- uint256 mintFeeAmount,
- address payable recipient,
- bytes32 vectorId
- ) private {
- if (totalAmount + mintFeeAmount != msg.value) {
- _revert(InvalidPaymentAmount.selector);
- }
-
- (bool sentToRecipient, bytes memory dataRecipient) = recipient.call{ value: totalAmount }("");
+ function _processNativeGasTokenPayment(uint256 totalAmount, address payable recipient, bytes32 vectorId) private {
+ (bool sentToRecipient, ) = recipient.call{ value: totalAmount }("");
if (!sentToRecipient) {
_revert(EtherSendFailed.selector);
}
@@ -1472,9 +1431,8 @@ contract MintManager is
}
/**
- * @notice Process payment in ERC20, sending to creator and platform
+ * @notice Process payment in ERC20
* @param totalAmount Total amount being paid
- * @param mintFeeAmount Amount to pay platform in mint fees
* @param recipient Creator recipient of payment
* @param payer Payer
* @param currency ERC20 currency
@@ -1482,18 +1440,12 @@ contract MintManager is
*/
function _processERC20Payment(
uint256 totalAmount,
- uint256 mintFeeAmount,
address recipient,
address payer,
address currency,
bytes32 vectorId
) private {
- if (mintFeeAmount != msg.value) {
- _revert(MintFeeTooLow.selector);
- }
IERC20(currency).transferFrom(payer, recipient, totalAmount);
- // IERC20(currency).transferFrom(payer, _platform, totalAmount - amountToCreator);
-
emit ERC20Payment(currency, recipient, vectorId, payer, totalAmount, 10000);
}
@@ -1539,6 +1491,94 @@ contract MintManager is
*/
}
+ /* solhint-disable code-complexity */
+ /**
+ * @notice Process payments (sale + mint fee) for classic vectors (direct + gated)
+ * @param vectorId Vector ID
+ * @param numToMint Number of tokens being minted
+ * @param msgSender Minter
+ * @param currency Sale currency
+ * @param salePrice Sale price
+ * @param salePaymentRecipient Sale payment recipient
+ */
+ function _processClassicVectorPayments(
+ bytes32 vectorId,
+ uint256 numToMint,
+ address msgSender,
+ address currency,
+ uint256 salePrice,
+ address payable salePaymentRecipient
+ ) private {
+ address _oracle = _mintFeeOracle;
+ uint256 mintFeeCap = IMintFeeOracle(_oracle).getClassicVectorMintFeeCap(
+ vectorId,
+ numToMint,
+ msgSender,
+ currency
+ );
+ uint256 mintFeeEtherValue = currency == address(0) ? mintFeeCap : 0;
+ uint256 saleAmount = numToMint * salePrice;
+ if (currency == address(0)) {
+ if (mintFeeEtherValue + saleAmount != msg.value) {
+ _revert(InvalidPaymentAmount.selector);
+ }
+ if (saleAmount > 0) {
+ // pay in native gas token
+ _processNativeGasTokenPayment(saleAmount, salePaymentRecipient, vectorId);
+ }
+ } else if (saleAmount > 0) {
+ // pay in ERC20
+ // tx.origin instead of msgSender for referrals
+ _processERC20Payment(saleAmount, salePaymentRecipient, tx.origin, currency, vectorId);
+ }
+
+ _processClassicVectorMintFee(
+ mintFeeCap,
+ msgSender,
+ currency,
+ _oracle,
+ salePaymentRecipient,
+ vectorId,
+ mintFeeEtherValue,
+ salePrice == 0
+ );
+ }
+
+ /**
+ * @notice Helper util to process the classic vector mint fee
+ */
+ function _processClassicVectorMintFee(
+ uint256 mintFeeCap,
+ address msgSender,
+ address currency,
+ address _oracle,
+ address salePaymentRecipient,
+ bytes32 vectorId,
+ uint256 mintFeeEtherValue,
+ bool isSaleFree
+ ) private {
+ if (mintFeeCap > 0) {
+ if (currency != address(0)) {
+ // send erc20 mint fee cap to the mint fee oracle
+ // tx.origin instead of msgSender for referrals
+ IERC20(currency).transferFrom(tx.origin, _oracle, mintFeeCap);
+ }
+ uint256 creatorPayout = IMintFeeOracle(_oracle).processClassicVectorMintFeeCap{ value: mintFeeEtherValue }(
+ vectorId,
+ isSaleFree,
+ salePaymentRecipient,
+ currency,
+ mintFeeCap,
+ msgSender
+ );
+ if (creatorPayout != 0) {
+ emit CreatorRewardPayout(vectorId, currency, salePaymentRecipient, creatorPayout);
+ }
+ }
+ }
+
+ /* solhint-enable code-complexity */
+
/**
* @notice Deterministically produce mechanic vector ID from mechanic vector inputs
* @param metadata Mechanic vector metadata
@@ -1677,4 +1717,4 @@ contract MintManager is
revert(0x00, 0x04)
}
}
-}
\ No newline at end of file
+}
diff --git a/contracts/mint/interfaces/IAbridgedMintVector.sol b/contracts/mint/interfaces/IAbridgedMintVector.sol
index 757e874..d6af14f 100644
--- a/contracts/mint/interfaces/IAbridgedMintVector.sol
+++ b/contracts/mint/interfaces/IAbridgedMintVector.sol
@@ -70,7 +70,7 @@ interface IAbridgedMintVector {
* @param updateTokenLimitPerTx If 1, update tokenLimitPerTx
* @param updateMaxUserClaimableViaVector If 1, update maxUserClaimableViaVector
* @param updatePricePerToken If 1, update pricePerToken
- * @param updateAllowlistRoot If 1, update allowlistRoot
+ * @param updateCurrency If 1, update currency
* @param updateRequireDirectEOA If 1, update requireDirectEOA
* @param updateMetadata If 1, update MintVector metadata
*/
@@ -82,7 +82,7 @@ interface IAbridgedMintVector {
uint16 updateTokenLimitPerTx;
uint16 updateMaxUserClaimableViaVector;
uint8 updatePricePerToken;
- uint8 updateAllowlistRoot;
+ uint8 updateCurrency;
uint8 updateRequireDirectEOA;
uint8 updateMetadata;
}
@@ -109,12 +109,6 @@ interface IAbridgedMintVector {
uint128 flexibleData
) external;
- /**
- * @notice Deletes on-chain vector
- * @param vectorId ID of abridged vector to delete
- */
- function deleteAbridgedVector(uint256 vectorId) external;
-
/**
* @notice Pauses or unpauses an on-chain mint vector
* @param vectorId ID of abridged vector to pause
diff --git a/contracts/mint/interfaces/IMintFeeOracle.sol b/contracts/mint/interfaces/IMintFeeOracle.sol
new file mode 100644
index 0000000..6b4e20b
--- /dev/null
+++ b/contracts/mint/interfaces/IMintFeeOracle.sol
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.10;
+
+/**
+ * @title MintManager interface for a mint fee oracle
+ * @author highlight.xyz
+ */
+interface IMintFeeOracle {
+ /**
+ * @notice Process the mint fee for a classic mv
+ * @param vectorId Vector ID
+ * @param payoutCreatorReward Payout creator reward
+ * @param vectorPaymentRecipient Vector payment recipient
+ * @param currency Mint fee currency currency
+ * @param amount Sale amount
+ * @param minter Minter address
+ */
+ function processClassicVectorMintFeeCap(
+ bytes32 vectorId,
+ bool payoutCreatorReward,
+ address vectorPaymentRecipient,
+ address currency,
+ uint256 amount,
+ address minter
+ ) external payable returns (uint256);
+
+ /**
+ * @notice Get the mint fee cap for a classic mv
+ * @param vectorId Vector ID (bytes32)
+ * @param numToMint Number of tokens to mint in this transaction
+ * @param minter Minter address
+ * @param currency Sale currency
+ */
+ function getClassicVectorMintFeeCap(
+ bytes32 vectorId,
+ uint256 numToMint,
+ address minter,
+ address currency
+ ) external view returns (uint256);
+
+ /**
+ * @notice Get the mint fee for a mechanic mint mv
+ * @param vectorId Vector ID
+ * @param numToMint Number of tokens to mint in this transaction
+ * @param mechanic Address of mechanic facilitating mint
+ * @param minter Address minting
+ */
+ function getMechanicMintFee(
+ bytes32 vectorId,
+ uint32 numToMint,
+ address mechanic,
+ address minter
+ ) external view returns (uint256);
+}
diff --git a/contracts/mint/mechanics/DiscreteDutchAuctionMechanic.sol b/contracts/mint/mechanics/DiscreteDutchAuctionMechanic.sol
index 75a32a4..4142fda 100644
--- a/contracts/mint/mechanics/DiscreteDutchAuctionMechanic.sol
+++ b/contracts/mint/mechanics/DiscreteDutchAuctionMechanic.sol
@@ -475,11 +475,17 @@ contract DiscreteDutchAuctionMechanic is MechanicMintManagerClientUpgradeable, U
_revert(InvalidDPPFundsWithdrawl.selector);
}
- (bool sentToPaymentRecipient, bytes memory data) = _vector.paymentRecipient.call{ value: totalRefund }("");
+ uint200 platformFee = (totalRefund * 500) / 10000;
+ (bool sentToPaymentRecipient, ) = _vector.paymentRecipient.call{ value: totalRefund - platformFee }("");
if (!sentToPaymentRecipient) {
_revert(EtherSendFailed.selector);
}
+ (bool sentToPlatform, ) = (payable(owner())).call{ value: platformFee }("");
+ if (!sentToPlatform) {
+ _revert(EtherSendFailed.selector);
+ }
+
emit DiscreteDutchAuctionDPPFundsWithdrawn(
mechanicVectorId,
_vector.paymentRecipient,
@@ -629,10 +635,16 @@ contract DiscreteDutchAuctionMechanic is MechanicMintManagerClientUpgradeable, U
if (_vector.payeeRevenueHasBeenWithdrawn) {
// send ether value to payment recipient
- (bool sentToPaymentRecipient, bytes memory data) = _vector.paymentRecipient.call{ value: totalPrice }("");
+ uint200 platformFee = (totalPrice * 500) / 10000;
+ (bool sentToPaymentRecipient, ) = _vector.paymentRecipient.call{ value: totalPrice - platformFee }("");
if (!sentToPaymentRecipient) {
_revert(EtherSendFailed.selector);
}
+
+ (bool sentToPlatform, ) = (payable(owner())).call{ value: platformFee }("");
+ if (!sentToPlatform) {
+ _revert(EtherSendFailed.selector);
+ }
}
emit DiscreteDutchAuctionMint(mechanicVectorId, recipient, price, numToMint);
@@ -706,7 +718,7 @@ contract DiscreteDutchAuctionMechanic is MechanicMintManagerClientUpgradeable, U
// escrowFunds is only final if auction is exhausted or in FPP
return (
- uint256(_vector.currentSupply * potentialClearingPrice),
+ (uint256(_vector.currentSupply * potentialClearingPrice) * 9500) / 10000, // 95%
(auctionExhausted || _auctionIsInFPP(_vector.currentSupply, priceIndex, _vector.numPrices))
);
}
diff --git a/contracts/mint/mechanics/MechanicMintManagerClientUpgradeable.sol b/contracts/mint/mechanics/MechanicMintManagerClientUpgradeable.sol
index 895aad4..ae85394 100644
--- a/contracts/mint/mechanics/MechanicMintManagerClientUpgradeable.sol
+++ b/contracts/mint/mechanics/MechanicMintManagerClientUpgradeable.sol
@@ -71,6 +71,10 @@ abstract contract MechanicMintManagerClientUpgradeable is OwnableUpgradeable, IM
return IMechanicMintManagerView(mintManager).mechanicVectorMetadata(mechanicVectorId);
}
+ function _isPlatformExecutor(address _executor) internal view returns (bool) {
+ return IMechanicMintManagerView(mintManager).isPlatformExecutor(_executor);
+ }
+
/**
* @dev For more efficient reverts.
*/
diff --git a/contracts/mint/mechanics/RankedAuctionMechanic.sol b/contracts/mint/mechanics/RankedAuctionMechanic.sol
new file mode 100644
index 0000000..8b01802
--- /dev/null
+++ b/contracts/mint/mechanics/RankedAuctionMechanic.sol
@@ -0,0 +1,781 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+import "./MechanicMintManagerClientUpgradeable.sol";
+import "../../erc721/interfaces/IEditionCollection.sol";
+import "../../erc721/interfaces/IERC721GeneralSupplyMetadata.sol";
+import "../../observability/IGengineObservability.sol";
+import "./interfaces/IManifold1155Burn.sol";
+
+import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
+import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
+import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
+
+/**
+ * @notice Ranked auctions
+ * @author highlight.xyz
+ */
+contract RankedAuctionMechanic is MechanicMintManagerClientUpgradeable, UUPSUpgradeable {
+ using EnumerableSet for EnumerableSet.UintSet;
+ using EnumerableSet for EnumerableSet.Bytes32Set;
+
+ /**
+ * @notice Throw when an action is unauthorized
+ */
+ error Unauthorized();
+
+ /**
+ * @notice Throw when signer of signature is invalid
+ */
+ error InvalidSigner();
+
+ /**
+ * @notice Throw when it is invalid to mint on a vector
+ */
+ error InvalidMint();
+
+ /**
+ * @notice Throw when it is invalid to mint a number of tokens
+ */
+ error InvalidMintAmount();
+
+ /**
+ * @notice Throw when it is invalid to bid
+ */
+ error InvalidBid();
+
+ /**
+ * @notice Throw when a vector is already created with a mechanic vector ID
+ */
+ error VectorAlreadyCreated();
+
+ /**
+ * @notice Throw when the vector update is invalid
+ */
+ error InvalidUpdate();
+
+ /**
+ * @notice Throw when code gets into impossible state
+ */
+ error ImpossibleState();
+
+ /**
+ * @notice Throw when an internal transfer of ether fails
+ */
+ error EtherSendFailed();
+
+ /**
+ * @notice Throw when a claim is invalid
+ */
+ error InvalidClaim();
+
+ /**
+ * @notice Throw when a claim signature is invalid
+ */
+ error InvalidSignature();
+
+ /**
+ * @notice Errors to throw when adding / removing bids from user bid ids
+ */
+ error BidAlreadyAdded();
+ error BidAlreadyReclaimed();
+
+ /**
+ * @notice On-chain mint vector (stored data)
+ * @param startTimestamp When minting opens on vector
+ * @param endTimestamp When minting ends on vector
+ * @param paymentRecipient Payment recipient
+ * @param maxUserClaimableViaVector Max number of tokens that can be minted by user via vector
+ * @param maxTotalClaimableViaVector Max number of tokens that can be minted via vector
+ * @param latestBidId Total number of bids (valid or invalid, deleted or not)
+ * @param currency Currency used for payment. Native gas token, if zero address
+ * @param bidFundsClaimed Bid funds claimed
+ * @param reserveBid Reserve bid
+ * @param maxEndTimestamp Maximium time the auction can go till (given extensions)
+ * @param actionId Action ID (create / update bid)
+ */
+ struct Vector {
+ uint48 startTimestamp;
+ uint48 endTimestamp;
+ address payable paymentRecipient;
+ uint32 maxUserClaimableViaVector;
+ uint32 maxTotalClaimableViaVector;
+ uint32 latestBidId;
+ address currency;
+ bool bidFundsClaimed;
+ uint96 reserveBid;
+ uint48 maxEndTimestamp;
+ uint96 actionId;
+ }
+
+ /**
+ * @notice Bid
+ * @dev Only handles bids below ~10B ether
+ * @param bidAmount Amount of bid
+ * @param bidder Bidder
+ */
+ struct Bid {
+ uint96 bidAmount;
+ address bidder;
+ }
+
+ /**
+ * @notice User bids' metadata
+ * @param numClaimed Number of valid bids redeemed for a token (after mint ends)
+ * @param numBids Number of bids by user
+ */
+ struct UserBidsMetadata {
+ uint32 numClaimed;
+ uint32 numBids;
+ }
+
+ /**
+ * @notice Config used to control updating of fields in Vector
+ */
+ struct VectorUpdateConfig {
+ bool updateStartTimestamp;
+ bool updateEndTimestamp;
+ bool updateMaxEndTimestamp;
+ bool updateMaxUserClaimableViaVector;
+ bool updateMaxTotalClaimableViaVector;
+ bool updatePaymentRecipient;
+ bool updateCurrency;
+ bool updateReserveBid;
+ }
+
+ /**
+ * @notice Used to claim funds from an invalid bid, mint tokens + claim rebate if eligible, claim auction earnings
+ */
+ struct RankedAuctionsClaim {
+ bytes32 mechanicVectorId;
+ uint256 rebateAmount;
+ address claimer;
+ uint32 claimerNumValidBids;
+ uint48 claimExpiryTimestamp;
+ uint256 cumulativeBidAmount;
+ uint32 bidId;
+ uint8 claimType;
+ }
+
+ /**
+ * @notice Constants that help with EIP-712, signature based minting
+ */
+ bytes32 private constant _DOMAIN_TYPEHASH =
+ keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)");
+
+ /* solhint-disable max-line-length */
+ bytes32 private constant _CLAIM_TYPEHASH =
+ keccak256(
+ "RankedAuctionsClaim(bytes32 mechanicVectorId,uint256 rebateAmount,address claimer,uint32 claimerNumValidBids,uint48 claimExpiryTimestamp,uint256 cumulativeBidAmount,uint32 bidId,uint8 claimType)"
+ );
+ /* solhint-enable max-line-length */
+
+ /**
+ * @notice Stores seed based vector, indexed by global mechanic vector id
+ */
+ mapping(bytes32 => Vector) private vector;
+
+ /**
+ * @notice Stores vector's current validity hash
+ */
+ mapping(bytes32 => bytes32) private vectorValidityHash;
+
+ /**
+ * @notice System-wide vector ids to bids by their ids
+ */
+ mapping(bytes32 => mapping(uint32 => Bid)) public bids;
+
+ /**
+ * @notice System-wide vector ids to user's bids metadata
+ */
+ mapping(bytes32 => mapping(address => UserBidsMetadata)) private _userBidsMetadata;
+
+ /**
+ * @notice System-wide vector ids to user's bid ids
+ */
+ mapping(bytes32 => mapping(address => EnumerableSet.UintSet)) private _userBidIds;
+
+ /**
+ * @notice System-wide used claims
+ */
+ mapping(bytes32 => EnumerableSet.Bytes32Set) private _usedClaims;
+
+ /**
+ * @notice Emitted when a mint vector is created
+ */
+ event RankedAuctionCreated(bytes32 indexed mechanicVectorId);
+
+ /**
+ * @notice Emitted when a mint vector is updated
+ */
+ event RankedAuctionUpdated(bytes32 indexed mechanicVectorId);
+
+ /**
+ * @notice Emitted when a bid is created or updated
+ */
+ event BidCreatedOrUpdated(
+ bytes32 indexed mechanicVectorId,
+ bytes32 indexed newValidityHash,
+ uint96 indexed actionId,
+ uint32 bidId,
+ address bidder,
+ uint96 bidAmount,
+ address currency,
+ bool created
+ );
+
+ /**
+ * @notice Emitted when bid funds are reclaimed
+ */
+ event BidReclaimed(bytes32 indexed mechanicVectorId, uint32 indexed bidId, uint96 amount, address currency);
+
+ /**
+ * @notice Emitted when bid funds are claimed
+ */
+ event AuctionEarningsClaimed(
+ bytes32 indexed mechanicVectorId,
+ uint256 earnings,
+ address paymentRecipient,
+ address currency
+ );
+
+ /**
+ * @notice Emitted when auction is lengthened
+ */
+ event AuctionLengthened(bytes32 indexed mechanicVectorId, uint48 newEndTimestamp);
+
+ /**
+ * @notice Initialize mechanic contract
+ * @param _mintManager Mint manager address
+ * @param platform Platform owning the contract
+ */
+ function initialize(address _mintManager, address platform) external initializer {
+ __MechanicMintManagerClientUpgradeable_initialize(_mintManager, platform);
+ }
+
+ /**
+ * @notice Create a seed based vector
+ * @param mechanicVectorId Global mechanic vector ID
+ * @param vectorData Vector data, to be deserialized into seed based vector data
+ */
+ function createVector(bytes32 mechanicVectorId, bytes memory vectorData) external onlyMintManager {
+ // precaution, although MintManager tightly controls creation and prevents double creation
+ if (vector[mechanicVectorId].startTimestamp != 0) {
+ _revert(VectorAlreadyCreated.selector);
+ }
+ (
+ uint48 startTimestamp,
+ uint48 endTimestamp,
+ uint48 maxEndTimestamp,
+ address paymentRecipient,
+ uint32 maxUserClaimableViaVector,
+ uint32 maxTotalClaimableViaVector,
+ uint96 reserveBid,
+ address currency
+ ) = abi.decode(vectorData, (uint48, uint48, uint48, address, uint32, uint32, uint96, address));
+
+ if (maxTotalClaimableViaVector == 0) {
+ _revert(InvalidUpdate.selector);
+ }
+
+ uint48 st = startTimestamp == 0 ? uint48(block.timestamp) : startTimestamp;
+ Vector memory _vector = Vector(
+ st,
+ endTimestamp == 0 ? uint48(st + 604800) : endTimestamp, // arbitrarily set for a week
+ payable(paymentRecipient),
+ maxUserClaimableViaVector,
+ maxTotalClaimableViaVector,
+ 0,
+ currency,
+ false,
+ reserveBid,
+ maxEndTimestamp,
+ 0
+ );
+
+ vector[mechanicVectorId] = _vector;
+
+ emit RankedAuctionCreated(mechanicVectorId);
+ }
+
+ /* solhint-disable code-complexity */
+ /**
+ * @notice Update a seed based vector
+ * @param mechanicVectorId Global mechanic vector ID
+ * @param newVector New vector fields
+ * @param updateConfig Config denoting what fields on vector to update
+ */
+ function updateVector(
+ bytes32 mechanicVectorId,
+ Vector calldata newVector,
+ VectorUpdateConfig calldata updateConfig
+ ) external {
+ MechanicVectorMetadata memory metadata = _getMechanicVectorMetadata(mechanicVectorId);
+ if (
+ OwnableUpgradeable(metadata.contractAddress).owner() != msg.sender && metadata.contractAddress != msg.sender
+ ) {
+ _revert(Unauthorized.selector);
+ }
+
+ // rather than updating entire vector, update per-field
+ if (updateConfig.updateStartTimestamp) {
+ vector[mechanicVectorId].startTimestamp = newVector.startTimestamp == 0
+ ? uint48(block.timestamp)
+ : newVector.startTimestamp;
+ }
+ if (updateConfig.updateEndTimestamp) {
+ if (newVector.endTimestamp == 0) {
+ _revert(InvalidUpdate.selector);
+ }
+ vector[mechanicVectorId].endTimestamp = newVector.endTimestamp;
+ }
+ if (updateConfig.updateMaxEndTimestamp) {
+ if (newVector.maxEndTimestamp == 0) {
+ _revert(InvalidUpdate.selector);
+ }
+ vector[mechanicVectorId].maxEndTimestamp = newVector.maxEndTimestamp;
+ }
+ if (updateConfig.updateMaxUserClaimableViaVector) {
+ vector[mechanicVectorId].maxUserClaimableViaVector = newVector.maxUserClaimableViaVector;
+ }
+ if (updateConfig.updateMaxTotalClaimableViaVector) {
+ if (
+ newVector.maxTotalClaimableViaVector == 0 ||
+ newVector.maxTotalClaimableViaVector < vector[mechanicVectorId].maxTotalClaimableViaVector
+ ) {
+ _revert(InvalidUpdate.selector);
+ }
+ vector[mechanicVectorId].maxTotalClaimableViaVector = newVector.maxTotalClaimableViaVector;
+ }
+ if (updateConfig.updateCurrency) {
+ if (vector[mechanicVectorId].latestBidId > 0) {
+ _revert(InvalidUpdate.selector);
+ }
+ vector[mechanicVectorId].currency = newVector.currency;
+ }
+ if (updateConfig.updatePaymentRecipient) {
+ vector[mechanicVectorId].paymentRecipient = newVector.paymentRecipient;
+ }
+ if (updateConfig.updateReserveBid) {
+ if (vector[mechanicVectorId].latestBidId > 0) {
+ _revert(InvalidUpdate.selector);
+ }
+ vector[mechanicVectorId].reserveBid = newVector.reserveBid;
+ }
+
+ emit RankedAuctionUpdated(mechanicVectorId);
+ }
+
+ /**
+ * @notice Create a new bid
+ */
+ function bid(bytes32 mechanicVectorId, uint96 bidAmount) external payable {
+ Vector memory _vector = vector[mechanicVectorId];
+ uint32 newUserNumBids = _userBidsMetadata[mechanicVectorId][msg.sender].numBids + 1;
+ if (
+ _vector.endTimestamp < uint48(block.timestamp) ||
+ _vector.startTimestamp > uint48(block.timestamp) ||
+ bidAmount < _vector.reserveBid ||
+ bidAmount != msg.value ||
+ (_vector.maxUserClaimableViaVector != 0 && newUserNumBids > uint256(_vector.maxUserClaimableViaVector))
+ ) {
+ _revert(InvalidBid.selector);
+ }
+
+ _vector.latestBidId += 1;
+ _vector.actionId += 1;
+
+ bids[mechanicVectorId][_vector.latestBidId] = Bid(bidAmount, msg.sender);
+ if (!_userBidIds[mechanicVectorId][msg.sender].add(uint256(_vector.latestBidId))) {
+ // impossible state
+ _revert(BidAlreadyAdded.selector);
+ }
+ _userBidsMetadata[mechanicVectorId][msg.sender].numBids = newUserNumBids;
+ vector[mechanicVectorId].latestBidId = _vector.latestBidId;
+ vector[mechanicVectorId].actionId = _vector.actionId;
+
+ if (_vector.endTimestamp - uint48(block.timestamp) <= 300) {
+ _vector.endTimestamp = _vector.maxEndTimestamp != 0
+ ? (
+ _vector.maxEndTimestamp > uint48(block.timestamp) + 300
+ ? uint48(block.timestamp) + 300
+ : _vector.maxEndTimestamp
+ )
+ : uint48(block.timestamp) + 300;
+ vector[mechanicVectorId].endTimestamp = _vector.endTimestamp;
+ emit AuctionLengthened(mechanicVectorId, _vector.endTimestamp);
+ }
+
+ bytes32 newValidityHash = _updateValidityHash(mechanicVectorId, _vector.latestBidId, bidAmount);
+
+ emit BidCreatedOrUpdated(
+ mechanicVectorId,
+ newValidityHash,
+ _vector.actionId,
+ _vector.latestBidId,
+ msg.sender,
+ bidAmount,
+ _vector.currency,
+ true
+ );
+ }
+
+ /**
+ * @notice Update a bid
+ */
+ function updateBid(bytes32 mechanicVectorId, uint32 bidId, uint96 newBidAmount) external payable {
+ Vector memory _vector = vector[mechanicVectorId];
+ Bid memory _bid = bids[mechanicVectorId][bidId];
+ if (
+ newBidAmount <= _bid.bidAmount ||
+ _bid.bidder == address(0) ||
+ _vector.endTimestamp < uint48(block.timestamp) ||
+ _vector.startTimestamp > uint48(block.timestamp) ||
+ newBidAmount < _vector.reserveBid ||
+ msg.value != newBidAmount - _bid.bidAmount
+ ) {
+ _revert(InvalidBid.selector);
+ }
+ if (_bid.bidder != msg.sender) {
+ _revert(Unauthorized.selector);
+ }
+
+ _vector.actionId += 1;
+
+ bids[mechanicVectorId][bidId].bidAmount = newBidAmount;
+ vector[mechanicVectorId].actionId = _vector.actionId;
+
+ if (_vector.endTimestamp - uint48(block.timestamp) <= 300) {
+ uint48 newEndTimestamp = _vector.maxEndTimestamp != 0
+ ? (
+ _vector.maxEndTimestamp > uint48(block.timestamp) + 300
+ ? uint48(block.timestamp) + 300
+ : _vector.maxEndTimestamp
+ )
+ : uint48(block.timestamp) + 300;
+ vector[mechanicVectorId].endTimestamp = newEndTimestamp;
+ emit AuctionLengthened(mechanicVectorId, newEndTimestamp);
+ }
+
+ bytes32 newValidityHash = _updateValidityHash(mechanicVectorId, bidId, newBidAmount);
+
+ emit BidCreatedOrUpdated(
+ mechanicVectorId,
+ newValidityHash,
+ _vector.actionId,
+ bidId,
+ msg.sender,
+ newBidAmount,
+ _vector.currency,
+ false
+ );
+ }
+
+ /**
+ * @notice Claim back funds for a bid that is currently invalid (effectively deleting the bid)
+ */
+ function reclaimBid(RankedAuctionsClaim calldata claim, bytes calldata claimSignature) external {
+ // validate signature
+ _validateClaim(claim, msg.sender, 1, claimSignature);
+
+ Bid memory _bid = bids[claim.mechanicVectorId][claim.bidId];
+ if (_bid.bidder != claim.claimer) {
+ _revert(Unauthorized.selector);
+ }
+
+ _sendEther(_bid.bidAmount, payable(_bid.bidder));
+
+ emit BidReclaimed(claim.mechanicVectorId, claim.bidId, _bid.bidAmount, vector[claim.mechanicVectorId].currency);
+
+ // remove bid
+ _userBidsMetadata[claim.mechanicVectorId][claim.claimer].numBids -= 1;
+ if (!_userBidIds[claim.mechanicVectorId][claim.claimer].remove(claim.bidId)) {
+ _revert(BidAlreadyReclaimed.selector);
+ }
+ delete bids[claim.mechanicVectorId][claim.bidId];
+ }
+
+ /**
+ * @notice Withdraw auction earnings to payment recipient
+ */
+ function withdrawAuctionEarnings(RankedAuctionsClaim calldata claim, bytes calldata claimSignature) external {
+ _validateClaim(claim, msg.sender, 2, claimSignature);
+
+ Vector memory _vector = vector[claim.mechanicVectorId];
+ // currently, only native gas token supported
+ if (
+ uint48(block.timestamp) <= _vector.endTimestamp || _vector.currency != address(0) || _vector.bidFundsClaimed
+ ) {
+ _revert(InvalidClaim.selector);
+ }
+
+ // 5% to platform
+ uint256 platformAmount = (claim.cumulativeBidAmount * 500) / 10000;
+ _sendEther(platformAmount, payable(owner()));
+ _sendEther(claim.cumulativeBidAmount - platformAmount, _vector.paymentRecipient);
+
+ vector[claim.mechanicVectorId].bidFundsClaimed = true;
+
+ emit AuctionEarningsClaimed(
+ claim.mechanicVectorId,
+ claim.cumulativeBidAmount,
+ _vector.paymentRecipient,
+ _vector.currency
+ );
+ }
+
+ /**
+ * @notice See {IMechanic-processNumMint}
+ */
+ function processNumMint(
+ bytes32 mechanicVectorId,
+ address recipient,
+ uint32 numToMint,
+ address minter,
+ MechanicVectorMetadata calldata mechanicVectorMetadata,
+ bytes calldata data
+ ) external payable onlyMintManager {
+ _processMint(mechanicVectorId, minter, numToMint, data);
+ }
+
+ /**
+ * @notice See {IMechanic-processChooseMint}
+ */
+ function processChooseMint(
+ bytes32 mechanicVectorId,
+ address recipient,
+ uint256[] calldata tokenIds,
+ address minter,
+ MechanicVectorMetadata calldata mechanicVectorMetadata,
+ bytes calldata data
+ ) external payable onlyMintManager {
+ // currently we don't support "choose token to mint" functionality for seed based mints
+ _revert(InvalidMint.selector);
+ }
+
+ /**
+ * @notice State readers
+ */
+ function getRawVector(bytes32 mechanicVectorId) external view returns (Vector memory _vector) {
+ _vector = vector[mechanicVectorId];
+ }
+
+ function getVectorState(
+ bytes32 mechanicVectorId
+ )
+ external
+ view
+ returns (Vector memory _vector, bytes32 validityHash, uint256 collectionSupply, uint256 collectionSize)
+ {
+ _vector = vector[mechanicVectorId];
+ validityHash = vectorValidityHash[mechanicVectorId];
+ (collectionSupply, collectionSize) = _collectionSupplyAndSize(mechanicVectorId);
+ }
+
+ function getBids(bytes32 mechanicVectorId, uint32[] calldata bidIds) external view returns (Bid[] memory) {
+ uint256 bidIdsLength = bidIds.length;
+ Bid[] memory _bids = new Bid[](bidIdsLength);
+ for (uint256 i = 0; i < bidIdsLength; i++) {
+ _bids[i] = bids[mechanicVectorId][bidIds[i]];
+ }
+ return _bids;
+ }
+
+ function getUserBids(
+ bytes32 mechanicVectorId,
+ address user
+ ) external view returns (Bid[] memory, uint256[] memory bidIds, uint32 numBids, uint32 numClaimed) {
+ UserBidsMetadata memory metadata = _userBidsMetadata[mechanicVectorId][user];
+
+ uint256[] memory _bidIds = _userBidIds[mechanicVectorId][user].values();
+ uint256 bidIdsLength = _bidIds.length;
+ Bid[] memory _bids = new Bid[](bidIdsLength);
+
+ for (uint256 i = 0; i < bidIdsLength; i++) {
+ _bids[i] = bids[mechanicVectorId][uint32(_bidIds[i])];
+ }
+
+ return (_bids, _bidIds, metadata.numBids, metadata.numClaimed);
+ }
+
+ /* solhint-disable no-empty-blocks */
+ /**
+ * @notice Limit upgrades of contract to SeedBasedMintMechanic owner
+ * @param // New implementation address
+ */
+ function _authorizeUpgrade(address) internal override onlyOwner {}
+
+ /**
+ * @notice Process sequential mint logic
+ * @param mechanicVectorId Mechanic vector ID
+ * @param minter Minter
+ * @param numToMint Number of tokens to mint
+ * @param data Mechanic mint data (signature)
+ */
+ function _processMint(bytes32 mechanicVectorId, address minter, uint32 numToMint, bytes calldata data) private {
+ (RankedAuctionsClaim memory _claim, bytes memory claimSignature) = _unwrapRankedAuctionClaim(
+ mechanicVectorId,
+ data
+ );
+
+ _validateClaim(_claim, minter, 3, claimSignature);
+
+ if (vector[mechanicVectorId].endTimestamp >= uint48(block.timestamp)) {
+ _revert(InvalidMint.selector);
+ }
+ uint32 numClaimed = _userBidsMetadata[mechanicVectorId][minter].numClaimed;
+ if (numToMint + numClaimed > _claim.claimerNumValidBids) {
+ _revert(InvalidMintAmount.selector);
+ }
+ _userBidsMetadata[mechanicVectorId][minter].numClaimed = numClaimed + numToMint;
+
+ // handle rebate
+ if (_claim.rebateAmount > 0) {
+ _sendEther(_claim.rebateAmount, payable(_claim.claimer));
+ }
+ }
+
+ /**
+ * @notice Send ether to a recipient
+ */
+ function _sendEther(uint256 amount, address payable recipient) private {
+ (bool sent, ) = recipient.call{ value: amount }("");
+ if (!sent) {
+ _revert(EtherSendFailed.selector);
+ }
+ }
+
+ /**
+ * @notice Update vector's validity hash
+ */
+ function _updateValidityHash(bytes32 mechanicVectorId, uint32 bidId, uint96 bidAmount) private returns (bytes32) {
+ bytes32 newValidityHash = keccak256(
+ abi.encodePacked(vectorValidityHash[mechanicVectorId], mechanicVectorId, bidId, bidAmount)
+ );
+ vectorValidityHash[mechanicVectorId] = newValidityHash;
+ return newValidityHash;
+ }
+
+ /**
+ * @notice Validate claim
+ * @param claim Claim
+ * @param expectedClaimer Expected claimer
+ * @param expectedClaimType Expected claim type
+ * @param claimSignature Claim signature
+ */
+ function _validateClaim(
+ RankedAuctionsClaim memory claim,
+ address expectedClaimer,
+ uint8 expectedClaimType,
+ bytes memory claimSignature
+ ) private {
+ if (claim.claimer != expectedClaimer) {
+ _revert(Unauthorized.selector);
+ }
+ if (claim.claimType != expectedClaimType) {
+ _revert(InvalidClaim.selector);
+ }
+ bytes32 claimId = keccak256(
+ abi.encode(
+ _CLAIM_TYPEHASH,
+ claim.mechanicVectorId,
+ claim.rebateAmount,
+ claim.claimer,
+ claim.claimerNumValidBids,
+ claim.claimExpiryTimestamp,
+ claim.cumulativeBidAmount,
+ claim.bidId,
+ claim.claimType
+ )
+ );
+ bytes32 digest = keccak256(abi.encodePacked("\x19\x01", _getDomainSeperator(), claimId));
+
+ address signer = ECDSA.recover(digest, claimSignature);
+ if (
+ signer == address(0) || !_isPlatformExecutor(signer) || uint48(block.timestamp) > claim.claimExpiryTimestamp
+ ) {
+ _revert(InvalidSignature.selector);
+ }
+ if (!_usedClaims[claim.mechanicVectorId].add(claimId)) {
+ // claim already used
+ _revert(InvalidClaim.selector);
+ }
+ }
+
+ /**
+ * @notice Validate mint claim
+ * @param mechanicVectorId Mechanic vector id
+ * @param data Mint data
+ */
+ function _unwrapRankedAuctionClaim(
+ bytes32 mechanicVectorId,
+ bytes calldata data
+ ) private returns (RankedAuctionsClaim memory, bytes memory) {
+ (
+ uint256 rebateAmount,
+ address claimer,
+ uint32 claimerNumValidBids,
+ uint48 claimExpiryTimestamp,
+ uint256 cumulativeBidAmount,
+ uint32 bidId,
+ uint8 claimType,
+ bytes memory claimSignature
+ ) = abi.decode(data, (uint256, address, uint32, uint48, uint256, uint32, uint8, bytes));
+
+ return (
+ RankedAuctionsClaim(
+ mechanicVectorId,
+ rebateAmount,
+ claimer,
+ claimerNumValidBids,
+ claimExpiryTimestamp,
+ cumulativeBidAmount,
+ bidId,
+ claimType
+ ),
+ claimSignature
+ );
+ }
+
+ /**
+ * @notice Returns a collection's current supply
+ * @param mechanicVectorId Mechanic vector ID
+ */
+ function _collectionSupplyAndSize(bytes32 mechanicVectorId) private view returns (uint256 supply, uint256 size) {
+ MechanicVectorMetadata memory metadata = _getMechanicVectorMetadata(mechanicVectorId);
+ if (metadata.contractAddress == address(0)) {
+ revert("Vector doesn't exist");
+ }
+ if (metadata.isEditionBased) {
+ IEditionCollection.EditionDetails memory edition = IEditionCollection(metadata.contractAddress)
+ .getEditionDetails(metadata.editionId);
+ supply = edition.supply;
+ size = edition.size;
+ } else {
+ // supply holds a tighter constraint (no burns), some old contracts don't have it
+ try IERC721GeneralSupplyMetadata(metadata.contractAddress).supply() returns (uint256 _supply) {
+ supply = _supply;
+ } catch {
+ supply = IERC721GeneralSupplyMetadata(metadata.contractAddress).totalSupply();
+ }
+ size = IERC721GeneralSupplyMetadata(metadata.contractAddress).limitSupply();
+ }
+ }
+
+ /**
+ * @notice Return EIP712 domain seperator
+ */
+ function _getDomainSeperator() private view returns (bytes32) {
+ return
+ keccak256(
+ abi.encode(
+ _DOMAIN_TYPEHASH,
+ keccak256("RankedAuctionMechanic"),
+ keccak256("1"),
+ block.chainid,
+ address(this),
+ 0x960bb3ecd14c38754109e5fe3a3b72aa0434091106c0fea200392fd413d44da0 // ranked auction mechanic salt
+ )
+ );
+ }
+}
diff --git a/contracts/mint/mechanics/SeedBasedMintMechanic.sol b/contracts/mint/mechanics/SeedBasedMintMechanic.sol
new file mode 100644
index 0000000..c900000
--- /dev/null
+++ b/contracts/mint/mechanics/SeedBasedMintMechanic.sol
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+import "./MechanicMintManagerClientUpgradeable.sol";
+import "../../erc721/interfaces/IEditionCollection.sol";
+import "../../erc721/interfaces/IERC721GeneralSupplyMetadata.sol";
+import "../../observability/IGengineObservability.sol";
+import "./interfaces/IManifold1155Burn.sol";
+
+import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
+import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
+
+/**
+ * @notice Highlight's bespoke Seed based mint mechanic
+ * @author highlight.xyz
+ */
+contract SeedBasedMintMechanic is MechanicMintManagerClientUpgradeable, UUPSUpgradeable {
+ using EnumerableSet for EnumerableSet.Bytes32Set;
+
+ /**
+ * @notice Throw when an action is unauthorized
+ */
+ error Unauthorized();
+
+ /**
+ * @notice Throw when it is invalid to mint on a vector
+ */
+ error InvalidMint();
+
+ /**
+ * @notice Throw when a vector is already created with a mechanic vector ID
+ */
+ error VectorAlreadyCreated();
+
+ /**
+ * @notice Throw when the seed has already been used
+ */
+ error SeedAlreadyUsed();
+
+ /**
+ * @notice Throw when the transaction sender has sent an invalid payment amount during a mint
+ */
+ error InvalidPaymentAmount();
+
+ /**
+ * @notice Throw when an internal transfer of ether fails
+ */
+ error EtherSendFailed();
+
+ /**
+ * @notice On-chain mint vector (stored data)
+ * @param startTimestamp When minting opens on vector
+ * @param endTimestamp When minting ends on vector
+ * @param maxUserClaimableViaVector Max number of tokens that can be minted by user via vector
+ * @param maxTotalClaimableViaVector Max number of tokens that can be minted via vector
+ * @param totalClaimedViaVector Total number of tokens minted via vector
+ * @param currency Currency used for payment. Native gas token, if zero address
+ * @param tokenLimitPerTx Max number of tokens that can be minted in one transaction
+ * @param paymentRecipient Payment recipient
+ * @param pricePerToken Price that has to be paid per minted token
+ * @param requireDirectEOA Require minters to directly be EOAs
+ */
+ struct SeedBasedVector {
+ uint48 startTimestamp;
+ uint48 endTimestamp;
+ uint32 maxUserClaimableViaVector;
+ uint32 maxTotalClaimableViaVector;
+ uint48 currentSupply;
+ uint48 tokenLimitPerTx;
+ uint192 pricePerToken;
+ address payable paymentRecipient;
+ bool uniqueSeeds;
+ }
+
+ /**
+ * @notice Config used to control updating of fields in SeedBasedVector
+ */
+ struct SeedBasedVectorUpdateConfig {
+ bool updateStartTimestamp;
+ bool updateEndTimestamp;
+ bool updateMaxUserClaimableViaVector;
+ bool updateMaxTotalClaimableViaVector;
+ bool updateTokenLimitPerTx;
+ bool updatePaymentRecipient;
+ bool updatePricePerToken;
+ }
+
+ /**
+ * @notice Config used to control burn / redeem mechanic when 1155 tokens are being burned
+ */
+ struct BurnRedeem1155Config {
+ address burnContract;
+ uint88 tokenId;
+ uint8 numToBurnPerMint;
+ }
+
+ /**
+ * @notice IGengineObservability contract
+ */
+ IGengineObservability public observability;
+
+ /**
+ * @notice Stores seed based vector, indexed by global mechanic vector id
+ */
+ mapping(bytes32 => SeedBasedVector) private vector;
+
+ /**
+ * @notice Stores already used seeds per mechanic
+ */
+ mapping(bytes32 => mapping(bytes32 => uint256)) public seedInfo;
+
+ /**
+ * @notice System-wide vector ids to (user to user claims count)
+ */
+ mapping(bytes32 => mapping(address => uint64)) public userClaims;
+
+ /**
+ * @notice System-wide vector ids to burn/redeem configuration
+ */
+ mapping(bytes32 => BurnRedeem1155Config) private _burnRedeem1155Config;
+
+ /**
+ * @notice Emitted when a mint vector is created
+ */
+ event SeedBasedVectorCreated(bytes32 indexed mechanicVectorId);
+
+ /**
+ * @notice Emitted when a mint vector is updated
+ */
+ event SeedBasedVectorUpdated(bytes32 indexed mechanicVectorId);
+
+ /**
+ * @notice Emitted when a number of tokens are minted
+ */
+ event SeedBasedMint(
+ bytes32 indexed mechanicVectorId,
+ address indexed recipient,
+ uint200 pricePerToken,
+ uint48 numMinted
+ );
+
+ /**
+ * @notice Emitted for the seed based data on mint
+ * @param sender contract emitting the event
+ * @param contractAddress NFT contract token resides on
+ * @param data custom mint data
+ */
+ event CustomMintData(address indexed sender, address indexed contractAddress, bytes data);
+
+ /**
+ * @notice Emitted when payment is made to payment recipient
+ * @param paymentRecipient Creator recipient of payment
+ * @param mechanicVectorId Mechanic vector ID
+ * @param amountToCreator Amount sent to creator
+ * @param percentageBPSOfTotal Percentage (in basis points) that was sent to creator, of total payment
+ */
+ event SeedBasedNativeTokenPayment(
+ bytes32 indexed mechanicVectorId,
+ address indexed paymentRecipient,
+ uint256 amountToCreator,
+ uint32 percentageBPSOfTotal
+ );
+
+ /**
+ * @notice Initialize mechanic contract
+ * @param _mintManager Mint manager address
+ * @param platform Platform owning the contract
+ */
+ function initialize(address _mintManager, address platform, address _observability) external initializer {
+ __MechanicMintManagerClientUpgradeable_initialize(_mintManager, platform);
+ observability = IGengineObservability(_observability);
+ }
+
+ /**
+ * @notice Create a seed based vector
+ * @param mechanicVectorId Global mechanic vector ID
+ * @param vectorData Vector data, to be deserialized into seed based vector data
+ */
+ function createVector(bytes32 mechanicVectorId, bytes memory vectorData) external onlyMintManager {
+ // precaution, although MintManager tightly controls creation and prevents double creation
+ if (vector[mechanicVectorId].startTimestamp != 0) {
+ _revert(VectorAlreadyCreated.selector);
+ }
+ (
+ uint48 startTimestamp,
+ uint48 endTimestamp,
+ uint32 maxUserClaimableViaVector,
+ uint32 maxTotalClaimableViaVector,
+ uint48 tokenLimitPerTx,
+ uint192 pricePerToken,
+ address paymentRecipient,
+ bool uniqueSeeds
+ ) = abi.decode(vectorData, (uint48, uint48, uint32, uint32, uint48, uint192, address, bool));
+
+ SeedBasedVector memory _vector = SeedBasedVector(
+ startTimestamp == 0 ? uint48(block.timestamp) : startTimestamp,
+ endTimestamp,
+ maxUserClaimableViaVector,
+ maxTotalClaimableViaVector,
+ 0,
+ tokenLimitPerTx,
+ pricePerToken,
+ payable(paymentRecipient),
+ uniqueSeeds
+ );
+
+ vector[mechanicVectorId] = _vector;
+
+ emit SeedBasedVectorCreated(mechanicVectorId);
+ }
+
+ /* solhint-disable code-complexity */
+ /**
+ * @notice Update a seed based vector
+ * @param mechanicVectorId Global mechanic vector ID
+ * @param newVector New vector fields
+ * @param updateConfig Config denoting what fields on vector to update
+ */
+ function updateVector(
+ bytes32 mechanicVectorId,
+ SeedBasedVector calldata newVector,
+ SeedBasedVectorUpdateConfig calldata updateConfig
+ ) external {
+ MechanicVectorMetadata memory metadata = _getMechanicVectorMetadata(mechanicVectorId);
+ if (
+ OwnableUpgradeable(metadata.contractAddress).owner() != msg.sender && metadata.contractAddress != msg.sender
+ ) {
+ _revert(Unauthorized.selector);
+ }
+
+ // rather than updating entire vector, update per-field
+ if (updateConfig.updateStartTimestamp) {
+ vector[mechanicVectorId].startTimestamp = newVector.startTimestamp == 0
+ ? uint48(block.timestamp)
+ : newVector.startTimestamp;
+ }
+ if (updateConfig.updateEndTimestamp) {
+ vector[mechanicVectorId].endTimestamp = newVector.endTimestamp;
+ }
+ if (updateConfig.updateMaxUserClaimableViaVector) {
+ vector[mechanicVectorId].maxUserClaimableViaVector = newVector.maxUserClaimableViaVector;
+ }
+ if (updateConfig.updateMaxTotalClaimableViaVector) {
+ vector[mechanicVectorId].maxTotalClaimableViaVector = newVector.maxTotalClaimableViaVector;
+ }
+ if (updateConfig.updateTokenLimitPerTx) {
+ vector[mechanicVectorId].tokenLimitPerTx = newVector.tokenLimitPerTx;
+ }
+ if (updateConfig.updatePaymentRecipient) {
+ vector[mechanicVectorId].paymentRecipient = newVector.paymentRecipient;
+ }
+ if (updateConfig.updatePricePerToken) {
+ vector[mechanicVectorId].pricePerToken = newVector.pricePerToken;
+ }
+
+ emit SeedBasedVectorUpdated(mechanicVectorId);
+ }
+
+ /**
+ * @notice Set the burn redeem 1155 config for a vector
+ * @param mechanicVectorId Global mechanic vector ID
+ * @param newConfig New Burn/Redeem 1155 config
+ */
+ function setBurnRedeem1155Config(bytes32 mechanicVectorId, BurnRedeem1155Config calldata newConfig) external {
+ MechanicVectorMetadata memory metadata = _getMechanicVectorMetadata(mechanicVectorId);
+ if (
+ OwnableUpgradeable(metadata.contractAddress).owner() != msg.sender && metadata.contractAddress != msg.sender
+ ) {
+ _revert(Unauthorized.selector);
+ }
+
+ _burnRedeem1155Config[mechanicVectorId] = newConfig;
+ }
+
+ /* solhint-enable code-complexity */
+
+ /**
+ * @notice See {IMechanic-processNumMint}
+ */
+ function processNumMint(
+ bytes32 mechanicVectorId,
+ address recipient,
+ uint32 numToMint,
+ address minter,
+ MechanicVectorMetadata calldata mechanicVectorMetadata,
+ bytes calldata data
+ ) external payable onlyMintManager {
+ _processMint(mechanicVectorId, recipient, numToMint, data);
+
+ BurnRedeem1155Config memory burnRedeemConfig = _burnRedeem1155Config[mechanicVectorId];
+ if (burnRedeemConfig.burnContract != address(0)) {
+ _processBurnRedeem(burnRedeemConfig, minter, numToMint);
+ }
+ }
+
+ /**
+ * @notice See {IMechanic-processChooseMint}
+ */
+ function processChooseMint(
+ bytes32 mechanicVectorId,
+ address recipient,
+ uint256[] calldata tokenIds,
+ address minter,
+ MechanicVectorMetadata calldata mechanicVectorMetadata,
+ bytes calldata data
+ ) external payable onlyMintManager {
+ // currently we don't support "choose token to mint" functionality for seed based mints
+ _revert(InvalidMint.selector);
+ }
+
+ /**
+ * @notice Get raw vector data
+ * @param mechanicVectorId Mechanic vector ID
+ */
+ function getRawVector(bytes32 mechanicVectorId) external view returns (SeedBasedVector memory _vector) {
+ _vector = vector[mechanicVectorId];
+ }
+
+ /**
+ * @notice Get a vector's full state
+ * @param mechanicVectorId Mechanic vector ID
+ */
+ function getVectorState(
+ bytes32 mechanicVectorId
+ ) external view returns (SeedBasedVector memory _vector, uint256 collectionSupply, uint256 collectionSize) {
+ _vector = vector[mechanicVectorId];
+ (collectionSupply, collectionSize) = _collectionSupplyAndSize(mechanicVectorId);
+ }
+
+ /**
+ * @notice Withdraw native gas token
+ */
+ function withdrawNativeGasToken(uint256 amountToWithdraw, address payable recipient) external onlyOwner {
+ (bool sentToRecipient, bytes memory data) = recipient.call{ value: amountToWithdraw }("");
+ if (!sentToRecipient) {
+ _revert(EtherSendFailed.selector);
+ }
+ }
+
+ /* solhint-disable no-empty-blocks */
+ /**
+ * @notice Limit upgrades of contract to SeedBasedMintMechanic owner
+ * @param // New implementation address
+ */
+ function _authorizeUpgrade(address) internal override onlyOwner {}
+
+ /**
+ * @notice Process sequential mint logic
+ * @param mechanicVectorId Mechanic vector ID
+ * @param recipient Mint recipient
+ * @param numToMint Number of tokens to mint
+ */
+ function _processMint(bytes32 mechanicVectorId, address recipient, uint32 numToMint, bytes calldata data) private {
+ MechanicVectorMetadata memory metadata = _getMechanicVectorMetadata(mechanicVectorId);
+ if (metadata.contractAddress == address(0)) {
+ revert("Vector doesn't exist");
+ }
+ SeedBasedVector memory _vector = vector[mechanicVectorId];
+ uint48 newNumClaimedForUser = uint48(userClaims[mechanicVectorId][recipient]) + numToMint;
+ bytes32 seedData = keccak256(data);
+ uint256 newSeedCount = seedInfo[mechanicVectorId][seedData] + 1;
+
+ uint48 newSupply = _vector.currentSupply + numToMint;
+ if (
+ block.timestamp < _vector.startTimestamp ||
+ (block.timestamp > _vector.endTimestamp && _vector.endTimestamp != 0) ||
+ (_vector.maxTotalClaimableViaVector != 0 && newSupply > _vector.maxTotalClaimableViaVector) ||
+ (_vector.maxUserClaimableViaVector != 0 && newNumClaimedForUser > _vector.maxUserClaimableViaVector) ||
+ (_vector.tokenLimitPerTx != 0 && numToMint > _vector.tokenLimitPerTx) ||
+ numToMint > 1
+ ) {
+ _revert(InvalidMint.selector);
+ }
+
+ if (_vector.uniqueSeeds && newSeedCount != 1) {
+ _revert(SeedAlreadyUsed.selector);
+ }
+
+ uint200 totalPrice = _vector.pricePerToken * numToMint;
+ _processPayment(mechanicVectorId, _vector.paymentRecipient, totalPrice);
+
+ seedInfo[mechanicVectorId][seedData] = newSeedCount;
+ vector[mechanicVectorId].currentSupply = newSupply;
+ userClaims[mechanicVectorId][recipient] = uint64(newNumClaimedForUser);
+
+ emit SeedBasedMint(mechanicVectorId, recipient, _vector.pricePerToken, numToMint);
+
+ emit CustomMintData(address(this), metadata.contractAddress, data);
+ }
+
+ /**
+ * @notice Returns a collection's current supply
+ * @param mechanicVectorId Mechanic vector ID
+ */
+ function _collectionSupplyAndSize(bytes32 mechanicVectorId) private view returns (uint256 supply, uint256 size) {
+ MechanicVectorMetadata memory metadata = _getMechanicVectorMetadata(mechanicVectorId);
+ if (metadata.contractAddress == address(0)) {
+ revert("Vector doesn't exist");
+ }
+ if (metadata.isEditionBased) {
+ IEditionCollection.EditionDetails memory edition = IEditionCollection(metadata.contractAddress)
+ .getEditionDetails(metadata.editionId);
+ supply = edition.supply;
+ size = edition.size;
+ } else {
+ // supply holds a tighter constraint (no burns), some old contracts don't have it
+ try IERC721GeneralSupplyMetadata(metadata.contractAddress).supply() returns (uint256 _supply) {
+ supply = _supply;
+ } catch {
+ supply = IERC721GeneralSupplyMetadata(metadata.contractAddress).totalSupply();
+ }
+ size = IERC721GeneralSupplyMetadata(metadata.contractAddress).limitSupply();
+ }
+ }
+
+ /**
+ * @notice Process payment in native gas token, sending to creator and platform
+ * @param mechanicVectorId ID of vector
+ * @param recipient Creator recipient of payment
+ * @param totalAmount Total amount being paid
+ */
+ function _processPayment(bytes32 mechanicVectorId, address payable recipient, uint256 totalAmount) private {
+ if (totalAmount > msg.value) {
+ _revert(InvalidPaymentAmount.selector);
+ }
+ (bool sentToRecipient, bytes memory dataRecipient) = recipient.call{ value: totalAmount }("");
+ if (!sentToRecipient) {
+ _revert(EtherSendFailed.selector);
+ }
+ emit SeedBasedNativeTokenPayment(mechanicVectorId, recipient, totalAmount, 10000);
+ }
+
+ /**
+ * @notice Process burn / redeem
+ * @param burnRedeemConfig Burn / redeem config
+ * @param minter Minter burning tokens
+ * @param numToMint Number of tokens to mint
+ */
+ function _processBurnRedeem(
+ BurnRedeem1155Config memory burnRedeemConfig,
+ address minter,
+ uint32 numToMint
+ ) private {
+ uint256[] memory tokenIds = new uint256[](1);
+ tokenIds[0] = uint256(burnRedeemConfig.tokenId);
+ uint256[] memory amounts = new uint256[](1);
+ amounts[0] = uint256(burnRedeemConfig.numToBurnPerMint) * uint256(numToMint);
+
+ IManifold1155Burn(burnRedeemConfig.burnContract).burn(minter, tokenIds, amounts);
+ }
+}
diff --git a/contracts/mint/mechanics/VerisartMechanic.sol b/contracts/mint/mechanics/VerisartMechanic.sol
new file mode 100644
index 0000000..3942aa2
--- /dev/null
+++ b/contracts/mint/mechanics/VerisartMechanic.sol
@@ -0,0 +1,549 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+import "./MechanicMintManagerClientUpgradeable.sol";
+import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
+import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
+import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
+
+/**
+ * @notice Highlight <> Verisart mint mechanic
+ */
+contract VerisartMechanic is MechanicMintManagerClientUpgradeable, UUPSUpgradeable {
+ using EnumerableSet for EnumerableSet.AddressSet;
+
+ /**
+ * @notice Throw when mint recipient cannot receive the mints, due to hitting the per-vector limit
+ */
+ error VectorMintLimitExceeded();
+
+ /**
+ * @notice Throw when mint recipient cannot receive the mints, due to hitting the per-user limit
+ */
+ error RecipientUserMintLimitExceeded();
+
+ /**
+ * @notice Throw when vector configuration is invalid
+ */
+ error InvalidVectorConfig();
+
+ /**
+ * @notice Throw when caller is unauthorized
+ */
+ error Unauthorized();
+
+ /**
+ * @notice Throw when admin is invalid to add or remove
+ */
+ error InvalidAdmin();
+
+ /**
+ * @notice Throw when minter is invalid to add or remove
+ */
+ error InvalidMinter();
+
+ /**
+ * @notice Throw when signed mint has used claim ID
+ */
+ error ClaimUsed();
+
+ /**
+ * @notice Throw when mechanic ID is invalid (missing mechanic vector on MintManager)
+ */
+ error InvalidMechanicID();
+
+ /**
+ * @notice Throw when attempting to mint via signature on a mint where sig-based mints are disallowed
+ */
+ error SignedMintingDisabled();
+
+ /**
+ * @notice Verisart vector data
+ */
+ struct VerisartVector {
+ uint48 size; // size == 0 means unlimited
+ uint48 supply;
+ uint32 maxClaimableByUser; // maxClaimableByUser == 0 means unlimited
+ bool signedMintingDisabled;
+ // remaining slots for future data
+ }
+
+ /**
+ * @notice Verisart vector update config (used for gas efficiency)
+ */
+ struct VerisartUpdateConfig {
+ bool updateSize;
+ bool updateMaxClaimableByUser;
+ bool updateSignedMintingDisabled;
+ }
+
+ /**
+ * @notice Admins
+ */
+ EnumerableSet.AddressSet private _admins;
+
+ /**
+ * @notice Allowed global minters
+ */
+ EnumerableSet.AddressSet private _globalMinters;
+
+ /**
+ * @notice Allowed minters per vector (indexed by hash(mechanicVectorId, minter))
+ */
+ mapping(bytes32 => bool) private _vectorLevelMinters;
+
+ /**
+ * @notice Data per vector
+ */
+ mapping(bytes32 => VerisartVector) private _vectors;
+
+ /**
+ * @notice Track number of mints per recipient per vector
+ */
+ mapping(bytes32 => mapping(address => uint32)) private _mintsPerRecipient;
+
+ /**
+ * @notice Track signed mints to avoid replay attacks
+ */
+ mapping(bytes32 => bool) private _signedMints;
+
+ /**
+ * @notice Constants that help with EIP-712, signature based minting
+ */
+ bytes32 private constant _DOMAIN_TYPEHASH =
+ keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)");
+
+ bytes32 private constant _MINT_SIGNED_TYPEHASH =
+ keccak256("VerisartMint(address sender,address to,bytes32 mechanicVectorId,bytes32 claimNonce)");
+
+ /**
+ * @notice Emitted when vector is created
+ */
+ event VerisartVectorCreated(bytes32 indexed mechanicVectorId);
+
+ /**
+ * @notice Emitted when vector is updated
+ */
+ event VerisartVectorUpdated(bytes32 indexed mechanicVectorId);
+
+ /**
+ * @notice Emitted when mint is processed
+ */
+ event VerisartMint(
+ bytes32 indexed mechanicVectorId,
+ address indexed minterOrSigner,
+ bool indexed signatureBased,
+ address recipient,
+ uint32 numToMint
+ );
+
+ /**
+ * @notice Events emitted during admin and minter re-configurations
+ */
+ event AdminAdded(address indexed admin, address by);
+ event AdminRemoved(address indexed admin, address by);
+ event GlobalMinterAdded(address indexed globalMinter, address by);
+ event GlobalMinterRemoved(address indexed globalMinter, address by);
+ event VectorLevelMinterAdded(bytes32 indexed mechanicVectorId, address indexed vectorLevelMinter, address by);
+ event VectorLevelMinterRemoved(bytes32 indexed mechanicVectorId, address indexed vectorLevelMinter, address by);
+
+ /**
+ * @notice Enforce caller to be an admin
+ */
+ modifier onlyAdmin() {
+ if (!_admins.contains(msg.sender)) {
+ _revert(Unauthorized.selector);
+ }
+ _;
+ }
+
+ /**
+ * @notice Enforce caller to be an admin, the collection contract itself, or the collection owner
+ */
+ modifier onlyVectorAdmin(bytes32 mechanicVectorId) {
+ MechanicVectorMetadata memory metadata = _getMechanicVectorMetadata(mechanicVectorId);
+ if (metadata.contractAddress == address(0) || metadata.mechanic != address(this)) {
+ _revert(InvalidMechanicID.selector);
+ }
+ if (
+ !_admins.contains(msg.sender) &&
+ metadata.contractAddress != msg.sender &&
+ OwnableUpgradeable(metadata.contractAddress).owner() != msg.sender
+ ) {
+ _revert(Unauthorized.selector);
+ }
+ _;
+ }
+
+ /**
+ * @notice Initialize mechanic contract
+ * @param _mintManager Mint manager address
+ * @param platform Platform
+ * @param initialAdmin Initial admin
+ * @param initialGlobalMinter Initial global minter
+ */
+ function initialize(
+ address _mintManager,
+ address platform,
+ address initialAdmin,
+ address initialGlobalMinter
+ ) external initializer {
+ __MechanicMintManagerClientUpgradeable_initialize(_mintManager, platform);
+ if (initialAdmin != address(0)) {
+ _admins.add(initialAdmin);
+ }
+ if (initialGlobalMinter != address(0)) {
+ _globalMinters.add(initialGlobalMinter);
+ }
+ }
+
+ /**
+ * @notice Create a Verisart mint vector
+ * @param mechanicVectorId Global mechanic vector ID
+ * @param vectorData Vector data, to be deserialized into Verisart vector data
+ */
+ function createVector(bytes32 mechanicVectorId, bytes memory vectorData) external onlyMintManager {
+ (uint48 size, uint32 maxClaimableByUser) = abi.decode(vectorData, (uint48, uint32));
+ VerisartVector memory _vector = VerisartVector(size, 0, maxClaimableByUser, false);
+
+ if (size != 0 || maxClaimableByUser != 0) {
+ _vectors[mechanicVectorId] = _vector;
+ }
+
+ emit VerisartVectorCreated(mechanicVectorId);
+ }
+
+ /**
+ * @notice Update a Verisart mint vector
+ * @dev Caller must either be the collection contract itself, the collection owner, or an admin
+ * @param mechanicVectorId Global mechanic vector ID
+ * @param newVector New vector fields
+ * @param updateConfig Config denoting what fields on vector to updatae
+ */
+ function updateVector(
+ bytes32 mechanicVectorId,
+ VerisartVector calldata newVector,
+ VerisartUpdateConfig calldata updateConfig
+ ) external onlyVectorAdmin(mechanicVectorId) {
+ // one slot, so load entirety into memory
+ VerisartVector memory _vector = _vectors[mechanicVectorId];
+
+ if (updateConfig.updateSize) {
+ if (newVector.size < _vector.supply) {
+ _revert(InvalidVectorConfig.selector);
+ }
+ _vector.size = newVector.size;
+ }
+ if (updateConfig.updateMaxClaimableByUser) {
+ _vector.maxClaimableByUser = newVector.maxClaimableByUser;
+ }
+ if (updateConfig.updateSignedMintingDisabled) {
+ _vector.signedMintingDisabled = newVector.signedMintingDisabled;
+ }
+ _vectors[mechanicVectorId] = _vector;
+
+ emit VerisartVectorUpdated(mechanicVectorId);
+ }
+
+ /**
+ * @notice See {IMechanic-processNumMint}
+ */
+ function processNumMint(
+ bytes32 mechanicVectorId,
+ address recipient,
+ uint32 numToMint,
+ address minter,
+ MechanicVectorMetadata calldata mechanicVectorMetadata,
+ bytes calldata data
+ ) external payable onlyMintManager {
+ _processMint(mechanicVectorId, numToMint, recipient, minter, data);
+ }
+
+ /**
+ * @notice See {IMechanic-processChooseMint}
+ */
+ function processChooseMint(
+ bytes32 mechanicVectorId,
+ address recipient,
+ uint256[] calldata tokenIds,
+ address minter,
+ MechanicVectorMetadata calldata mechanicVectorMetadata,
+ bytes calldata data
+ ) external payable onlyMintManager {
+ _processMint(mechanicVectorId, uint32(tokenIds.length), recipient, minter, data);
+ }
+
+ /**
+ * @notice Add an admin
+ * @param admin Admin to add
+ */
+ function addAdmin(address admin) external onlyOwner {
+ if (!_admins.add(admin)) {
+ _revert(InvalidAdmin.selector);
+ }
+
+ emit AdminAdded(admin, msg.sender);
+ }
+
+ /**
+ * @notice Remove an admin
+ * @param admin Admin to remove
+ */
+ function removeAdmin(address admin) external onlyOwner {
+ if (!_admins.remove(admin)) {
+ _revert(InvalidAdmin.selector);
+ }
+
+ emit AdminRemoved(admin, msg.sender);
+ }
+
+ /**
+ * @notice Add a global minter
+ * @param globalMinter Global minter to add
+ */
+ function addGlobalMinter(address globalMinter) external onlyAdmin {
+ if (!_globalMinters.add(globalMinter)) {
+ _revert(InvalidMinter.selector);
+ }
+
+ emit GlobalMinterAdded(globalMinter, msg.sender);
+ }
+
+ /**
+ * @notice Remove a global minter
+ * @param globalMinter Global minter to remove
+ */
+ function removeGlobalMinter(address globalMinter) external onlyAdmin {
+ if (!_globalMinters.remove(globalMinter)) {
+ _revert(InvalidMinter.selector);
+ }
+
+ emit GlobalMinterRemoved(globalMinter, msg.sender);
+ }
+
+ /**
+ * @notice Add a vector-level minter
+ * @param mechanicVectorId ID of vector to add vector-level minter to
+ * @param vectorLevelMinter Vector level minter to add
+ */
+ function addVectorLevelMinter(
+ bytes32 mechanicVectorId,
+ address vectorLevelMinter
+ ) external onlyVectorAdmin(mechanicVectorId) {
+ bytes32 permissionId = keccak256(abi.encodePacked(mechanicVectorId, vectorLevelMinter));
+ if (_vectorLevelMinters[permissionId]) {
+ _revert(InvalidMinter.selector);
+ } else {
+ _vectorLevelMinters[permissionId] = true;
+ }
+
+ emit VectorLevelMinterAdded(mechanicVectorId, vectorLevelMinter, msg.sender);
+ }
+
+ /**
+ * @notice Remove a vector-level minter
+ * @param mechanicVectorId ID of vector to remove vector-level minter from
+ * @param vectorLevelMinter Vector level minter to remove
+ */
+ function removeVectorLevelMinter(
+ bytes32 mechanicVectorId,
+ address vectorLevelMinter
+ ) external onlyVectorAdmin(mechanicVectorId) {
+ bytes32 permissionId = keccak256(abi.encodePacked(mechanicVectorId, vectorLevelMinter));
+ if (!_vectorLevelMinters[permissionId]) {
+ _revert(InvalidMinter.selector);
+ } else {
+ _vectorLevelMinters[permissionId] = false;
+ }
+
+ emit VectorLevelMinterRemoved(mechanicVectorId, vectorLevelMinter, msg.sender);
+ }
+
+ /**
+ * @notice Return vector data
+ * @param mechanicVectorId Global mechanic vector ID
+ */
+ function getVectorData(bytes32 mechanicVectorId) external view returns (VerisartVector memory) {
+ return _vectors[mechanicVectorId];
+ }
+
+ /**
+ * @notice Return vector supply
+ * @param mechanicVectorId Global mechanic vector ID
+ */
+ function getVectorSupply(bytes32 mechanicVectorId) external view returns (uint64) {
+ return _vectors[mechanicVectorId].supply;
+ }
+
+ /**
+ * @notice Return vector size
+ * @param mechanicVectorId Global mechanic vector ID
+ */
+ function getVectorSize(bytes32 mechanicVectorId) external view returns (uint64) {
+ return _vectors[mechanicVectorId].size;
+ }
+
+ /**
+ * @notice Returns if signed minting is allowed for the vector
+ * @param mechanicVectorId ID of vector to check
+ */
+ function signedMintingAllowed(bytes32 mechanicVectorId) external view returns (bool) {
+ return !_vectors[mechanicVectorId].signedMintingDisabled;
+ }
+
+ /**
+ * @notice Return global minters
+ */
+ function globalMinters() external view returns (address[] memory) {
+ return _globalMinters.values();
+ }
+
+ /**
+ * @notice Return admins
+ */
+ function admins() external view returns (address[] memory) {
+ return _admins.values();
+ }
+
+ /**
+ * @notice Compatible identifier
+ */
+ function minterType() external pure returns (string memory) {
+ return "VerisartHighlightIntegrationMechanic";
+ }
+
+ /**
+ * @notice Return if a minter is a global minter
+ * @param minter Minter to check
+ */
+ function isGlobalMinter(address minter) public view returns (bool) {
+ return _globalMinters.contains(minter);
+ }
+
+ /**
+ * @notice Return if minter is enabled as a vector-level minter for a given vector
+ * @param mechanicVectorId ID of vector
+ * @param minter Minter to check
+ */
+ function isVectorLevelMinter(bytes32 mechanicVectorId, address minter) public view returns (bool) {
+ return _vectorLevelMinters[keccak256(abi.encodePacked(mechanicVectorId, minter))];
+ }
+
+ /* solhint-disable no-empty-blocks */
+ /**
+ * @notice Limit upgrades of contract to VerisartMechanic owner
+ * @param // New implementation address
+ */
+ function _authorizeUpgrade(address) internal override onlyOwner {}
+
+ /**
+ * @notice Process Verisart mint
+ * @param mechanicVectorId Mechanic vector ID
+ * @param numToMint Number of tokens to mint
+ * @param recipient Mint recipient
+ * @param minter Original caller of mint on MintManager or signer of mint
+ * @param data Mint signature data (if applicable)
+ */
+ function _processMint(
+ bytes32 mechanicVectorId,
+ uint32 numToMint,
+ address recipient,
+ address minter,
+ bytes calldata data
+ ) private {
+ (bool isSignatureBased, address minterOrSigner) = _validateMinterOrSigner(
+ mechanicVectorId,
+ minter,
+ recipient,
+ data
+ );
+ // one slot, so load entirety into memory
+ VerisartVector memory _vector = _vectors[mechanicVectorId];
+
+ if (isSignatureBased && _vector.signedMintingDisabled) {
+ _revert(SignedMintingDisabled.selector);
+ }
+ uint48 newVectorSupply = _vector.supply + numToMint;
+ if (newVectorSupply > _vector.size && _vector.size != 0) {
+ _revert(VectorMintLimitExceeded.selector);
+ }
+ uint32 newUserSupply = _mintsPerRecipient[mechanicVectorId][recipient] + numToMint;
+ if (newUserSupply > _vector.maxClaimableByUser && _vector.maxClaimableByUser != 0) {
+ _revert(RecipientUserMintLimitExceeded.selector);
+ }
+
+ _vectors[mechanicVectorId].supply = newVectorSupply;
+ _mintsPerRecipient[mechanicVectorId][recipient] = newUserSupply;
+
+ emit VerisartMint(mechanicVectorId, minterOrSigner, isSignatureBased, recipient, numToMint);
+ }
+
+ /**
+ * @notice Recover address of signer and update relevant state
+ * @param args Input to signature
+ * @param claimNonce Claim identifier
+ * @param signature Signature
+ * @param mechanicVectorId ID of vector
+ */
+ function _checkSigned(
+ bytes memory args,
+ bytes32 claimNonce,
+ bytes memory signature,
+ bytes32 mechanicVectorId
+ ) private returns (address) {
+ bytes32 digest = keccak256(abi.encodePacked("\x19\x01", _getDomainSeperator(), keccak256(args)));
+ if (_signedMints[claimNonce]) {
+ _revert(ClaimUsed.selector);
+ }
+ _signedMints[claimNonce] = true;
+ return ECDSA.recover(digest, signature);
+ }
+
+ /**
+ * @notice Validate minter or signer
+ * @param mechanicVectorId Mechanic vector ID
+ * @param minter Original minting address
+ * @param recipient Mint recipient
+ * @param data Mint signature data (optional)
+ */
+ function _validateMinterOrSigner(
+ bytes32 mechanicVectorId,
+ address minter,
+ address recipient,
+ bytes calldata data
+ ) private returns (bool, address) {
+ bool isSignatureBased = false;
+ if (data.length > 0) {
+ isSignatureBased = true;
+ (bytes32 claimNonce, bytes memory signature) = abi.decode(data, (bytes32, bytes));
+
+ minter = _checkSigned(
+ abi.encode(_MINT_SIGNED_TYPEHASH, minter, recipient, mechanicVectorId, claimNonce),
+ claimNonce,
+ signature,
+ mechanicVectorId
+ );
+ }
+ if (!isGlobalMinter(minter) && !isVectorLevelMinter(mechanicVectorId, minter)) {
+ _revert(Unauthorized.selector);
+ }
+ return (isSignatureBased, minter);
+ }
+
+ /**
+ * @notice Return EIP712 domain seperator
+ */
+ function _getDomainSeperator() private view returns (bytes32) {
+ return
+ keccak256(
+ abi.encode(
+ _DOMAIN_TYPEHASH,
+ keccak256("Verisart"),
+ keccak256("1"),
+ block.chainid,
+ address(this),
+ 0xf84c063feaae44fa2f4a846cf2dadc08b50b6a5b0b04bed3d70ed9fa1a199edc // verisart salt
+ )
+ );
+ }
+}
diff --git a/contracts/mint/mechanics/interfaces/IManifold1155Burn.sol b/contracts/mint/mechanics/interfaces/IManifold1155Burn.sol
new file mode 100644
index 0000000..151d584
--- /dev/null
+++ b/contracts/mint/mechanics/interfaces/IManifold1155Burn.sol
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+/**
+ * @notice Interface to burn tokens on a Manifold 1155 Creator contract
+ */
+interface IManifold1155Burn {
+ function burn(address account, uint256[] memory tokenIds, uint256[] memory amounts) external;
+}
diff --git a/contracts/mint/mechanics/interfaces/IMechanic.sol b/contracts/mint/mechanics/interfaces/IMechanic.sol
index c9d1307..59698c9 100644
--- a/contracts/mint/mechanics/interfaces/IMechanic.sol
+++ b/contracts/mint/mechanics/interfaces/IMechanic.sol
@@ -50,4 +50,4 @@ interface IMechanic is IMechanicData {
MechanicVectorMetadata calldata mechanicVectorMetadata,
bytes calldata data
) external payable;
-}
\ No newline at end of file
+}
diff --git a/contracts/mint/mechanics/interfaces/IMechanicMintManagerView.sol b/contracts/mint/mechanics/interfaces/IMechanicMintManagerView.sol
index 5d0a23b..78eef8e 100644
--- a/contracts/mint/mechanics/interfaces/IMechanicMintManagerView.sol
+++ b/contracts/mint/mechanics/interfaces/IMechanicMintManagerView.sol
@@ -9,4 +9,10 @@ interface IMechanicMintManagerView is IMechanicData {
* @param mechanicVectorId Global mechanic vector ID
*/
function mechanicVectorMetadata(bytes32 mechanicVectorId) external view returns (MechanicVectorMetadata memory);
+
+ /**
+ * @notice Returns whether an address is a valid platform executor
+ * @param _executor Address to be checked
+ */
+ function isPlatformExecutor(address _executor) external view returns (bool);
}
diff --git a/contracts/mint/referrals/IReferralManagerView.sol b/contracts/mint/referrals/IReferralManagerView.sol
new file mode 100644
index 0000000..bc6124b
--- /dev/null
+++ b/contracts/mint/referrals/IReferralManagerView.sol
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+interface IReferralManagerView {
+ /**
+ * @notice Get referrer for a tx
+ */
+ function getCurrentReferrer(bytes32 vectorId) external view returns (address);
+}
diff --git a/contracts/mint/referrals/ReferralManager.sol b/contracts/mint/referrals/ReferralManager.sol
new file mode 100644
index 0000000..a0c02b7
--- /dev/null
+++ b/contracts/mint/referrals/ReferralManager.sol
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.10;
+
+import "./IReferralManagerView.sol";
+import "../MintManager.sol";
+
+contract ReferralManager is IReferralManagerView {
+ /**
+ * @notice Throw if referrer is passed in is tx sender
+ */
+ error InvalidReferrer_ReferralManager();
+
+ /**
+ * @notice Store the referrer for a tx
+ */
+ mapping(bytes32 => address) private _txReferrer;
+
+ /**
+ * @notice MintManager backup address
+ */
+ address private _backupMintManager;
+
+ /**
+ * @notice Initialize contract
+ */
+ constructor(address backupMintManager) {
+ _backupMintManager = backupMintManager;
+ }
+
+ /**
+ * @notice Mint via an abridged vector
+ * @param vectorId ID of vector
+ * @param numTokensToMint Number of tokens to mint
+ * @param mintRecipient Who to mint the NFT(s) to
+ * @param referrer Referrer
+ */
+ function vectorMint721WithReferral(
+ uint256 vectorId,
+ uint48 numTokensToMint,
+ address mintRecipient,
+ address referrer
+ ) external payable {
+ _txReferrer[_encodeCurrentTx(bytes32(vectorId))] = referrer;
+
+ MintManager(_mintManager()).vectorMint721{ value: msg.value }(vectorId, numTokensToMint, mintRecipient);
+ }
+
+ /**
+ * @notice Mint on a collection with sequentially minted token IDs with a valid claim
+ * @param claim Claim
+ * @param claimSignature Signed + encoded claim
+ * @param mintRecipient Who to mint the NFT(s) to.
+ * Can't mint to different recipient if tx isn't sent by claim.claimer.
+ * @param referrer Referrer
+ */
+ function gatedNumMint721WithReferral(
+ MintManager.Claim calldata claim,
+ bytes calldata claimSignature,
+ address mintRecipient,
+ bool isEditionBased,
+ address referrer
+ ) external payable {
+ _txReferrer[_encodeCurrentTx(claim.offchainVectorId)] = referrer;
+
+ MintManager(_mintManager()).gatedNumMint{ value: msg.value }(
+ claim,
+ claimSignature,
+ mintRecipient,
+ isEditionBased
+ );
+ }
+
+ /**
+ * @notice Mint on a Series with a valid claim where one can choose the tokens to mint
+ * @param claim Series Claim
+ * @param claimSignature Signed + encoded claim
+ * @param mintRecipient Who to mint the NFT(s) to.
+ * Can't mint to different recipient if tx isn't sent by claim.claimer.
+ * @param tokenIds IDs of NFTs to mint
+ * @param referrer Referrer
+ */
+ function gatedChooseMint721WithReferral(
+ MintManager.SeriesClaim calldata claim,
+ bytes calldata claimSignature,
+ address mintRecipient,
+ uint256[] calldata tokenIds,
+ address referrer
+ ) external payable {
+ _txReferrer[_encodeCurrentTx(claim.offchainVectorId)] = referrer;
+
+ MintManager(_mintManager()).gatedSeriesMintChooseToken{ value: msg.value }(
+ claim,
+ claimSignature,
+ mintRecipient,
+ tokenIds
+ );
+ }
+
+ /**
+ * @notice Get referrer for a tx
+ */
+ function getReferrer(bytes32 vectorId, address txSender, uint256 blockNumber) external view returns (address) {
+ return _txReferrer[_encodeTx(vectorId, txSender, blockNumber)];
+ }
+
+ /**
+ * @notice Get referrer for a tx
+ */
+ function getCurrentReferrer(bytes32 vectorId) external view returns (address) {
+ return _txReferrer[_encodeCurrentTx(vectorId)];
+ }
+
+ /**
+ * @notice Encode tx for referrer
+ */
+ function _encodeTx(bytes32 vectorId, address txSender, uint256 blockNumber) private view returns (bytes32) {
+ return keccak256(abi.encodePacked(vectorId, txSender, blockNumber));
+ }
+
+ /**
+ * @notice Encode tx for referrer
+ */
+ function _encodeCurrentTx(bytes32 vectorId) private view returns (bytes32) {
+ return _encodeTx(vectorId, tx.origin, block.number);
+ }
+
+ /* solhint-disable code-complexity */
+ /**
+ * @notice Get the MintManager address
+ */
+ function _mintManager() private view returns (address) {
+ if (block.chainid == 1) {
+ return 0x1bf979282181f2b7a640d17aB5D2e25125F2de5e;
+ } else if (block.chainid == 8453) {
+ return 0x8087039152c472Fa74F47398628fF002994056EA;
+ } else if (block.chainid == 10) {
+ return 0xFafd47bb399d570b5AC95694c5D2a1fb5EA030bB;
+ } else if (block.chainid == 7777777) {
+ return 0x3AD45858a983D193D98BD4e6C14852a4cADcDBeA;
+ } else if (block.chainid == 42161) {
+ return 0x41cbab1028984A34C1338F437C726de791695AE8;
+ } else if (block.chainid == 137) {
+ return 0xfbb65C52f439B762F712026CF6DD7D8E82F81eb9;
+ } else if (block.chainid == 84532) {
+ return 0x41cbab1028984A34C1338F437C726de791695AE8;
+ } else if (block.chainid == 11155111) {
+ return 0xd698911B1Bb2a9c849Bf5e2604aF110766f396b6;
+ } else {
+ return _backupMintManager;
+ }
+ }
+}
diff --git a/contracts/observability/GengineObservability.sol b/contracts/observability/GengineObservability.sol
new file mode 100644
index 0000000..0865c1e
--- /dev/null
+++ b/contracts/observability/GengineObservability.sol
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+import "./IGengineObservability.sol";
+import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
+import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
+
+/**
+ * @title Observability
+ * @author highlight.xyz
+ * @notice Highlight Observability
+ * @dev Singleton to coalesce select Highlight protocol events
+ */
+contract GengineObservability is IGengineObservability, UUPSUpgradeable, OwnableUpgradeable {
+ /**
+ * @notice Initialize implementation with initial owner
+ * @param _owner Initial owner
+ */
+ function initialize(address _owner) external initializer {
+ __Ownable_init();
+ _transferOwnership(_owner);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitContractMetadataSet}
+ */
+ function emitContractMetadataSet(
+ string calldata name,
+ string calldata symbol,
+ string calldata contractURI
+ ) external {
+ emit ContractMetadataSet(msg.sender, name, symbol, contractURI);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitLimitSupplySet}
+ */
+ function emitLimitSupplySet(uint256 newLimitSupply) external {
+ emit LimitSupplySet(msg.sender, newLimitSupply);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitBaseUriSet}
+ */
+ function emitBaseUriSet(string calldata newBaseUri) external {
+ emit BaseUriSet(msg.sender, newBaseUri);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitGenerativeSeriesDeployed}
+ */
+ function emitGenerativeSeriesDeployed(address contractAddress) external {
+ emit GenerativeSeriesDeployed(msg.sender, contractAddress);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitSeriesDeployed}
+ */
+ function emitSeriesDeployed(address contractAddress) external {
+ emit SeriesDeployed(msg.sender, contractAddress);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitTokenMint}
+ */
+ function emitTokenMint(address to, uint256 numMinted) external {
+ emit TokenMint(msg.sender, to, numMinted);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitTokenUpdated}
+ */
+ function emitTokenUpdated(address contractAddress, uint256 tokenId) external {
+ emit TokenUpdated(msg.sender, tokenId);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitTransfer}
+ */
+ function emitTransfer(address from, address to, uint256 tokenId) external {
+ emit Transfer(msg.sender, from, to, tokenId);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitCustomMintData}
+ */
+ function emitCustomMintData(address contractAddress, bytes calldata data) external {
+ emit CustomMintData(msg.sender, contractAddress, data);
+ }
+
+ /**
+ * @notice See {IGengineObservability-emitHighlightRegenerate}
+ */
+ function emitHighlightRegenerate(address collection, uint256 tokenId) external {
+ emit HighlightRegenerate(msg.sender, collection, tokenId);
+ }
+
+ /* solhint-disable no-empty-blocks */
+ /**
+ * @notice Limit upgrades of contract to owner
+ * @param // New implementation
+ */
+ function _authorizeUpgrade(address) internal override onlyOwner {}
+}
diff --git a/contracts/observability/IGengineObservability.sol b/contracts/observability/IGengineObservability.sol
new file mode 100644
index 0000000..6e991a4
--- /dev/null
+++ b/contracts/observability/IGengineObservability.sol
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+/**
+ * @title IGengineObservability
+ * @author highlight.xyz
+ * @notice Interface to interact with the Highlight Gengine observability singleton
+ * @dev Singleton to coalesce select Highlight Gengine protocol events
+ */
+interface IGengineObservability {
+ /**
+ * @notice Emitted when contract metadata is set
+ * @param contractAddress Initial contract that emitted event
+ * @param name New name
+ * @param symbol New symbol
+ * @param contractURI New contract uri
+ */
+ event ContractMetadataSet(address indexed contractAddress, string name, string symbol, string contractURI);
+
+ /**
+ * @notice Emitted when limit supply is set
+ * @param contractAddress Initial contract that emitted event
+ * @param newLimitSupply Limit supply to set
+ */
+ event LimitSupplySet(address indexed contractAddress, uint256 indexed newLimitSupply);
+
+ /**
+ * @notice Emits when a series collection has its base uri set
+ * @param contractAddress Contract with updated base uri
+ * @param newBaseUri New base uri
+ */
+ event BaseUriSet(address indexed contractAddress, string newBaseUri);
+
+ /**************************
+ Deployment events
+ **************************/
+
+ /**
+ * @notice Emitted when Generative Series contract is deployed
+ * @param deployer Contract deployer
+ * @param contractAddress Address of contract that was deployed
+ */
+ event GenerativeSeriesDeployed(address indexed deployer, address indexed contractAddress);
+
+ /**
+ * @notice Emitted when Series contract is deployed
+ * @param deployer Contract deployer
+ * @param contractAddress Address of contract that was deployed
+ */
+ event SeriesDeployed(address indexed deployer, address indexed contractAddress);
+
+ /**************************
+ ERC721 events
+ **************************/
+
+ /**
+ * @notice Emitted on a mint where a number of tokens are minted
+ * @param contractAddress Address of contract being minted on
+ * @param numMinted Number of tokens minted
+ */
+ event TokenMint(address indexed contractAddress, address indexed to, uint256 indexed numMinted);
+
+ /**
+ * @notice Emitted whenever the metadata for the token is updated
+ * @param contractAddress NFT contract token resides on
+ * @param tokenId Token being updated
+ */
+ event TokenUpdated(address indexed contractAddress, uint256 indexed tokenId);
+
+ /**
+ * @notice Emitted when `tokenId` token is transferred from `from` to `to` on contractAddress
+ * @param contractAddress NFT contract token resides on
+ * @param from Token sender
+ * @param to Token receiver
+ * @param tokenId Token being sent
+ */
+ event Transfer(address indexed contractAddress, address indexed from, address to, uint256 indexed tokenId);
+
+ /**
+ * @notice Emitted for the seed based data on mint
+ * @param sender contract emitting the event
+ * @param contractAddress NFT contract token resides on
+ * @param data custom mint data
+ */
+ event CustomMintData(address indexed sender, address indexed contractAddress, bytes data);
+
+ /**
+ * @notice Emitted to regenerate the generative art for a token
+ * @param sender contract emitting the event
+ * @param collection NFT contract token resides on
+ * @param tokenId Token ID
+ */
+ event HighlightRegenerate(address indexed sender, address indexed collection, uint256 indexed tokenId);
+
+ /**
+ * @notice Emit ContractMetadataSet
+ */
+ function emitContractMetadataSet(
+ string calldata name,
+ string calldata symbol,
+ string calldata contractURI
+ ) external;
+
+ /**
+ * @notice Emit LimitSupplySet
+ */
+ function emitLimitSupplySet(uint256 newLimitSupply) external;
+
+ /**
+ * @notice Emit BaseUriSet
+ */
+ function emitBaseUriSet(string calldata newBaseUri) external;
+
+ /**
+ * @notice Emit GenerativeSeriesDeployed
+ */
+ function emitGenerativeSeriesDeployed(address contractAddress) external;
+
+ /**
+ * @notice Emit SeriesDeployed
+ */
+ function emitSeriesDeployed(address contractAddress) external;
+
+ /**
+ * @notice Emit Token Mint
+ */
+ function emitTokenMint(address to, uint256 numMinted) external;
+
+ /**
+ * @notice Emit Token Updated
+ */
+ function emitTokenUpdated(address contractAddress, uint256 tokenId) external;
+
+ /**
+ * @notice Emit Transfer
+ */
+ function emitTransfer(address from, address to, uint256 tokenId) external;
+
+ /**
+ * @notice Emit Custom Mint Data
+ */
+ function emitCustomMintData(address contractAddress, bytes calldata data) external;
+
+ /**
+ * @notice Emit HighlightRegenerate
+ */
+ function emitHighlightRegenerate(address collection, uint256 tokenId) external;
+}
diff --git a/contracts/test/Manifold1155CreatorMock.sol b/contracts/test/Manifold1155CreatorMock.sol
new file mode 100644
index 0000000..9a3e5ce
--- /dev/null
+++ b/contracts/test/Manifold1155CreatorMock.sol
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.10;
+
+import "@manifoldxyz/creator-core-solidity/contracts/ERC1155Creator.sol";
+
+contract Manifold1155CreatorMock is ERC1155Creator {
+ constructor() ERC1155Creator("MyContract", "MC") {}
+
+ function contractType() external view returns (string memory) {
+ return "Manifold1155CreatorMock";
+ }
+}
diff --git a/contracts/tokenManager/FarcasterBoundTokenManager.sol b/contracts/tokenManager/FarcasterBoundTokenManager.sol
new file mode 100644
index 0000000..32b534e
--- /dev/null
+++ b/contracts/tokenManager/FarcasterBoundTokenManager.sol
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.10;
+
+import "../utils/Ownable.sol";
+import "./InterfaceSupportTokenManager.sol";
+import "./interfaces/IPostTransfer.sol";
+import "./interfaces/IFarcaster.sol";
+
+/**
+ * @author highlight.xyz
+ * @notice A basic token manager that prevents transfers to addresses without a Farcaster ID
+ */
+contract FarcasterBoundTokenManager is ITokenManager, IPostTransfer, InterfaceSupportTokenManager {
+ /**
+ * @notice See {ITokenManager-canUpdateMetadata}
+ */
+ function canUpdateMetadata(
+ address sender,
+ uint256 /* id */,
+ bytes calldata /* newTokenUri */
+ ) external view override returns (bool) {
+ return Ownable(msg.sender).owner() == sender;
+ }
+
+ /**
+ * @notice See {ITokenManager-canSwap}
+ */
+ function canSwap(
+ address sender,
+ uint256 /* id */,
+ address /* newTokenManager */
+ ) external view override returns (bool) {
+ return Ownable(msg.sender).owner() == sender;
+ }
+
+ /**
+ * @notice See {ITokenManager-canRemoveItself}
+ */
+ function canRemoveItself(address sender, uint256 /* id */) external view override returns (bool) {
+ return Ownable(msg.sender).owner() == sender;
+ }
+
+ /**
+ * @notice See {IPostTransfer-postSafeTransferFrom}
+ */
+ function postSafeTransferFrom(
+ address /* operator */,
+ address /* from */,
+ address to,
+ uint256 /* id */,
+ bytes memory /* data */
+ ) external view override {
+ if (IFarcaster(0x00000000Fc6c5F01Fc30151999387Bb99A9f489b).idOf(to) == 0) {
+ revert("Can only transfer to a Farcaster user");
+ }
+ }
+
+ /**
+ * @notice See {IPostTransfer-postTransferFrom}
+ */
+ function postTransferFrom(
+ address /* operator */,
+ address /* from */,
+ address to,
+ uint256 /* id */
+ ) external view override {
+ if (IFarcaster(0x00000000Fc6c5F01Fc30151999387Bb99A9f489b).idOf(to) == 0) {
+ revert("Can only transfer to a Farcaster user");
+ }
+ }
+
+ /**
+ * @notice See {IERC165-supportsInterface}.
+ */
+ function supportsInterface(
+ bytes4 interfaceId
+ ) public view virtual override(InterfaceSupportTokenManager) returns (bool) {
+ return
+ interfaceId == type(IPostTransfer).interfaceId ||
+ InterfaceSupportTokenManager.supportsInterface(interfaceId);
+ }
+}
diff --git a/contracts/tokenManager/interfaces/IFarcaster.sol b/contracts/tokenManager/interfaces/IFarcaster.sol
new file mode 100644
index 0000000..c5a3f69
--- /dev/null
+++ b/contracts/tokenManager/interfaces/IFarcaster.sol
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity 0.8.10;
+
+/**
+ * @author highlight.xyz
+ * @notice Interact with idOf on the farcaster id registry
+ */
+interface IFarcaster {
+ function idOf(address user) external view returns (uint256);
+}
diff --git a/contracts/utils/FullMath.sol b/contracts/utils/FullMath.sol
new file mode 100644
index 0000000..d152cdf
--- /dev/null
+++ b/contracts/utils/FullMath.sol
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.10;
+
+/* solhint-disable max-line-length */
+/// @title Contains 512-bit math functions
+/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
+/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
+library FullMath {
+ /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
+ /// @param a The multiplicand
+ /// @param b The multiplier
+ /// @param denominator The divisor
+ /// @return result The 256-bit result
+ /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
+ function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
+ unchecked {
+ // 512-bit multiply [prod1 prod0] = a * b
+ // Compute the product mod 2**256 and mod 2**256 - 1
+ // then use the Chinese Remainder Theorem to reconstruct
+ // the 512 bit result. The result is stored in two 256
+ // variables such that product = prod1 * 2**256 + prod0
+ uint256 prod0; // Least significant 256 bits of the product
+ uint256 prod1; // Most significant 256 bits of the product
+ assembly {
+ let mm := mulmod(a, b, not(0))
+ prod0 := mul(a, b)
+ prod1 := sub(sub(mm, prod0), lt(mm, prod0))
+ }
+
+ // Handle non-overflow cases, 256 by 256 division
+ if (prod1 == 0) {
+ require(denominator > 0);
+ assembly {
+ result := div(prod0, denominator)
+ }
+ return result;
+ }
+
+ // Make sure the result is less than 2**256.
+ // Also prevents denominator == 0
+ require(denominator > prod1);
+
+ ///////////////////////////////////////////////
+ // 512 by 256 division.
+ ///////////////////////////////////////////////
+
+ // Make division exact by subtracting the remainder from [prod1 prod0]
+ // Compute remainder using mulmod
+ uint256 remainder;
+ assembly {
+ remainder := mulmod(a, b, denominator)
+ }
+ // Subtract 256 bit number from 512 bit number
+ assembly {
+ prod1 := sub(prod1, gt(remainder, prod0))
+ prod0 := sub(prod0, remainder)
+ }
+
+ // Factor powers of two out of denominator
+ // Compute largest power of two divisor of denominator.
+ // Always >= 1.
+ uint256 twos = (0 - denominator) & denominator;
+ // Divide denominator by power of two
+ assembly {
+ denominator := div(denominator, twos)
+ }
+
+ // Divide [prod1 prod0] by the factors of two
+ assembly {
+ prod0 := div(prod0, twos)
+ }
+ // Shift in bits from prod1 into prod0. For this we need
+ // to flip `twos` such that it is 2**256 / twos.
+ // If twos is zero, then it becomes one
+ assembly {
+ twos := add(div(sub(0, twos), twos), 1)
+ }
+ prod0 |= prod1 * twos;
+
+ // Invert denominator mod 2**256
+ // Now that denominator is an odd number, it has an inverse
+ // modulo 2**256 such that denominator * inv = 1 mod 2**256.
+ // Compute the inverse by starting with a seed that is correct
+ // correct for four bits. That is, denominator * inv = 1 mod 2**4
+ uint256 inv = (3 * denominator) ^ 2;
+ // Now use Newton-Raphson iteration to improve the precision.
+ // Thanks to Hensel's lifting lemma, this also works in modular
+ // arithmetic, doubling the correct bits in each step.
+ inv *= 2 - denominator * inv; // inverse mod 2**8
+ inv *= 2 - denominator * inv; // inverse mod 2**16
+ inv *= 2 - denominator * inv; // inverse mod 2**32
+ inv *= 2 - denominator * inv; // inverse mod 2**64
+ inv *= 2 - denominator * inv; // inverse mod 2**128
+ inv *= 2 - denominator * inv; // inverse mod 2**256
+
+ // Because the division is now exact we can divide by multiplying
+ // with the modular inverse of denominator. This will give us the
+ // correct result modulo 2**256. Since the precoditions guarantee
+ // that the outcome is less than 2**256, this is the final result.
+ // We don't need to compute the high bits of the result and prod1
+ // is no longer required.
+ result = prod0 * inv;
+ return result;
+ }
+ }
+
+ /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
+ /// @param a The multiplicand
+ /// @param b The multiplier
+ /// @param denominator The divisor
+ /// @return result The 256-bit result
+ function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
+ unchecked {
+ result = mulDiv(a, b, denominator);
+ if (mulmod(a, b, denominator) > 0) {
+ require(result < type(uint256).max);
+ result++;
+ }
+ }
+ }
+}
diff --git a/contracts/utils/IUniswapV3PoolState.sol b/contracts/utils/IUniswapV3PoolState.sol
new file mode 100644
index 0000000..ab2f748
--- /dev/null
+++ b/contracts/utils/IUniswapV3PoolState.sol
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+pragma solidity 0.8.10;
+
+/* solhint-disable max-line-length */
+/// @title Pool state that can change
+/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
+/// per transaction
+interface IUniswapV3PoolState {
+ /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
+ /// when accessed externally.
+ /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
+ /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
+ /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
+ /// boundary.
+ /// observationIndex The index of the last oracle observation that was written,
+ /// observationCardinality The current maximum number of observations stored in the pool,
+ /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
+ /// feeProtocol The protocol fee for both tokens of the pool.
+ /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
+ /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
+ /// unlocked Whether the pool is currently locked to reentrancy
+ function slot0()
+ external
+ view
+ returns (
+ uint160 sqrtPriceX96,
+ int24 tick,
+ uint16 observationIndex,
+ uint16 observationCardinality,
+ uint16 observationCardinalityNext,
+ uint8 feeProtocol,
+ bool unlocked
+ );
+
+ /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
+ /// @dev This value can overflow the uint256
+ function feeGrowthGlobal0X128() external view returns (uint256);
+
+ /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
+ /// @dev This value can overflow the uint256
+ function feeGrowthGlobal1X128() external view returns (uint256);
+
+ /// @notice The amounts of token0 and token1 that are owed to the protocol
+ /// @dev Protocol fees will never exceed uint128 max in either token
+ function protocolFees() external view returns (uint128 token0, uint128 token1);
+
+ /// @notice The currently in range liquidity available to the pool
+ /// @dev This value has no relationship to the total liquidity across all ticks
+ function liquidity() external view returns (uint128);
+
+ /// @notice Look up information about a specific tick in the pool
+ /// @param tick The tick to look up
+ /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
+ /// tick upper,
+ /// liquidityNet how much liquidity changes when the pool price crosses the tick,
+ /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
+ /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
+ /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
+ /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
+ /// secondsOutside the seconds spent on the other side of the tick from the current tick,
+ /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
+ /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
+ /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
+ /// a specific position.
+ function ticks(
+ int24 tick
+ )
+ external
+ view
+ returns (
+ uint128 liquidityGross,
+ int128 liquidityNet,
+ uint256 feeGrowthOutside0X128,
+ uint256 feeGrowthOutside1X128,
+ int56 tickCumulativeOutside,
+ uint160 secondsPerLiquidityOutsideX128,
+ uint32 secondsOutside,
+ bool initialized
+ );
+
+ /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
+ function tickBitmap(int16 wordPosition) external view returns (uint256);
+
+ /// @notice Returns the information about a position by the position's key
+ /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
+ /// @return _liquidity The amount of liquidity in the position,
+ /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
+ /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
+ /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
+ /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
+ function positions(
+ bytes32 key
+ )
+ external
+ view
+ returns (
+ uint128 _liquidity,
+ uint256 feeGrowthInside0LastX128,
+ uint256 feeGrowthInside1LastX128,
+ uint128 tokensOwed0,
+ uint128 tokensOwed1
+ );
+
+ /// @notice Returns data about a specific observation index
+ /// @param index The element of the observations array to fetch
+ /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
+ /// ago, rather than at a specific index in the array.
+ /// @return blockTimestamp The timestamp of the observation,
+ /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
+ /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
+ /// Returns initialized whether the observation has been initialized and the values are safe to use
+ function observations(
+ uint256 index
+ )
+ external
+ view
+ returns (
+ uint32 blockTimestamp,
+ int56 tickCumulative,
+ uint160 secondsPerLiquidityCumulativeX128,
+ bool initialized
+ );
+}
diff --git a/hardhat.config.ts b/hardhat.config.ts
index c4e7244..dd64164 100644
--- a/hardhat.config.ts
+++ b/hardhat.config.ts
@@ -1,4 +1,5 @@
import "@nomicfoundation/hardhat-toolbox";
+import "@nomiclabs/hardhat-ethers";
import { config as dotenvConfig } from "dotenv";
import "hardhat-contract-sizer";
import "hardhat-gas-reporter";
@@ -26,12 +27,14 @@ export const chainIds = {
"polygon-mainnet": 137,
"polygon-mumbai": 80001,
goerli: 5,
+ sepolia: 11155111,
arbitrum: 42161,
"arbitrum-goerli": 421613,
optimism: 10,
"optimism-goerli": 420,
base: 8453,
"base-goerli": 84531,
+ "base-sepolia": 84532,
zora: 7777777,
"zora-goerli": 999,
};
@@ -46,21 +49,25 @@ function getChainConfig(chain: keyof typeof chainIds): NetworkUserConfig {
export function getUrl(chain: keyof typeof chainIds): string {
if (chain === "arbitrum") {
- return "https://arb-mainnet.g.alchemy.com/v2/6RXKTS3PtSM59L41inqVagpZW3-r_rq9";
+ return "https://arb1.arbitrum.io/rpc";
} else if (chain === "arbitrum-goerli") {
- return "https://arb-goerli.g.alchemy.com/v2/jK7-UD3iCOzaFUqa2L_SVI7fkdzCYfwc";
+ return "https://arbitrum-goerli-rpc.publicnode.com";
} else if (chain === "optimism") {
- return "https://opt-mainnet.g.alchemy.com/v2/XtgT_4vf4xad9To3EOhQpH_7i62hYhKD";
+ return "https://optimism.llamarpc.com";
} else if (chain === "optimism-goerli") {
- return "https://opt-goerli.g.alchemy.com/v2/COI6ezi-VSOBEQIMKbX5sImZ_mYy6urr";
+ return "https://optimism-goerli-rpc.publicnode.com";
} else if (chain === "base") {
- return "https://developer-access-mainnet.base.org";
+ return "https://mainnet.base.org";
} else if (chain === "base-goerli") {
return "https://base-goerli.public.blastapi.io";
} else if (chain === "zora") {
return "https://rpc.zora.co";
} else if (chain === "zora-goerli") {
return "https://testnet.rpc.zora.co";
+ } else if (chain === "polygon-mainnet") {
+ return "https://polygon-rpc.com/";
+ } else if (chain === "base-sepolia") {
+ return "https://base-sepolia.blockpi.network/v1/rpc/public ";
} else {
return "https://" + chain + ".infura.io/v3/" + infuraApiKey;
}
@@ -74,12 +81,14 @@ const config: HardhatUserConfig = {
polygon: process.env.POLYGONSCAN_API_KEY || "",
polygonMumbai: process.env.POLYGONSCAN_API_KEY || "",
goerli: process.env.ETHERSCAN_API_KEY || "",
+ sepolia: process.env.ETHERSCAN_API_KEY || "",
optimisticEthereum: process.env.OPTIMISMSCAN_API_KEY || "",
arbitrumOne: process.env.ARBITRUMSCAN_API_KEY || "",
"optimism-goerli": process.env.OPTIMISMSCAN_API_KEY || "",
"arbitrum-goerli": process.env.ARBITRUMSCAN_API_KEY || "",
base: process.env.BASESCAN_API_KEY || "",
"base-goerli": process.env.BASESCAN_API_KEY || "",
+ "base-sepolia": process.env.BASESCAN_API_KEY || "",
zora: process.env.ZORASCAN_API_KEY || "",
"zora-goerli": process.env.ZORASCAN_API_KEY || "",
},
@@ -100,6 +109,14 @@ const config: HardhatUserConfig = {
browserURL: "https://goerli.basescan.org",
},
},
+ {
+ network: "base-sepolia",
+ chainId: 84532,
+ urls: {
+ apiURL: "https://api-sepolia.basescan.org/api",
+ browserURL: "https://sepolia-explorer.base.org/",
+ },
+ },
{
network: "optimism-goerli",
chainId: 420,
@@ -151,6 +168,7 @@ const config: HardhatUserConfig = {
},
mainnet: getChainConfig("mainnet"),
goerli: getChainConfig("goerli"),
+ sepolia: getChainConfig("sepolia"),
"polygon-mainnet": getChainConfig("polygon-mainnet"),
"polygon-mumbai": getChainConfig("polygon-mumbai"),
arbitrum: getChainConfig("arbitrum"),
@@ -159,6 +177,7 @@ const config: HardhatUserConfig = {
"optimism-goerli": getChainConfig("optimism-goerli"),
base: getChainConfig("base"),
"base-goerli": getChainConfig("base-goerli"),
+ "base-sepolia": getChainConfig("base-sepolia"),
zora: getChainConfig("zora"),
"zora-goerli": getChainConfig("zora-goerli"),
},
@@ -187,7 +206,7 @@ const config: HardhatUserConfig = {
},
typechain: {
outDir: "types",
- target: "ethers-v5",
+ target: "ethers-v6",
},
contractSizer: {
runOnCompile: true,
diff --git a/package.json b/package.json
index 2251747..81a072f 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "evm-contracts",
- "version": "1.4.0",
- "description": "Highlight EVM Smart Contract Protocols",
+ "version": "1.5.0",
+ "description": "Highlight EVM Smart Contract Protocol",
"main": "index.js",
"repository": "git@github.com:highlightxyz/evm-contracts.git",
"author": "Ishan ",
@@ -36,7 +36,7 @@
"dotenv": "^16.0.1",
"eslint": "^8.20.0",
"eslint-config-prettier": "^8.5.0",
- "ethers": "^5.7.1",
+ "ethers": "^6.13.0",
"fs-extra": "^10.1.0",
"hardhat": "^2.10.1",
"hardhat-gas-reporter": "^1.0.8",
@@ -45,6 +45,7 @@
"lint-staged": "^13.0.3",
"lodash": "^4.17.21",
"mocha": "^10.0.0",
+ "mongodb": "^5.7.0",
"pinst": "^3.0.0",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-dev.23",
@@ -73,18 +74,25 @@
"prettier:fix": "yarn prettier:check -w",
"test": "yarn test:separate",
"test:individual": "AUTO_MINING_ON=true hardhat test",
- "test:separate": "yarn test:individual test/AuctionsTest.ts && yarn test:individual test/EditionsMetadataRendererTest.ts && yarn test:individual test/ERC721BaseTest.ts && yarn test:individual test/ERC721EditionsTest.ts && yarn test:individual test/ERC721GeneralTest.ts && yarn test:individual test/ERC721GeneralSequenceTest.ts && yarn test:individual test/ERC721GenerativeTest.ts && yarn test:individual test/ERC721SingleEditionTest.ts && yarn test:individual test/ERC721StandardTest.ts && yarn test:individual test/MetaTransactionsTest.ts && yarn test:individual test/MintManagerTest.ts && yarn test:individual test/MechanicMintVectorsTest.ts && yarn test:individual test/UpgradesTest.ts",
+ "test:separate": "yarn test:individual test/AuctionsTest.ts && yarn test:individual test/EditionsMetadataRendererTest.ts && yarn test:individual test/ERC721BaseTest.ts && yarn test:individual test/ERC721EditionsTest.ts && yarn test:individual test/ERC721GeneralTest.ts && yarn test:individual test/ERC721GeneralSequenceTest.ts && yarn test:individual test/ERC721GenerativeTest.ts && yarn test:individual test/ERC721SingleEditionTest.ts && yarn test:individual test/ERC721StandardTest.ts && yarn test:individual test/MetaTransactionsTest.ts && yarn test:individual test/MintManagerTest.ts && yarn test:individual test/MechanicMintVectorsTest.ts && yarn test:individual test/RankedAuctionsMechanicTest.ts",
"typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain",
"local": "hardhat node",
"local:ipv4": "hardhat node --hostname 127.0.0.1",
"task:local": "hardhat --network localhost",
"build-contracts": "sol-merger \"./contracts/factory/*.sol\" ./build",
- "deploy:all:local": "hardhat deploy --initial-platform-executor-address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --mint-manager-owner 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --editions-metadata-owner 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --platform-payment-address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
+ "deploy:all:local": "hardhat deploy --initial-platform-executor-address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --mint-manager-owner 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --editions-metadata-owner 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --platform-payment-address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
+ "reformat:system-contracts-config": "node reformatSystemContractsConfig.js && yarn prettier"
},
"dependencies": {
+ "@chainlink/contracts": "^0.8.0",
+ "@manifoldxyz/creator-core-solidity": "^3.0.0",
+ "@nomicfoundation/hardhat-ethers": "^3.0.6",
"@openzeppelin/contracts-upgradeable": "^4.7.3",
+ "@typechain/ethers-v6": "^0.5.1",
+ "@uniswap/v3-core": "^1.0.1",
"axios": "^1.5.1",
"csv-parse": "^5.5.1",
+ "ethereum-multicall": "^2.24.0",
"hardhat-contract-sizer": "^2.6.1",
"keccak256": "^1.0.6",
"merkletreejs": "^0.2.32",
diff --git a/protocol-addresses.json b/protocol-addresses.json
index a63fb05..ef8b873 100644
--- a/protocol-addresses.json
+++ b/protocol-addresses.json
@@ -6,15 +6,22 @@
"AuctionManager": "0x3216FB0105f64cC375E2f431d1a6D00A1A955559",
"ERC721EditionsImplementation": "0x91cDE68af933688116337EEBD7d11e8d63AAA76E",
"ERC721SingleEditionImplementation": "0x59cC2a7D1Bf61256EbE39F1e9F2497e95317Ea2D",
- "ERC721EditionsDFSImplementation": "0x006cdD31f45F7e544a874B28763E1825C81128d5",
+ "ERC721EditionsDFSImplementation": "0xbAF6c8B147Bc50F265E7C673Df21d53B0A9325be",
"ERC721SingleEditionDFSImplementation": "0xD09F64dbbCAa076Fde30Ddd2e23194f5F786665E",
"ERC721GeneralImplementation": "0xAAa81ce4795001654Dc56577ed431950D633dABA",
- "ERC721GeneralSequenceImplementation": "0x8d67B6ACE3fC4d90b8c276e1d70646ec705b0C9b",
- "ERC721GenerativeImplementation": "0x2Be3CE514884dcF92505a9FDaBDe6541779C129b",
+ "ERC721GeneralSequenceImplementation": "0x611c4E7cde9eb10019BEa9a7725409Ca154Fd1e6",
+ "ERC721GenerativeImplementation": "0x68bB0F207F0184bf754C141d56939251BbB38Be7",
"MinimalForwarder": "0x7Ab179690168f06D4F897A6C0b749C1524F4C772",
"Observability": "0xD21cf74A08CEb52555702658d3556300B0983158",
"FileDeployer": "0xd687847559A3bEc088251f3cC33E7BAf31e4aB48",
- "DiscreteDutchAuctionMechanic": "0x94fa6e7fc2555ada63ea56cfff425558360f0074"
+ "DiscreteDutchAuctionMechanic": "0x94Fa6e7Fc2555aDA63eA56cfFF425558360F0074",
+ "GengineObservability": "0xf40cd0797c7a2ace16fB680B0556eED2b691cDAD",
+ "VerisartMechanic": "0xe66CE7640A5Ecf430F7e02d17fC6F224bE9afF66",
+ "MintFeeOracle": "0x31BFD198BC59EC2379A5Fb57F26b5Ef3AD126848",
+ "SeedBasedMintMechanic": "0x922E9f8cc491fACBd403afa143AA53ee9146474C",
+ "RandomRankedAuctionMechanic": "0xF01E8a33E3a69799401c181a7b3D2c7F28485EC9",
+ "RankedAuctionMechanic": "0xDFEe0Ed4A217F37b3FA87624eE00fe5685bDc509",
+ "ReferralManager": "0xD3C63951b2Ed18e8d92B5b251C3B636A45A547d0"
},
"goerli": {
"MintManager": "0xBF6B4F9Ef1E4B371c40701b5f856F9Fc1d659c70",
@@ -40,7 +47,7 @@
"AuctionManager": "0x3AD45858a983D193D98BD4e6C14852a4cADcDBeA",
"ERC721EditionsImplementation": "0x23E4ffb289f7696b9957De566A06cF9B325d9bCA",
"ERC721SingleEditionImplementation": "0xcCC80ea84E3e6Ee8CaAB489092d46bb912b493AD",
- "ERC721EditionsDFSImplementation": "0xecA1aAfE5437B3a231B9E450c47Ffa8De8575a03",
+ "ERC721EditionsDFSImplementation": "0x47f690ad0168B6E3476B59A15a5dFe7adb8E34bc",
"ERC721SingleEditionDFSImplementation": "0x9fA3eA2B36fed5803Ca743E09fEd3204E2B59866",
"ERC721GeneralImplementation": "0x51544960e278b38c13c29F2944C1C839fEfCE6E2",
"ERC721GeneralSequenceImplementation": "0xF4007F45DCd05BE758Fe9b26500B0010a07dB3cB",
@@ -48,7 +55,11 @@
"MinimalForwarder": "0xC5402e0BAF74c1042D72749cB8cA78c58dD93D6f",
"Observability": "0xAA45a6e4e1E6e43c14B366Dd0228874fb1DC0eF9",
"FileDeployer": "0x21c3a69EaD9b81863B83757ff2645803fF7c7690",
- "DiscreteDutchAuctionMechanic": "0x15753e20667961fB30d5aa92e2255B876568BE7e"
+ "DiscreteDutchAuctionMechanic": "0x15753e20667961fB30d5aa92e2255B876568BE7e",
+ "GengineObservability": "0x1f6D904685A84C3417Fe0192b53474320CaAca63",
+ "MintFeeOracle": "0xD09F64dbbCAa076Fde30Ddd2e23194f5F786665E",
+ "RankedAuctionMechanic": "0xb207774Ac4E32eCE47771e64BDE5ec3894C1De6b",
+ "ReferralManager": "0x9CF5B12D2e2a88083647Ff2Fe0610F818b28eC77"
},
"polygon-mainnet": {
"MintManager": "0xfbb65C52f439B762F712026CF6DD7D8E82F81eb9",
@@ -57,7 +68,7 @@
"AuctionManager": "0x3CEDCb3170489f2FB509DB23D8A864A55B45036F",
"ERC721EditionsImplementation": "0xF150CB22e56FDA37F3c51A6a35f0aC0fd771db2f",
"ERC721SingleEditionImplementation": "0x91cDE68af933688116337EEBD7d11e8d63AAA76E",
- "ERC721EditionsDFSImplementation": "0x939Fd86C2a0c58202d1F14F59Acd4466A85bC412",
+ "ERC721EditionsDFSImplementation": "0xe083c23Bd5d873B637fca3DF8A4b8d1D5e8f3a69",
"ERC721SingleEditionDFSImplementation": "0x6abC18F4e8c7D8980DdBb97FDE7d6521B394F16A",
"ERC721GeneralImplementation": "0x64b35B64DAB456c489124Dc07aA3eD100DdFeD7E",
"ERC721GeneralSequenceImplementation": "0xc27925863bF67384e16Dcb1225228c88d0F44A8f",
@@ -65,7 +76,11 @@
"MinimalForwarder": "0x03214f1434D84Dd58FcDFc339577c1B3a7Dd9BdE",
"Observability": "0x43Ef6CB43586B4B3ce0F4b728D4AE08dD30a0d1e",
"FileDeployer": "0x117542b736cB5314a59453081b66208863CC1Acc",
- "DiscreteDutchAuctionMechanic": "0xAE22Cd8052D64e7C2aF6B5E3045Fab0a86C8334C"
+ "DiscreteDutchAuctionMechanic": "0xAE22Cd8052D64e7C2aF6B5E3045Fab0a86C8334C",
+ "GengineObservability": "0x086984b6C8CBF78EFD3c16e28029C038e700debE",
+ "MintFeeOracle": "0x9B64EFf523abbb7c52C7da469569704565Bd6Aa7",
+ "RankedAuctionMechanic": "0x4CCB72E7E0Cd948aF50bC7Bf598Fc4E027b70f98",
+ "ReferralManager": "0x6fd07d4B5fd7093762Fb2f278769aa7e2511d45c"
},
"optimism-goerli": {
"MintManager": "0x41cbab1028984A34C1338F437C726de791695AE8",
@@ -108,7 +123,7 @@
"AuctionManager": "0x9AcDfE8020c3c191F7aA158e1c155F12e55c9717",
"ERC721EditionsImplementation": "0xa95DE682A887A7e7f781F7832CF52a3b59E336F6",
"ERC721SingleEditionImplementation": "0x778b5ef98f0C8803F6424bB07412489b2Fbd58B3",
- "ERC721EditionsDFSImplementation": "0x9a304EFD52C63F030f2910f484d517faA2444575",
+ "ERC721EditionsDFSImplementation": "0x6606cA22030010F6ADAF9334E9f39d2aa89aa523",
"ERC721SingleEditionDFSImplementation": "0x295D4e1472CdEe0bB3a2D03fF56dA5a2f8C81197",
"ERC721GeneralImplementation": "0x1eB81B6A226591DF4D3248B4f55456De357929e2",
"ERC721GeneralSequenceImplementation": "0xae7Fc5F056Ebd29FAdCC390e83EeDaeEEc8674E9",
@@ -116,7 +131,13 @@
"MinimalForwarder": "0xAB98CD0e04Bb1FCd6320611fCAD6a7e534d8B302",
"Observability": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1",
"FileDeployer": "0x799d1CC242637847756f0400d1F83FCF94Cb051e",
- "DiscreteDutchAuctionMechanic": "0xA748BE280C9a00edaF7d04076FE8A93c59e95B03"
+ "DiscreteDutchAuctionMechanic": "0xA748BE280C9a00edaF7d04076FE8A93c59e95B03",
+ "GengineObservability": "0xf410f38BCA0a6Db2Bb543D8980a7147CfAb1441b",
+ "MintFeeOracle": "0xF05887d5EC9d51Db7aE3117B6F1f1da5E9416A05",
+ "SeedBasedMintMechanic": "0xDB38e10f38722Ca91006c7eEcc4DA7a731027344",
+ "RandomRankedAuctionMechanic": "0xE7F8B25C6D14864Bba6f0D5cc34874a5626e523C",
+ "RankedAuctionMechanic": "0x922E9f8cc491fACBd403afa143AA53ee9146474C",
+ "ReferralManager": "0xd9E58978808d17F99ccCEAb5195B052E972c0188"
},
"arbitrum": {
"MintManager": "0x41cbab1028984A34C1338F437C726de791695AE8",
@@ -125,7 +146,7 @@
"AuctionManager": "0x79307CeE06153CA7986759B0727023A2472F395B",
"ERC721EditionsImplementation": "0xF6c1093E467Ba60a41aBf901D875CDB027F924ac",
"ERC721SingleEditionImplementation": "0x20475183625aE0eD5Dcd2553a660B06FF52af8Bd",
- "ERC721EditionsDFSImplementation": "0x31C5C70330c9a1D3099d8f77381e82a218d5c71a",
+ "ERC721EditionsDFSImplementation": "0xa97425f8Ba83B01de22FE5d2F72dcA65A6c26DC5",
"ERC721SingleEditionDFSImplementation": "0xB0101CC0443768e5990Cfd9adC03313D283B1a7E",
"ERC721GeneralImplementation": "0x23E4ffb289f7696b9957De566A06cF9B325d9bCA",
"ERC721GeneralSequenceImplementation": "0xcCC80ea84E3e6Ee8CaAB489092d46bb912b493AD",
@@ -133,7 +154,11 @@
"MinimalForwarder": "0x3AD45858a983D193D98BD4e6C14852a4cADcDBeA",
"Observability": "0xF18660E9E7c1B6015c0f491F4b5602fB3a626Caa",
"FileDeployer": "0x4c3896dd0b55B3B62D560620B1D8bF99643fFCCE",
- "DiscreteDutchAuctionMechanic": "0x3a2aFe86E594540cbf3eA345dd29e09228f186D2"
+ "DiscreteDutchAuctionMechanic": "0x3a2aFe86E594540cbf3eA345dd29e09228f186D2",
+ "GengineObservability": "0x5c70CeB3fFEa9CfA0230511295f5A377e59E15C7",
+ "MintFeeOracle": "0x21372c2f48A06bEAf7833371Fc2FDD99D6D2E563",
+ "RankedAuctionMechanic": "0x7f75358787f880506c5dc6100386F77be8DE0A30",
+ "ReferralManager": "0x617b2383D93909590fAC0b2aaa547EC5615d82eF"
},
"polygon-mumbai": {
"MintManager": "0x2C92212426Ea6E41C894F8db3bEb1E6f4991c75c",
@@ -169,6 +194,29 @@
"FileDeployer": "0xB644D70A52b4e555815EF3Ec76488dbdA9DF972D",
"DiscreteDutchAuctionMechanic": "0x887A07d968b9b515E85a428c287397F4488005EE"
},
+ "base-sepolia": {
+ "MintManager": "0x41cbab1028984A34C1338F437C726de791695AE8",
+ "EditionsMetadataRenderer": "0x0266115EBa50E6EE69C067C0D6c5d542E9b40Bd5",
+ "NonTransferableTokenManager": "0xE019FF8033d9C761985A3EE1fa5d97Cc9Cf6d5c0",
+ "AuctionManager": "0x79307CeE06153CA7986759B0727023A2472F395B",
+ "ERC721EditionsImplementation": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1",
+ "ERC721SingleEditionImplementation": "0xAA45a6e4e1E6e43c14B366Dd0228874fb1DC0eF9",
+ "ERC721EditionsDFSImplementation": "0x5D1C39936B64bd127905D7A55e72547b87769aaB",
+ "ERC721SingleEditionDFSImplementation": "0x526fe4Ed6f23f34a97015E41f469fD54f37036f5",
+ "ERC721GeneralImplementation": "0xa1Cef877695E24DF6643f5B6B47Eb6fCeF214A38",
+ "ERC721GeneralSequenceImplementation": "0xF6C67C7bb7018E4609d571023196A4682FdA6F2f",
+ "ERC721GenerativeImplementation": "0x21fed85E54507164FD6c9Eb76870AFF41098106b",
+ "MinimalForwarder": "0x3AD45858a983D193D98BD4e6C14852a4cADcDBeA",
+ "Observability": "0xF18660E9E7c1B6015c0f491F4b5602fB3a626Caa",
+ "FileDeployer": "0x8c23711a0536397C261Bf83Ec474B9aAf05C549B",
+ "DiscreteDutchAuctionMechanic": "0x4821B6e9aC0CCC590acCe2442bb6BB32388C1CB7",
+ "GengineObservability": "0xceBc3B3134FbEF95ED13AEcdF997D4371d022385",
+ "MintFeeOracle": "0x23E4ffb289f7696b9957De566A06cF9B325d9bCA",
+ "SeedBasedMintMechanic": "0x7ac05e1b7dF40EE74D6adeA47bB169923e237e8e",
+ "RandomRankedAuctionMechanic": "0x66cCdD047d23d17887331BAf500FccDFAc1EB8b9",
+ "RankedAuctionMechanic": "0x9958F83F383CA150BB2252B4275D3e3051be469F",
+ "ReferralManager": "0x4619b9673241eB41B642Dc04371100d238b73fFE"
+ },
"arbitrum-goerli": {
"MintManager": "0xd698911B1Bb2a9c849Bf5e2604aF110766f396b6",
"EditionsMetadataRenderer": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1",
@@ -193,7 +241,7 @@
"AuctionManager": "0x41cbab1028984A34C1338F437C726de791695AE8",
"ERC721EditionsImplementation": "0x4AFa58b8c2Dfe756e851d9073aeA95467fc1BBf5",
"ERC721SingleEditionImplementation": "0xFAd107F688301db69e99693e00D1D891c44a0913",
- "ERC721EditionsDFSImplementation": "0x68bB0F207F0184bf754C141d56939251BbB38Be7",
+ "ERC721EditionsDFSImplementation": "0x9b334866A4f18626460809dF5ec1b9103114edCc",
"ERC721SingleEditionDFSImplementation": "0x799d1CC242637847756f0400d1F83FCF94Cb051e",
"ERC721GeneralImplementation": "0xbE5AdDc34D89E12572C80C5f672E17C6b6e7c988",
"ERC721GeneralSequenceImplementation": "0x4619b9673241eB41B642Dc04371100d238b73fFE",
@@ -201,6 +249,33 @@
"MinimalForwarder": "0xFafd47bb399d570b5AC95694c5D2a1fb5EA030bB",
"Observability": "0x21fed85E54507164FD6c9Eb76870AFF41098106b",
"FileDeployer": "0xB627f0469683f68aC78E1deD4eFA8545aa4c4DE3",
- "DiscreteDutchAuctionMechanic": "0xf12A4018647DD2275072967Fd5F3ac5Fef7a0471"
+ "DiscreteDutchAuctionMechanic": "0xf12A4018647DD2275072967Fd5F3ac5Fef7a0471",
+ "GengineObservability": "0x44E15126aECC211dE5601d9403Ec318840Cc05f6",
+ "MintFeeOracle": "0x7E17C071c6171E4B33cad3415e4a51E2Ca6C5cD7",
+ "RankedAuctionMechanic": "0x0AFB6566C836D1C4788cD2b54Bd9cA0158CC2D3D",
+ "ReferralManager": "0x7Cb2cecFCFFdccE0bf69366e52caec6BD719CD44"
+ },
+ "sepolia": {
+ "MintManager": "0xd698911B1Bb2a9c849Bf5e2604aF110766f396b6",
+ "EditionsMetadataRenderer": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1",
+ "NonTransferableTokenManager": "0xF18660E9E7c1B6015c0f491F4b5602fB3a626Caa",
+ "AuctionManager": "0x970a9F248Fc6AE03BB255E8863Cd6fc36E631e5d",
+ "ERC721EditionsImplementation": "0x4821B6e9aC0CCC590acCe2442bb6BB32388C1CB7",
+ "ERC721SingleEditionImplementation": "0xa1Cef877695E24DF6643f5B6B47Eb6fCeF214A38",
+ "ERC721EditionsDFSImplementation": "0x6d5365f07Dc1F99Ebed1F99ecA99E41b569DdaF8",
+ "ERC721SingleEditionDFSImplementation": "0xd8f0A3AA4067be3D70a5B46A795Ad9dF9E65Cd3C",
+ "ERC721GeneralImplementation": "0x21fed85E54507164FD6c9Eb76870AFF41098106b",
+ "ERC721GeneralSequenceImplementation": "0xbc0eec8ab1d314293eFbb0A58b149Ef209aF610d",
+ "ERC721GenerativeImplementation": "0xE019FF8033d9C761985A3EE1fa5d97Cc9Cf6d5c0",
+ "MinimalForwarder": "0xa594011DB733d09C1EEB347fb2f7dFc99d118ba1",
+ "Observability": "0x526fe4Ed6f23f34a97015E41f469fD54f37036f5",
+ "FileDeployer": "0x9c602CE508E41ccAF2cF997D93A9FbE0166D8aE6",
+ "DiscreteDutchAuctionMechanic": "0xceBc3B3134FbEF95ED13AEcdF997D4371d022385",
+ "GengineObservability": "0xaF4d61951A425BA60ac1E7EA6d51e92d2F4748E4",
+ "VerisartMechanic": "0x642fEd40AeD8e7A7CF7c4CFf77F2529a4348ccC3",
+ "MintFeeOracle": "0x17241166279FD953AA4020e1580A57caCfF5f94F",
+ "SeedBasedMintMechanic": "0xaF2B525fFd402cA926969D730488863dec5cB8d6",
+ "RankedAuctionMechanic": "0xa2D14CA9985De170db128c8CB74Cecb35eEAF47E",
+ "ReferralManager": "0xd33c1bE264bb98F86e18CD816D5fd44e97cb7163"
}
}
diff --git a/systemContractsConfig.json b/systemContractsConfig.json
index 1f8493b..55794ed 100644
--- a/systemContractsConfig.json
+++ b/systemContractsConfig.json
@@ -1,6 +1,7 @@
{
"__chainIdToNetworkName": {
"5": "goerli",
+ "11155111": "sepolia",
"1337": "localhost",
"80001": "polygon-mumbai",
"137": "polygon-mainnet",
@@ -14,6 +15,7 @@
"420": "optimism-goerli",
"8453": "base",
"84531": "base-goerli",
+ "84532": "base-sepolia",
"7777777": "zora",
"999": "zora-goerli"
},
@@ -26,6 +28,7 @@
},
"MintManager": {
"5": "0xBF6B4F9Ef1E4B371c40701b5f856F9Fc1d659c70",
+ "11155111": "0xd698911B1Bb2a9c849Bf5e2604aF110766f396b6",
"1337": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
"80001": "0x2C92212426Ea6E41C894F8db3bEb1E6f4991c75c",
"137": "0xfbb65C52f439B762F712026CF6DD7D8E82F81eb9",
@@ -36,12 +39,14 @@
"10": "0xFafd47bb399d570b5AC95694c5D2a1fb5EA030bB",
"420": "0x41cbab1028984A34C1338F437C726de791695AE8",
"84531": "0xa1Cef877695E24DF6643f5B6B47Eb6fCeF214A38",
+ "84532": "0x41cbab1028984A34C1338F437C726de791695AE8",
"7777777": "0x3AD45858a983D193D98BD4e6C14852a4cADcDBeA",
"999": "0x9AcDfE8020c3c191F7aA158e1c155F12e55c9717",
"8453": "0x8087039152c472Fa74F47398628fF002994056EA"
},
"EditionsMetadataRenderer": {
"5": "0xeA2fC80731D96D57Cf80E8643f7DF35C055A0e85",
+ "11155111": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1",
"1337": "0x0165878A594ca255338adfa4d48449f69242Eb8F",
"80001": "0x77834F55641F13e03951088ADFb54a377A873308",
"137": "0x32c56B178cAA486177ee9A942611A2f8844872f5",
@@ -52,12 +57,14 @@
"10": "0x41cbab1028984A34C1338F437C726de791695AE8",
"420": "0x0266115EBa50E6EE69C067C0D6c5d542E9b40Bd5",
"84531": "0x526fe4Ed6f23f34a97015E41f469fD54f37036f5",
+ "84532": "0x0266115EBa50E6EE69C067C0D6c5d542E9b40Bd5",
"7777777": "0x79307CeE06153CA7986759B0727023A2472F395B",
"999": "0xd698911B1Bb2a9c849Bf5e2604aF110766f396b6",
"8453": "0xa594011DB733d09C1EEB347fb2f7dFc99d118ba1"
},
"NonTransferableTokenManager": {
"5": "0x2ac592Eb32fe61EFFd57Ac0A681Fe24E87e3069c",
+ "11155111": "0xF18660E9E7c1B6015c0f491F4b5602fB3a626Caa",
"1337": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
"80001": "0x3861f9F623611C27b2614873BBB74635c21ffaA7",
"137": "0x481f9289257795bbC5Cc9bab8c986D3377450331",
@@ -68,12 +75,14 @@
"10": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1",
"420": "0xE019FF8033d9C761985A3EE1fa5d97Cc9Cf6d5c0",
"84531": "0x4821B6e9aC0CCC590acCe2442bb6BB32388C1CB7",
+ "84532": "0xE019FF8033d9C761985A3EE1fa5d97Cc9Cf6d5c0",
"7777777": "0xa1Cef877695E24DF6643f5B6B47Eb6fCeF214A38",
"999": "0xAA45a6e4e1E6e43c14B366Dd0228874fb1DC0eF9",
"8453": "0x0266115EBa50E6EE69C067C0D6c5d542E9b40Bd5"
},
"AuctionManager": {
"5": "0xa94310AeeD50687f7c39ACdAA5FCd311AEDB25f8",
+ "11155111": "0x970a9F248Fc6AE03BB255E8863Cd6fc36E631e5d",
"1337": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9",
"80001": "0xF9FEf499aDF4550FA87C63E1111C8a0531DF45a1",
"137": "0x3CEDCb3170489f2FB509DB23D8A864A55B45036F",
@@ -84,6 +93,7 @@
"10": "0x3AD45858a983D193D98BD4e6C14852a4cADcDBeA",
"420": "0x79307CeE06153CA7986759B0727023A2472F395B",
"84531": "0xE019FF8033d9C761985A3EE1fa5d97Cc9Cf6d5c0",
+ "84532": "0x79307CeE06153CA7986759B0727023A2472F395B",
"7777777": "0x41cbab1028984A34C1338F437C726de791695AE8",
"999": "0xa594011DB733d09C1EEB347fb2f7dFc99d118ba1",
"8453": "0x9AcDfE8020c3c191F7aA158e1c155F12e55c9717"
@@ -93,6 +103,7 @@
},
"ERC721EditionsImplementation": {
"5": "0x703Fd59DEee1727eaf7751EDe79ec22c3F7Db07B",
+ "11155111": "0x4821B6e9aC0CCC590acCe2442bb6BB32388C1CB7",
"1337": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853",
"80001": "0x248AE3998B98D9eb046205f18c9B9210fFECFE2a",
"137": "0xF150CB22e56FDA37F3c51A6a35f0aC0fd771db2f",
@@ -103,12 +114,14 @@
"10": "0x23E4ffb289f7696b9957De566A06cF9B325d9bCA",
"420": "0xB2416393Ce488DA1EA2Ac86ab0e87a2Cf5d7a44F",
"84531": "0x1800E1Db8513Bc6c96E38D9DB840cDFcAb8f9944",
+ "84532": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1",
"7777777": "0x4AFa58b8c2Dfe756e851d9073aeA95467fc1BBf5",
"999": "0x954386A2b103A8AD2B933E44Ea148036f73DC4B9",
"8453": "0xa95DE682A887A7e7f781F7832CF52a3b59E336F6"
},
"ERC721SingleEditionImplementation": {
"5": "0x8B7E1DC485e931F4a15392a0E2DC0D61A16A68aB",
+ "11155111": "0xa1Cef877695E24DF6643f5B6B47Eb6fCeF214A38",
"1337": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6",
"80001": "0x479Cc569416E8934403E12Ec56475Ad6f8aBa3a4",
"137": "0x91cDE68af933688116337EEBD7d11e8d63AAA76E",
@@ -119,28 +132,32 @@
"10": "0xcCC80ea84E3e6Ee8CaAB489092d46bb912b493AD",
"420": "0xC0CEC6dd216C0388CD28DeC2F6FBe9aaFf749e9c",
"84531": "0xCbd8d75658f82c680727C36AF6c1c365B118938F",
+ "84532": "0xAA45a6e4e1E6e43c14B366Dd0228874fb1DC0eF9",
"7777777": "0xFAd107F688301db69e99693e00D1D891c44a0913",
"999": "0x473F9552a53595887074B8A8B798509e223B118E",
"8453": "0x778b5ef98f0C8803F6424bB07412489b2Fbd58B3"
},
"ERC721EditionsDFSImplementation": {
"5": "0x14d986A1743af0A53B7D60De5189B7fff3494AFa",
+ "11155111": "0x6d5365f07Dc1F99Ebed1F99ecA99E41b569DdaF8",
"1337": "0x9A676e781A523b5d0C0e43731313A708CB607508",
"80001": "0xEE5D605bE1aB67344C80F9Dc4836460f56614566",
- "137": "0x939Fd86C2a0c58202d1F14F59Acd4466A85bC412",
- "137-staging": "0x939Fd86C2a0c58202d1F14F59Acd4466A85bC412",
- "1": "0x006cdD31f45F7e544a874B28763E1825C81128d5",
- "42161": "0x31C5C70330c9a1D3099d8f77381e82a218d5c71a",
+ "137": "0xe083c23Bd5d873B637fca3DF8A4b8d1D5e8f3a69",
+ "137-staging": "0xe083c23Bd5d873B637fca3DF8A4b8d1D5e8f3a69",
+ "1": "0xbAF6c8B147Bc50F265E7C673Df21d53B0A9325be",
+ "42161": "0xa97425f8Ba83B01de22FE5d2F72dcA65A6c26DC5",
"421613": "0x32e187F0B32C9B8Cbc5980a16C5ED0EcD6f9d96E",
- "10": "0xecA1aAfE5437B3a231B9E450c47Ffa8De8575a03",
+ "10": "0x47f690ad0168B6E3476B59A15a5dFe7adb8E34bc",
"420": "0xB0101CC0443768e5990Cfd9adC03313D283B1a7E",
"84531": "0xdeAa8693C7085FaC16B20Cd5C69d84F7790926bf",
- "7777777": "0x68bB0F207F0184bf754C141d56939251BbB38Be7",
+ "84532": "0x5D1C39936B64bd127905D7A55e72547b87769aaB",
+ "7777777": "0x9b334866A4f18626460809dF5ec1b9103114edCc",
"999": "0x734ACE995eaE06cFCBfE6cc33e0F524ab27e4ac1",
- "8453": "0x9a304EFD52C63F030f2910f484d517faA2444575"
+ "8453": "0x6606cA22030010F6ADAF9334E9f39d2aa89aa523"
},
"ERC721SingleEditionDFSImplementation": {
"5": "0xAbAE4df16c1262F8465FCBDcD6E006a75Fb3b739",
+ "11155111": "0xd8f0A3AA4067be3D70a5B46A795Ad9dF9E65Cd3C",
"1337": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82",
"80001": "0xe4C6a0a3cFe2c004DDFC7eA12726bdE4C53A2784",
"137": "0x6abC18F4e8c7D8980DdBb97FDE7d6521B394F16A",
@@ -151,12 +168,14 @@
"10": "0x9fA3eA2B36fed5803Ca743E09fEd3204E2B59866",
"420": "0x621c7cE76Cde5761c7611721B770f347a0b6376E",
"84531": "0x23E4ffb289f7696b9957De566A06cF9B325d9bCA",
+ "84532": "0x526fe4Ed6f23f34a97015E41f469fD54f37036f5",
"7777777": "0x799d1CC242637847756f0400d1F83FCF94Cb051e",
"999": "0x701703EF716c4fe4086ef9a904683683d553e282",
"8453": "0x295D4e1472CdEe0bB3a2D03fF56dA5a2f8C81197"
},
"ERC721GeneralImplementation": {
"5": "0x667f810C960537A53532c00a0973205bE2fe2165",
+ "11155111": "0x21fed85E54507164FD6c9Eb76870AFF41098106b",
"1337": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318",
"80001": "0xFC954d004b8e4a6F82BEeE38a0C41A89Af3866cE",
"137": "0x64b35B64DAB456c489124Dc07aA3eD100DdFeD7E",
@@ -167,44 +186,50 @@
"10": "0x51544960e278b38c13c29F2944C1C839fEfCE6E2",
"420": "0xBe2099b6361e4551BDdF953011Ed1DD39CCfa2a1",
"84531": "0xa12f77d7b39a7b556Ba4BE6ec7328B0049288ac3",
+ "84532": "0xa1Cef877695E24DF6643f5B6B47Eb6fCeF214A38",
"7777777": "0xbE5AdDc34D89E12572C80C5f672E17C6b6e7c988",
"999": "0xf60cb5F236A344080Ca3bF50C5dC523309809F80",
"8453": "0x1eB81B6A226591DF4D3248B4f55456De357929e2"
},
"ERC721GeneralSequenceImplementation": {
"5": "0x96dB8495a5dEA40aDc1d4CFE45eB84F1c82d143B",
+ "11155111": "0xbc0eec8ab1d314293eFbb0A58b149Ef209aF610d",
"1337": "0x0B306BF915C4d645ff596e518fAf3F9669b97016",
"80001": "0x1333328f8b76a4d0a91a30bea67A9Cd6164A9b96",
"137": "0xc27925863bF67384e16Dcb1225228c88d0F44A8f",
"137-staging": "0xc27925863bF67384e16Dcb1225228c88d0F44A8f",
- "1": "0x8d67B6ACE3fC4d90b8c276e1d70646ec705b0C9b",
+ "1": "0x611c4E7cde9eb10019BEa9a7725409Ca154Fd1e6",
"42161": "0xcCC80ea84E3e6Ee8CaAB489092d46bb912b493AD",
"421613": "0xa12f77d7b39a7b556Ba4BE6ec7328B0049288ac3",
"10": "0xF4007F45DCd05BE758Fe9b26500B0010a07dB3cB",
"420": "0xe254901fC1F3ACd6E6AA409f95dB718235a015c8",
"84531": "0x4c3896dd0b55B3B62D560620B1D8bF99643fFCCE",
+ "84532": "0xF6C67C7bb7018E4609d571023196A4682FdA6F2f",
"7777777": "0x4619b9673241eB41B642Dc04371100d238b73fFE",
"999": "0x9491aA1c2f46319A645637c4105f4199B251e4dD",
"8453": "0xae7Fc5F056Ebd29FAdCC390e83EeDaeEEc8674E9"
},
"ERC721GenerativeImplementation": {
"5": "0x00Edfc8bE8897893786232e96367c8E040E2eb6D",
+ "11155111": "0xE019FF8033d9C761985A3EE1fa5d97Cc9Cf6d5c0",
"1337": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788",
"80001": "0xdc01D22327d142f45070Cb01f3f507878734A6f9",
"137": "0xa79dafa06bFF0765baa36C4f6731FdC755553887",
"137-staging": "0xa79dafa06bFF0765baa36C4f6731FdC755553887",
- "1": "0x2Be3CE514884dcF92505a9FDaBDe6541779C129b",
+ "1": "0x68bB0F207F0184bf754C141d56939251BbB38Be7",
"42161": "0xfF1C44BbE0943931E5E8962DAA0885a4f5Dd4fcd",
"421613": "0x90618E3338dd970ca634ac92dAa9E1DcF66B1c57",
"10": "0x1372557dF3Cc3F8616D416e52217c797Ae3eEdce",
"420": "0xab162414800fdf441B18F2f5af94334840b8f678",
"84531": "0xdD606eb8af309BD6e901b2d6E6dE2F233358b324",
+ "84532": "0x21fed85E54507164FD6c9Eb76870AFF41098106b",
"7777777": "0xcEC770BA360aDf184C961A3494521f1B5DCEa39C",
"999": "0x6e83e7ec8dBF2a21C6FE90d95E250158313FDcc3",
"8453": "0x08FD471a972Ad95FE2BF14d490EB2aaFE28f0aff"
},
"MinimalForwarder": {
"5": "0x4905B2ee259994F664d443e740bC2cA1d9cf2f1D",
+ "11155111": "0xa594011DB733d09C1EEB347fb2f7dFc99d118ba1",
"1337": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"80001": "0xD66A0f91BAFD0Fd6b7503ff97E028c9B54a7001f",
"137": "0x03214f1434D84Dd58FcDFc339577c1B3a7Dd9BdE",
@@ -215,12 +240,14 @@
"10": "0xC5402e0BAF74c1042D72749cB8cA78c58dD93D6f",
"420": "0x3AD45858a983D193D98BD4e6C14852a4cADcDBeA",
"84531": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1",
+ "84532": "0x3AD45858a983D193D98BD4e6C14852a4cADcDBeA",
"7777777": "0xFafd47bb399d570b5AC95694c5D2a1fb5EA030bB",
"999": "0x8087039152c472Fa74F47398628fF002994056EA",
"8453": "0xAB98CD0e04Bb1FCd6320611fCAD6a7e534d8B302"
},
"Observability": {
"5": "0xFfEc25843068E69CAA0E36eA004D7749bD9EfB19",
+ "11155111": "0x526fe4Ed6f23f34a97015E41f469fD54f37036f5",
"1337": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0",
"80001": "0x74A07B1F2B1d1Dec82341F18959cfb8B89353c87",
"137": "0x43Ef6CB43586B4B3ce0F4b728D4AE08dD30a0d1e",
@@ -231,12 +258,14 @@
"10": "0xAA45a6e4e1E6e43c14B366Dd0228874fb1DC0eF9",
"420": "0xF18660E9E7c1B6015c0f491F4b5602fB3a626Caa",
"84531": "0xe2CE42156E8456704fbEA047419404858E9324Af",
+ "84532": "0xF18660E9E7c1B6015c0f491F4b5602fB3a626Caa",
"7777777": "0x21fed85E54507164FD6c9Eb76870AFF41098106b",
"999": "0xa1Cef877695E24DF6643f5B6B47Eb6fCeF214A38",
"8453": "0x4e0AfBa59894060369881f4Bc9ba05731A4119f1"
},
"FileDeployer": {
"5": "0x29e3F4B932c1E0B989E3B6AbCf56Ae342c5AD65a",
+ "11155111": "0x9c602CE508E41ccAF2cF997D93A9FbE0166D8aE6",
"1337": "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1",
"80001": "0x513bd3bc623c42a807fBD162a58682941A12935F",
"137": "0x117542b736cB5314a59453081b66208863CC1Acc",
@@ -247,24 +276,85 @@
"10": "0x21c3a69EaD9b81863B83757ff2645803fF7c7690",
"420": "0x20475183625aE0eD5Dcd2553a660B06FF52af8Bd",
"84531": "0xB644D70A52b4e555815EF3Ec76488dbdA9DF972D",
+ "84532": "0x8c23711a0536397C261Bf83Ec474B9aAf05C549B",
"7777777": "0xB627f0469683f68aC78E1deD4eFA8545aa4c4DE3",
"999": "0xAFfC7C9BfB48FFD2a580e1a0d36f8cc7D45Dcb58",
"8453": "0x799d1CC242637847756f0400d1F83FCF94Cb051e"
},
"DiscreteDutchAuctionMechanic": {
"5": "0xae129080C7840538301550802cBc520c336CEEca",
+ "11155111": "0xceBc3B3134FbEF95ED13AEcdF997D4371d022385",
"1337": "0x68B1D87F95878fE05B998F19b66F4baba5De1aed",
"80001": "0xBf0ddCC1cC1635Ade2F99042771e7cD7a923a187",
"137": "0xAE22Cd8052D64e7C2aF6B5E3045Fab0a86C8334C",
"137-staging": "0xAE22Cd8052D64e7C2aF6B5E3045Fab0a86C8334C",
- "1": "0x94fa6e7fc2555ada63ea56cfff425558360f0074",
+ "1": "0x94Fa6e7Fc2555aDA63eA56cfFF425558360F0074",
"42161": "0x3a2aFe86E594540cbf3eA345dd29e09228f186D2",
"421613": "0x5437D752A878f6969bEd14fD733782BBD230489b",
"10": "0x15753e20667961fB30d5aa92e2255B876568BE7e",
"420": "0x5ae0bE472147dd425f73F5c10069043133401427",
"84531": "0x887A07d968b9b515E85a428c287397F4488005EE",
+ "84532": "0x4821B6e9aC0CCC590acCe2442bb6BB32388C1CB7",
"7777777": "0xf12A4018647DD2275072967Fd5F3ac5Fef7a0471",
"999": "0x778b5ef98f0C8803F6424bB07412489b2Fbd58B3",
"8453": "0xA748BE280C9a00edaF7d04076FE8A93c59e95B03"
+ },
+ "GengineObservability": {
+ "11155111": "0xaF4d61951A425BA60ac1E7EA6d51e92d2F4748E4",
+ "1337": "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d",
+ "137": "0x086984b6C8CBF78EFD3c16e28029C038e700debE",
+ "1": "0xf40cd0797c7a2ace16fB680B0556eED2b691cDAD",
+ "42161": "0x5c70CeB3fFEa9CfA0230511295f5A377e59E15C7",
+ "10": "0x1f6D904685A84C3417Fe0192b53474320CaAca63",
+ "84532": "0xceBc3B3134FbEF95ED13AEcdF997D4371d022385",
+ "7777777": "0x44E15126aECC211dE5601d9403Ec318840Cc05f6",
+ "8453": "0xf410f38BCA0a6Db2Bb543D8980a7147CfAb1441b"
+ },
+ "VerisartMechanic": {
+ "11155111": "0x642fEd40AeD8e7A7CF7c4CFf77F2529a4348ccC3",
+ "1": "0xe66CE7640A5Ecf430F7e02d17fC6F224bE9afF66"
+ },
+ "MintFeeOracle": {
+ "11155111": "0x17241166279FD953AA4020e1580A57caCfF5f94F",
+ "42161": "0x21372c2f48A06bEAf7833371Fc2FDD99D6D2E563",
+ "137": "0x9B64EFf523abbb7c52C7da469569704565Bd6Aa7",
+ "10": "0xD09F64dbbCAa076Fde30Ddd2e23194f5F786665E",
+ "7777777": "0x7E17C071c6171E4B33cad3415e4a51E2Ca6C5cD7",
+ "84532": "0x23E4ffb289f7696b9957De566A06cF9B325d9bCA",
+ "8453": "0xF05887d5EC9d51Db7aE3117B6F1f1da5E9416A05",
+ "1": "0x31BFD198BC59EC2379A5Fb57F26b5Ef3AD126848"
+ },
+ "SeedBasedMintMechanic": {
+ "11155111": "0xaF2B525fFd402cA926969D730488863dec5cB8d6",
+ "1": "0x922E9f8cc491fACBd403afa143AA53ee9146474C",
+ "8453": "0xDB38e10f38722Ca91006c7eEcc4DA7a731027344",
+ "84532": "0x7ac05e1b7dF40EE74D6adeA47bB169923e237e8e"
+ },
+ "RandomRankedAuctionMechanic": {
+ "84532": "0x66cCdD047d23d17887331BAf500FccDFAc1EB8b9",
+ "1": "0xF01E8a33E3a69799401c181a7b3D2c7F28485EC9",
+ "8453": "0xE7F8B25C6D14864Bba6f0D5cc34874a5626e523C"
+ },
+ "RankedAuctionMechanic": {
+ "11155111": "0xa2D14CA9985De170db128c8CB74Cecb35eEAF47E",
+ "1337": "",
+ "137": "0x4CCB72E7E0Cd948aF50bC7Bf598Fc4E027b70f98",
+ "1": "0xDFEe0Ed4A217F37b3FA87624eE00fe5685bDc509",
+ "42161": "0x7f75358787f880506c5dc6100386F77be8DE0A30",
+ "10": "0xb207774Ac4E32eCE47771e64BDE5ec3894C1De6b",
+ "84532": "0x9958F83F383CA150BB2252B4275D3e3051be469F",
+ "7777777": "0x0AFB6566C836D1C4788cD2b54Bd9cA0158CC2D3D",
+ "8453": "0x922E9f8cc491fACBd403afa143AA53ee9146474C"
+ },
+ "ReferralManager": {
+ "11155111": "0xd33c1bE264bb98F86e18CD816D5fd44e97cb7163",
+ "1337": "",
+ "137": "0x6fd07d4B5fd7093762Fb2f278769aa7e2511d45c",
+ "1": "0xD3C63951b2Ed18e8d92B5b251C3B636A45A547d0",
+ "42161": "0x617b2383D93909590fAC0b2aaa547EC5615d82eF",
+ "10": "0x9CF5B12D2e2a88083647Ff2Fe0610F818b28eC77",
+ "84532": "0x4619b9673241eB41B642Dc04371100d238b73fFE",
+ "7777777": "0x7Cb2cecFCFFdccE0bf69366e52caec6BD719CD44",
+ "8453": "0xd9E58978808d17F99ccCEAb5195B052E972c0188"
}
}
diff --git a/test/AuctionsTest.ts b/test/AuctionsTest.ts
deleted file mode 100644
index 976fe28..0000000
--- a/test/AuctionsTest.ts
+++ /dev/null
@@ -1,761 +0,0 @@
-import { parseEther } from "@ethersproject/units";
-import { time } from "@nomicfoundation/hardhat-network-helpers";
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ERC721Editions,
- EditionsMetadataRenderer,
- IAuctionManager,
- MinimalForwarder,
- MintManager,
- Observability,
-} from "../types";
-import { signGatedBid } from "./__utils__/auction";
-import { hourFromNow, setupEtherAuctionWithNewToken, setupSystem } from "./__utils__/helpers";
-
-describe("Auction Manager", () => {
- let initialPlatformExecutor: SignerWithAddress,
- additionalPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAccount: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- fan1: SignerWithAddress,
- randomEOA: SignerWithAddress;
-
- let auctionManager: AuctionManager;
- let mintManager: MintManager;
- let emr: EditionsMetadataRenderer;
- let minimalForwarder: MinimalForwarder;
- let observability: Observability;
-
- let editionsImplementation: string;
-
- before(async () => {
- [
- initialPlatformExecutor,
- additionalPlatformExecutor,
- mintManagerOwner,
- editionsMetadataOwner,
- platformPaymentAccount,
- editionsOwner,
- fan1,
- randomEOA,
- ] = await ethers.getSigners();
-
- const {
- mintManagerProxy,
- auctionManagerProxy,
- emrProxy,
- observability: observabilityInstance,
- minimalForwarder: minimalForwarderContract,
- editionsImplementationAddress,
- } = await setupSystem(
- platformPaymentAccount.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
- auctionManager = auctionManagerProxy;
- mintManager = mintManagerProxy;
- emr = emrProxy;
- observability = observabilityInstance;
- minimalForwarder = minimalForwarderContract;
- editionsImplementation = editionsImplementationAddress;
- });
-
- describe("Platform Executor", function () {
- before(async () => {
- auctionManager = auctionManager.connect(mintManagerOwner);
- });
- it("Should be able add a new platform executor as Owner", async () => {
- await expect(auctionManager.addPlatformExecutor(additionalPlatformExecutor.address)).to.emit(
- auctionManager,
- "PlatformExecutorChanged",
- );
- expect(await auctionManager.platformExecutors()).to.include(additionalPlatformExecutor.address);
- });
- it("Should be able deprecate platform executor as Owner", async () => {
- //deprecate platform executor
- await expect(auctionManager.deprecatePlatformExecutor(additionalPlatformExecutor.address)).to.emit(
- auctionManager,
- "PlatformExecutorChanged",
- );
- expect(await auctionManager.platformExecutors()).to.not.include(additionalPlatformExecutor.address);
- });
- it("Should not be able to add Zero address as platform executor", async () => {
- await expect(auctionManager.addPlatformExecutor(ethers.constants.AddressZero)).to.be.revertedWith(
- "Cannot set to null address",
- );
- expect(await auctionManager.platformExecutors()).to.not.include(ethers.constants.AddressZero);
- });
- it("Should not be able to add a platform executor that already exists", async () => {
- await expect(auctionManager.addPlatformExecutor(additionalPlatformExecutor.address)).to.emit(
- auctionManager,
- "PlatformExecutorChanged",
- );
- expect(await auctionManager.platformExecutors()).to.include(additionalPlatformExecutor.address);
- await expect(auctionManager.addPlatformExecutor(additionalPlatformExecutor.address)).to.be.revertedWith(
- "Already added",
- );
- });
- it("Should reject all platform executor changes from non owner", async () => {
- const auctionManagerForFan1 = await auctionManager.connect(fan1);
-
- //Add platform executor
- await expect(auctionManagerForFan1.addPlatformExecutor(mintManagerOwner.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- expect(await auctionManagerForFan1.platformExecutors()).to.not.include(mintManagerOwner.address);
-
- //deprecate platform executor
- await expect(auctionManagerForFan1.deprecatePlatformExecutor(initialPlatformExecutor.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- expect(await auctionManagerForFan1.platformExecutors()).to.include(initialPlatformExecutor.address);
- });
- });
-
- describe("Auction for new tokens", function () {
- let editions: ERC721Editions;
- let editions2: ERC721Editions;
- let editionsWithMarketplaceFilterer: ERC721Editions;
- let defaultAuction: IAuctionManager.EnglishAuctionStruct;
- const endTime1 = hourFromNow();
- const endTime2 = hourFromNow();
- const endTime3 = hourFromNow();
-
- before(async () => {
- auctionManager = auctionManager.connect(editionsOwner);
- editions = await setupEtherAuctionWithNewToken(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- emr.address,
- minimalForwarder.address,
- editionsOwner,
- "id1",
- endTime1,
- editionsOwner.address,
- );
- editions2 = await setupEtherAuctionWithNewToken(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- emr.address,
- minimalForwarder.address,
- editionsOwner,
- "id2",
- endTime2,
- editionsOwner.address,
- );
- editionsWithMarketplaceFilterer = await setupEtherAuctionWithNewToken(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- emr.address,
- minimalForwarder.address,
- editionsOwner,
- "id3",
- endTime3,
- editionsOwner.address,
- );
-
- const res = await auctionManager.getFullAuctionsData([
- ethers.utils.formatBytes32String("id1"),
- ethers.utils.formatBytes32String("id2"),
- ethers.utils.formatBytes32String("id3"),
- ]);
- expect(res[0][0][0]).to.equal(editions.address);
- expect(res[0][1][0]).to.equal(editions2.address);
- expect(res[0][2][0]).to.equal(editionsWithMarketplaceFilterer.address);
- expect(res[0][0][1]).to.equal(ethers.constants.AddressZero).to.equal(res[0][1][1]).to.equal(res[0][2][1]);
- expect(res[0][0][4].toNumber()).to.equal(endTime1);
- expect(res[0][1][4].toNumber()).to.equal(endTime2);
- expect(res[0][2][4].toNumber()).to.equal(endTime3);
-
- defaultAuction = {
- collection: editions.address,
- currency: ethers.constants.AddressZero,
- owner: editionsOwner.address,
- paymentRecipient: editionsOwner.address,
- endTime: hourFromNow() + hourFromNow(),
- tokenId: 0,
- mintWhenReserveMet: true,
- state: 0,
- };
- });
-
- // validate correct events being emitted in all of these
-
- it("Cannot create auction not as the collection owner", async function () {
- auctionManager = auctionManager.connect(fan1);
- await expect(
- auctionManager.createAuctionForExistingToken(ethers.utils.formatBytes32String("id4"), defaultAuction),
- ).to.be.revertedWith("Not collection owner or collection");
-
- await expect(
- auctionManager.createAuctionForNewToken(ethers.utils.formatBytes32String("id4"), defaultAuction),
- ).to.be.revertedWith("Not collection owner or collection");
- });
-
- it("Cannot create auction with already used auction id", async function () {
- auctionManager = auctionManager.connect(editionsOwner);
- await expect(
- auctionManager.createAuctionForNewToken(ethers.utils.formatBytes32String("id2"), defaultAuction),
- ).to.be.revertedWith("Auction id used");
- });
-
- it("Cannot bid with a non platform executor signer signing the claim", async function () {
- const { signature, claim } = await signGatedBid(editionsOwner, auctionManager, {
- auctionId: "id1",
- bidPrice: "1.0",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- let errorOnRead = false;
- try {
- await auctionManager.verifyClaim(claim, signature, editionsOwner.address);
- } catch (error) {
- errorOnRead = true;
- }
- expect(errorOnRead).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address)).to.be.revertedWith("Claim signer not executor");
- });
-
- it("Cannot bid on non-existent auction", async function () {
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id4",
- bidPrice: "1.0",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- let errorOnRead = false;
- try {
- await auctionManager.verifyClaim(claim, signature, editionsOwner.address);
- } catch (error) {
- errorOnRead = true;
- }
- expect(errorOnRead).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address)).to.be.revertedWith("Not live");
- });
-
- it("Cannot bid with an expired claim", async function () {
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id2",
- bidPrice: "1.0",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "1",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- let errorOnRead = false;
- try {
- await auctionManager.verifyClaim(claim, signature, editionsOwner.address);
- } catch (error) {
- errorOnRead = true;
- }
- expect(errorOnRead).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address)).to.be.revertedWith("Claim expired");
- });
-
- it("Cannot bid below reserve", async function () {
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id2",
- bidPrice: "0.4999",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- let errorOnRead = false;
- try {
- await auctionManager.verifyClaim(claim, signature, editionsOwner.address);
- } catch (error) {
- errorOnRead = true;
- }
- expect(errorOnRead).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address)).to.be.revertedWith("Reserve price not met");
- });
-
- it("Non auction owners cannot update auction", async function () {
- auctionManager = auctionManager.connect(fan1);
- await expect(
- auctionManager.updateEndTime(ethers.utils.formatBytes32String("id1"), hourFromNow()),
- ).to.be.revertedWith("Not auction owner");
-
- await expect(
- auctionManager.updatePaymentRecipient(ethers.utils.formatBytes32String("id1"), initialPlatformExecutor.address),
- ).to.be.revertedWith("Not auction owner");
- });
-
- it("Non auction owners cannot cancel auction on chain", async function () {
- auctionManager = auctionManager.connect(fan1);
- await expect(auctionManager.cancelAuctionOnChain(ethers.utils.formatBytes32String("id1"))).to.be.revertedWith(
- "Not auction owner",
- );
-
- await expect(auctionManager.cancelAuctionOnChain(ethers.utils.formatBytes32String("id1"))).to.be.revertedWith(
- "Not auction owner",
- );
- });
-
- it("Auction owners only can cancel auction on chain before minimum reserve bid is made", async function () {
- auctionManager = auctionManager.connect(editionsOwner);
- await expect(auctionManager.cancelAuctionOnChain(ethers.utils.formatBytes32String("id3")))
- .to.emit(auctionManager, "AuctionCanceledOnChain")
- .withArgs(
- ethers.utils.formatBytes32String("id3"),
- editionsOwner.address,
- editionsWithMarketplaceFilterer.address,
- 0,
- );
- });
-
- it("Cannot bid with invalid ether amount", async function () {
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id1",
- bidPrice: "1.0",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- await expect(auctionManager.bid(claim, signature, fan1.address)).to.be.revertedWith(
- "Invalid native gas token payment",
- );
-
- await expect(
- auctionManager.bid(claim, signature, fan1.address, { value: ethers.utils.parseEther("0.9") }),
- ).to.be.revertedWith("Invalid native gas token payment");
- });
-
- it("Can make valid first bid on auction", async function () {
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id1",
- bidPrice: "1.0",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- const claimVerified = await auctionManager.verifyClaim(claim, signature, editionsOwner.address);
- expect(claimVerified).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address, { value: ethers.utils.parseEther("1.0") }))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, auctionManager.address, 1)
- .to.emit(auctionManager, "Bid")
- .withArgs(
- ethers.utils.formatBytes32String("id1"),
- editionsOwner.address,
- true,
- editions.address,
- ethers.BigNumber.from(1),
- parseEther("1.0"),
- false,
- fan1.address,
- ethers.BigNumber.from(endTime1),
- );
-
- expect(await editionsOwner.provider?.getBalance(auctionManager.address)).to.equal(ethers.utils.parseEther("1.0"));
- expect(await editions.ownerOf(1)).to.equal(auctionManager.address);
- expect(await editions.tokenURI(1)).to.equal(
- "data:application/json;base64,eyJuYW1lIjogImR1bW15IiwgImRlc2NyaXB0aW9uIjogImRlc2NyaXB0aW9uIiwgImltYWdlIjogImltYWdlVXJsIiwgImFuaW1hdGlvbl91cmwiOiAiYW5pbWF0aW9uVXJsIiwgImV4dGVybmFsX3VybCI6ICJleHRlcm5hbFVybCIsICJhdHRyaWJ1dGVzIjogYXR0cmlidXRlc30=",
- );
- const res = await auctionManager.getFullAuctionData(ethers.utils.formatBytes32String("id1"));
- expect(res[0][0]).to.equal(editions.address);
- // highest bidder data
- expect(res[1][0]).to.equal(editionsOwner.address);
- expect(res[1][1]).to.equal(fan1.address);
- expect(res[1][2]).to.equal(ethers.utils.parseEther("1.0"));
- });
-
- it("Cannot update end time of auction after minimum reserve bid is made", async function () {
- auctionManager = auctionManager.connect(editionsOwner);
- await expect(auctionManager.updateEndTime(ethers.utils.formatBytes32String("id1"), endTime2)).to.be.revertedWith(
- "Can't update after first valid bid",
- );
- });
-
- it("Cannot update end time of non-live auction", async function () {
- auctionManager = auctionManager.connect(editionsOwner);
- await expect(auctionManager.updateEndTime(ethers.utils.formatBytes32String("id3"), endTime2)).to.be.revertedWith(
- "Not live",
- );
- });
-
- it("Cannot cancel auction after minimum reserve bid is made", async function () {
- auctionManager = auctionManager.connect(editionsOwner);
- await expect(auctionManager.cancelAuctionOnChain(ethers.utils.formatBytes32String("id1"))).to.be.revertedWith(
- "Reserve price met already",
- );
- });
-
- it("Cannot bid on cancelled auction", async function () {
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id3",
- bidPrice: "0.6",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- let errorOnRead = false;
- try {
- await auctionManager.verifyClaim(claim, signature, editionsOwner.address);
- } catch (error) {
- errorOnRead = true;
- }
- expect(errorOnRead).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address)).to.be.revertedWith("Not live");
- });
-
- it("Auction owners only can update an auction's payment recipient", async function () {
- auctionManager = auctionManager.connect(editionsOwner);
- await expect(auctionManager.updatePaymentRecipient(ethers.utils.formatBytes32String("id1"), randomEOA.address))
- .to.emit(auctionManager, "PaymentRecipientUpdated")
- .withArgs(ethers.utils.formatBytes32String("id1"), editionsOwner.address, randomEOA.address);
- });
-
- it("Cannot make bid with bid price not sufficiently higher than the previous one", async function () {
- const { signature: signature1, claim: claim1 } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id1",
- bidPrice: "1.04",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- let errorOnRead1 = false;
- try {
- await auctionManager.verifyClaim(claim1, signature1, editionsOwner.address);
- } catch (error) {
- errorOnRead1 = true;
- }
- expect(errorOnRead1).to.equal(true);
-
- await expect(auctionManager.bid(claim1, signature1, fan1.address)).to.be.revertedWith(
- "Bid not big enough of a jump",
- );
-
- const { signature: signature2, claim: claim2 } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id1",
- bidPrice: "1.059",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 600,
- claimer: editionsOwner.address,
- });
-
- let errorOnRead2 = false;
- try {
- await auctionManager.verifyClaim(claim2, signature2, editionsOwner.address);
- } catch (error) {
- errorOnRead2 = true;
- }
- expect(errorOnRead2).to.equal(true);
-
- await expect(auctionManager.bid(claim2, signature2, fan1.address)).to.be.revertedWith(
- "Bid not big enough of a jump",
- );
- });
-
- it("Cannot make bid if user exceeds maxClaimsPerAccount", async function () {
- const numBids = await auctionManager.auctionBids(ethers.utils.formatBytes32String("id1"), editionsOwner.address);
- expect(numBids).to.equal(ethers.BigNumber.from(1));
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id1",
- bidPrice: "1.1",
- reservePrice: "0.5",
- maxClaimsPerAccount: 1,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 500,
- claimer: editionsOwner.address,
- });
-
- let errorOnRead1 = false;
- try {
- await auctionManager.verifyClaim(claim, signature, editionsOwner.address);
- } catch (error) {
- errorOnRead1 = true;
- }
- expect(errorOnRead1).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address)).to.be.revertedWith(
- "Exceeded max claims for account",
- );
- });
-
- it("Can make valid higher bids", async function () {
- auctionManager = auctionManager.connect(fan1);
- const prevHighestBidderBalance = await editionsOwner.provider?.getBalance(editionsOwner.address);
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id1",
- bidPrice: "1.04",
- reservePrice: "1.8", // reserve price shouldn't matter
- maxClaimsPerAccount: 1,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 0,
- claimer: fan1.address,
- });
-
- const claimVerified = await auctionManager.verifyClaim(claim, signature, fan1.address);
- expect(claimVerified).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address, { value: ethers.utils.parseEther("1.04") }))
- .to.emit(auctionManager, "Bid")
- .withArgs(
- ethers.utils.formatBytes32String("id1"),
- fan1.address,
- false,
- editions.address,
- ethers.BigNumber.from(1),
- parseEther("1.04"),
- false,
- fan1.address,
- ethers.BigNumber.from(endTime1),
- );
-
- // refunded to previous highest bidder
- expect(await editionsOwner.provider?.getBalance(editionsOwner.address)).to.equal(
- prevHighestBidderBalance?.add(ethers.utils.parseEther("1")),
- );
- expect(await editionsOwner.provider?.getBalance(auctionManager.address)).to.equal(
- ethers.utils.parseEther("1.04"),
- );
- expect(await editions.ownerOf(1)).to.equal(auctionManager.address);
- const res = await auctionManager.getFullAuctionData(ethers.utils.formatBytes32String("id1"));
- expect(res[0][0]).to.equal(editions.address);
- // highest bidder data
- expect(res[1][0]).to.equal(fan1.address);
- expect(res[1][1]).to.equal(fan1.address);
- expect(res[1][2]).to.equal(ethers.utils.parseEther("1.04"));
- });
-
- it("Cannot bid same price even if minimum percentage increase is 0", async function () {
- auctionManager = auctionManager.connect(fan1);
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id1",
- bidPrice: "1.04",
- reservePrice: "0.5",
- maxClaimsPerAccount: 0,
- claimExpiryTimestamp: "0",
- buffer: 300,
- minimumIncrementPerBidPctBPS: 0,
- claimer: fan1.address,
- });
-
- let errorOnRead1 = false;
- try {
- await auctionManager.verifyClaim(claim, signature, editionsOwner.address);
- } catch (error) {
- errorOnRead1 = true;
- }
- expect(errorOnRead1).to.equal(true);
-
- await expect(auctionManager.bid(claim, signature, fan1.address)).to.be.revertedWith("Bid not higher");
- });
-
- it("If bid is made in buffer, end time is extended to current time + buffer", async function () {
- auctionManager = auctionManager.connect(fan1);
- const { signature, claim } = await signGatedBid(initialPlatformExecutor, auctionManager, {
- auctionId: "id2",
- bidPrice: "2",
- reservePrice: "1.8", // reserve price shouldn't matter
- maxClaimsPerAccount: 1,
- claimExpiryTimestamp: "0",
- buffer: 300000000,
- minimumIncrementPerBidPctBPS: 0,
- claimer: fan1.address,
- });
-
- const claimVerified = await auctionManager.verifyClaim(claim, signature, fan1.address);
- expect(claimVerified).to.equal(true);
-
- const blockTime = Math.floor(Date.now() / 1000) + 1000;
- const newEndTimeExpectedMin = ethers.BigNumber.from(blockTime + 300000000).add(2);
- await time.setNextBlockTimestamp(blockTime);
- await expect(auctionManager.bid(claim, signature, fan1.address, { value: ethers.utils.parseEther("2") }))
- .to.emit(editions2, "Transfer")
- .to.emit(auctionManager, "TimeLengthened")
- .to.emit(auctionManager, "Bid")
- .withArgs(
- ethers.utils.formatBytes32String("id2"),
- fan1.address,
- true,
- editions2.address,
- ethers.BigNumber.from(1),
- parseEther("2"),
- true,
- fan1.address,
- newEndTimeExpectedMin,
- );
-
- expect(await editionsOwner.provider?.getBalance(auctionManager.address)).to.equal(
- ethers.utils.parseEther("3.04"),
- );
- expect(await editions2.ownerOf(1)).to.equal(auctionManager.address);
- const res = await auctionManager.getFullAuctionData(ethers.utils.formatBytes32String("id2"));
- expect(res[0][0]).to.equal(editions2.address);
- // highest bidder data
- expect(res[1][0]).to.equal(fan1.address);
- expect(res[1][1]).to.equal(fan1.address);
- expect(res[1][2]).to.equal(ethers.utils.parseEther("2"));
- expect(res[0][4]).to.be.equal(newEndTimeExpectedMin);
- });
-
- it("Cannot fulfill an auction before it has ended", async function () {
- await expect(auctionManager.fulfillAuction(ethers.utils.formatBytes32String("id2"))).to.be.revertedWith(
- "Auction hasn't ended",
- );
- });
-
- it("Cannot fulfill a cancelled auction", async function () {
- await expect(auctionManager.fulfillAuction(ethers.utils.formatBytes32String("id3"))).to.be.revertedWith(
- "Not live",
- );
- });
-
- it("Anyone can fulfill won auction", async function () {
- await time.increaseTo(Math.floor(Date.now() / 1000) + 300005000);
- const provider = editionsOwner.provider!;
- const paymentRecipientPreviousBalance = await provider.getBalance(randomEOA.address);
- const platformPreviousBalance = await provider.getBalance(platformPaymentAccount.address);
- const recipientCut = ethers.utils.parseEther("1.04").mul(9500).div(10000);
- const platformCut = ethers.utils.parseEther("1.04").sub(recipientCut);
- console.log("pre fulfill");
- await expect(auctionManager.fulfillAuction(ethers.utils.formatBytes32String("id1")))
- .to.emit(auctionManager, "AuctionWon")
- .withArgs(
- ethers.utils.formatBytes32String("id1"),
- 1,
- editions.address,
- editionsOwner.address,
- fan1.address,
- randomEOA.address,
- fan1.address,
- ethers.constants.AddressZero,
- ethers.utils.parseEther("1.04"),
- 9500,
- );
- console.log("post fulfill");
-
- expect(await provider.getBalance(randomEOA.address)).to.equal(paymentRecipientPreviousBalance.add(recipientCut));
- expect(await provider.getBalance(platformPaymentAccount.address)).to.equal(
- platformPreviousBalance.add(platformCut),
- );
- expect(await provider.getBalance(auctionManager.address)).to.equal(ethers.utils.parseEther("2.0"));
- expect(await editions.ownerOf(1)).to.equal(fan1.address);
- const res = await auctionManager.getFullAuctionData(ethers.utils.formatBytes32String("id1"));
- expect(res[0][7]).to.equal(3); // FULFILLED state
- });
-
- it("Cannot fulfill already fulfilled auction", async function () {
- await expect(auctionManager.fulfillAuction(ethers.utils.formatBytes32String("id1"))).to.be.revertedWith(
- "Not live",
- );
- });
-
- it("Non highest bidder cannot call updatePreferredNFTRecipient", async function () {
- auctionManager = auctionManager.connect(editionsOwner);
- await expect(
- auctionManager.updatePreferredNFTRecipient(ethers.utils.formatBytes32String("id2"), mintManager.address),
- ).to.be.revertedWith("Not current highest bidder");
- });
-
- it("If preferred nft recipient is invalid, revert", async function () {
- auctionManager = auctionManager.connect(fan1);
- await expect(
- auctionManager.updatePreferredNFTRecipient(ethers.utils.formatBytes32String("id2"), mintManager.address),
- )
- .to.emit(auctionManager, "PreferredNFTRecipientUpdated")
- .withArgs(ethers.utils.formatBytes32String("id2"), editionsOwner.address, mintManager.address);
-
- await expect(auctionManager.fulfillAuction(ethers.utils.formatBytes32String("id2"))).to.be.revertedWith(
- "Preferred nft recipient is an invalid receiver",
- );
- });
- });
-
- describe("Auctions for existing tokens", function () {
- // in the beforeEach, create 2 auctions, one each via each method
- // one in native gas token, other in erc20
- // in the beforeEach, do the validations on the created parameters
- /*
- await expect(
- auctionManager.createAuctionForExistingToken(ethers.utils.formatBytes32String("id1"), defaultAuction),
- ).to.be.revertedWith("ERC721: invalid token ID");
- */
- });
-
- describe("Multiple auctions", function () {
- // just validate that there's no logic mishap with multiple auctions (one new token, one existing), don't need to validate as much as previous 2 describes
- });
-
- describe("Admin", function () {
- it("Only the owner can update the platform account", async function () {
- auctionManager = auctionManager.connect(editionsOwner);
- await expect(auctionManager.updatePlatform(editionsOwner.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- auctionManager = auctionManager.connect(mintManagerOwner);
- await expect(auctionManager.updatePlatform(editionsOwner.address))
- .to.emit(auctionManager, "PlatformUpdated")
- .withArgs(editionsOwner.address);
- });
-
- it("Only the owner can upgrade the auction manager", async function () {
- // upgrade to mock contract
- // validate
- });
- });
-});
-
-// need to validate behaviour with many auctions, for eg.
-// to show that correct amount of tokens / native currency held by contract is retained (through auctions)
diff --git a/test/ERC721BaseTest.ts b/test/ERC721BaseTest.ts
deleted file mode 100644
index b4db6e8..0000000
--- a/test/ERC721BaseTest.ts
+++ /dev/null
@@ -1,920 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ERC721Editions,
- ERC721SingleEdition,
- EditionsMetadataRenderer,
- InvalidRoyaltyManager,
- InvalidTokenManager,
- LockedRoyaltyManager,
- LockedTokenManager,
- MinimalForwarder,
- MintManager,
- Observability,
- OwnerOnlyRoyaltyManager,
- OwnerOnlyTokenManager,
-} from "../types";
-import { Errors } from "./__utils__/data";
-import { DEFAULT_ONCHAIN_MINT_VECTOR, setupEditions, setupSingleEdition, setupSystem } from "./__utils__/helpers";
-
-enum BaseEvents {
- MinterRegistrationChanged = "MinterRegistrationChanged",
- GranularTokenManagersSet = "GranularTokenManagersSet",
- GranularTokenManagersRemoved = "GranularTokenManagersRemoved",
- DefaultTokenManagerChanged = "DefaultTokenManagerChanged",
- DefaultRoyaltySet = "DefaultRoyaltySet",
- GranularRoyaltiesSet = "GranularRoyaltiesSet",
- RoyaltyManagerChanged = "RoyaltyManagerChanged",
- MintsFrozen = "MintsFrozen",
-}
-
-const defaultEditionInfo = ethers.utils.defaultAbiCoder.encode(
- ["tuple(string, string, string, string, string, string)"],
- [["name", "description", "imageUrl", "animationUrl", "externalUrl", "attributes"]],
-);
-
-describe("ERC721 Base functionality", () => {
- let invalidRoyaltyManager: InvalidRoyaltyManager;
- let lockedRoyaltyManager: LockedRoyaltyManager;
- let ownerOnlyRoyaltyManager: OwnerOnlyRoyaltyManager;
- let invalidTokenManager: InvalidTokenManager;
- let lockedTokenManager: LockedTokenManager;
- let ownerOnlyTokenManager: OwnerOnlyTokenManager;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let emr: EditionsMetadataRenderer;
- let mintManager: MintManager;
- let auctionManager: AuctionManager;
- let trustedForwarder: MinimalForwarder;
- let observability: Observability;
- let editionsImplementation: string;
- let singleEditionImplementation: string;
-
- const zeroRoyalty = {
- recipientAddress: ethers.constants.AddressZero,
- royaltyPercentageBPS: 0,
- };
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, editionsOwner, fan1] =
- await ethers.getSigners();
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
-
- emr = emrProxy;
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- observability = observabilityInstance;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
-
- invalidRoyaltyManager = await (await ethers.getContractFactory("InvalidRoyaltyManager")).deploy();
- lockedRoyaltyManager = await (await ethers.getContractFactory("LockedRoyaltyManager")).deploy();
- ownerOnlyRoyaltyManager = await (await ethers.getContractFactory("OwnerOnlyRoyaltyManager")).deploy();
-
- invalidTokenManager = await (await ethers.getContractFactory("InvalidTokenManager")).deploy();
- lockedTokenManager = await (await ethers.getContractFactory("LockedTokenManager")).deploy();
- ownerOnlyTokenManager = await (await ethers.getContractFactory("OwnerOnlyTokenManager")).deploy();
- });
-
- describe("Base (using Editions)", function () {
- let editions: ERC721Editions;
-
- beforeEach(async () => {
- editions = await setupEditions(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- );
- });
-
- describe("Minter registration", function () {
- it("Non-owners cannot register or unregister minters", async function () {
- editions = editions.connect(fan1);
- await expect(editions.registerMinter(fan1.address)).to.be.revertedWith("Ownable: caller is not the owner");
-
- await expect(editions.unregisterMinter(fan1.address)).to.be.revertedWith("Ownable: caller is not the owner");
- });
-
- it("Can only register unregistered minters", async function () {
- await expect(editions.registerMinter(fan1.address))
- .to.emit(editions, BaseEvents.MinterRegistrationChanged)
- .withArgs(fan1.address, true)
- .to.emit(observability, BaseEvents.MinterRegistrationChanged)
- .withArgs(editions.address, fan1.address, true);
-
- await expect(editions.registerMinter(fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.MinterRegistrationInvalid,
- );
- });
-
- it("Can only unregister registered minters", async function () {
- await expect(editions.unregisterMinter(fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.MinterRegistrationInvalid,
- );
-
- await editions.registerMinter(fan1.address);
-
- await expect(editions.unregisterMinter(fan1.address))
- .to.emit(editions, BaseEvents.MinterRegistrationChanged)
- .withArgs(fan1.address, false)
- .to.emit(observability, BaseEvents.MinterRegistrationChanged)
- .withArgs(editions.address, fan1.address, false);
- });
- });
-
- describe("Granular token managers management", function () {
- describe("Current token manager not existing", function () {
- it("An invalid token manager cannot be set", async function () {
- await expect(
- editions.setGranularTokenManagers([0, 1], [invalidTokenManager.address, invalidTokenManager.address]),
- ).to.be.revertedWithCustomError(editions, Errors.InvalidManager);
-
- await expect(
- editions.setGranularTokenManagers([0, 1], [lockedTokenManager.address, invalidTokenManager.address]),
- ).to.be.revertedWithCustomError(editions, Errors.InvalidManager);
-
- await expect(
- editions.setGranularTokenManagers([0, 1], [invalidTokenManager.address, lockedTokenManager.address]),
- ).to.be.revertedWithCustomError(editions, Errors.InvalidManager);
- });
-
- it("Non owners cannot call", async function () {
- editions = editions.connect(fan1);
- await expect(
- editions.setGranularTokenManagers([0, 1], [lockedTokenManager.address, lockedTokenManager.address]),
- ).to.be.revertedWithCustomError(editions, Errors.Unauthorized);
- });
-
- it("Owner can set granular token managers", async function () {
- await expect(
- editions.setGranularTokenManagers([0, 1], [lockedTokenManager.address, lockedTokenManager.address]),
- )
- .to.be.emit(editions, BaseEvents.GranularTokenManagersSet)
- .withArgs([0, 1], [lockedTokenManager.address, lockedTokenManager.address]);
-
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- expect(await editions.tokenManager(1)).to.eql(lockedTokenManager.address);
- });
-
- it("Cannot remove non-existent token manager", async function () {
- await expect(editions.removeGranularTokenManagers([0])).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerDoesNotExist,
- );
- });
- });
-
- describe("Current token manager exists", async function () {
- beforeEach(async function () {
- await editions.setGranularTokenManagers(
- [0, 1],
- [ownerOnlyTokenManager.address, ownerOnlyTokenManager.address],
- );
-
- expect(await editions.tokenManager(0)).to.eql(ownerOnlyTokenManager.address);
- expect(await editions.tokenManager(1)).to.eql(ownerOnlyTokenManager.address);
- });
-
- it("Swap attempts respect the wishes of current token managers", async function () {
- editions = editions.connect(fan1);
-
- await expect(
- editions.setGranularTokenManagers([0, 1], [lockedTokenManager.address, lockedTokenManager.address]),
- ).to.be.revertedWithCustomError(editions, Errors.ManagerSwapBlocked);
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setGranularTokenManagers([0], [lockedTokenManager.address]))
- .to.be.emit(editions, BaseEvents.GranularTokenManagersSet)
- .withArgs([0], [lockedTokenManager.address]);
-
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
-
- await expect(
- editions.setGranularTokenManagers([0, 1], [ownerOnlyTokenManager.address, lockedTokenManager.address]),
- ).to.be.revertedWithCustomError(editions, Errors.ManagerSwapBlocked);
-
- await expect(editions.setGranularTokenManagers([1], [lockedTokenManager.address]))
- .to.be.emit(editions, BaseEvents.GranularTokenManagersSet)
- .withArgs([1], [lockedTokenManager.address]);
-
- expect(await editions.tokenManager(1)).to.eql(lockedTokenManager.address);
- });
-
- it("Remove attempts respect the wishes of current token managers", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.removeGranularTokenManagers([0, 1])).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerRemoveBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.removeGranularTokenManagers([0]))
- .to.emit(editions, BaseEvents.GranularTokenManagersRemoved)
- .withArgs([0])
- .to.emit(observability, BaseEvents.GranularTokenManagersRemoved)
- .withArgs(editions.address, [0]);
-
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- });
- });
- });
-
- describe("Default token manager management", function () {
- describe("Current default token manager not existing", function () {
- it("An invalid default token manager cannot be set", async function () {
- await expect(editions.setDefaultTokenManager(invalidTokenManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.InvalidManager,
- );
- });
-
- it("Non owners cannot call", async function () {
- editions = editions.connect(fan1);
- await expect(editions.setDefaultTokenManager(lockedTokenManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.Unauthorized,
- );
- });
-
- it("Owner can set default token manager", async function () {
- await expect(editions.setDefaultTokenManager(lockedTokenManager.address))
- .to.be.emit(editions, BaseEvents.DefaultTokenManagerChanged)
- .withArgs(lockedTokenManager.address);
-
- for (let i = 0; i < 5; i++) {
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- }
- });
-
- it("Cannot remove non-existent default token manager", async function () {
- await expect(editions.removeDefaultTokenManager()).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerDoesNotExist,
- );
- });
- });
-
- describe("Current default token manager existing", function () {
- beforeEach(async function () {
- editions = await setupEditions(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- null,
- null,
- ownerOnlyTokenManager.address,
- );
-
- for (let i = 0; i < 5; i++) {
- expect(await editions.tokenManager(0)).to.eql(ownerOnlyTokenManager.address);
- }
- });
-
- it("Swap attempts respect the wishes of current default token manager", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.setDefaultTokenManager(lockedTokenManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerSwapBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setDefaultTokenManager(lockedTokenManager.address))
- .to.emit(editions, BaseEvents.DefaultTokenManagerChanged)
- .withArgs(lockedTokenManager.address)
- .to.emit(observability, BaseEvents.DefaultTokenManagerChanged)
- .withArgs(editions.address, lockedTokenManager.address);
-
- for (let i = 0; i < 5; i++) {
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- }
-
- await expect(editions.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerSwapBlocked,
- );
- });
-
- it("Remove attempts respect the wishes of current default token", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.removeDefaultTokenManager()).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerRemoveBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.removeDefaultTokenManager())
- .to.emit(editions, BaseEvents.DefaultTokenManagerChanged)
- .withArgs(ethers.constants.AddressZero);
-
- for (let i = 0; i < 5; i++) {
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- }
- });
- });
- });
-
- describe("Royalty manager management", function () {
- describe("Current royalty manager not existing", function () {
- it("An invalid royalty manager cannot be set", async function () {
- await expect(editions.setRoyaltyManager(invalidRoyaltyManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.InvalidManager,
- );
- });
-
- it("Non owners cannot call", async function () {
- editions = editions.connect(fan1);
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.Unauthorized,
- );
- });
-
- it("Owner can set royalty manager", async function () {
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(ownerOnlyRoyaltyManager.address);
-
- expect(await editions.royaltyManager()).to.eql(ownerOnlyRoyaltyManager.address);
- });
-
- it("Cannot remove non-existent royalty manager", async function () {
- await expect(editions.removeRoyaltyManager()).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerDoesNotExist,
- );
- });
- });
-
- describe("Current royalty manager exists", function () {
- beforeEach(async function () {
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(ownerOnlyRoyaltyManager.address);
-
- expect(await editions.royaltyManager()).to.eql(ownerOnlyRoyaltyManager.address);
- });
-
- it("Swap attempts respect the wishes of current royalty manager", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.setRoyaltyManager(lockedRoyaltyManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerSwapBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setRoyaltyManager(lockedRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(lockedRoyaltyManager.address)
- .to.be.emit(observability, BaseEvents.RoyaltyManagerChanged)
- .withArgs(editions.address, lockedRoyaltyManager.address);
-
- expect(await editions.royaltyManager()).to.eql(lockedRoyaltyManager.address);
-
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerSwapBlocked,
- );
- });
-
- it("Remove attempts respect the wishes of current royalty manager", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.removeRoyaltyManager()).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerRemoveBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.removeRoyaltyManager())
- .to.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(ethers.constants.AddressZero);
-
- expect(await editions.royaltyManager()).to.eql(ethers.constants.AddressZero);
- });
- });
- });
-
- describe("Royalty management", function () {
- describe("Current royalty manager does not exist", async function () {
- it("Royalty perentage BPS cannot be greater than 10000 for setting default royalty", async function () {
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 10001 }),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltyBPSInvalid);
- });
-
- it("Non-owner cannot set default royalty", async function () {
- editions = editions.connect(fan1);
-
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 }),
- ).to.be.revertedWithCustomError(editions, Errors.Unauthorized);
- });
-
- it("Owner can set default royalty", async function () {
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 }),
- )
- .to.emit(editions, BaseEvents.DefaultRoyaltySet)
- .withArgs(ethers.constants.AddressZero, 100)
- .to.emit(observability, BaseEvents.DefaultRoyaltySet)
- .withArgs(editions.address, ethers.constants.AddressZero, 100);
-
- // mint some tokens to test royalties
- await expect(
- editions.createEdition(defaultEditionInfo, 4, ethers.constants.AddressZero, zeroRoyalty, "0x"),
- ).to.emit(editions, "EditionCreated");
-
- await expect(editions.registerMinter(editionsOwner.address)).to.emit(editions, "MinterRegistrationChanged");
-
- await expect(editions.mintAmountToRecipient(0, editionsOwner.address, 2)).to.emit(editions, "Transfer");
-
- const royaltyInfo = await editions.royaltyInfo(1, 10000);
- expect(royaltyInfo.receiver).to.eql(ethers.constants.AddressZero);
- expect(royaltyInfo.royaltyAmount.toNumber()).to.eql(100);
- });
-
- it("Royalty perentage BPS cannot be greater than 10000 for setting granular royalties", async function () {
- await expect(
- editions.setGranularRoyalties(
- [0, 1],
- [
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 },
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 10001 },
- ],
- ),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltyBPSInvalid);
- });
-
- it("Non-owner cannot set granular royalties", async function () {
- editions = editions.connect(fan1);
-
- await expect(
- editions.setGranularRoyalties(
- [0, 1],
- [
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 },
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 1000 },
- ],
- ),
- ).to.be.revertedWithCustomError(editions, Errors.Unauthorized);
- });
-
- it("Owner can set granular royalties", async function () {
- await expect(
- editions.setGranularRoyalties(
- [0, 1],
- [
- { recipientAddress: fan1.address, royaltyPercentageBPS: 100 },
- { recipientAddress: fan1.address, royaltyPercentageBPS: 1000 },
- ],
- ),
- )
- .to.emit(editions, BaseEvents.GranularRoyaltiesSet)
- .to.emit(observability, BaseEvents.GranularRoyaltiesSet);
-
- // mint some tokens to test royalties
- await expect(
- editions.createEdition(defaultEditionInfo, 1, ethers.constants.AddressZero, zeroRoyalty, "0x"),
- ).to.emit(editions, "EditionCreated");
-
- await expect(
- editions.createEdition(defaultEditionInfo, 1, ethers.constants.AddressZero, zeroRoyalty, "0x"),
- ).to.emit(editions, "EditionCreated");
-
- await expect(editions.registerMinter(editionsOwner.address)).to.emit(editions, "MinterRegistrationChanged");
-
- await expect(editions.mintOneToRecipient(0, editionsOwner.address)).to.emit(editions, "Transfer");
-
- await expect(editions.mintOneToRecipient(1, editionsOwner.address)).to.emit(editions, "Transfer");
-
- const royaltyInfo1 = await editions.royaltyInfo(1, 10000);
- expect(royaltyInfo1.receiver).to.eql(fan1.address);
- expect(royaltyInfo1.royaltyAmount.toNumber()).to.eql(100);
-
- const royaltyInfo2 = await editions.royaltyInfo(2, 10000);
- expect(royaltyInfo2.receiver).to.eql(fan1.address);
- expect(royaltyInfo2.royaltyAmount.toNumber()).to.eql(1000);
- });
- });
-
- describe("Current royalty manager does exist", async function () {
- beforeEach(async function () {
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(ownerOnlyRoyaltyManager.address);
- });
-
- it("Setting royalties respects the wishes of current royalty manager", async function () {
- editions = editions.connect(fan1);
-
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 }),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltySetBlocked);
-
- await expect(
- editions.setGranularRoyalties(
- [0, 1],
- [
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 },
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 1000 },
- ],
- ),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltySetBlocked);
-
- editions = editions.connect(editionsOwner);
-
- // mint some tokens to test royalties
- await expect(
- editions.createEdition(defaultEditionInfo, 2, ethers.constants.AddressZero, zeroRoyalty, "0x"),
- ).to.emit(editions, "EditionCreated");
-
- await expect(editions.registerMinter(editionsOwner.address)).to.emit(editions, "MinterRegistrationChanged");
-
- await expect(editions.mintAmountToRecipient(0, editionsOwner.address, 2)).to.emit(editions, "Transfer");
-
- await expect(editions.setDefaultRoyalty({ recipientAddress: fan1.address, royaltyPercentageBPS: 100 }))
- .to.emit(editions, BaseEvents.DefaultRoyaltySet)
- .withArgs(fan1.address, 100);
-
- const royaltyInfo1 = await editions.royaltyInfo(1, 10000);
- expect(royaltyInfo1.receiver).to.eql(fan1.address);
- expect(royaltyInfo1.royaltyAmount.toNumber()).to.eql(100);
-
- await expect(
- editions.setGranularRoyalties(
- [0],
- [{ recipientAddress: editionsOwner.address, royaltyPercentageBPS: 1000 }],
- ),
- ).to.emit(editions, BaseEvents.GranularRoyaltiesSet);
-
- const royaltyInfo2 = await editions.royaltyInfo(1, 10000);
- expect(royaltyInfo2.receiver).to.eql(editionsOwner.address);
- expect(royaltyInfo2.royaltyAmount.toNumber()).to.eql(1000);
-
- await expect(editions.setRoyaltyManager(lockedRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(lockedRoyaltyManager.address);
-
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 }),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltySetBlocked);
-
- await expect(
- editions.setGranularRoyalties(
- [0, 1],
- [
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 },
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 1000 },
- ],
- ),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltySetBlocked);
- });
- });
- });
- });
-
- describe("MinimizedBase (using SingleEdition)", function () {
- let editions: ERC721SingleEdition;
-
- beforeEach(async () => {
- editions = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 100,
- "name",
- "SYM",
- );
- });
-
- describe("Minter registration", function () {
- it("Non-owners cannot register or unregister minters", async function () {
- editions = editions.connect(fan1);
- await expect(editions.registerMinter(fan1.address)).to.be.revertedWith("Ownable: caller is not the owner");
-
- await expect(editions.unregisterMinter(fan1.address)).to.be.revertedWith("Ownable: caller is not the owner");
- });
-
- it("Can only register unregistered minters", async function () {
- await expect(editions.registerMinter(fan1.address))
- .to.emit(editions, BaseEvents.MinterRegistrationChanged)
- .withArgs(fan1.address, true);
-
- await expect(editions.registerMinter(fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.MinterRegistrationInvalid,
- );
- });
-
- it("Can only unregister registered minters", async function () {
- await expect(editions.unregisterMinter(fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.MinterRegistrationInvalid,
- );
-
- await editions.registerMinter(fan1.address);
-
- await expect(editions.unregisterMinter(fan1.address))
- .to.emit(editions, BaseEvents.MinterRegistrationChanged)
- .withArgs(fan1.address, false);
- });
- });
-
- describe("Default token manager management", function () {
- describe("Current default token manager not existing", function () {
- it("An invalid default token manager cannot be set", async function () {
- await expect(editions.setDefaultTokenManager(invalidTokenManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.InvalidManager,
- );
- });
-
- it("Non owners cannot call", async function () {
- editions = editions.connect(fan1);
- await expect(editions.setDefaultTokenManager(lockedTokenManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.Unauthorized,
- );
- });
-
- it("Owner can set default token manager", async function () {
- await expect(editions.setDefaultTokenManager(lockedTokenManager.address))
- .to.be.emit(editions, BaseEvents.DefaultTokenManagerChanged)
- .withArgs(lockedTokenManager.address);
-
- for (let i = 0; i < 5; i++) {
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- }
- });
-
- it("Cannot remove non-existent default token manager", async function () {
- await expect(editions.removeDefaultTokenManager()).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerDoesNotExist,
- );
- });
- });
-
- describe("Current default token manager existing", function () {
- beforeEach(async function () {
- editions = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 100,
- "name",
- "SYM",
- null,
- null,
- false,
- ownerOnlyTokenManager.address,
- );
-
- for (let i = 0; i < 5; i++) {
- expect(await editions.tokenManager(0)).to.eql(ownerOnlyTokenManager.address);
- }
- });
-
- it("Swap attempts respect the wishes of current default token manager", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.setDefaultTokenManager(lockedTokenManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerSwapBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setDefaultTokenManager(lockedTokenManager.address))
- .to.be.emit(editions, BaseEvents.DefaultTokenManagerChanged)
- .withArgs(lockedTokenManager.address);
-
- for (let i = 0; i < 5; i++) {
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- }
-
- await expect(editions.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerSwapBlocked,
- );
- });
-
- it("Remove attempts respect the wishes of current default token", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.removeDefaultTokenManager()).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerRemoveBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.removeDefaultTokenManager())
- .to.emit(editions, BaseEvents.DefaultTokenManagerChanged)
- .withArgs(ethers.constants.AddressZero);
-
- for (let i = 0; i < 5; i++) {
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- }
- });
- });
- });
-
- describe("Royalty manager management", function () {
- describe("Current royalty manager not existing", function () {
- it("An invalid royalty manager cannot be set", async function () {
- await expect(editions.setRoyaltyManager(invalidRoyaltyManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.InvalidManager,
- );
- });
-
- it("Non owners cannot call", async function () {
- editions = editions.connect(fan1);
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.Unauthorized,
- );
- });
-
- it("Owner can set royalty manager", async function () {
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(ownerOnlyRoyaltyManager.address);
-
- expect(await editions.royaltyManager()).to.eql(ownerOnlyRoyaltyManager.address);
- });
-
- it("Cannot remove non-existent royalty manager", async function () {
- await expect(editions.removeRoyaltyManager()).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerDoesNotExist,
- );
- });
- });
-
- describe("Current royalty manager exists", function () {
- beforeEach(async function () {
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(ownerOnlyRoyaltyManager.address);
-
- expect(await editions.royaltyManager()).to.eql(ownerOnlyRoyaltyManager.address);
- });
-
- it("Swap attempts respect the wishes of current royalty manager", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.setRoyaltyManager(lockedRoyaltyManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerSwapBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setRoyaltyManager(lockedRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(lockedRoyaltyManager.address);
-
- expect(await editions.royaltyManager()).to.eql(lockedRoyaltyManager.address);
-
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address)).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerSwapBlocked,
- );
- });
-
- it("Remove attempts respect the wishes of current royalty manager", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.removeRoyaltyManager()).to.be.revertedWithCustomError(
- editions,
- Errors.ManagerRemoveBlocked,
- );
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.removeRoyaltyManager())
- .to.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(ethers.constants.AddressZero);
-
- expect(await editions.royaltyManager()).to.eql(ethers.constants.AddressZero);
- });
- });
- });
-
- describe("Royalty management", function () {
- describe("Current royalty manager does not exist", async function () {
- it("Royalty perentage BPS cannot be greater than 10000 for setting default royalty", async function () {
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 10001 }),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltyBPSInvalid);
- });
-
- it("Non-owner cannot set default royalty", async function () {
- editions = editions.connect(fan1);
-
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 }),
- ).to.be.revertedWithCustomError(editions, Errors.Unauthorized);
- });
-
- it("Owner can set default royalty", async function () {
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 }),
- )
- .to.emit(editions, BaseEvents.DefaultRoyaltySet)
- .withArgs(ethers.constants.AddressZero, 100);
-
- const royaltyInfo = await editions.royaltyInfo(1, 10000);
- expect(royaltyInfo.receiver).to.eql(ethers.constants.AddressZero);
- expect(royaltyInfo.royaltyAmount.toNumber()).to.eql(100);
- });
- });
-
- describe("Current royalty manager does exist", async function () {
- beforeEach(async function () {
- await expect(editions.setRoyaltyManager(ownerOnlyRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(ownerOnlyRoyaltyManager.address);
- });
-
- it("Setting royalties respects the wishes of current royalty manager", async function () {
- editions = editions.connect(fan1);
-
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 }),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltySetBlocked);
-
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setDefaultRoyalty({ recipientAddress: fan1.address, royaltyPercentageBPS: 100 }))
- .to.emit(editions, BaseEvents.DefaultRoyaltySet)
- .withArgs(fan1.address, 100);
-
- const royaltyInfo1 = await editions.royaltyInfo(0, 10000);
- expect(royaltyInfo1.receiver).to.eql(fan1.address);
- expect(royaltyInfo1.royaltyAmount.toNumber()).to.eql(100);
-
- await expect(editions.setRoyaltyManager(lockedRoyaltyManager.address))
- .to.be.emit(editions, BaseEvents.RoyaltyManagerChanged)
- .withArgs(lockedRoyaltyManager.address);
-
- await expect(
- editions.setDefaultRoyalty({ recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 100 }),
- ).to.be.revertedWithCustomError(editions, Errors.RoyaltySetBlocked);
- });
- });
- });
- });
-});
diff --git a/test/ERC721EditionsDFSTest.ts b/test/ERC721EditionsDFSTest.ts
deleted file mode 100644
index 1b9f021..0000000
--- a/test/ERC721EditionsDFSTest.ts
+++ /dev/null
@@ -1,733 +0,0 @@
-import { parseEther } from "@ethersproject/units";
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ERC721EditionsDFS,
- LockedTokenManager,
- MinimalForwarder,
- MintManager,
- Observability,
-} from "../types";
-import { Errors } from "./__utils__/data";
-import {
- DEFAULT_ONCHAIN_MINT_VECTOR,
- setupEditionsDFS,
- setupMultipleEditionDFS,
- setupSystem,
-} from "./__utils__/helpers";
-import { getValidClaimTimestamp } from "./__utils__/mint";
-
-describe("ERC721EditionsDFS functionality", () => {
- let lockedTokenManager: LockedTokenManager;
- let editions: ERC721EditionsDFS;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let mintManager: MintManager;
- let auctionManager: AuctionManager;
- let trustedForwarder: MinimalForwarder;
- let observability: Observability;
- let editionsImplementation: string;
- let auctionData: string;
-
- const zeroRoyalty = {
- recipientAddress: ethers.constants.AddressZero,
- royaltyPercentageBPS: 0,
- };
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, editionsOwner, fan1] =
- await ethers.getSigners();
- const {
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- auctionManagerProxy,
- editionsDFSImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
-
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- observability = observabilityInstance;
- auctionManager = auctionManagerProxy;
- editionsImplementation = editionsDFSImplementationAddress;
-
- lockedTokenManager = await (await ethers.getContractFactory("LockedTokenManager")).deploy();
- });
-
- beforeEach(async () => {
- editions = await setupEditionsDFS(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- editionsOwner,
- );
- auctionData = ethers.utils.defaultAbiCoder.encode(
- ["address", "bytes32", "address", "address", "uint256"],
- [
- auctionManager.address,
- ethers.utils.formatBytes32String("auctionId"),
- ethers.constants.AddressZero,
- editionsOwner.address,
- getValidClaimTimestamp(),
- ],
- );
- });
-
- describe("createEdition", async function () {
- it("Edition size has to be greater than 0", async function () {
- await expect(
- editions.createEdition("editionUri", 0, ethers.constants.AddressZero, zeroRoyalty, "0x"),
- ).to.be.revertedWithCustomError(editions, Errors.InvalidSize);
- });
-
- it("Non-owner cannot create edition", async function () {
- editions = editions.connect(fan1);
- await expect(
- editions.createEdition("editionUri", 100, ethers.constants.AddressZero, zeroRoyalty, "0x"),
- ).to.be.revertedWith("Ownable: caller is not the owner");
- });
-
- it("Can create edition without passing in edition token manager", async function () {
- await expect(editions.createEdition("editionUri", 100, ethers.constants.AddressZero, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 100, ethers.constants.AddressZero);
-
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- });
-
- it("Can create edition with passing in edition token manager", async function () {
- await expect(editions.createEdition("editionUri", 100, lockedTokenManager.address, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 100, lockedTokenManager.address);
-
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- });
- });
-
- describe("createEditionWithAuction", async function () {
- it("Non-owner cannot create edition/auction", async function () {
- editions = editions.connect(fan1);
- await expect(
- editions.createEditionWithAuction("editionUri", auctionData, ethers.constants.AddressZero, zeroRoyalty),
- ).to.be.revertedWith("Ownable: caller is not the owner");
- });
-
- it("Can create edition without passing in edition token manager", async function () {
- const timestamp = getValidClaimTimestamp();
- auctionData = ethers.utils.defaultAbiCoder.encode(
- ["address", "bytes32", "address", "address", "uint256"],
- [
- auctionManager.address,
- ethers.utils.formatBytes32String("auctionId1"),
- ethers.constants.AddressZero,
- editionsOwner.address,
- timestamp,
- ],
- );
-
- await expect(
- editions.createEditionWithAuction("editionUri", auctionData, ethers.constants.AddressZero, zeroRoyalty),
- )
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 1, ethers.constants.AddressZero);
-
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- const res = await auctionManager.getFullAuctionData(ethers.utils.formatBytes32String("auctionId1"));
- expect(res[0][0]).to.equal(editions.address);
- expect(res[0][4]).to.equal(ethers.BigNumber.from(timestamp));
- expect(res[2][0]).to.equal(true);
- expect(res[2][1]).to.equal(ethers.BigNumber.from(0));
- });
-
- it("Can create edition with passing in edition token manager", async function () {
- const timestamp = getValidClaimTimestamp();
- auctionData = ethers.utils.defaultAbiCoder.encode(
- ["address", "bytes32", "address", "address", "uint256"],
- [
- auctionManager.address,
- ethers.utils.formatBytes32String("auctionId2"),
- ethers.constants.AddressZero,
- editionsOwner.address,
- timestamp,
- ],
- );
-
- await expect(
- editions.createEditionWithAuction("editionUri", auctionData, lockedTokenManager.address, zeroRoyalty),
- )
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 1, lockedTokenManager.address);
-
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- const res = await auctionManager.getFullAuctionData(ethers.utils.formatBytes32String("auctionId2"));
- expect(res[0][0]).to.equal(editions.address);
- expect(res[0][4]).to.equal(ethers.BigNumber.from(timestamp));
- expect(res[2][0]).to.equal(true);
- expect(res[2][1]).to.equal(ethers.BigNumber.from(0));
- });
- });
-
- describe("Minting", function () {
- beforeEach(async function () {
- await expect(editions.createEdition("editionUri", 5, lockedTokenManager.address, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 5, lockedTokenManager.address);
-
- await expect(editions.registerMinter(editionsOwner.address));
-
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- });
-
- describe("mintOneToRecipient", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintOneToRecipient(1, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Can mint validly up until max supply", async function () {
- for (let i = 1; i <= 5; i++) {
- await expect(editions.mintOneToRecipient(0, fan1.address))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await editions.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- expect((await editions.getEditionStartIds()).map(x => x.toNumber())).to.eql([1]);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, i, 1]);
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(i);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
- });
-
- describe("mintAmountToRecipient", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintAmountToRecipient(1, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 6)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- expect((await editions.balanceOf(fan1.address)).toNumber()).to.equal(3);
-
- for (let i = 1; i <= 3; i++) {
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, 3, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(3);
- expect(res[0][0][3].toNumber()).to.equal(1);
- expect(res[1][0]).to.equal("editionUri");
- });
-
- it("Minter can mint validly (running variation)", async function () {
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 2);
-
- expect((await editions.balanceOf(fan1.address)).toNumber()).to.equal((i + 1) * 2);
-
- for (let j = 1; j <= (i + 1) * 2; j++) {
- expect(await editions.ownerOf(j)).to.equal(fan1.address);
- expect((await editions.getEditionId(j)).toNumber()).to.equal(0);
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, (i + 1) * 2, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 2);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("mintOneToRecipients", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintOneToRecipients(0, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintOneToRecipients(1, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints())
- .to.emit(editions, "MintsFrozen")
- .to.emit(observability, "MintsFrozen")
- .withArgs(editions.address);
-
- await expect(editions.mintOneToRecipients(0, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address, fan1.address, fan1.address, fan1.address];
- await expect(editions.mintOneToRecipients(0, recipientAddresses)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintOneToRecipients(0, recipientAddresses.slice(0, 3)))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(editions.mintOneToRecipients(0, recipientAddresses.slice(0, 3))).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, editionsMetadataOwner.address, editionsOwner.address];
- await expect(editions.mintOneToRecipients(0, recipientAddresses))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsMetadataOwner.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 3);
-
- let i = 1;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal(1);
- expect(await editions.ownerOf(i)).to.equal(recipient);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- i += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, 3, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(3);
- expect(res[0][0][3].toNumber()).to.equal(1);
- });
-
- it("Minter can mint validly (running variation)", async function () {
- const recipientAddresses = [fan1.address, editionsOwner.address];
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintOneToRecipients(0, recipientAddresses))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 2 + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 2 + 2);
-
- let j = 1;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal(i + 1);
- expect(await editions.ownerOf(i * 2 + j)).to.equal(recipient);
- expect((await editions.getEditionId(i * 2 + j)).toNumber()).to.equal(0);
- j += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, (i + 1) * 2, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 2);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("mintAmountToRecipients", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintAmountToRecipients(0, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintAmountToRecipients(1, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintAmountToRecipients(0, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 1),
- ).to.be.revertedWithCustomError(editions, Errors.SoldOut);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2),
- ).to.be.revertedWithCustomError(editions, Errors.SoldOut);
- });
-
- it("Minter can mint validly (complex variation) (with multiple editions)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(editions.createEdition("editionUri", 10, lockedTokenManager.address, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(1, 10, lockedTokenManager.address);
-
- const recipientAddresses2 = [fan1.address, editionsOwner.address];
-
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintAmountToRecipients(1, recipientAddresses2, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 1 + 5)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 2 + 5)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 4 + 3 + 5)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 4 + 4 + 5);
-
- let j = 0;
- for (const recipient of recipientAddresses2) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal((i + 1) * 2 + 4 * (1 - j)); // (1-j) encodes whether it's the fan or not (fan received 4 token prior already)
- expect(await editions.ownerOf(i * 4 + j * 2 + 1 + 5)).to.equal(recipient);
- expect(await editions.ownerOf(i * 4 + j * 2 + 2 + 5)).to.equal(recipient);
- expect((await editions.getEditionId(i * 4 + j * 2 + 1 + 5)).toNumber()).to.equal(1);
- expect((await editions.getEditionId(i * 4 + j * 2 + 2 + 5)).toNumber()).to.equal(1);
- j += 1;
- }
-
- expect(
- (await editions.getEditionDetails(1)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 10, (i + 1) * 4, 6]);
-
- const res = await editions.getEditionsDetailsAndUri([1]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(10);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 4);
- expect(res[0][0][3].toNumber()).to.equal(6);
- }
-
- expect((await editions.getEditionStartIds()).map(x => x.toNumber())).to.eql([1, 6]);
- });
- });
-
- describe("Contract metadata updates", function () {
- it("Owner can change the contract level metadata", async function () {
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri"))
- .to.emit(observability, "ContractMetadataSet")
- .withArgs(editions.address, "new name", "new symbol", "new contract uri");
-
- expect(await editions.name()).to.equal("new name");
- expect(await editions.symbol()).to.equal("new symbol");
- expect(await editions.contractURI()).to.equal("new contract uri");
- });
-
- it("Non-owners cannot change the contract level metadata", async function () {
- editions = editions.connect(fan1);
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- editions = editions.connect(editionsMetadataOwner);
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
- });
- });
-
- it("Can deploy with direct mint and create editions with direct mints after", async function () {
- editions = await setupMultipleEditionDFS(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- editionsOwner,
- 100,
- "symbol",
- { ...DEFAULT_ONCHAIN_MINT_VECTOR, maxUserClaimableViaVector: 2 },
- );
-
- expect((await mintManager.getAbridgedVector(1)).slice(0, 14)).to.deep.equal([
- editions.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- editionsOwner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxTotalClaimableViaVector,
- 0,
- ethers.constants.AddressZero,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 2,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- DEFAULT_ONCHAIN_MINT_VECTOR.editionId ?? 0,
- true,
- false,
- DEFAULT_ONCHAIN_MINT_VECTOR.allowlistRoot,
- ]);
-
- await expect(mintManager.vectorMint721(1, 2, editionsOwner.address, { value: parseEther("0.0008").mul(2) }))
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32), editions.address, true, 2);
-
- await expect(mintManager.vectorMint721(1, 1, editionsOwner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(1, editionsOwner.address)).to.equal(2);
-
- const mintVectorData = ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintManager.address,
- editionsOwner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 1,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- );
- await expect(
- editions.createEdition(
- "editionUri",
- 10,
- ethers.constants.AddressZero,
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 0 },
- mintVectorData,
- ),
- )
- .to.emit(editions, "EditionCreated")
- .withArgs(1, 10, ethers.constants.AddressZero)
- .to.emit(mintManager, "EditionVectorCreated")
- .withArgs(2, 1, editions.address);
-
- await expect(mintManager.vectorMint721(2, 1, editionsOwner.address, { value: parseEther("0.0008") }))
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(2), 32), editions.address, true, 1);
-
- await expect(mintManager.vectorMint721(2, 1, editionsOwner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(2, editionsOwner.address)).to.equal(1);
- });
-});
diff --git a/test/ERC721EditionsTest.ts b/test/ERC721EditionsTest.ts
deleted file mode 100644
index 8fc27bc..0000000
--- a/test/ERC721EditionsTest.ts
+++ /dev/null
@@ -1,747 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ERC721Editions,
- EditionsMetadataRenderer,
- LockedTokenManager,
- MinimalForwarder,
- MintManager,
- Observability,
-} from "../types";
-import { Errors } from "./__utils__/data";
-import { DEFAULT_ONCHAIN_MINT_VECTOR, setupEditions, setupMultipleEdition, setupSystem } from "./__utils__/helpers";
-import { getValidClaimTimestamp } from "./__utils__/mint";
-
-const defaultEditionInfo = ethers.utils.defaultAbiCoder.encode(
- ["tuple(string, string, string, string, string, string)"],
- [["name", "description", "imageUrl", "animationUrl", "externalUrl", "attributes"]],
-);
-
-describe("ERC721Editions functionality", () => {
- let lockedTokenManager: LockedTokenManager;
- let editions: ERC721Editions;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let emr: EditionsMetadataRenderer;
- let mintManager: MintManager;
- let auctionManager: AuctionManager;
- let trustedForwarder: MinimalForwarder;
- let observability: Observability;
- let editionsImplementation: string;
- let auctionData: string;
-
- const zeroRoyalty = {
- recipientAddress: ethers.constants.AddressZero,
- royaltyPercentageBPS: 0,
- };
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, editionsOwner, fan1] =
- await ethers.getSigners();
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- auctionManagerProxy,
- editionsImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
-
- emr = emrProxy;
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- observability = observabilityInstance;
- auctionManager = auctionManagerProxy;
- editionsImplementation = editionsImplementationAddress;
-
- lockedTokenManager = await (await ethers.getContractFactory("LockedTokenManager")).deploy();
- });
-
- beforeEach(async () => {
- editions = await setupEditions(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- );
- auctionData = ethers.utils.defaultAbiCoder.encode(
- ["address", "bytes32", "address", "address", "uint256"],
- [
- auctionManager.address,
- ethers.utils.formatBytes32String("auctionId"),
- ethers.constants.AddressZero,
- editionsOwner.address,
- getValidClaimTimestamp(),
- ],
- );
- });
-
- describe("createEdition", async function () {
- it("Edition size has to be greater than 0", async function () {
- await expect(
- editions.createEdition(defaultEditionInfo, 0, ethers.constants.AddressZero, zeroRoyalty, "0x"),
- ).to.be.revertedWithCustomError(editions, Errors.InvalidSize);
- });
-
- it("Non-owner cannot create edition", async function () {
- editions = editions.connect(fan1);
- await expect(
- editions.createEdition(defaultEditionInfo, 100, ethers.constants.AddressZero, zeroRoyalty, "0x"),
- ).to.be.revertedWith("Ownable: caller is not the owner");
- });
-
- it("Can create edition without passing in edition token manager", async function () {
- await expect(editions.createEdition(defaultEditionInfo, 100, ethers.constants.AddressZero, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 100, ethers.constants.AddressZero);
-
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- });
-
- it("Can create edition with passing in edition token manager", async function () {
- await expect(editions.createEdition(defaultEditionInfo, 100, lockedTokenManager.address, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 100, lockedTokenManager.address);
-
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- });
- });
-
- describe("createEditionWithAuction", async function () {
- it("Non-owner cannot create edition/auction", async function () {
- editions = editions.connect(fan1);
- await expect(
- editions.createEditionWithAuction(defaultEditionInfo, auctionData, ethers.constants.AddressZero, zeroRoyalty),
- ).to.be.revertedWith("Ownable: caller is not the owner");
- });
-
- it("Can create edition without passing in edition token manager", async function () {
- const timestamp = getValidClaimTimestamp();
- auctionData = ethers.utils.defaultAbiCoder.encode(
- ["address", "bytes32", "address", "address", "uint256"],
- [
- auctionManager.address,
- ethers.utils.formatBytes32String("auctionId1"),
- ethers.constants.AddressZero,
- editionsOwner.address,
- timestamp,
- ],
- );
-
- await expect(
- editions.createEditionWithAuction(defaultEditionInfo, auctionData, ethers.constants.AddressZero, zeroRoyalty),
- )
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 1, ethers.constants.AddressZero);
-
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- const res = await auctionManager.getFullAuctionData(ethers.utils.formatBytes32String("auctionId1"));
- expect(res[0][0]).to.equal(editions.address);
- expect(res[0][4]).to.equal(ethers.BigNumber.from(timestamp));
- expect(res[2][0]).to.equal(true);
- expect(res[2][1]).to.equal(ethers.BigNumber.from(0));
- });
-
- it("Can create edition with passing in edition token manager", async function () {
- const timestamp = getValidClaimTimestamp();
- auctionData = ethers.utils.defaultAbiCoder.encode(
- ["address", "bytes32", "address", "address", "uint256"],
- [
- auctionManager.address,
- ethers.utils.formatBytes32String("auctionId2"),
- ethers.constants.AddressZero,
- editionsOwner.address,
- timestamp,
- ],
- );
-
- await expect(
- editions.createEditionWithAuction(defaultEditionInfo, auctionData, lockedTokenManager.address, zeroRoyalty),
- )
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 1, lockedTokenManager.address);
-
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- const res = await auctionManager.getFullAuctionData(ethers.utils.formatBytes32String("auctionId2"));
- expect(res[0][0]).to.equal(editions.address);
- expect(res[0][4]).to.equal(ethers.BigNumber.from(timestamp));
- expect(res[2][0]).to.equal(true);
- expect(res[2][1]).to.equal(ethers.BigNumber.from(0));
- });
- });
-
- describe("Minting", function () {
- beforeEach(async function () {
- await expect(editions.createEdition(defaultEditionInfo, 5, lockedTokenManager.address, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 5, lockedTokenManager.address);
-
- await expect(editions.registerMinter(editionsOwner.address));
-
- expect(await editions.tokenManager(0)).to.eql(lockedTokenManager.address);
- });
-
- describe("mintOneToRecipient", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintOneToRecipient(1, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Can mint validly up until max supply", async function () {
- for (let i = 1; i <= 5; i++) {
- await expect(editions.mintOneToRecipient(0, fan1.address))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await editions.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- expect((await editions.getEditionStartIds()).map(x => x.toNumber())).to.eql([1]);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, i, 1]);
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(i);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
- });
-
- describe("mintAmountToRecipient", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintAmountToRecipient(1, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 6)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- expect((await editions.balanceOf(fan1.address)).toNumber()).to.equal(3);
-
- for (let i = 1; i <= 3; i++) {
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, 3, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(3);
- expect(res[0][0][3].toNumber()).to.equal(1);
- });
-
- it("Minter can mint validly (running variation)", async function () {
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 2);
-
- expect((await editions.balanceOf(fan1.address)).toNumber()).to.equal((i + 1) * 2);
-
- for (let j = 1; j <= (i + 1) * 2; j++) {
- expect(await editions.ownerOf(j)).to.equal(fan1.address);
- expect((await editions.getEditionId(j)).toNumber()).to.equal(0);
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, (i + 1) * 2, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 2);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("mintOneToRecipients", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintOneToRecipients(0, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintOneToRecipients(1, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints())
- .to.emit(editions, "MintsFrozen")
- .to.emit(observability, "MintsFrozen")
- .withArgs(editions.address);
-
- await expect(editions.mintOneToRecipients(0, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address, fan1.address, fan1.address, fan1.address];
- await expect(editions.mintOneToRecipients(0, recipientAddresses)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintOneToRecipients(0, recipientAddresses.slice(0, 3)))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(editions.mintOneToRecipients(0, recipientAddresses.slice(0, 3))).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, editionsMetadataOwner.address, editionsOwner.address];
- await expect(editions.mintOneToRecipients(0, recipientAddresses))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsMetadataOwner.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 3);
-
- let i = 1;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal(1);
- expect(await editions.ownerOf(i)).to.equal(recipient);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- i += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, 3, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(3);
- expect(res[0][0][3].toNumber()).to.equal(1);
- });
-
- it("Minter can mint validly (running variation)", async function () {
- const recipientAddresses = [fan1.address, editionsOwner.address];
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintOneToRecipients(0, recipientAddresses))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 2 + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 2 + 2);
-
- let j = 1;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal(i + 1);
- expect(await editions.ownerOf(i * 2 + j)).to.equal(recipient);
- expect((await editions.getEditionId(i * 2 + j)).toNumber()).to.equal(0);
- j += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, (i + 1) * 2, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 2);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("mintAmountToRecipients", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintAmountToRecipients(0, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintAmountToRecipients(1, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintAmountToRecipients(0, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 1),
- ).to.be.revertedWithCustomError(editions, Errors.SoldOut);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2),
- ).to.be.revertedWithCustomError(editions, Errors.SoldOut);
- });
-
- it("Minter can mint validly (complex variation) (with multiple editions)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(editions.createEdition(defaultEditionInfo, 10, lockedTokenManager.address, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(1, 10, lockedTokenManager.address);
-
- const recipientAddresses2 = [fan1.address, editionsOwner.address];
-
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintAmountToRecipients(1, recipientAddresses2, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 1 + 5)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 2 + 5)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 4 + 3 + 5)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 4 + 4 + 5);
-
- let j = 0;
- for (const recipient of recipientAddresses2) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal((i + 1) * 2 + 4 * (1 - j)); // (1-j) encodes whether it's the fan or not (fan received 4 token prior already)
- expect(await editions.ownerOf(i * 4 + j * 2 + 1 + 5)).to.equal(recipient);
- expect(await editions.ownerOf(i * 4 + j * 2 + 2 + 5)).to.equal(recipient);
- expect((await editions.getEditionId(i * 4 + j * 2 + 1 + 5)).toNumber()).to.equal(1);
- expect((await editions.getEditionId(i * 4 + j * 2 + 2 + 5)).toNumber()).to.equal(1);
- j += 1;
- }
-
- expect(
- (await editions.getEditionDetails(1)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 10, (i + 1) * 4, 6]);
-
- const res = await editions.getEditionsDetailsAndUri([1]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(10);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 4);
- expect(res[0][0][3].toNumber()).to.equal(6);
- }
-
- expect((await editions.getEditionStartIds()).map(x => x.toNumber())).to.eql([1, 6]);
- });
- });
-
- describe("Contract metadata updates", function () {
- it("Owner can change the contract level metadata", async function () {
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri"))
- .to.emit(observability, "ContractMetadataSet")
- .withArgs(editions.address, "new name", "new symbol", "new contract uri");
-
- expect(await editions.name()).to.equal("new name");
- expect(await editions.symbol()).to.equal("new symbol");
- expect(await editions.contractURI()).to.equal("new contract uri");
- });
-
- it("Non-owners cannot change the contract level metadata", async function () {
- editions = editions.connect(fan1);
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- editions = editions.connect(editionsMetadataOwner);
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
- });
- });
-
- it("Can deploy with direct mint and create editions with direct mints after", async function () {
- editions = await setupMultipleEdition(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 100,
- "name",
- "symbol",
- { ...DEFAULT_ONCHAIN_MINT_VECTOR, maxUserClaimableViaVector: 2 },
- );
-
- const defaultEditionInfo = ethers.utils.defaultAbiCoder.encode(
- ["tuple(string, string, string, string, string, string)"],
- [["name", "description", "imageUrl", "animationUrl", "externalUrl", "attributes"]],
- );
-
- expect((await mintManager.getAbridgedVector(1)).slice(0, 14)).to.deep.equal([
- editions.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- editionsOwner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxTotalClaimableViaVector,
- 0,
- ethers.constants.AddressZero,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 2,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- DEFAULT_ONCHAIN_MINT_VECTOR.editionId ?? 0,
- true,
- false,
- DEFAULT_ONCHAIN_MINT_VECTOR.allowlistRoot,
- ]);
-
- await expect(
- mintManager.vectorMint721(1, 2, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- )
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32), editions.address, true, 2);
-
- await expect(mintManager.vectorMint721(1, 1, editionsOwner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(1, editionsOwner.address)).to.equal(2);
-
- const mintVectorData = ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintManager.address,
- editionsOwner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 1,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- );
- await expect(
- editions.createEdition(
- defaultEditionInfo,
- 10,
- ethers.constants.AddressZero,
- { recipientAddress: ethers.constants.AddressZero, royaltyPercentageBPS: 0 },
- mintVectorData,
- ),
- )
- .to.emit(editions, "EditionCreated")
- .withArgs(1, 10, ethers.constants.AddressZero)
- .to.emit(mintManager, "EditionVectorCreated")
- .withArgs(2, 1, editions.address);
-
- await expect(mintManager.vectorMint721(2, 1, editionsOwner.address, { value: ethers.utils.parseEther("0.0008") }))
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(2), 32), editions.address, true, 1);
-
- await expect(mintManager.vectorMint721(2, 1, editionsOwner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(2, editionsOwner.address)).to.equal(1);
- });
-});
diff --git a/test/ERC721GeneralSequenceTest.ts b/test/ERC721GeneralSequenceTest.ts
deleted file mode 100644
index aafb121..0000000
--- a/test/ERC721GeneralSequenceTest.ts
+++ /dev/null
@@ -1,608 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- ERC721General,
- MinimalForwarder,
- MintManager,
- Observability,
- OwnerOnlyTokenManager,
- TotalLockedTokenManager,
-} from "../types";
-import { Errors } from "./__utils__/data";
-import { DEFAULT_ONCHAIN_MINT_VECTOR, setupGeneral, setupSystem } from "./__utils__/helpers";
-
-describe("ERC721GeneralSequence functionality", () => {
- let totalLockedTokenManager: TotalLockedTokenManager;
- let ownerOnlyTokenManager: OwnerOnlyTokenManager;
- let general: ERC721General;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- owner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let mintManager: MintManager;
- let trustedForwarder: MinimalForwarder;
- let observability: Observability;
- let generalImplementation: string;
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, owner, fan1] =
- await ethers.getSigners();
- const {
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- generalSequenceImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- owner,
- );
-
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- observability = observabilityInstance;
- generalImplementation = generalSequenceImplementationAddress;
-
- totalLockedTokenManager = await (await ethers.getContractFactory("TotalLockedTokenManager")).deploy();
- ownerOnlyTokenManager = await (await ethers.getContractFactory("OwnerOnlyTokenManager")).deploy();
- });
-
- beforeEach(async () => {
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- owner,
- );
- });
-
- describe("URIs", function () {
- beforeEach(async () => {
- // mint a couple tokens to validate uris
- await expect(general.registerMinter(owner.address)).to.emit(general, "MinterRegistrationChanged");
-
- await expect(general.mintSameAmountToMultipleRecipients([owner.address, fan1.address], 2)).to.emit(
- general,
- "Transfer",
- );
- });
-
- it("Base uri concatenation should be respected for tokens without overwritten uris", async function () {
- for (let i = 1; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
- });
-
- describe("setBaseUri", function () {
- it("Cannot set to empty string", async function () {
- await expect(general.setBaseURI("")).to.be.revertedWithCustomError(general, Errors.EmptyString);
- });
-
- it("If default manager is non-existent, invocation from non-owner fails", async function () {
- general = general.connect(fan1);
- await expect(general.setBaseURI("testing")).to.be.revertedWithCustomError(general, Errors.Unauthorized);
- });
-
- it("If default manager is non-existent, invocation from owner succeeds", async function () {
- await expect(general.setBaseURI("testing"))
- .to.emit(general, "BaseURISet")
- .withArgs("baseUri", "testing")
- .to.emit(observability, "BaseUriSet")
- .withArgs(general.address, "testing");
-
- for (let i = 1; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing/${i}`);
- }
- });
-
- it("If default manager exists, invocation respects token manager", async function () {
- await expect(general.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- general = general.connect(fan1);
- await expect(general.setBaseURI("testing")).to.be.revertedWithCustomError(general, Errors.Unauthorized);
-
- general = general.connect(owner);
- await expect(general.setBaseURI("testing")).to.emit(general, "BaseURISet").withArgs("baseUri", "testing");
-
- for (let i = 1; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing/${i}`);
- }
- });
- });
-
- describe("setTokenUris", function () {
- it("ids and uris length cannot mismatch", async function () {
- await expect(general.setTokenURIs([1, 2], ["test"])).to.be.revertedWithCustomError(
- general,
- Errors.MismatchedArrayLengths,
- );
- });
-
- it("If token manager is non-existent, invocation from non-owner fails", async function () {
- general = general.connect(fan1);
- await expect(general.setTokenURIs([1, 2], ["testing1", "testing2"])).to.be.revertedWithCustomError(
- general,
- Errors.Unauthorized,
- );
- });
-
- it("If tokens manager is non-existent, invocation owner succeeds", async function () {
- await expect(general.setTokenURIs([1, 2], ["testing1", "testing2"]))
- .to.emit(general, "TokenURIsSet")
- .withArgs([1, 2], ["testing1", "testing2"]);
-
- for (let i = 1; i <= 2; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing${i}`);
- }
- for (let i = 3; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
- });
-
- it("If token manager exists either as a default or an overwriting token manager, invocation respects token manager", async function () {
- await expect(general.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- general = general.connect(fan1);
- await expect(general.setTokenURIs([1, 2], ["testing1", "testing2"])).to.be.revertedWithCustomError(
- general,
- Errors.Unauthorized,
- );
-
- general = general.connect(owner);
-
- await expect(general.setTokenURIs([1, 2], ["testing1", "testing2"]))
- .to.emit(general, "TokenURIsSet")
- .withArgs([1, 2], ["testing1", "testing2"])
- .to.emit(observability, "TokenURIsSet")
- .withArgs(general.address, [1, 2], ["testing1", "testing2"]);
-
- for (let i = 1; i <= 2; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing${i}`);
- }
- for (let i = 3; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
-
- await expect(
- general.setGranularTokenManagers([1, 2], [totalLockedTokenManager.address, totalLockedTokenManager.address]),
- )
- .to.emit(general, "GranularTokenManagersSet")
- .to.emit(observability, "GranularTokenManagersSet");
-
- await expect(
- general.setTokenURIs([1, 2, 3], ["testing1", "testing2", "testing3"]),
- ).to.be.revertedWithCustomError(general, Errors.Unauthorized);
-
- await expect(general.setTokenURIs([2, 3], ["testing2", "testing3"])).to.be.revertedWithCustomError(
- general,
- Errors.Unauthorized,
- );
-
- await expect(general.setTokenURIs([1, 3], ["testing1", "testing3"])).to.be.revertedWithCustomError(
- general,
- Errors.Unauthorized,
- );
-
- await expect(general.setTokenURIs([3], ["testing3"]))
- .to.emit(general, "TokenURIsSet")
- .withArgs([3], ["testing3"]);
-
- for (let i = 1; i <= 3; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing${i}`);
- }
- expect(await general.tokenURI(4)).to.equal(`baseUri/4`);
- });
- });
- });
-
- describe("Minting", function () {
- beforeEach(async function () {
- await expect(general.registerMinter(owner.address));
-
- expect(await general.tokenManager(0)).to.eql(ethers.constants.AddressZero);
-
- await expect(general.setLimitSupply(4)).to.emit(general, "LimitSupplySet").withArgs(4);
- });
-
- describe("mintOneToOneRecipient", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Can mint validly up until limit supply", async function () {
- for (let i = 1; i <= 4; i++) {
- await expect(general.mintOneToOneRecipient(fan1.address))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await general.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await general.ownerOf(i)).to.equal(fan1.address);
- }
-
- await expect(general.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.setLimitSupply(0)).to.emit(general, "LimitSupplySet").withArgs(0);
-
- for (let i = 5; i <= 8; i++) {
- await expect(general.mintOneToOneRecipient(fan1.address))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await general.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await general.ownerOf(i)).to.equal(fan1.address);
- }
- });
- });
-
- describe("mintAmountToOneRecipient", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 2)).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 2)).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- await expect(general.mintAmountToOneRecipient(fan1.address, 6)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 3)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.setLimitSupply(0))
- .to.emit(general, "LimitSupplySet")
- .withArgs(0)
- .to.emit(observability, "LimitSupplySet")
- .withArgs(general.address, 0);
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- await expect(general.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- expect((await general.balanceOf(fan1.address)).toNumber()).to.equal(3);
-
- for (let i = 1; i <= 3; i++) {
- expect(await general.ownerOf(i)).to.equal(fan1.address);
- }
- });
-
- it("Minter can mint validly (running variation)", async function () {
- for (let i = 0; i < 2; i++) {
- await expect(general.mintAmountToOneRecipient(fan1.address, 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 2);
-
- expect((await general.balanceOf(fan1.address)).toNumber()).to.equal((i + 1) * 2);
-
- for (let j = 1; j <= (i + 1) * 2; j++) {
- expect(await general.ownerOf(j)).to.equal(fan1.address);
- }
- }
- });
- });
-
- describe("mintOneToMultipleRecipients", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintOneToMultipleRecipients([fan1.address])).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintOneToMultipleRecipients([fan1.address])).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address, fan1.address, fan1.address, fan1.address];
- await expect(general.mintOneToMultipleRecipients(recipientAddresses)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.mintOneToMultipleRecipients(recipientAddresses.slice(3)))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(general.mintOneToMultipleRecipients(recipientAddresses.slice(3))).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.setLimitSupply(0)).to.emit(general, "LimitSupplySet").withArgs(0);
-
- await expect(general.mintOneToMultipleRecipients(recipientAddresses.slice(3)))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address, editionsMetadataOwner.address];
- await expect(general.mintOneToMultipleRecipients(recipientAddresses))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsMetadataOwner.address, 3);
-
- let i = 1;
- for (const recipient of recipientAddresses) {
- expect((await general.balanceOf(recipient)).toNumber()).to.equal(1);
- expect(await general.ownerOf(i)).to.equal(recipient);
- i += 1;
- }
- });
-
- it("Minter can mint validly (running variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address];
- for (let i = 0; i < 2; i++) {
- await expect(general.mintOneToMultipleRecipients(recipientAddresses))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 2 + 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 2 + 2);
-
- let j = 1;
- for (const recipient of recipientAddresses) {
- expect((await general.balanceOf(recipient)).toNumber()).to.equal(i + 1);
- expect(await general.ownerOf(i * 2 + j)).to.equal(recipient);
- j += 1;
- }
- }
- });
- });
-
- describe("mintSameAmountToMultipleRecipients", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintSameAmountToMultipleRecipients([fan1.address], 2)).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintSameAmountToMultipleRecipients([fan1.address], 2)).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses, 2)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 1),
- ).to.be.revertedWithCustomError(general, Errors.OverLimitSupply);
-
- await expect(general.setLimitSupply(0)).to.emit(general, "LimitSupplySet").withArgs(0);
-
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 7)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 8);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses, 2)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2),
- ).to.be.revertedWithCustomError(general, Errors.OverLimitSupply);
- });
-
- it("Minter can mint validly (complex variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address];
-
- for (let i = 0; i < 2; i++) {
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses, 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 4 + 3)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 4 + 4);
-
- let j = 0;
- for (const recipient of recipientAddresses) {
- expect((await general.balanceOf(recipient)).toNumber()).to.equal((i + 1) * 2);
- expect(await general.ownerOf(i * 4 + j * 2 + 1)).to.equal(recipient);
- expect(await general.ownerOf(i * 4 + j * 2 + 2)).to.equal(recipient);
- j += 1;
- }
-
- await expect(general.setLimitSupply(8)).to.emit(general, "LimitSupplySet").withArgs(8);
- }
- });
- });
-
- describe("Contract metadata updates", function () {
- it("Owner can change the contract level metadata", async function () {
- general = general.connect(owner);
-
- await expect(general.setContractMetadata("new name", "new symbol", "new contract uri"))
- .to.emit(observability, "ContractMetadataSet")
- .withArgs(general.address, "new name", "new symbol", "new contract uri");
-
- expect(await general.name()).to.equal("new name");
- expect(await general.symbol()).to.equal("new symbol");
- expect(await general.contractURI()).to.equal("new contract uri");
- });
-
- it("Non-owners cannot change the contract level metadata", async function () {
- general = general.connect(fan1);
- await expect(general.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- general = general.connect(editionsMetadataOwner);
- await expect(general.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
- });
- });
-
- it("Can deploy with direct mint", async function () {
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- owner,
- { ...DEFAULT_ONCHAIN_MINT_VECTOR, maxUserClaimableViaVector: 2 },
- );
-
- expect((await mintManager.getAbridgedVector(1)).slice(0, 14)).to.deep.equal([
- general.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- owner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxTotalClaimableViaVector,
- 0,
- ethers.constants.AddressZero,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 2,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- 0,
- false,
- false,
- DEFAULT_ONCHAIN_MINT_VECTOR.allowlistRoot,
- ]);
-
- await expect(mintManager.vectorMint721(1, 2, owner.address, { value: ethers.utils.parseEther("0.0008").mul(2) }))
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32), general.address, true, 2);
-
- await expect(mintManager.vectorMint721(1, 1, owner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(1, owner.address)).to.equal(2);
- });
-});
diff --git a/test/ERC721GeneralTest.ts b/test/ERC721GeneralTest.ts
deleted file mode 100644
index ea3d122..0000000
--- a/test/ERC721GeneralTest.ts
+++ /dev/null
@@ -1,701 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- ERC721General,
- MinimalForwarder,
- MintManager,
- Observability,
- OwnerOnlyTokenManager,
- TotalLockedTokenManager,
-} from "../types";
-import { Errors } from "./__utils__/data";
-import { DEFAULT_ONCHAIN_MINT_VECTOR, setupGeneral, setupSystem } from "./__utils__/helpers";
-
-describe("ERC721General functionality", () => {
- let totalLockedTokenManager: TotalLockedTokenManager;
- let ownerOnlyTokenManager: OwnerOnlyTokenManager;
- let general: ERC721General;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- owner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let mintManager: MintManager;
- let trustedForwarder: MinimalForwarder;
- let observability: Observability;
- let generalImplementation: string;
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, owner, fan1] =
- await ethers.getSigners();
- const {
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- owner,
- );
-
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- observability = observabilityInstance;
- generalImplementation = generalImplementationAddress;
-
- totalLockedTokenManager = await (await ethers.getContractFactory("TotalLockedTokenManager")).deploy();
- ownerOnlyTokenManager = await (await ethers.getContractFactory("OwnerOnlyTokenManager")).deploy();
- });
-
- beforeEach(async () => {
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- owner,
- );
- });
-
- describe("URIs", function () {
- beforeEach(async () => {
- // mint a couple tokens to validate uris
- await expect(general.registerMinter(owner.address)).to.emit(general, "MinterRegistrationChanged");
-
- await expect(general.mintSameAmountToMultipleRecipients([owner.address, fan1.address], 2)).to.emit(
- general,
- "Transfer",
- );
- });
-
- it("Base uri concatenation should be respected for tokens without overwritten uris", async function () {
- for (let i = 1; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
- });
-
- describe("setBaseUri", function () {
- it("Cannot set to empty string", async function () {
- await expect(general.setBaseURI("")).to.be.revertedWithCustomError(general, Errors.EmptyString);
- });
-
- it("If default manager is non-existent, invocation from non-owner fails", async function () {
- general = general.connect(fan1);
- await expect(general.setBaseURI("testing")).to.be.revertedWithCustomError(general, Errors.Unauthorized);
- });
-
- it("If default manager is non-existent, invocation from owner succeeds", async function () {
- await expect(general.setBaseURI("testing"))
- .to.emit(general, "BaseURISet")
- .withArgs("baseUri", "testing")
- .to.emit(observability, "BaseUriSet")
- .withArgs(general.address, "testing");
-
- for (let i = 1; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing/${i}`);
- }
- });
-
- it("If default manager exists, invocation respects token manager", async function () {
- await expect(general.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- general = general.connect(fan1);
- await expect(general.setBaseURI("testing")).to.be.revertedWithCustomError(general, Errors.Unauthorized);
-
- general = general.connect(owner);
- await expect(general.setBaseURI("testing")).to.emit(general, "BaseURISet").withArgs("baseUri", "testing");
-
- for (let i = 1; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing/${i}`);
- }
- });
- });
-
- describe("setTokenUris", function () {
- it("ids and uris length cannot mismatch", async function () {
- await expect(general.setTokenURIs([1, 2], ["test"])).to.be.revertedWithCustomError(
- general,
- Errors.MismatchedArrayLengths,
- );
- });
-
- it("If token manager is non-existent, invocation from non-owner fails", async function () {
- general = general.connect(fan1);
- await expect(general.setTokenURIs([1, 2], ["testing1", "testing2"])).to.be.revertedWithCustomError(
- general,
- Errors.Unauthorized,
- );
- });
-
- it("If tokens manager is non-existent, invocation owner succeeds", async function () {
- await expect(general.setTokenURIs([1, 2], ["testing1", "testing2"]))
- .to.emit(general, "TokenURIsSet")
- .withArgs([1, 2], ["testing1", "testing2"]);
-
- for (let i = 1; i <= 2; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing${i}`);
- }
- for (let i = 3; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
- });
-
- it("If token manager exists either as a default or an overwriting token manager, invocation respects token manager", async function () {
- await expect(general.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- general = general.connect(fan1);
- await expect(general.setTokenURIs([1, 2], ["testing1", "testing2"])).to.be.revertedWithCustomError(
- general,
- Errors.Unauthorized,
- );
-
- general = general.connect(owner);
-
- await expect(general.setTokenURIs([1, 2], ["testing1", "testing2"]))
- .to.emit(general, "TokenURIsSet")
- .withArgs([1, 2], ["testing1", "testing2"])
- .to.emit(observability, "TokenURIsSet")
- .withArgs(general.address, [1, 2], ["testing1", "testing2"]);
-
- for (let i = 1; i <= 2; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing${i}`);
- }
- for (let i = 3; i <= 4; i++) {
- expect(await general.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
-
- await expect(
- general.setGranularTokenManagers([1, 2], [totalLockedTokenManager.address, totalLockedTokenManager.address]),
- )
- .to.emit(general, "GranularTokenManagersSet")
- .to.emit(observability, "GranularTokenManagersSet");
-
- await expect(
- general.setTokenURIs([1, 2, 3], ["testing1", "testing2", "testing3"]),
- ).to.be.revertedWithCustomError(general, Errors.Unauthorized);
-
- await expect(general.setTokenURIs([2, 3], ["testing2", "testing3"])).to.be.revertedWithCustomError(
- general,
- Errors.Unauthorized,
- );
-
- await expect(general.setTokenURIs([1, 3], ["testing1", "testing3"])).to.be.revertedWithCustomError(
- general,
- Errors.Unauthorized,
- );
-
- await expect(general.setTokenURIs([3], ["testing3"]))
- .to.emit(general, "TokenURIsSet")
- .withArgs([3], ["testing3"]);
-
- for (let i = 1; i <= 3; i++) {
- expect(await general.tokenURI(i)).to.equal(`testing${i}`);
- }
- expect(await general.tokenURI(4)).to.equal(`baseUri/4`);
- });
- });
- });
-
- describe("Minting", function () {
- beforeEach(async function () {
- await expect(general.registerMinter(owner.address));
-
- expect(await general.tokenManager(0)).to.eql(ethers.constants.AddressZero);
-
- await expect(general.setLimitSupply(4)).to.emit(general, "LimitSupplySet").withArgs(4);
- });
-
- describe("mintOneToOneRecipient", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Can mint validly up until limit supply", async function () {
- for (let i = 1; i <= 4; i++) {
- await expect(general.mintOneToOneRecipient(fan1.address))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await general.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await general.ownerOf(i)).to.equal(fan1.address);
- }
-
- await expect(general.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.setLimitSupply(0)).to.emit(general, "LimitSupplySet").withArgs(0);
-
- for (let i = 5; i <= 8; i++) {
- await expect(general.mintOneToOneRecipient(fan1.address))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await general.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await general.ownerOf(i)).to.equal(fan1.address);
- }
- });
- });
-
- describe("mintAmountToOneRecipient", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 2)).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 2)).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- await expect(general.mintAmountToOneRecipient(fan1.address, 6)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 3)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.setLimitSupply(0))
- .to.emit(general, "LimitSupplySet")
- .withArgs(0)
- .to.emit(observability, "LimitSupplySet")
- .withArgs(general.address, 0);
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- await expect(general.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- expect((await general.balanceOf(fan1.address)).toNumber()).to.equal(3);
-
- for (let i = 1; i <= 3; i++) {
- expect(await general.ownerOf(i)).to.equal(fan1.address);
- }
- });
-
- it("Minter can mint validly (running variation)", async function () {
- for (let i = 0; i < 2; i++) {
- await expect(general.mintAmountToOneRecipient(fan1.address, 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 2);
-
- expect((await general.balanceOf(fan1.address)).toNumber()).to.equal((i + 1) * 2);
-
- for (let j = 1; j <= (i + 1) * 2; j++) {
- expect(await general.ownerOf(j)).to.equal(fan1.address);
- }
- }
- });
- });
-
- describe("mintOneToMultipleRecipients", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintOneToMultipleRecipients([fan1.address])).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintOneToMultipleRecipients([fan1.address])).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address, fan1.address, fan1.address, fan1.address];
- await expect(general.mintOneToMultipleRecipients(recipientAddresses)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.mintOneToMultipleRecipients(recipientAddresses.slice(3)))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(general.mintOneToMultipleRecipients(recipientAddresses.slice(3))).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.setLimitSupply(0)).to.emit(general, "LimitSupplySet").withArgs(0);
-
- await expect(general.mintOneToMultipleRecipients(recipientAddresses.slice(3)))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address, editionsMetadataOwner.address];
- await expect(general.mintOneToMultipleRecipients(recipientAddresses))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsMetadataOwner.address, 3);
-
- let i = 1;
- for (const recipient of recipientAddresses) {
- expect((await general.balanceOf(recipient)).toNumber()).to.equal(1);
- expect(await general.ownerOf(i)).to.equal(recipient);
- i += 1;
- }
- });
-
- it("Minter can mint validly (running variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address];
- for (let i = 0; i < 2; i++) {
- await expect(general.mintOneToMultipleRecipients(recipientAddresses))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 2 + 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 2 + 2);
-
- let j = 1;
- for (const recipient of recipientAddresses) {
- expect((await general.balanceOf(recipient)).toNumber()).to.equal(i + 1);
- expect(await general.ownerOf(i * 2 + j)).to.equal(recipient);
- j += 1;
- }
- }
- });
- });
-
- describe("mintSameAmountToMultipleRecipients", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintSameAmountToMultipleRecipients([fan1.address], 2)).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintSameAmountToMultipleRecipients([fan1.address], 2)).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses, 2)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 1),
- ).to.be.revertedWithCustomError(general, Errors.OverLimitSupply);
-
- await expect(general.setLimitSupply(0)).to.emit(general, "LimitSupplySet").withArgs(0);
-
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 7)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 8);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses, 2)).to.be.revertedWithCustomError(
- general,
- Errors.OverLimitSupply,
- );
-
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- general.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2),
- ).to.be.revertedWithCustomError(general, Errors.OverLimitSupply);
- });
-
- it("Minter can mint validly (complex variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address];
-
- for (let i = 0; i < 2; i++) {
- await expect(general.mintSameAmountToMultipleRecipients(recipientAddresses, 2))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 4 + 3)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 4 + 4);
-
- let j = 0;
- for (const recipient of recipientAddresses) {
- expect((await general.balanceOf(recipient)).toNumber()).to.equal((i + 1) * 2);
- expect(await general.ownerOf(i * 4 + j * 2 + 1)).to.equal(recipient);
- expect(await general.ownerOf(i * 4 + j * 2 + 2)).to.equal(recipient);
- j += 1;
- }
-
- await expect(general.setLimitSupply(8)).to.emit(general, "LimitSupplySet").withArgs(8);
- }
- });
- });
-
- describe("mintSpecificTokenToOneRecipient", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 1)).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 2)).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint token not in range, but can mint in-range ones", async function () {
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 1)).to.emit(general, "Transfer");
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 2)).to.emit(general, "Transfer");
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 5)).to.be.revertedWithCustomError(
- general,
- Errors.TokenNotInRange,
- );
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 3)).to.emit(general, "Transfer");
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 4)).to.emit(general, "Transfer");
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 5)).to.be.revertedWithCustomError(
- general,
- Errors.TokenNotInRange,
- );
-
- await expect(general.setLimitSupply(0)).to.emit(general, "LimitSupplySet").withArgs(0);
-
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 5)).to.emit(general, "Transfer");
- });
-
- it("Cannot mint already minted token", async function () {
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 4)).to.emit(general, "Transfer");
- await expect(general.mintSpecificTokenToOneRecipient(fan1.address, 4)).to.be.revertedWith(
- "ERC721: token minted",
- );
- });
- });
-
- describe("mintSpecificTokensToOneRecipient", function () {
- it("Non minter cannot call", async function () {
- general = general.connect(fan1);
-
- await expect(general.mintSpecificTokensToOneRecipient(fan1.address, [1, 2])).to.be.revertedWithCustomError(
- general,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(general.freezeMints()).to.emit(general, "MintsFrozen");
-
- await expect(general.mintSpecificTokensToOneRecipient(fan1.address, [2])).to.be.revertedWithCustomError(
- general,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint token not in range, but can mint in-range ones", async function () {
- await expect(general.mintSpecificTokensToOneRecipient(fan1.address, [1, 4]))
- .to.emit(general, "Transfer")
- .to.emit(general, "Transfer");
- await expect(general.mintSpecificTokensToOneRecipient(fan1.address, [2, 5])).to.be.revertedWithCustomError(
- general,
- Errors.TokenNotInRange,
- );
- await expect(general.mintSpecificTokensToOneRecipient(fan1.address, [2, 3]))
- .to.emit(general, "Transfer")
- .to.emit(general, "Transfer");
-
- await expect(general.setLimitSupply(0)).to.emit(general, "LimitSupplySet").withArgs(0);
-
- await expect(general.mintSpecificTokensToOneRecipient(fan1.address, [6, 19, 20]))
- .to.emit(general, "Transfer")
- .to.emit(general, "Transfer")
- .to.emit(general, "Transfer");
- });
-
- it("Cannot mint already minted token", async function () {
- await expect(general.mintSpecificTokensToOneRecipient(fan1.address, [4, 1])).to.emit(general, "Transfer");
- await expect(general.mintSpecificTokensToOneRecipient(fan1.address, [2, 1, 3])).to.be.revertedWith(
- "ERC721: token minted",
- );
- });
- });
-
- describe("Contract metadata updates", function () {
- it("Owner can change the contract level metadata", async function () {
- general = general.connect(owner);
-
- await expect(general.setContractMetadata("new name", "new symbol", "new contract uri"))
- .to.emit(observability, "ContractMetadataSet")
- .withArgs(general.address, "new name", "new symbol", "new contract uri");
-
- expect(await general.name()).to.equal("new name");
- expect(await general.symbol()).to.equal("new symbol");
- expect(await general.contractURI()).to.equal("new contract uri");
- });
-
- it("Non-owners cannot change the contract level metadata", async function () {
- general = general.connect(fan1);
- await expect(general.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- general = general.connect(editionsMetadataOwner);
- await expect(general.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
- });
- });
-
- it("Can deploy with direct mint", async function () {
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- owner,
- { ...DEFAULT_ONCHAIN_MINT_VECTOR, maxUserClaimableViaVector: 2 },
- );
-
- expect((await mintManager.getAbridgedVector(1)).slice(0, 14)).to.deep.equal([
- general.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- owner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxTotalClaimableViaVector,
- 0,
- ethers.constants.AddressZero,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 2,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- 0,
- false,
- false,
- DEFAULT_ONCHAIN_MINT_VECTOR.allowlistRoot,
- ]);
-
- await expect(mintManager.vectorMint721(1, 2, owner.address, { value: ethers.utils.parseEther("0.0008").mul(2) }))
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32), general.address, true, 2);
-
- await expect(mintManager.vectorMint721(1, 1, owner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(1, owner.address)).to.equal(2);
- });
-});
diff --git a/test/ERC721GenerativeTest.ts b/test/ERC721GenerativeTest.ts
deleted file mode 100644
index 8070b35..0000000
--- a/test/ERC721GenerativeTest.ts
+++ /dev/null
@@ -1,886 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- BitRotGenerative,
- BitRotGenerativeTest,
- BitRotGenerativeTest__factory,
- BitRotGenerative__factory,
- ERC721GeneralSequence,
- ERC721GenerativeOnchain,
- ERC721GenerativeOnchain__factory,
- FileDeployer,
- MinimalForwarder,
- MintManager,
- Observability,
- OwnerOnlyTokenManager,
- TestHighlightRenderer,
- TotalLockedTokenManager,
-} from "../types";
-import { Errors } from "./__utils__/data";
-import { DEFAULT_ONCHAIN_MINT_VECTOR, setupGenerative, setupSystem } from "./__utils__/helpers";
-
-describe("ERC721Generative functionality", () => {
- let totalLockedTokenManager: TotalLockedTokenManager;
- let ownerOnlyTokenManager: OwnerOnlyTokenManager;
- let generative: ERC721GeneralSequence;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- owner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let mintManager: MintManager;
- let observability: Observability;
- let trustedForwarder: MinimalForwarder;
- let generativeImplementation: string;
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, owner, fan1] =
- await ethers.getSigners();
- const {
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- generativeImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- owner,
- );
-
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- observability = observabilityInstance;
- generativeImplementation = generativeImplementationAddress;
-
- totalLockedTokenManager = await (await ethers.getContractFactory("TotalLockedTokenManager")).deploy();
- ownerOnlyTokenManager = await (await ethers.getContractFactory("OwnerOnlyTokenManager")).deploy();
- });
-
- beforeEach(async () => {
- generative = await setupGenerative(
- observability.address,
- generativeImplementation,
- trustedForwarder.address,
- mintManager.address,
- owner,
- );
- });
-
- describe("URIs", function () {
- beforeEach(async () => {
- // mint a couple tokens to validate uris
- await expect(generative.registerMinter(owner.address)).to.emit(generative, "MinterRegistrationChanged");
-
- await expect(generative.mintSameAmountToMultipleRecipients([owner.address, fan1.address], 2)).to.emit(
- generative,
- "Transfer",
- );
- });
-
- it("Base uri concatenation should be respected for tokens without overwritten uris", async function () {
- for (let i = 1; i <= 4; i++) {
- expect(await generative.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
- });
-
- describe("setBaseUri", function () {
- it("Cannot set to empty string", async function () {
- await expect(generative.setBaseURI("")).to.be.revertedWithCustomError(generative, Errors.EmptyString);
- });
-
- it("If default manager is non-existent, invocation from non-owner fails", async function () {
- generative = generative.connect(fan1);
- await expect(generative.setBaseURI("testing")).to.be.revertedWithCustomError(generative, Errors.Unauthorized);
- });
-
- it("If default manager is non-existent, invocation from owner succeeds", async function () {
- await expect(generative.setBaseURI("testing")).to.emit(generative, "BaseURISet").withArgs("baseUri", "testing");
-
- for (let i = 1; i <= 4; i++) {
- expect(await generative.tokenURI(i)).to.equal(`testing/${i}`);
- }
- });
-
- it("If default manager exists, invocation respects token manager", async function () {
- await expect(generative.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.emit(
- generative,
- "DefaultTokenManagerChanged",
- );
-
- generative = generative.connect(fan1);
- await expect(generative.setBaseURI("testing")).to.be.revertedWithCustomError(generative, Errors.Unauthorized);
-
- generative = generative.connect(owner);
- await expect(generative.setBaseURI("testing")).to.emit(generative, "BaseURISet").withArgs("baseUri", "testing");
-
- for (let i = 1; i <= 4; i++) {
- expect(await generative.tokenURI(i)).to.equal(`testing/${i}`);
- }
- });
- });
-
- describe("setTokenUris", function () {
- it("ids and uris length cannot mismatch", async function () {
- await expect(generative.setTokenURIs([1, 2], ["test"])).to.be.revertedWithCustomError(
- generative,
- Errors.MismatchedArrayLengths,
- );
- });
-
- it("If token manager is non-existent, invocation from non-owner fails", async function () {
- generative = generative.connect(fan1);
- await expect(generative.setTokenURIs([1, 2], ["testing1", "testing2"])).to.be.revertedWithCustomError(
- generative,
- Errors.Unauthorized,
- );
- });
-
- it("If tokens manager is non-existent, invocation owner succeeds", async function () {
- await expect(generative.setTokenURIs([1, 2], ["testing1", "testing2"]))
- .to.emit(generative, "TokenURIsSet")
- .withArgs([1, 2], ["testing1", "testing2"])
- .to.emit(observability, "TokenURIsSet")
- .withArgs(generative.address, [1, 2], ["testing1", "testing2"]);
-
- for (let i = 1; i <= 2; i++) {
- expect(await generative.tokenURI(i)).to.equal(`testing${i}`);
- }
- for (let i = 3; i <= 4; i++) {
- expect(await generative.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
- });
-
- it("If token manager exists either as a default or an overwriting token manager, invocation respects token manager", async function () {
- await expect(generative.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.emit(
- generative,
- "DefaultTokenManagerChanged",
- );
-
- generative = generative.connect(fan1);
- await expect(generative.setTokenURIs([1, 2], ["testing1", "testing2"])).to.be.revertedWithCustomError(
- generative,
- Errors.Unauthorized,
- );
-
- generative = generative.connect(owner);
-
- await expect(generative.setTokenURIs([1, 2], ["testing1", "testing2"]))
- .to.emit(generative, "TokenURIsSet")
- .withArgs([1, 2], ["testing1", "testing2"]);
-
- for (let i = 1; i <= 2; i++) {
- expect(await generative.tokenURI(i)).to.equal(`testing${i}`);
- }
- for (let i = 3; i <= 4; i++) {
- expect(await generative.tokenURI(i)).to.equal(`baseUri/${i}`);
- }
-
- await expect(
- generative.setGranularTokenManagers(
- [1, 2],
- [totalLockedTokenManager.address, totalLockedTokenManager.address],
- ),
- ).to.emit(generative, "GranularTokenManagersSet");
-
- await expect(
- generative.setTokenURIs([1, 2, 3], ["testing1", "testing2", "testing3"]),
- ).to.be.revertedWithCustomError(generative, Errors.Unauthorized);
-
- await expect(generative.setTokenURIs([2, 3], ["testing2", "testing3"])).to.be.revertedWithCustomError(
- generative,
- Errors.Unauthorized,
- );
-
- await expect(generative.setTokenURIs([1, 3], ["testing1", "testing3"])).to.be.revertedWithCustomError(
- generative,
- Errors.Unauthorized,
- );
-
- await expect(generative.setTokenURIs([3], ["testing3"]))
- .to.emit(generative, "TokenURIsSet")
- .withArgs([3], ["testing3"]);
-
- for (let i = 1; i <= 3; i++) {
- expect(await generative.tokenURI(i)).to.equal(`testing${i}`);
- }
- expect(await generative.tokenURI(4)).to.equal(`baseUri/4`);
- });
- });
- });
-
- describe("Minting", function () {
- beforeEach(async function () {
- await expect(generative.registerMinter(owner.address));
-
- expect(await generative.tokenManager(0)).to.eql(ethers.constants.AddressZero);
-
- await expect(generative.setLimitSupply(4)).to.emit(generative, "LimitSupplySet").withArgs(4);
- });
-
- describe("mintOneToOneRecipient", function () {
- it("Non minter cannot call", async function () {
- generative = generative.connect(fan1);
-
- await expect(generative.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- generative,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(generative.freezeMints()).to.emit(generative, "MintsFrozen");
-
- await expect(generative.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- generative,
- Errors.MintFrozen,
- );
- });
-
- it("Can mint validly up until limit supply", async function () {
- for (let i = 1; i <= 4; i++) {
- await expect(generative.mintOneToOneRecipient(fan1.address))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await generative.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await generative.ownerOf(i)).to.equal(fan1.address);
- }
-
- await expect(generative.mintOneToOneRecipient(fan1.address)).to.be.revertedWithCustomError(
- generative,
- Errors.OverLimitSupply,
- );
-
- await expect(generative.setLimitSupply(0)).to.emit(generative, "LimitSupplySet").withArgs(0);
-
- for (let i = 5; i <= 8; i++) {
- await expect(generative.mintOneToOneRecipient(fan1.address))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await generative.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await generative.ownerOf(i)).to.equal(fan1.address);
- }
- });
- });
-
- describe("mintAmountToOneRecipient", function () {
- it("Non minter cannot call", async function () {
- generative = generative.connect(fan1);
-
- await expect(generative.mintAmountToOneRecipient(fan1.address, 2)).to.be.revertedWithCustomError(
- generative,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(generative.freezeMints()).to.emit(generative, "MintsFrozen");
-
- await expect(generative.mintAmountToOneRecipient(fan1.address, 2)).to.be.revertedWithCustomError(
- generative,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- await expect(generative.mintAmountToOneRecipient(fan1.address, 6)).to.be.revertedWithCustomError(
- generative,
- Errors.OverLimitSupply,
- );
-
- await expect(generative.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(generative.mintAmountToOneRecipient(fan1.address, 3)).to.be.revertedWithCustomError(
- generative,
- Errors.OverLimitSupply,
- );
-
- await expect(generative.setLimitSupply(0)).to.emit(generative, "LimitSupplySet").withArgs(0);
-
- await expect(generative.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- await expect(generative.mintAmountToOneRecipient(fan1.address, 3))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- expect((await generative.balanceOf(fan1.address)).toNumber()).to.equal(3);
-
- for (let i = 1; i <= 3; i++) {
- expect(await generative.ownerOf(i)).to.equal(fan1.address);
- }
- });
-
- it("Minter can mint validly (running variation)", async function () {
- for (let i = 0; i < 2; i++) {
- await expect(generative.mintAmountToOneRecipient(fan1.address, 2))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 2);
-
- expect((await generative.balanceOf(fan1.address)).toNumber()).to.equal((i + 1) * 2);
-
- for (let j = 1; j <= (i + 1) * 2; j++) {
- expect(await generative.ownerOf(j)).to.equal(fan1.address);
- }
- }
- });
- });
-
- describe("mintOneToMultipleRecipients", function () {
- it("Non minter cannot call", async function () {
- generative = generative.connect(fan1);
-
- await expect(generative.mintOneToMultipleRecipients([fan1.address])).to.be.revertedWithCustomError(
- generative,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(generative.freezeMints()).to.emit(generative, "MintsFrozen");
-
- await expect(generative.mintOneToMultipleRecipients([fan1.address])).to.be.revertedWithCustomError(
- generative,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address, fan1.address, fan1.address, fan1.address];
- await expect(generative.mintOneToMultipleRecipients(recipientAddresses)).to.be.revertedWithCustomError(
- generative,
- Errors.OverLimitSupply,
- );
-
- await expect(generative.mintOneToMultipleRecipients(recipientAddresses.slice(3)))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(generative.mintOneToMultipleRecipients(recipientAddresses.slice(3))).to.be.revertedWithCustomError(
- generative,
- Errors.OverLimitSupply,
- );
-
- await expect(generative.setLimitSupply(0)).to.emit(generative, "LimitSupplySet").withArgs(0);
-
- await expect(generative.mintOneToMultipleRecipients(recipientAddresses.slice(3)))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address, editionsMetadataOwner.address];
- await expect(generative.mintOneToMultipleRecipients(recipientAddresses))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, 2)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsMetadataOwner.address, 3);
-
- let i = 1;
- for (const recipient of recipientAddresses) {
- expect((await generative.balanceOf(recipient)).toNumber()).to.equal(1);
- expect(await generative.ownerOf(i)).to.equal(recipient);
- i += 1;
- }
- });
-
- it("Minter can mint validly (running variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address];
- for (let i = 0; i < 2; i++) {
- await expect(generative.mintOneToMultipleRecipients(recipientAddresses))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 2 + 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 2 + 2);
-
- let j = 1;
- for (const recipient of recipientAddresses) {
- expect((await generative.balanceOf(recipient)).toNumber()).to.equal(i + 1);
- expect(await generative.ownerOf(i * 2 + j)).to.equal(recipient);
- j += 1;
- }
- }
- });
- });
-
- describe("mintSameAmountToMultipleRecipients", function () {
- it("Non minter cannot call", async function () {
- generative = generative.connect(fan1);
-
- await expect(generative.mintSameAmountToMultipleRecipients([fan1.address], 2)).to.be.revertedWithCustomError(
- generative,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(generative.freezeMints()).to.emit(generative, "MintsFrozen");
-
- await expect(generative.mintSameAmountToMultipleRecipients([fan1.address], 2)).to.be.revertedWithCustomError(
- generative,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than limitSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(
- generative.mintSameAmountToMultipleRecipients(recipientAddresses, 2),
- ).to.be.revertedWithCustomError(generative, Errors.OverLimitSupply);
-
- await expect(generative.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- generative.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 1),
- ).to.be.revertedWithCustomError(generative, Errors.OverLimitSupply);
-
- await expect(generative.setLimitSupply(0))
- .to.emit(generative, "LimitSupplySet")
- .withArgs(0)
- .to.emit(observability, "LimitSupplySet")
- .withArgs(generative.address, 0);
-
- await expect(generative.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 7)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 8);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(
- generative.mintSameAmountToMultipleRecipients(recipientAddresses, 2),
- ).to.be.revertedWithCustomError(generative, Errors.OverLimitSupply);
-
- await expect(generative.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- generative.mintSameAmountToMultipleRecipients(recipientAddresses.slice(1), 2),
- ).to.be.revertedWithCustomError(generative, Errors.OverLimitSupply);
- });
-
- it("Minter can mint validly (complex variation)", async function () {
- const recipientAddresses = [fan1.address, owner.address];
-
- for (let i = 0; i < 2; i++) {
- await expect(generative.mintSameAmountToMultipleRecipients(recipientAddresses, 2))
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 2)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 4 + 3)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, owner.address, i * 4 + 4);
-
- let j = 0;
- for (const recipient of recipientAddresses) {
- expect((await generative.balanceOf(recipient)).toNumber()).to.equal((i + 1) * 2);
- expect(await generative.ownerOf(i * 4 + j * 2 + 1)).to.equal(recipient);
- expect(await generative.ownerOf(i * 4 + j * 2 + 2)).to.equal(recipient);
- j += 1;
- }
-
- await expect(generative.setLimitSupply(8)).to.emit(generative, "LimitSupplySet").withArgs(8);
- }
- });
- });
- });
-
- it("Can deploy with direct mint", async function () {
- generative = await setupGenerative(
- observability.address,
- generativeImplementation,
- trustedForwarder.address,
- mintManager.address,
- owner,
- { ...DEFAULT_ONCHAIN_MINT_VECTOR, maxUserClaimableViaVector: 2 },
- null,
- false,
- 0,
- ethers.constants.AddressZero,
- fan1.address,
- 1000,
- );
-
- expect((await generative.royaltyInfo(1, 10000)).royaltyAmount.toNumber()).to.equal(1000);
- expect((await generative.royaltyInfo(1, 10000)).receiver).to.equal(fan1.address);
-
- expect((await mintManager.getAbridgedVector(1)).slice(0, 14)).to.deep.equal([
- generative.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- owner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxTotalClaimableViaVector,
- 0,
- ethers.constants.AddressZero,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 2,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- 0,
- false,
- false,
- DEFAULT_ONCHAIN_MINT_VECTOR.allowlistRoot,
- ]);
-
- await expect(mintManager.vectorMint721(1, 2, owner.address, { value: ethers.utils.parseEther("0.0008").mul(2) }))
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32), generative.address, true, 2);
-
- await expect(mintManager.vectorMint721(1, 1, owner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(1, owner.address)).to.equal(2);
- });
-
- describe("OCS onchain contracts", function () {
- let ocsERC721: ERC721GenerativeOnchain;
- let fileDeployer: FileDeployer;
- const file = `
- function _readBytecode(
- address pointer,
- uint256 start,
- uint256 size
- ) private view returns (bytes memory data) {
- /// @solidity memory-safe-assembly
- assembly {
- // Get a pointer to some free memory.
- data := mload(0x40)
-
- // Update the free memory pointer to prevent overriding our data.
- // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
- // Adding 31 to size and running the result through the logic above ensures
- // the memory pointer remains word-aligned, following the Solidity convention.
- mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))
-
- // Store the size of the data in the first 32 byte chunk of free memory.
- mstore(data, size)
-
- // Copy the code into memory right after the 32 bytes we used to store the size.
- extcodecopy(pointer, add(data, 32), start, size)
- }
- }
- `;
- const secondFile = `
- function _revert(bytes4 errorSelector) internal pure virtual {
- assembly {
- mstore(0x00, errorSelector)
- revert(0x00, 0x04)
- }
- }
- `;
- const fileAddresses1: string[] = [];
- const fileAddresses2: string[] = [];
-
- before(async () => {
- // deploy FileDeployer
- const FileDeployer = await ethers.getContractFactory("FileDeployer");
- fileDeployer = await FileDeployer.deploy();
- await fileDeployer.deployed();
-
- const OCSERC721Implementation = await ethers.getContractFactory("ERC721GenerativeOnchain");
- const ocsERC721Implementation = await OCSERC721Implementation.deploy();
- await ocsERC721Implementation.deployed();
-
- // deploy instance of ocs721
- ocsERC721 = ERC721GenerativeOnchain__factory.connect(
- (
- await setupGenerative(
- observability.address,
- ocsERC721Implementation.address,
- trustedForwarder.address,
- mintManager.address,
- owner,
- )
- ).address,
- owner,
- );
- });
-
- it("Can deploy files via the file deployer", async function () {
- const filePart1 = file.slice(0, file.length / 3);
- const filePart2 = file.slice(file.length / 3, (file.length * 2) / 3);
- const filePart3 = file.slice((file.length * 2) / 3);
-
- const tx1 = await fileDeployer.deploy(
- ["1", "2"].map(name => {
- return ethers.utils.formatBytes32String(name);
- }),
- [filePart1, filePart2],
- );
- const receipt1 = await tx1.wait();
- fileAddresses1.push("0x" + receipt1.logs[0].topics[2].slice(26));
- fileAddresses1.push("0x" + receipt1.logs[1].topics[2].slice(26));
-
- const tx3 = await fileDeployer.deploy(
- ["3"].map(name => {
- return ethers.utils.formatBytes32String(name);
- }),
- [filePart3],
- );
- const receipt3 = await tx3.wait();
- fileAddresses1.push("0x" + receipt3.logs[0].topics[2].slice(26));
-
- const secondFilePart1 = secondFile.slice(0, secondFile.length / 2);
- const secondFilePart2 = secondFile.slice(secondFile.length / 2);
- const tx4 = await fileDeployer.deploy(
- ["4"].map(name => {
- return ethers.utils.formatBytes32String(name);
- }),
- [secondFilePart1],
- );
- const receipt4 = await tx4.wait();
- fileAddresses2.push("0x" + receipt4.logs[0].topics[2].slice(26));
-
- const tx5 = await fileDeployer.deploy(
- ["5"].map(name => {
- return ethers.utils.formatBytes32String(name);
- }),
- [secondFilePart2],
- );
- const receipt5 = await tx5.wait();
- fileAddresses2.push("0x" + receipt5.logs[0].topics[2].slice(26));
- });
-
- it("Owner can register files and view their contents + bytecode addresses", async function () {
- await expect(ocsERC721.addFile("readBytecodeSnippet.sol", fileAddresses1)).to.not.be.reverted;
- await expect(ocsERC721.addFile("revertSnippet.sol", fileAddresses2)).to.not.be.reverted;
-
- expect(await ocsERC721.files()).to.eql(["readBytecodeSnippet.sol", "revertSnippet.sol"]);
- expect((await ocsERC721.fileStorage("readBytecodeSnippet.sol")).map(address => address.toLowerCase())).to.eql(
- fileAddresses1.map(address => address.toLowerCase()),
- );
- expect((await ocsERC721.fileStorage("revertSnippet.sol")).map(address => address.toLowerCase())).to.eql(
- fileAddresses2.map(address => address.toLowerCase()),
- );
-
- // viewing contents
- expect(await ocsERC721.fileContents("readBytecodeSnippet.sol")).to.eql(file);
- expect(await ocsERC721.fileContents("revertSnippet.sol")).to.eql(secondFile);
- });
-
- it("Cannot register an already registered file", async function () {
- await expect(ocsERC721.addFile("readBytecodeSnippet.sol", fileAddresses1)).to.be.revertedWithCustomError(
- ocsERC721,
- "FileAlreadyRegistered",
- );
- });
-
- it("Non-owner cannot register a file", async function () {
- ocsERC721 = ocsERC721.connect(fan1);
- await expect(ocsERC721.addFile("readBytecodeSnippet3.sol", fileAddresses1)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- ocsERC721 = ocsERC721.connect(owner);
- });
-
- it("Cannot remove a non-registered file", async function () {
- await expect(ocsERC721.removeFile("readBytecodeSnippet3.sol")).to.be.revertedWithCustomError(
- ocsERC721,
- "FileNotRegistered",
- );
- });
-
- it("Non-owner cannot remove a file", async function () {
- ocsERC721 = ocsERC721.connect(fan1);
- await expect(ocsERC721.removeFile("readBytecodeSnippet.sol")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- ocsERC721 = ocsERC721.connect(owner);
- });
-
- it("Owner can remove a file in any position", async function () {
- await expect(ocsERC721.addFile("readBytecodeSnippet2.sol", fileAddresses1)).to.not.be.reverted;
- expect(await ocsERC721.files()).to.eql([
- "readBytecodeSnippet.sol",
- "revertSnippet.sol",
- "readBytecodeSnippet2.sol",
- ]);
-
- await expect(ocsERC721.removeFile("readBytecodeSnippet.sol")).to.not.be.reverted;
- expect(await ocsERC721.files()).to.eql(["revertSnippet.sol", "readBytecodeSnippet2.sol"]);
-
- await expect(ocsERC721.removeFile("readBytecodeSnippet2.sol")).to.not.be.reverted;
- expect(await ocsERC721.files()).to.eql(["revertSnippet.sol"]);
-
- await expect(ocsERC721.addFile("readBytecodeSnippet.sol", fileAddresses1)).to.not.be.reverted;
- await expect(ocsERC721.addFile("readBytecodeSnippet2.sol", fileAddresses1)).to.not.be.reverted;
- expect(await ocsERC721.files()).to.eql([
- "revertSnippet.sol",
- "readBytecodeSnippet.sol",
- "readBytecodeSnippet2.sol",
- ]);
-
- await expect(ocsERC721.removeFile("readBytecodeSnippet.sol")).to.not.be.reverted;
- expect(await ocsERC721.files()).to.eql(["revertSnippet.sol", "readBytecodeSnippet2.sol"]);
- });
- });
-
- describe("Custom", function () {
- let bitRotTest: BitRotGenerativeTest;
- let bitRotGenerative: BitRotGenerative;
-
- before(async () => {
- const bitRotGenerativeImpl = await (await ethers.getContractFactory("BitRotGenerative")).deploy();
- bitRotGenerative = BitRotGenerative__factory.connect(
- (
- await setupGenerative(
- observability.address,
- bitRotGenerativeImpl.address,
- trustedForwarder.address,
- mintManager.address,
- owner,
- )
- ).address,
- owner,
- );
- bitRotTest = BitRotGenerativeTest__factory.connect(
- (await (await ethers.getContractFactory("BitRotGenerativeTest")).deploy(bitRotGenerative.address)).address,
- owner,
- );
- await expect(bitRotGenerative.registerMinter(bitRotTest.address)).to.not.be.reverted;
- });
-
- it("BitRot test passes", async function () {
- await expect(bitRotTest.test()).to.not.be.reverted;
- });
- });
-
- describe("Custom renderer", function () {
- let testHighlightRenderer: TestHighlightRenderer;
-
- beforeEach(async () => {
- await expect(generative.registerMinter(owner.address)).to.emit(generative, "MinterRegistrationChanged");
- const TestHighlightRenderer = await ethers.getContractFactory("TestHighlightRenderer");
-
- testHighlightRenderer = await TestHighlightRenderer.deploy();
- await testHighlightRenderer.deployed();
- });
-
- it("Only owner can set custom renderer config", async function () {
- generative = generative.connect(fan1);
- await expect(
- generative.setCustomRenderer({
- renderer: testHighlightRenderer.address,
- processMintDataOnRenderer: true,
- }),
- ).to.be.revertedWith("Ownable: caller is not the owner");
- });
-
- it("Invalid custom renderer config not allowed", async function () {
- generative = generative.connect(owner);
- await expect(
- generative.setCustomRenderer({
- renderer: ethers.constants.AddressZero,
- processMintDataOnRenderer: true,
- }),
- ).to.be.revertedWith("Invalid input");
- });
-
- it("Custom renderer can process mint data, then re-use it on tokenURI query", async function () {
- await expect(
- generative.setCustomRenderer({
- renderer: testHighlightRenderer.address,
- processMintDataOnRenderer: true,
- }),
- ).to.not.be.reverted;
-
- await expect(generative.mintOneToOneRecipient(owner.address)).to.not.be.reverted;
- await expect(generative.mintAmountToOneRecipient(owner.address, 2)).to.not.be.reverted;
- await expect(generative.mintOneToMultipleRecipients([owner.address, fan1.address])).to.not.be.reverted;
- await expect(generative.mintSameAmountToMultipleRecipients([owner.address, fan1.address], 2)).to.not.be.reverted;
-
- const tokenIds = Array.from({ length: 9 }, (_, i) => i + 1);
- const seedDetailsPerToken = await Promise.all(
- tokenIds.map(async tokenId => {
- const seedDetails = await testHighlightRenderer.getSeedDetails(tokenId, 10, generative.address);
- expect(seedDetails.blockTimestamp.eq(0)).to.be.false;
-
- return { ...seedDetails, tokenId };
- }),
- );
-
- await expect(
- generative.setCustomRenderer({
- renderer: testHighlightRenderer.address,
- processMintDataOnRenderer: false,
- }),
- ).to.not.be.reverted;
-
- await Promise.all(
- seedDetailsPerToken.map(async seedDetails => {
- const uri = await generative.tokenURI(seedDetails.tokenId);
- const predictedUri = await testHighlightRenderer.concatenateSeedDetails(
- {
- previousBlockHash: seedDetails.previousBlockHash,
- blockTimestamp: seedDetails.blockTimestamp,
- },
- seedDetails.tokenId,
- );
- expect(uri).to.equal(predictedUri);
- }),
- );
- });
- });
-});
diff --git a/test/ERC721SingleEditionDFSTest.ts b/test/ERC721SingleEditionDFSTest.ts
deleted file mode 100644
index 46149c1..0000000
--- a/test/ERC721SingleEditionDFSTest.ts
+++ /dev/null
@@ -1,551 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import { ERC721SingleEditionDFS, MinimalForwarder, MintManager, Observability } from "../types";
-import { Errors } from "./__utils__/data";
-import { DEFAULT_ONCHAIN_MINT_VECTOR, setupSingleEditionDFS, setupSystem } from "./__utils__/helpers";
-
-describe("ERC721SingleEdition functionality", () => {
- let editions: ERC721SingleEditionDFS;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let observability: Observability;
- let mintManager: MintManager;
- let trustedForwarder: MinimalForwarder;
- let singleEditionImplementation: string;
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, editionsOwner, fan1] =
- await ethers.getSigners();
- const {
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- singleEditionDFSImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
-
- mintManager = mintManagerProxy;
- observability = observabilityInstance;
- trustedForwarder = minimalForwarder;
- singleEditionImplementation = singleEditionDFSImplementationAddress;
- });
-
- beforeEach(async () => {
- editions = await setupSingleEditionDFS(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- editionsOwner,
- 5,
- "",
- "NM",
- );
- });
-
- describe("Minting", function () {
- beforeEach(async function () {
- await expect(editions.registerMinter(editionsOwner.address));
-
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- });
-
- describe("mintOneToRecipient", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintOneToRecipient(1, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Can mint validly up until max supply", async function () {
- for (let i = 1; i <= 5; i++) {
- await expect(editions.mintOneToRecipient(0, fan1.address))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await editions.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, i, 1]);
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(i);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
- });
-
- describe("mintAmountToRecipient", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintAmountToRecipient(1, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 6)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- expect((await editions.balanceOf(fan1.address)).toNumber()).to.equal(3);
-
- for (let i = 1; i <= 3; i++) {
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, 3, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(3);
- expect(res[0][0][3].toNumber()).to.equal(1);
- });
-
- it("Minter can mint validly (running variation)", async function () {
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 2);
-
- expect((await editions.balanceOf(fan1.address)).toNumber()).to.equal((i + 1) * 2);
-
- for (let j = 1; j <= (i + 1) * 2; j++) {
- expect(await editions.ownerOf(j)).to.equal(fan1.address);
- expect((await editions.getEditionId(j)).toNumber()).to.equal(0);
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, (i + 1) * 2, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 2);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("mintOneToRecipients", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintOneToRecipients(0, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintOneToRecipients(1, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintOneToRecipients(0, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address, fan1.address, fan1.address, fan1.address];
- await expect(editions.mintOneToRecipients(0, recipientAddresses)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintOneToRecipients(0, recipientAddresses.slice(0, 3)))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(editions.mintOneToRecipients(0, recipientAddresses.slice(0, 3))).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, editionsMetadataOwner.address, editionsOwner.address];
- await expect(editions.mintOneToRecipients(0, recipientAddresses))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsMetadataOwner.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 3);
-
- let i = 1;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal(1);
- expect(await editions.ownerOf(i)).to.equal(recipient);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- i += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, 3, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(3);
- expect(res[0][0][3].toNumber()).to.equal(1);
- });
-
- it("Minter can mint validly (running variation)", async function () {
- const recipientAddresses = [fan1.address, editionsOwner.address];
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintOneToRecipients(0, recipientAddresses))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 2 + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 2 + 2);
-
- let j = 1;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal(i + 1);
- expect(await editions.ownerOf(i * 2 + j)).to.equal(recipient);
- expect((await editions.getEditionId(i * 2 + j)).toNumber()).to.equal(0);
- j += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 5, (i + 1) * 2, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 2);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("mintAmountToRecipients", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintAmountToRecipients(0, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintAmountToRecipients(1, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintAmountToRecipients(0, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 1),
- ).to.be.revertedWithCustomError(editions, Errors.SoldOut);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2),
- ).to.be.revertedWithCustomError(editions, Errors.SoldOut);
- });
-
- it("Minter can mint validly (complex variation)", async function () {
- editions = await setupSingleEditionDFS(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- editionsOwner,
- 8,
- "",
- "NM",
- );
- await expect(editions.registerMinter(editionsOwner.address)).to.emit(editions, "MinterRegistrationChanged");
- const recipientAddresses = [fan1.address, editionsOwner.address];
-
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 4 + 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 4 + 4);
-
- let j = 0;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal((i + 1) * 2);
- expect(await editions.ownerOf(i * 4 + j * 2 + 1)).to.equal(recipient);
- expect(await editions.ownerOf(i * 4 + j * 2 + 2)).to.equal(recipient);
- expect((await editions.getEditionId(i * 4 + j * 2 + 1)).toNumber()).to.equal(0);
- expect((await editions.getEditionId(i * 4 + j * 2 + 2)).toNumber()).to.equal(0);
- j += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["", 8, (i + 1) * 4, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("");
- expect(res[0][0][1].toNumber()).to.equal(8);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 4);
- expect(res[0][0][3].toNumber()).to.equal(1);
- expect(res[1][0]).to.equal("editionUri");
- }
- });
- });
-
- describe("Contract metadata updates", function () {
- it("Owner can change the contract level metadata", async function () {
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri"))
- .to.emit(observability, "ContractMetadataSet")
- .withArgs(editions.address, "new name", "new symbol", "new contract uri");
-
- expect(await editions.name()).to.equal("new name");
- expect(await editions.symbol()).to.equal("new symbol");
- expect(await editions.contractURI()).to.equal("new contract uri");
- });
-
- it("Non-owners cannot change the contract level metadata", async function () {
- editions = editions.connect(fan1);
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- editions = editions.connect(editionsMetadataOwner);
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
- });
- });
-
- it("Can deploy with direct mint", async function () {
- editions = await setupSingleEditionDFS(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- editionsOwner,
- 100,
- "name",
- "symbol",
- { ...DEFAULT_ONCHAIN_MINT_VECTOR, maxUserClaimableViaVector: 2 },
- );
-
- expect((await mintManager.getAbridgedVector(1)).slice(0, 14)).to.deep.equal([
- editions.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- editionsOwner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxTotalClaimableViaVector,
- 0,
- ethers.constants.AddressZero,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 2,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- 0,
- true,
- false,
- DEFAULT_ONCHAIN_MINT_VECTOR.allowlistRoot,
- ]);
-
- await expect(
- mintManager.vectorMint721(1, 2, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- )
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32), editions.address, true, 2);
-
- await expect(mintManager.vectorMint721(1, 1, editionsOwner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(1, editionsOwner.address)).to.equal(2);
- });
-});
diff --git a/test/ERC721SingleEditionTest.ts b/test/ERC721SingleEditionTest.ts
deleted file mode 100644
index 547b328..0000000
--- a/test/ERC721SingleEditionTest.ts
+++ /dev/null
@@ -1,556 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import { ERC721SingleEdition, EditionsMetadataRenderer, MinimalForwarder, MintManager, Observability } from "../types";
-import { Errors } from "./__utils__/data";
-import { DEFAULT_ONCHAIN_MINT_VECTOR, setupSingleEdition, setupSystem } from "./__utils__/helpers";
-
-describe("ERC721SingleEdition functionality", () => {
- let editions: ERC721SingleEdition;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let emr: EditionsMetadataRenderer;
- let observability: Observability;
- let mintManager: MintManager;
- let trustedForwarder: MinimalForwarder;
- let singleEditionImplementation: string;
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, editionsOwner, fan1] =
- await ethers.getSigners();
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- singleEditionImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
-
- emr = emrProxy;
- mintManager = mintManagerProxy;
- observability = observabilityInstance;
- trustedForwarder = minimalForwarder;
- singleEditionImplementation = singleEditionImplementationAddress;
- });
-
- beforeEach(async () => {
- editions = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 5,
- "name",
- "NM",
- );
- });
-
- describe("Minting", function () {
- beforeEach(async function () {
- await expect(editions.registerMinter(editionsOwner.address));
-
- expect(await editions.tokenManager(0)).to.eql(ethers.constants.AddressZero);
- });
-
- describe("mintOneToRecipient", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintOneToRecipient(1, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Can mint validly up until max supply", async function () {
- for (let i = 1; i <= 5; i++) {
- await expect(editions.mintOneToRecipient(0, fan1.address))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i);
-
- expect(await editions.balanceOf(fan1.address)).to.equal(ethers.BigNumber.from(i));
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, i, 1]);
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(i);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
-
- await expect(editions.mintOneToRecipient(0, fan1.address)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
- });
-
- describe("mintAmountToRecipient", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintAmountToRecipient(1, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 6)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- expect((await editions.balanceOf(fan1.address)).toNumber()).to.equal(3);
-
- for (let i = 1; i <= 3; i++) {
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, 3, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(3);
- expect(res[0][0][3].toNumber()).to.equal(1);
- });
-
- it("Minter can mint validly (running variation)", async function () {
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintAmountToRecipient(0, fan1.address, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2 * i + 2);
-
- expect((await editions.balanceOf(fan1.address)).toNumber()).to.equal((i + 1) * 2);
-
- for (let j = 1; j <= (i + 1) * 2; j++) {
- expect(await editions.ownerOf(j)).to.equal(fan1.address);
- expect((await editions.getEditionId(j)).toNumber()).to.equal(0);
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, (i + 1) * 2, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 2);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("mintOneToRecipients", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintOneToRecipients(0, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintOneToRecipients(1, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintOneToRecipients(0, [fan1.address])).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address, fan1.address, fan1.address, fan1.address];
- await expect(editions.mintOneToRecipients(0, recipientAddresses)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintOneToRecipients(0, recipientAddresses.slice(0, 3)))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3);
-
- await expect(editions.mintOneToRecipients(0, recipientAddresses.slice(0, 3))).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, editionsMetadataOwner.address, editionsOwner.address];
- await expect(editions.mintOneToRecipients(0, recipientAddresses))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsMetadataOwner.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 3);
-
- let i = 1;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal(1);
- expect(await editions.ownerOf(i)).to.equal(recipient);
- expect((await editions.getEditionId(i)).toNumber()).to.equal(0);
- i += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, 3, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal(3);
- expect(res[0][0][3].toNumber()).to.equal(1);
- });
-
- it("Minter can mint validly (running variation)", async function () {
- const recipientAddresses = [fan1.address, editionsOwner.address];
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintOneToRecipients(0, recipientAddresses))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 2 + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 2 + 2);
-
- let j = 1;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal(i + 1);
- expect(await editions.ownerOf(i * 2 + j)).to.equal(recipient);
- expect((await editions.getEditionId(i * 2 + j)).toNumber()).to.equal(0);
- j += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 5, (i + 1) * 2, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(5);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 2);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("mintAmountToRecipients", function () {
- it("Non minter cannot call", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.mintAmountToRecipients(0, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.NotMinter,
- );
- });
-
- it("Cannot mint on non-existent edition", async function () {
- await expect(editions.mintAmountToRecipients(1, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.EditionDoesNotExist,
- );
- });
-
- it("Cannot mint if mint frozen", async function () {
- await expect(editions.freezeMints()).to.emit(editions, "MintsFrozen");
-
- await expect(editions.mintAmountToRecipients(0, [fan1.address], 2)).to.be.revertedWithCustomError(
- editions,
- Errors.MintFrozen,
- );
- });
-
- it("Cannot mint more than maxSupply, in multiple variations", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 1),
- ).to.be.revertedWithCustomError(editions, Errors.SoldOut);
- });
-
- it("Minter can mint validly (simple variation)", async function () {
- const recipientAddresses = [fan1.address, fan1.address, fan1.address];
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2)).to.be.revertedWithCustomError(
- editions,
- Errors.SoldOut,
- );
-
- await expect(editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(
- editions.mintAmountToRecipients(0, recipientAddresses.slice(0, 2), 2),
- ).to.be.revertedWithCustomError(editions, Errors.SoldOut);
- });
-
- it("Minter can mint validly (complex variation)", async function () {
- editions = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 8,
- "name",
- "NM",
- );
- await expect(editions.registerMinter(editionsOwner.address)).to.emit(editions, "MinterRegistrationChanged");
- const recipientAddresses = [fan1.address, editionsOwner.address];
-
- for (let i = 0; i < 2; i++) {
- await expect(editions.mintAmountToRecipients(0, recipientAddresses, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, i * 4 + 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 4 + 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, i * 4 + 4);
-
- let j = 0;
- for (const recipient of recipientAddresses) {
- expect((await editions.balanceOf(recipient)).toNumber()).to.equal((i + 1) * 2);
- expect(await editions.ownerOf(i * 4 + j * 2 + 1)).to.equal(recipient);
- expect(await editions.ownerOf(i * 4 + j * 2 + 2)).to.equal(recipient);
- expect((await editions.getEditionId(i * 4 + j * 2 + 1)).toNumber()).to.equal(0);
- expect((await editions.getEditionId(i * 4 + j * 2 + 2)).toNumber()).to.equal(0);
- j += 1;
- }
-
- expect(
- (await editions.getEditionDetails(0)).map(x => {
- if (typeof x != "string") {
- return x.toNumber();
- } else {
- return x;
- }
- }),
- ).to.eql(["name", 8, (i + 1) * 4, 1]);
-
- const res = await editions.getEditionsDetailsAndUri([0]);
- expect(res[0][0][0]).to.equal("name");
- expect(res[0][0][1].toNumber()).to.equal(8);
- expect(res[0][0][2].toNumber()).to.equal((i + 1) * 4);
- expect(res[0][0][3].toNumber()).to.equal(1);
- }
- });
- });
-
- describe("Contract metadata updates", function () {
- it("Owner can change the contract level metadata", async function () {
- editions = editions.connect(editionsOwner);
-
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri"))
- .to.emit(observability, "ContractMetadataSet")
- .withArgs(editions.address, "new name", "new symbol", "new contract uri");
-
- expect(await editions.name()).to.equal("new name");
- expect(await editions.symbol()).to.equal("new symbol");
- expect(await editions.contractURI()).to.equal("new contract uri");
- });
-
- it("Non-owners cannot change the contract level metadata", async function () {
- editions = editions.connect(fan1);
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- editions = editions.connect(editionsMetadataOwner);
- await expect(editions.setContractMetadata("new name", "new symbol", "new contract uri")).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
- });
- });
-
- it("Can deploy with direct mint", async function () {
- editions = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 100,
- "name",
- "symbol",
- { ...DEFAULT_ONCHAIN_MINT_VECTOR, maxUserClaimableViaVector: 2 },
- );
-
- expect((await mintManager.getAbridgedVector(1)).slice(0, 14)).to.deep.equal([
- editions.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.startTimestamp,
- DEFAULT_ONCHAIN_MINT_VECTOR.endTimestamp,
- editionsOwner.address,
- DEFAULT_ONCHAIN_MINT_VECTOR.maxTotalClaimableViaVector,
- 0,
- ethers.constants.AddressZero,
- DEFAULT_ONCHAIN_MINT_VECTOR.tokenLimitPerTx,
- 2,
- DEFAULT_ONCHAIN_MINT_VECTOR.pricePerToken,
- 0,
- true,
- false,
- DEFAULT_ONCHAIN_MINT_VECTOR.allowlistRoot,
- ]);
-
- await expect(
- mintManager.vectorMint721(1, 2, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- )
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32), editions.address, true, 2);
-
- await expect(mintManager.vectorMint721(1, 1, editionsOwner.address)).to.be.revertedWithCustomError(
- mintManager,
- "OnchainVectorMintGuardFailed",
- );
-
- expect(await mintManager.userClaims(1, editionsOwner.address)).to.equal(2);
- });
-});
diff --git a/test/ERC721StandardTest.ts b/test/ERC721StandardTest.ts
deleted file mode 100644
index 5e6edea..0000000
--- a/test/ERC721StandardTest.ts
+++ /dev/null
@@ -1,1285 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ConsensualNonTransferableTokenManager,
- ERC721Editions,
- ERC721General,
- ERC721SingleEdition,
- EditionsMetadataRenderer,
- MinimalForwarder,
- MintManager,
- NonTransferableTokenManager,
- Observability,
- TotalLockedTokenManager,
- TransferAndBurnLockedTokenManager,
-} from "../types";
-import { Errors } from "./__utils__/data";
-import {
- DEFAULT_ONCHAIN_MINT_VECTOR,
- setupEditions,
- setupGeneral,
- setupSingleEdition,
- setupSystem,
-} from "./__utils__/helpers";
-
-const defaultEditionInfo = ethers.utils.defaultAbiCoder.encode(
- ["tuple(string, string, string, string, string, string)"],
- [["name", "description", "imageUrl", "animationUrl", "externalUrl", "attributes"]],
-);
-
-describe("ERC721 Standard with token managers functionality", () => {
- let totalLockedTokenManager: TotalLockedTokenManager;
- let transferAndBurnLockedTokenManager: TransferAndBurnLockedTokenManager;
- let nonTransferableTokenManager: NonTransferableTokenManager;
- let consensualNonTransferableTokenManager: ConsensualNonTransferableTokenManager;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- owner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let emr: EditionsMetadataRenderer;
- let mintManager: MintManager;
- let auctionManager: AuctionManager;
- let observability: Observability;
- let trustedForwarder: MinimalForwarder;
- let editionsImplementation: string;
- let singleEditionImplementation: string;
- let generalImplementation: string;
-
- const zeroRoyalty = {
- recipientAddress: ethers.constants.AddressZero,
- royaltyPercentageBPS: 0,
- };
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, owner, fan1] =
- await ethers.getSigners();
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- owner,
- );
-
- emr = emrProxy;
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- observability = observabilityInstance;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
-
- totalLockedTokenManager = await (await ethers.getContractFactory("TotalLockedTokenManager")).deploy();
- transferAndBurnLockedTokenManager = await (
- await ethers.getContractFactory("TransferAndBurnLockedTokenManager")
- ).deploy();
-
- nonTransferableTokenManager = await (await ethers.getContractFactory("NonTransferableTokenManager")).deploy();
-
- consensualNonTransferableTokenManager = await (
- await ethers.getContractFactory("ConsensualNonTransferableTokenManager")
- ).deploy();
- });
-
- describe("Testing 721 standard on ERC721General", function () {
- let general: ERC721General;
-
- beforeEach(async function () {
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- owner,
- );
-
- await expect(general.registerMinter(owner.address)).to.emit(general, "MinterRegistrationChanged");
-
- expect(await general.tokenManager(0)).to.eql(ethers.constants.AddressZero);
-
- await expect(general.mintAmountToOneRecipient(fan1.address, 4))
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- for (let i = 1; i <= 4; i++) {
- expect(await general.ownerOf(i)).to.equal(fan1.address);
- }
- });
-
- describe("Without a token manager", function () {
- it("safeTransferFrom works as expected", async function () {
- general = general.connect(fan1);
-
- await expect(general["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 1))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 1)
- .to.emit(observability, "Transfer")
- .withArgs(general.address, fan1.address, owner.address, 1);
-
- await expect(
- general["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 2);
-
- expect(await general.ownerOf(1)).to.equal(owner.address);
- expect(await general.ownerOf(2)).to.equal(owner.address);
-
- await expect(general.approve(owner.address, 3))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(general.approve(owner.address, 4))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- general = general.connect(owner);
-
- await expect(general["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(
- general["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 4,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await general.ownerOf(3)).to.equal(owner.address);
- expect(await general.ownerOf(4)).to.equal(owner.address);
- });
-
- it("transferFrom works as expected", async function () {
- general = general.connect(fan1);
-
- expect(await general.ownerOf(1)).to.equal(fan1.address);
- expect(await general.ownerOf(2)).to.equal(fan1.address);
-
- await expect(general.transferFrom(fan1.address, owner.address, 1))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 1)
- .to.emit(observability, "Transfer")
- .withArgs(general.address, fan1.address, owner.address, 1);
-
- expect(await general.ownerOf(1)).to.equal(owner.address);
-
- await expect(general.approve(owner.address, 2))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- general = general.connect(owner);
-
- await expect(general.transferFrom(fan1.address, owner.address, 2))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 2);
-
- expect(await general.ownerOf(2)).to.equal(owner.address);
- });
-
- it("burn works as expected", async function () {
- general = general.connect(fan1);
-
- expect(await general.ownerOf(1)).to.equal(fan1.address);
- expect(await general.ownerOf(2)).to.equal(fan1.address);
-
- await expect(general.burn(1))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 1)
- .to.emit(observability, "Transfer")
- .withArgs(general.address, fan1.address, ethers.constants.AddressZero, 1);
-
- await expect(general.approve(owner.address, 2))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- general = general.connect(owner);
-
- await expect(general.burn(2))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 2);
- });
- });
-
- describe("With a default token manager", function () {
- beforeEach(async function () {
- await expect(general.setDefaultTokenManager(transferAndBurnLockedTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- for (let i = 1; i <= 4; i++) {
- expect(await general.tokenManager(1)).to.equal(transferAndBurnLockedTokenManager.address);
- }
- });
-
- it("safeTransferFrom works as expected", async function () {
- general = general.connect(fan1);
-
- await expect(
- general["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 1),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- general["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(general.approve(owner.address, 3))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(general.approve(owner.address, 4))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- general = general.connect(owner);
-
- await expect(
- general["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(general.removeDefaultTokenManager()).to.emit(general, "DefaultTokenManagerChanged");
-
- await expect(general["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- expect(await general.ownerOf(3)).to.equal(owner.address);
-
- await expect(general.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- await expect(
- general["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 4,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await general.ownerOf(4)).to.equal(owner.address);
- });
-
- it("transferFrom works as expected", async function () {
- general = general.connect(fan1);
-
- await expect(general.transferFrom(fan1.address, owner.address, 1)).to.be.revertedWith("Transfers disallowed");
-
- await expect(general.transferFrom(fan1.address, owner.address, 2)).to.be.revertedWith("Transfers disallowed");
-
- await expect(general.approve(owner.address, 3))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(general.approve(owner.address, 4))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- general = general.connect(owner);
-
- await expect(general.transferFrom(fan1.address, owner.address, 3)).to.be.revertedWith("Transfers disallowed");
-
- await expect(general.removeDefaultTokenManager()).to.emit(general, "DefaultTokenManagerChanged");
-
- await expect(general.transferFrom(fan1.address, owner.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- expect(await general.ownerOf(3)).to.equal(owner.address);
-
- await expect(general.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- await expect(general.transferFrom(fan1.address, owner.address, 4))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await general.ownerOf(4)).to.equal(owner.address);
- });
-
- it("burn works as expected", async function () {
- general = general.connect(fan1);
-
- expect(await general.ownerOf(1)).to.equal(fan1.address);
- expect(await general.ownerOf(2)).to.equal(fan1.address);
-
- await expect(general.burn(1)).to.be.revertedWith("Burns disallowed");
-
- await expect(general.approve(owner.address, 1))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 1);
-
- await expect(general.approve(owner.address, 2))
- .to.emit(general, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- general = general.connect(owner);
-
- await expect(general.removeDefaultTokenManager()).to.emit(general, "DefaultTokenManagerChanged");
-
- await expect(general.burn(1))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 1);
-
- await expect(general.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- await expect(general.burn(2))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 2);
- });
- });
-
- describe("With multiple overwriting token managers per token", function () {
- beforeEach(async function () {
- await expect(general.setDefaultTokenManager(transferAndBurnLockedTokenManager.address)).to.emit(
- general,
- "DefaultTokenManagerChanged",
- );
-
- await expect(
- general.setGranularTokenManagers([3, 4], [totalLockedTokenManager.address, totalLockedTokenManager.address]),
- ).to.emit(general, "GranularTokenManagersSet");
-
- for (let i = 1; i <= 2; i++) {
- expect(await general.tokenManager(i)).to.equal(transferAndBurnLockedTokenManager.address);
- }
-
- for (let i = 3; i <= 4; i++) {
- expect(await general.tokenManager(i)).to.equal(totalLockedTokenManager.address);
- }
- });
-
- it("safeTransferFrom works as expected", async function () {
- general = general.connect(fan1);
-
- await expect(
- general["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 1),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- general["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(general["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(
- general["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 4,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await general.ownerOf(3)).to.equal(owner.address);
- expect(await general.ownerOf(4)).to.equal(owner.address);
- });
-
- it("transferFrom works as expected", async function () {
- general = general.connect(fan1);
-
- await expect(general.transferFrom(fan1.address, owner.address, 1)).to.be.revertedWith("Transfers disallowed");
-
- await expect(general.transferFrom(fan1.address, owner.address, 2)).to.be.revertedWith("Transfers disallowed");
-
- await expect(general.transferFrom(fan1.address, owner.address, 3))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(general.transferFrom(fan1.address, owner.address, 4))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await general.ownerOf(3)).to.equal(owner.address);
- expect(await general.ownerOf(4)).to.equal(owner.address);
- });
-
- it("burn works as expected", async function () {
- general = general.connect(fan1);
-
- await expect(general.burn(1)).to.be.revertedWith("Burns disallowed");
-
- await expect(general.burn(2)).to.be.revertedWith("Burns disallowed");
-
- await expect(general.burn(3))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 3);
-
- await expect(general.burn(4))
- .to.emit(general, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 4);
- });
-
- it("ConsensualNonTransferableTokenManager burns properly", async function () {
- general = general.connect(owner);
- // burn allowed
- await expect(
- general.setGranularTokenManagers(
- [1, 2],
- [consensualNonTransferableTokenManager.address, nonTransferableTokenManager.address],
- ),
- ).to.emit(general, "GranularTokenManagersSet");
-
- general = general.connect(fan1);
-
- await expect(general.burn(1)).to.emit(general, "Transfer");
-
- general = general.connect(editionsMetadataOwner);
-
- await expect(general.burn(2)).to.be.revertedWithCustomError(general, Errors.Unauthorized);
- });
- });
- });
-
- describe("Testing 721 standard on ERC721Editions", function () {
- let editions: ERC721Editions;
-
- beforeEach(async function () {
- editions = await setupEditions(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- owner,
- );
-
- await expect(editions.registerMinter(owner.address)).to.emit(editions, "MinterRegistrationChanged");
-
- await expect(editions.createEdition(defaultEditionInfo, 4, ethers.constants.AddressZero, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(0, 4, ethers.constants.AddressZero);
-
- await expect(editions.createEdition(defaultEditionInfo, 2, totalLockedTokenManager.address, zeroRoyalty, "0x"))
- .to.emit(editions, "EditionCreated")
- .withArgs(1, 2, totalLockedTokenManager.address);
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 4))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- await expect(editions.mintAmountToRecipient(1, fan1.address, 2))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 5)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 6);
-
- for (let i = 1; i <= 6; i++) {
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- }
- });
-
- describe("Without a token manager", function () {
- it("safeTransferFrom works as expected", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 1))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 1)
- .to.emit(observability, "Transfer")
- .withArgs(editions.address, fan1.address, owner.address, 1);
-
- await expect(
- editions["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 2);
-
- expect(await editions.ownerOf(1)).to.equal(owner.address);
- expect(await editions.ownerOf(2)).to.equal(owner.address);
-
- await expect(editions.approve(owner.address, 3))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(editions.approve(owner.address, 4))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- editions = editions.connect(owner);
-
- await expect(editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(
- editions["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 4,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await editions.ownerOf(3)).to.equal(owner.address);
- expect(await editions.ownerOf(4)).to.equal(owner.address);
- });
-
- it("transferFrom works as expected", async function () {
- editions = editions.connect(fan1);
-
- expect(await editions.ownerOf(1)).to.equal(fan1.address);
- expect(await editions.ownerOf(2)).to.equal(fan1.address);
-
- await expect(editions.transferFrom(fan1.address, owner.address, 1))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 1)
- .to.emit(observability, "Transfer")
- .withArgs(editions.address, fan1.address, owner.address, 1);
-
- expect(await editions.ownerOf(1)).to.equal(owner.address);
-
- await expect(editions.approve(owner.address, 2))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- editions = editions.connect(owner);
-
- await expect(editions.transferFrom(fan1.address, owner.address, 2))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 2);
-
- expect(await editions.ownerOf(2)).to.equal(owner.address);
- });
-
- it("burn works as expected", async function () {
- editions = editions.connect(fan1);
-
- expect(await editions.ownerOf(1)).to.equal(fan1.address);
- expect(await editions.ownerOf(2)).to.equal(fan1.address);
-
- await expect(editions.burn(1))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 1)
- .to.emit(observability, "Transfer")
- .withArgs(editions.address, fan1.address, ethers.constants.AddressZero, 1);
-
- await expect(editions.approve(owner.address, 2))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- editions = editions.connect(owner);
-
- await expect(editions.burn(2))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 2);
- });
- });
-
- describe("With a default token manager", function () {
- beforeEach(async function () {
- await expect(await editions.setDefaultTokenManager(transferAndBurnLockedTokenManager.address)).to.emit(
- editions,
- "DefaultTokenManagerChanged",
- );
-
- // TODO: use tokenManagerByTokenId
- expect(await editions.tokenManager(0)).to.equal(transferAndBurnLockedTokenManager.address);
- expect(await editions.tokenManager(1)).to.equal(totalLockedTokenManager.address);
- });
-
- it("safeTransferFrom works as expected", async function () {
- editions = editions.connect(fan1);
-
- await expect(
- editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 1),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- editions["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.approve(owner.address, 3))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(editions.approve(owner.address, 4))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- editions = editions.connect(owner);
-
- await expect(
- editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.removeDefaultTokenManager()).to.emit(editions, "DefaultTokenManagerChanged");
-
- await expect(editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- expect(await editions.ownerOf(3)).to.equal(owner.address);
-
- await expect(editions.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- editions,
- "DefaultTokenManagerChanged",
- );
-
- await expect(
- editions["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 4,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await editions.ownerOf(4)).to.equal(owner.address);
- });
-
- it("transferFrom works as expected", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.transferFrom(fan1.address, owner.address, 1)).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.transferFrom(fan1.address, owner.address, 2)).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.approve(owner.address, 3))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(editions.approve(owner.address, 4))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- editions = editions.connect(owner);
-
- await expect(editions.transferFrom(fan1.address, owner.address, 3)).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.removeDefaultTokenManager()).to.emit(editions, "DefaultTokenManagerChanged");
-
- await expect(editions.transferFrom(fan1.address, owner.address, 3))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- expect(await editions.ownerOf(3)).to.equal(owner.address);
-
- await expect(editions.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- editions,
- "DefaultTokenManagerChanged",
- );
-
- await expect(editions.transferFrom(fan1.address, owner.address, 4))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await editions.ownerOf(4)).to.equal(owner.address);
- });
-
- it("burn works as expected", async function () {
- editions = editions.connect(fan1);
-
- expect(await editions.ownerOf(1)).to.equal(fan1.address);
- expect(await editions.ownerOf(2)).to.equal(fan1.address);
-
- await expect(editions.burn(1)).to.be.revertedWith("Burns disallowed");
-
- await expect(editions.approve(owner.address, 1))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 1);
-
- await expect(editions.approve(owner.address, 2))
- .to.emit(editions, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- editions = editions.connect(owner);
-
- await expect(editions.removeDefaultTokenManager()).to.emit(editions, "DefaultTokenManagerChanged");
-
- await expect(editions.burn(1))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 1);
-
- await expect(editions.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- editions,
- "DefaultTokenManagerChanged",
- );
-
- await expect(editions.burn(2))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 2);
- });
- });
-
- describe("With multiple overwriting token managers per edition", function () {
- beforeEach(async function () {
- await expect(await editions.setDefaultTokenManager(transferAndBurnLockedTokenManager.address)).to.emit(
- editions,
- "DefaultTokenManagerChanged",
- );
-
- // TODO: use tokenManagerByTokenId
- expect(await editions.tokenManager(0)).to.equal(transferAndBurnLockedTokenManager.address);
- expect(await editions.tokenManager(1)).to.equal(totalLockedTokenManager.address);
- });
-
- it("safeTransferFrom works as expected", async function () {
- editions = editions.connect(fan1);
-
- await expect(
- editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 1),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- editions["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 4),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- editions["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- editions["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 5))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 5);
-
- await expect(
- editions["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 6,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 6);
-
- expect(await editions.ownerOf(5)).to.equal(owner.address);
- expect(await editions.ownerOf(6)).to.equal(owner.address);
- });
-
- it("transferFrom works as expected", async function () {
- editions = editions.connect(fan1);
-
- await expect(editions.transferFrom(fan1.address, owner.address, 1)).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.transferFrom(fan1.address, owner.address, 2)).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.transferFrom(fan1.address, owner.address, 3)).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.transferFrom(fan1.address, owner.address, 4)).to.be.revertedWith("Transfers disallowed");
-
- await expect(editions.transferFrom(fan1.address, owner.address, 5))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 5);
-
- await expect(editions.transferFrom(fan1.address, owner.address, 6))
- .to.emit(editions, "Transfer")
- .withArgs(fan1.address, owner.address, 6);
-
- expect(await editions.ownerOf(5)).to.equal(owner.address);
- expect(await editions.ownerOf(6)).to.equal(owner.address);
- });
- });
- });
-
- describe("Testing 721 standard on ERC721SingleEdition", function () {
- let singleEdition: ERC721SingleEdition;
-
- beforeEach(async function () {
- singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- owner,
- 4,
- "name",
- "SYM",
- );
-
- await expect(singleEdition.registerMinter(owner.address)).to.emit(singleEdition, "MinterRegistrationChanged");
-
- await expect(singleEdition.mintAmountToRecipient(0, fan1.address, 4))
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- for (let i = 1; i <= 4; i++) {
- expect(await singleEdition.ownerOf(i)).to.equal(fan1.address);
- }
- });
-
- describe("Without a token manager", function () {
- it("safeTransferFrom works as expected", async function () {
- singleEdition = singleEdition.connect(fan1);
-
- await expect(singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 1))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 1);
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 2);
-
- expect(await singleEdition.ownerOf(1)).to.equal(owner.address);
- expect(await singleEdition.ownerOf(2)).to.equal(owner.address);
-
- await expect(singleEdition.approve(owner.address, 3))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(singleEdition.approve(owner.address, 4))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- singleEdition = singleEdition.connect(owner);
-
- await expect(singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 4,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await singleEdition.ownerOf(3)).to.equal(owner.address);
- expect(await singleEdition.ownerOf(4)).to.equal(owner.address);
- });
-
- it("transferFrom works as expected", async function () {
- singleEdition = singleEdition.connect(fan1);
-
- expect(await singleEdition.ownerOf(1)).to.equal(fan1.address);
- expect(await singleEdition.ownerOf(2)).to.equal(fan1.address);
-
- await expect(singleEdition.transferFrom(fan1.address, owner.address, 1))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 1)
- .to.emit(observability, "Transfer")
- .withArgs(singleEdition.address, fan1.address, owner.address, 1);
-
- expect(await singleEdition.ownerOf(1)).to.equal(owner.address);
-
- await expect(singleEdition.approve(owner.address, 2))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- singleEdition = singleEdition.connect(owner);
-
- await expect(singleEdition.transferFrom(fan1.address, owner.address, 2))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 2);
-
- expect(await singleEdition.ownerOf(2)).to.equal(owner.address);
- });
-
- it("burn works as expected", async function () {
- singleEdition = singleEdition.connect(fan1);
-
- expect(await singleEdition.ownerOf(1)).to.equal(fan1.address);
- expect(await singleEdition.ownerOf(2)).to.equal(fan1.address);
-
- await expect(singleEdition.burn(1))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 1)
- .to.emit(observability, "Transfer")
- .withArgs(singleEdition.address, fan1.address, ethers.constants.AddressZero, 1);
-
- await expect(singleEdition.approve(owner.address, 2))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- singleEdition = singleEdition.connect(owner);
-
- await expect(singleEdition.burn(2))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 2);
- });
- });
-
- describe("With a default token manager", function () {
- beforeEach(async function () {
- await expect(await singleEdition.setDefaultTokenManager(transferAndBurnLockedTokenManager.address)).to.emit(
- singleEdition,
- "DefaultTokenManagerChanged",
- );
-
- // TODO: use tokenManagerByTokenId
- expect(await singleEdition.tokenManager(0)).to.equal(transferAndBurnLockedTokenManager.address);
- });
-
- it("safeTransferFrom works as expected", async function () {
- singleEdition = singleEdition.connect(fan1);
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 1),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 2,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(singleEdition.approve(owner.address, 3))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(singleEdition.approve(owner.address, 4))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- singleEdition = singleEdition.connect(owner);
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(singleEdition.removeDefaultTokenManager()).to.emit(singleEdition, "DefaultTokenManagerChanged");
-
- await expect(singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, 3))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 3)
- .to.emit(observability, "Transfer")
- .withArgs(singleEdition.address, fan1.address, owner.address, 3);
-
- expect(await singleEdition.ownerOf(3)).to.equal(owner.address);
-
- await expect(singleEdition.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- singleEdition,
- "DefaultTokenManagerChanged",
- );
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- 4,
- ethers.utils.arrayify("0x"),
- ),
- )
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await singleEdition.ownerOf(4)).to.equal(owner.address);
- });
-
- it("transferFrom works as expected", async function () {
- singleEdition = singleEdition.connect(fan1);
-
- await expect(singleEdition.transferFrom(fan1.address, owner.address, 1)).to.be.revertedWith(
- "Transfers disallowed",
- );
-
- await expect(singleEdition.transferFrom(fan1.address, owner.address, 2)).to.be.revertedWith(
- "Transfers disallowed",
- );
-
- await expect(singleEdition.approve(owner.address, 3))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 3);
-
- await expect(singleEdition.approve(owner.address, 4))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 4);
-
- singleEdition = singleEdition.connect(owner);
-
- await expect(singleEdition.transferFrom(fan1.address, owner.address, 3)).to.be.revertedWith(
- "Transfers disallowed",
- );
-
- await expect(singleEdition.removeDefaultTokenManager()).to.emit(singleEdition, "DefaultTokenManagerChanged");
-
- await expect(singleEdition.transferFrom(fan1.address, owner.address, 3))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 3);
-
- expect(await singleEdition.ownerOf(3)).to.equal(owner.address);
-
- await expect(singleEdition.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- singleEdition,
- "DefaultTokenManagerChanged",
- );
-
- await expect(singleEdition.transferFrom(fan1.address, owner.address, 4))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, owner.address, 4);
-
- expect(await singleEdition.ownerOf(4)).to.equal(owner.address);
- });
-
- it("burn works as expected", async function () {
- singleEdition = singleEdition.connect(fan1);
-
- expect(await singleEdition.ownerOf(1)).to.equal(fan1.address);
- expect(await singleEdition.ownerOf(2)).to.equal(fan1.address);
-
- await expect(singleEdition.burn(1)).to.be.revertedWith("Burns disallowed");
-
- await expect(singleEdition.approve(owner.address, 1))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 1);
-
- await expect(singleEdition.approve(owner.address, 2))
- .to.emit(singleEdition, "Approval")
- .withArgs(fan1.address, owner.address, 2);
-
- singleEdition = singleEdition.connect(owner);
-
- await expect(singleEdition.removeDefaultTokenManager()).to.emit(singleEdition, "DefaultTokenManagerChanged");
-
- await expect(singleEdition.burn(1))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 1);
-
- await expect(singleEdition.setDefaultTokenManager(totalLockedTokenManager.address)).to.emit(
- singleEdition,
- "DefaultTokenManagerChanged",
- );
-
- await expect(singleEdition.burn(2))
- .to.emit(singleEdition, "Transfer")
- .withArgs(fan1.address, ethers.constants.AddressZero, 2);
- });
-
- it("NonTransferable token manager works properly", async function () {
- singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- owner,
- 4,
- "name",
- "SYM",
- null,
- null,
- false,
- nonTransferableTokenManager.address,
- );
-
- await expect(singleEdition.registerMinter(owner.address)).to.emit(singleEdition, "MinterRegistrationChanged");
-
- await expect(singleEdition.mintAmountToRecipient(0, fan1.address, 4))
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- singleEdition = singleEdition.connect(fan1);
- for (let i = 1; i <= 4; i++) {
- expect(await singleEdition.ownerOf(i)).to.equal(fan1.address);
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, i),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- i,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(singleEdition.transferFrom(fan1.address, owner.address, i)).to.be.revertedWith(
- "Transfers disallowed",
- );
- }
- });
-
- it("ConsensualNonTransferableTokenManager token manager works properly", async function () {
- singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- owner,
- 4,
- "name",
- "SYM",
- null,
- null,
- false,
- consensualNonTransferableTokenManager.address,
- );
-
- await expect(singleEdition.registerMinter(owner.address)).to.emit(singleEdition, "MinterRegistrationChanged");
-
- await expect(singleEdition.mintAmountToRecipient(0, fan1.address, 4))
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- singleEdition = singleEdition.connect(fan1);
- for (let i = 1; i <= 4; i++) {
- expect(await singleEdition.ownerOf(i)).to.equal(fan1.address);
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, editionsMetadataOwner.address, i),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- editionsMetadataOwner.address,
- i,
- ethers.utils.arrayify("0x"),
- ),
- ).to.be.revertedWith("Transfers disallowed");
-
- await expect(singleEdition.transferFrom(fan1.address, editionsMetadataOwner.address, i)).to.be.revertedWith(
- "Transfers disallowed",
- );
-
- if (i == 1 || i == 2) {
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, owner.address, i),
- ).to.emit(singleEdition, "Transfer");
- } else if (i == 3) {
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256,bytes)"](
- fan1.address,
- owner.address,
- i,
- ethers.utils.arrayify("0x"),
- ),
- ).to.emit(singleEdition, "Transfer");
- } else {
- await expect(singleEdition.transferFrom(fan1.address, owner.address, i)).to.emit(singleEdition, "Transfer");
- }
- }
- });
- });
- });
-});
diff --git a/test/EditionsMetadataRendererTest.ts b/test/EditionsMetadataRendererTest.ts
deleted file mode 100644
index e598950..0000000
--- a/test/EditionsMetadataRendererTest.ts
+++ /dev/null
@@ -1,354 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ERC721Editions,
- ERC721General,
- ERC721SingleEdition,
- EditionsMetadataRenderer,
- MinimalForwarder,
- MintManager,
- Observability,
-} from "../types";
-
-/* eslint-disable prefer-const */
-import {
- DEFAULT_ONCHAIN_MINT_VECTOR,
- generateClaim,
- setupGeneral,
- setupMultipleEdition,
- setupSingleEdition,
- setupSystem,
-} from "./__utils__/helpers";
-
-//TODO: Token URI
-
-describe("Editions Metadata Renderer", () => {
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- generalOwner: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let mintFeeWei = ethers.BigNumber.from("800000000000000");
-
- before(async () => {
- [
- initialPlatformExecutor,
- mintManagerOwner,
- editionsMetadataOwner,
- platformPaymentAddress,
- generalOwner,
- editionsOwner,
- fan1,
- ] = await ethers.getSigners();
- });
-
- async function setup() {
- const size = 10,
- name = "Test 1",
- symbol = "T1";
-
- let auctionManager: AuctionManager;
- let mintManager: MintManager;
- let emr: EditionsMetadataRenderer;
- let minimalForwarder: MinimalForwarder;
- let observability: Observability;
-
- const {
- mintManagerProxy,
- auctionManagerProxy,
- emrProxy,
- observability: observabilityInstance,
- minimalForwarder: minimalForwarderContract,
- generalImplementationAddress,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
-
- auctionManager = auctionManagerProxy;
- mintManager = mintManagerProxy;
- emr = emrProxy;
- observability = observabilityInstance;
- minimalForwarder = minimalForwarderContract;
-
- const general: ERC721General = await setupGeneral(
- observability.address,
- generalImplementationAddress,
- minimalForwarder.address,
- emr.address,
- generalOwner,
- null,
- null,
- false,
- false,
- 0,
- ethers.constants.AddressZero,
- ethers.constants.AddressZero,
- 0,
- name,
- symbol,
- );
- const singleEdition: ERC721SingleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementationAddress,
- mintManager.address,
- minimalForwarder.address,
- emr.address,
- editionsOwner,
- size,
- name,
- symbol,
- );
- const editions: ERC721Editions = await setupMultipleEdition(
- observability.address,
- editionsImplementationAddress,
- mintManager.address,
- auctionManager.address,
- minimalForwarder.address,
- emr.address,
- editionsOwner,
- size,
- name,
- symbol,
- );
-
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- );
-
- const ownerOnlyTokenManager = await (await ethers.getContractFactory("OwnerOnlyTokenManager")).deploy();
- await ownerOnlyTokenManager.deployed();
-
- const checkersTokenManager = await (
- await ethers.getContractFactory("CheckerboardTokenManager")
- ).deploy(initialPlatformExecutor.address, emr.address, "allowed 1");
- await checkersTokenManager.deployed();
-
- return {
- auctionManager,
- mintManager,
- emr,
- minimalForwarder,
- general,
- singleEdition,
- editions,
- ownerOnlyTokenManager,
- checkersTokenManager,
- name,
- symbol,
- size,
- singleEditionClaim: { claim, signature },
- };
- }
-
- it("Should generate correct uri for editions uri", async () => {
- let { emr, singleEdition } = await setup();
- const fakeContractSigner = await ethers.getSigner(singleEdition.address);
- emr = emr.connect(fakeContractSigner);
- const uri = (await emr.editionURI(0)).replace("data:application/json;base64,", "");
- const buff = Buffer.from(uri, "base64");
- const metadata: Record = JSON.parse(buff.toString());
- expect(Object.keys(metadata)).to.include.members(["name", "size", "description", "external_url", "attributes"]);
- });
- it("Should generate correct uri for token uri", async () => {
- let { emr, mintManager, singleEdition, singleEditionClaim } = await setup();
- const mintManagerForFan1 = mintManager.connect(fan1);
- const tx = await mintManagerForFan1.gatedMintEdition721(
- singleEditionClaim.claim,
- singleEditionClaim.signature,
- fan1.address,
- { value: mintFeeWei.mul(singleEditionClaim.claim.numTokensToMint) },
- );
- await tx.wait();
- const emrImp = await ethers.getSigner(singleEdition.address);
- emr = emr.connect(emrImp);
- const uri = (await emr.tokenURI(1)).replace("data:application/json;base64,", "");
- const buff = Buffer.from(uri, "base64");
- const metadata: Record = JSON.parse(buff.toString());
- expect(Object.keys(metadata)).to.include.members(["name", "description", "external_url", "attributes"]);
- });
- it("Should return correct token edition info", async () => {
- const { emr, singleEdition, name } = await setup();
- const editionInfo = await emr.editionInfo(singleEdition.address, 0);
- expect(editionInfo.name).to.be.equal(name);
- expect(editionInfo.description).to.be.equal("");
- expect(editionInfo.imageUrl).to.be.equal("");
- expect(editionInfo.animationUrl).to.be.equal("");
- expect(editionInfo.attributes).to.be.equal("");
- });
-
- it("Updating metadata fields without a token manager is restricted to the owner", async () => {
- const { emr, singleEdition } = await setup();
-
- await expect(emr.updateName(singleEdition.address, 0, "new name"))
- .to.emit(emr, "NameUpdated")
- .withArgs(singleEdition.address, 0, "new name");
-
- await expect(emr.updateDescription(singleEdition.address, 0, "new description"))
- .to.emit(emr, "DescriptionUpdated")
- .withArgs(singleEdition.address, 0, "new description");
-
- await expect(emr.updateImageUrl(singleEdition.address, 0, "new image url"))
- .to.emit(emr, "ImageUrlUpdated")
- .withArgs(singleEdition.address, 0, "new image url");
-
- await expect(emr.updateAnimationUrl(singleEdition.address, 0, "new animation url"))
- .to.emit(emr, "AnimationUrlUpdated")
- .withArgs(singleEdition.address, 0, "new animation url");
-
- await expect(emr.updateExternalUrl(singleEdition.address, 0, "new external url"))
- .to.emit(emr, "ExternalUrlUpdated")
- .withArgs(singleEdition.address, 0, "new external url");
-
- await expect(emr.updateAttributes(singleEdition.address, 0, "new attributes"))
- .to.emit(emr, "AttributesUpdated")
- .withArgs(singleEdition.address, 0, "new attributes");
-
- const editionInfo = await emr.editionInfo(singleEdition.address, 0);
- expect(editionInfo.name).to.be.equal("new name");
- expect(editionInfo.description).to.be.equal("new description");
- expect(editionInfo.imageUrl).to.be.equal("new image url");
- expect(editionInfo.animationUrl).to.be.equal("new animation url");
- expect(editionInfo.externalUrl).to.be.equal("new external url");
- expect(editionInfo.attributes).to.be.equal("new attributes");
- });
-
- it("Updating metadata with a non-conforming (to ITokenManagerEditions) token manager uses the ITokenManager standard", async () => {
- let { emr, singleEdition, ownerOnlyTokenManager } = await setup();
- emr = emr.connect(editionsMetadataOwner);
- await expect(singleEdition.setDefaultTokenManager(ownerOnlyTokenManager.address)).to.emit(
- singleEdition,
- "DefaultTokenManagerChanged",
- );
-
- emr = emr.connect(editionsOwner);
- await expect(emr.updateName(singleEdition.address, 0, "new name")).to.be.revertedWith("Can't update metadata");
-
- emr = emr.connect(editionsMetadataOwner);
- await expect(emr.updateName(singleEdition.address, 0, "new name")).to.emit(emr, "NameUpdated");
- });
-
- it("Updating metadata with a checkerboard token manager works as expected", async () => {
- let { emr, singleEdition, mintManager, checkersTokenManager, singleEditionClaim } = await setup();
- emr = emr.connect(editionsMetadataOwner);
- await expect(singleEdition.setDefaultTokenManager(checkersTokenManager.address)).to.emit(
- singleEdition,
- "DefaultTokenManagerChanged",
- );
-
- // editions metadata owner not allowed + name update not allowed
- await expect(emr.updateName(singleEdition.address, 0, "allowed 1")).to.be.revertedWith("Can't update metadata");
-
- // name update not allowed
- emr = emr.connect(editionsOwner);
- await expect(emr.updateName(singleEdition.address, 0, "allowed 1")).to.be.revertedWith("Can't update metadata");
-
- // editions metadata owner not alowed
- emr = emr.connect(editionsMetadataOwner);
- await expect(emr.updateImageUrl(singleEdition.address, 0, "new image")).to.be.revertedWith("Can't update metadata");
-
- // invalid name update not allowed
- emr = emr.connect(editionsOwner);
- await expect(emr.updateImageUrl(singleEdition.address, 0, "new image")).to.be.revertedWith("Can't update metadata");
-
- await expect(emr.updateImageUrl(singleEdition.address, 0, "allowed 1")).to.emit(emr, "ImageUrlUpdated");
-
- const editionInfo = await emr.editionInfo(singleEdition.address, 0);
- expect(editionInfo.imageUrl).to.be.equal("allowed 1");
-
- checkersTokenManager = checkersTokenManager.connect(editionsOwner);
- // this is working as expected
- //await expect(checkersTokenManager.setAllowedMoveForTheDay("allowed 2"))
- // .to.be.revertedWith("Ownable: caller is not the owner")
-
- checkersTokenManager = checkersTokenManager.connect(initialPlatformExecutor);
- await expect(checkersTokenManager.setAllowedMoveForTheDay("allowed 2"))
- .to.emit(checkersTokenManager, "SetAllowedMove")
- .withArgs("allowed 2");
-
- emr = emr.connect(fan1);
- await expect(emr.updateImageUrl(singleEdition.address, 0, "allowed 2")).to.be.revertedWith("Can't update metadata");
-
- const mintManagerForFan1 = mintManager.connect(fan1);
- const tx = await mintManagerForFan1.gatedMintEdition721(
- singleEditionClaim.claim,
- singleEditionClaim.signature,
- fan1.address,
- { value: mintFeeWei.mul(singleEditionClaim.claim.numTokensToMint) },
- );
- await tx.wait();
-
- // now fan holds nft from single edition
- await expect(emr.updateImageUrl(singleEdition.address, 0, "allowed 2")).to.emit(emr, "ImageUrlUpdated");
- });
-
- it("Can update all metadata fields at once", async () => {
- const { emr, singleEdition } = await setup();
-
- const newEditionMetadata = {
- name: "new name",
- description: "new description",
- imageUrl: "new image url",
- animationUrl: "new animation url",
- externalUrl: "new external url",
- attributes: "new attributes",
- };
-
- await expect(emr.updateMetadata(singleEdition.address, 0, newEditionMetadata, [1, 3]))
- .to.emit(emr, "NameUpdated")
- .withArgs(singleEdition.address, 0, "new name")
- .to.emit(emr, "ImageUrlUpdated")
- .withArgs(singleEdition.address, 0, "new image url");
-
- const editionInfo = await emr.editionInfo(singleEdition.address, 0);
- expect(editionInfo.name).to.be.equal("new name");
- expect(editionInfo.imageUrl).to.be.equal("new image url");
- expect(editionInfo.description).to.be.equal("");
- expect(editionInfo.animationUrl).to.be.equal("");
- expect(editionInfo.externalUrl).to.be.equal("");
- expect(editionInfo.attributes).to.be.equal("");
-
- await expect(emr.updateMetadata(singleEdition.address, 0, newEditionMetadata, [2, 4]))
- .to.emit(emr, "DescriptionUpdated")
- .withArgs(singleEdition.address, 0, "new description")
- .to.emit(emr, "AnimationUrlUpdated")
- .withArgs(singleEdition.address, 0, "new animation url");
-
- const editionInfo2 = await emr.editionInfo(singleEdition.address, 0);
- expect(editionInfo2.name).to.be.equal("new name");
- expect(editionInfo2.imageUrl).to.be.equal("new image url");
- expect(editionInfo2.description).to.be.equal("new description");
- expect(editionInfo2.animationUrl).to.be.equal("new animation url");
- expect(editionInfo2.externalUrl).to.be.equal("");
- expect(editionInfo2.attributes).to.be.equal("");
-
- await expect(emr.updateMetadata(singleEdition.address, 0, newEditionMetadata, [5, 6]))
- .to.emit(emr, "ExternalUrlUpdated")
- .withArgs(singleEdition.address, 0, "new external url")
- .to.emit(emr, "AttributesUpdated")
- .withArgs(singleEdition.address, 0, "new attributes");
-
- const editionInfo3 = await emr.editionInfo(singleEdition.address, 0);
- expect(editionInfo3.name).to.be.equal("new name");
- expect(editionInfo3.imageUrl).to.be.equal("new image url");
- expect(editionInfo3.description).to.be.equal("new description");
- expect(editionInfo3.animationUrl).to.be.equal("new animation url");
- expect(editionInfo3.externalUrl).to.be.equal("new external url");
- expect(editionInfo3.attributes).to.be.equal("new attributes");
- });
-});
diff --git a/test/MarketplaceFiltererTest.ts b/test/MarketplaceFiltererTest.ts
deleted file mode 100644
index 8fa8162..0000000
--- a/test/MarketplaceFiltererTest.ts
+++ /dev/null
@@ -1,391 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ERC721Editions,
- ERC721EditionsDFS,
- ERC721SingleEdition,
- EditionsMetadataRenderer,
- MinimalForwarder,
- MintManager,
- Observability,
- OperatorFilterRegistry,
-} from "../types";
-import { Errors } from "./__utils__/data";
-import { setupMultipleEdition, setupMultipleEditionDFS, setupSingleEdition, setupSystem } from "./__utils__/helpers";
-
-describe("MarketplaceFilterer functionality", () => {
- let singleEdition: ERC721SingleEdition;
- let operatorRegistry: OperatorFilterRegistry;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let emr: EditionsMetadataRenderer;
- let mintManager: MintManager;
- let auctionManager: AuctionManager;
- let observability: Observability;
- let trustedForwarder: MinimalForwarder;
- let singleEditionImplementation: string;
- let editionsImplementation: string;
- let editionsDFSImplementation: string;
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, editionsOwner, fan1] =
- await ethers.getSigners();
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- singleEditionImplementationAddress,
- editionsImplementationAddress,
- editionsDFSImplementationAddress,
- auctionManagerProxy,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
-
- emr = emrProxy;
- mintManager = mintManagerProxy;
- auctionManager = auctionManagerProxy;
- observability = observabilityInstance;
- trustedForwarder = minimalForwarder;
- singleEditionImplementation = singleEditionImplementationAddress;
- editionsImplementation = editionsImplementationAddress;
- editionsDFSImplementation = editionsDFSImplementationAddress;
-
- operatorRegistry = await (await ethers.getContractFactory("OperatorFilterRegistry")).deploy();
- });
-
- beforeEach(async () => {
- singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 10,
- "Test 1",
- "T1",
- null,
- true,
- );
-
- expect(await singleEdition.operatorFiltererRegistry()).to.equal("0x000000000000AAeB6D7670E522A718067333cd4E");
-
- const tx = await singleEdition.removeMarketplaceFiltererRegistryAndUnregister();
- await tx.wait();
-
- expect(await singleEdition.operatorFiltererRegistry()).to.equal(ethers.constants.AddressZero);
-
- // setup some minted nfts to transfer later
- await expect(singleEdition.registerMinter(editionsOwner.address)).to.emit(
- singleEdition,
- "MinterRegistrationChanged",
- );
-
- await expect(singleEdition.mintAmountToRecipient(0, fan1.address, 4))
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(singleEdition, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- for (let i = 1; i <= 4; i++) {
- expect(await singleEdition.ownerOf(i)).to.equal(fan1.address);
- }
- });
-
- describe("Registering/unregistering filterer registry", async function () {
- it("Registering/unregistering filterer registry sets data properly", async function () {
- // register and subscribe should fail due to non registration of default registrant
- await expect(
- singleEdition.setCustomMarketplaceFiltererRegistryAndRegisterDefaultSubscription(operatorRegistry.address),
- ).to.emit(operatorRegistry, "RegistrationUpdated");
- });
-
- it("Existence of filterer restricts/unrestricts transfers/approvals", async function () {
- // manually register subscription and seed it direclty via registry restricted address
- operatorRegistry = operatorRegistry.connect(initialPlatformExecutor);
- await expect(operatorRegistry.register(initialPlatformExecutor.address))
- .to.emit(operatorRegistry, "RegistrationUpdated")
- .withArgs(initialPlatformExecutor.address, true);
-
- expect(await operatorRegistry.isRegistered(initialPlatformExecutor.address)).to.equal(true);
-
- await expect(operatorRegistry.updateOperator(initialPlatformExecutor.address, editionsOwner.address, true))
- .to.emit(operatorRegistry, "OperatorUpdated")
- .withArgs(initialPlatformExecutor.address, editionsOwner.address, true);
-
- // register and subscribe collection manually to initialPlatformExecutor subscription
- singleEdition = singleEdition.connect(editionsOwner);
- await expect(
- singleEdition.setCustomMarketplaceFiltererRegistryAndRegisterDefaultSubscription(operatorRegistry.address),
- )
- .to.emit(operatorRegistry, "RegistrationUpdated")
- .to.emit(operatorRegistry, "SubscriptionUpdated");
-
- expect(await operatorRegistry.subscriptionOf(singleEdition.address)).to.equal(
- "0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6",
- );
-
- operatorRegistry = operatorRegistry.connect(editionsOwner);
- await expect(operatorRegistry.subscribe(singleEdition.address, initialPlatformExecutor.address)).to.emit(
- operatorRegistry,
- "SubscriptionUpdated",
- );
-
- expect(await operatorRegistry.subscriptionOf(singleEdition.address)).to.equal(initialPlatformExecutor.address);
-
- // all 4 transfer / approval functions should fail for editions owner now, and succeed for others
-
- singleEdition = singleEdition.connect(fan1);
- await expect(singleEdition.approve(editionsOwner.address, 1)).to.be.revertedWithCustomError(
- operatorRegistry,
- "AddressFiltered",
- );
-
- await expect(singleEdition.setApprovalForAll(editionsOwner.address, true)).to.be.revertedWithCustomError(
- operatorRegistry,
- "AddressFiltered",
- );
-
- await expect(singleEdition.approve(editionsMetadataOwner.address, 1)).to.emit(singleEdition, "Approval");
-
- await expect(singleEdition.setApprovalForAll(editionsMetadataOwner.address, true)).to.emit(
- singleEdition,
- "ApprovalForAll",
- );
-
- singleEdition = singleEdition.connect(editionsOwner);
- await expect(singleEdition.transferFrom(fan1.address, editionsOwner.address, 1)).to.be.revertedWithCustomError(
- operatorRegistry,
- "AddressFiltered",
- );
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, editionsOwner.address, 1),
- ).to.be.revertedWithCustomError(operatorRegistry, "AddressFiltered");
-
- singleEdition = singleEdition.connect(editionsMetadataOwner);
- await expect(singleEdition.transferFrom(fan1.address, editionsOwner.address, 1)).to.emit(
- singleEdition,
- "Transfer",
- );
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, editionsOwner.address, 2),
- ).to.emit(singleEdition, "Transfer");
-
- // remove / unregister (validate event as well)
- singleEdition = singleEdition.connect(editionsOwner);
- await expect(singleEdition.removeMarketplaceFiltererRegistryAndUnregister())
- .to.emit(operatorRegistry, "RegistrationUpdated")
- .withArgs(singleEdition.address, false);
-
- // try all 4 transfer / approval with formerly restricted address
- singleEdition = singleEdition.connect(fan1);
- await expect(singleEdition.approve(editionsOwner.address, 3)).to.emit(singleEdition, "Approval");
-
- await expect(singleEdition.setApprovalForAll(editionsOwner.address, true)).to.emit(
- singleEdition,
- "ApprovalForAll",
- );
-
- singleEdition = singleEdition.connect(editionsOwner);
- await expect(singleEdition.transferFrom(fan1.address, editionsOwner.address, 1)).to.be.revertedWithCustomError(
- singleEdition,
- Errors.TransferFromIncorrectOwner,
- );
-
- await expect(
- singleEdition["safeTransferFrom(address,address,uint256)"](fan1.address, editionsOwner.address, 1),
- ).to.be.revertedWithCustomError(singleEdition, Errors.TransferFromIncorrectOwner);
- });
- });
-
- describe("MarketplaceFiltererAbridged", function () {
- let editions: ERC721Editions;
- let editionsDFS: ERC721EditionsDFS;
-
- beforeEach(async () => {
- editions = await setupMultipleEdition(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 10,
- "Test 1",
- "T1",
- );
-
- editionsDFS = await setupMultipleEditionDFS(
- observability.address,
- editionsDFSImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- editionsOwner,
- 10,
- "Test 2",
- );
-
- // setup some minted nfts to transfer later
- await expect(editions.registerMinter(editionsOwner.address)).to.emit(editions, "MinterRegistrationChanged");
-
- await expect(editions.mintAmountToRecipient(0, fan1.address, 4))
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- for (let i = 1; i <= 4; i++) {
- expect(await editions.ownerOf(i)).to.equal(fan1.address);
- }
-
- await expect(editionsDFS.registerMinter(editionsOwner.address)).to.emit(editionsDFS, "MinterRegistrationChanged");
-
- await expect(editionsDFS.mintAmountToRecipient(0, fan1.address, 4))
- .to.emit(editionsDFS, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 1)
- .to.emit(editionsDFS, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 2)
- .to.emit(editionsDFS, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 3)
- .to.emit(editionsDFS, "Transfer")
- .withArgs(ethers.constants.AddressZero, fan1.address, 4);
-
- for (let i = 1; i <= 4; i++) {
- expect(await editionsDFS.ownerOf(i)).to.equal(fan1.address);
- }
- });
-
- it("Registering/unregistering filterer registry sets data properly", async function () {
- await expect(
- editions.setRegistryAndSubscription(operatorRegistry.address, "0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6"),
- ).to.emit(operatorRegistry, "RegistrationUpdated");
-
- await expect(
- editionsDFS.setRegistryAndSubscription(operatorRegistry.address, "0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6"),
- ).to.emit(operatorRegistry, "RegistrationUpdated");
-
- expect(await editions.operatorFiltererRegistry()).to.equal(operatorRegistry.address);
- expect(await editionsDFS.operatorFiltererRegistry()).to.equal(operatorRegistry.address);
-
- await expect(editions.setRegistryAndSubscription(ethers.constants.AddressZero, ethers.constants.AddressZero)).to
- .not.be.reverted;
- await expect(editionsDFS.setRegistryAndSubscription(ethers.constants.AddressZero, ethers.constants.AddressZero))
- .to.not.be.reverted;
- expect(await editions.operatorFiltererRegistry()).to.equal(ethers.constants.AddressZero);
- expect(await editionsDFS.operatorFiltererRegistry()).to.equal(ethers.constants.AddressZero);
- });
-
- it("Existence of filterer restricts/unrestricts transfers/approvals (MultipleEditions)", async function () {
- expect(await operatorRegistry.isRegistered(initialPlatformExecutor.address)).to.equal(true);
-
- // register and subscribe collection manually to initialPlatformExecutor subscription
- editions = editions.connect(editionsOwner);
- await expect(
- editions.setRegistryAndSubscription(operatorRegistry.address, "0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6"),
- )
- .to.emit(operatorRegistry, "RegistrationUpdated")
- .to.emit(operatorRegistry, "SubscriptionUpdated");
-
- expect(await operatorRegistry.subscriptionOf(editions.address)).to.equal(
- "0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6",
- );
-
- operatorRegistry = operatorRegistry.connect(editionsOwner);
- await expect(operatorRegistry.subscribe(editions.address, initialPlatformExecutor.address)).to.emit(
- operatorRegistry,
- "SubscriptionUpdated",
- );
-
- expect(await operatorRegistry.subscriptionOf(editions.address)).to.equal(initialPlatformExecutor.address);
-
- // all 4 transfer / approval functions should fail for editions owner now, and succeed for others
- // TODO: transfer functions
-
- editions = editions.connect(fan1);
- await expect(editions.approve(editionsOwner.address, 1)).to.be.revertedWithCustomError(
- operatorRegistry,
- "AddressFiltered",
- );
-
- await expect(editions.setApprovalForAll(editionsOwner.address, true)).to.be.revertedWithCustomError(
- operatorRegistry,
- "AddressFiltered",
- );
-
- await expect(editions.approve(editionsMetadataOwner.address, 1)).to.emit(editions, "Approval");
-
- await expect(editions.setApprovalForAll(editionsMetadataOwner.address, true)).to.emit(editions, "ApprovalForAll");
- });
-
- it("Existence of filterer restricts/unrestricts transfers/approvals (MultipleEditionsDFS)", async function () {
- expect(await operatorRegistry.isRegistered(initialPlatformExecutor.address)).to.equal(true);
-
- // register and subscribe collection manually to initialPlatformExecutor subscription
- editionsDFS = editionsDFS.connect(editionsOwner);
- await expect(
- editionsDFS.setRegistryAndSubscription(operatorRegistry.address, "0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6"),
- )
- .to.emit(operatorRegistry, "RegistrationUpdated")
- .to.emit(operatorRegistry, "SubscriptionUpdated");
-
- expect(await operatorRegistry.subscriptionOf(editionsDFS.address)).to.equal(
- "0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6",
- );
-
- operatorRegistry = operatorRegistry.connect(editionsOwner);
- await expect(operatorRegistry.subscribe(editionsDFS.address, initialPlatformExecutor.address)).to.emit(
- operatorRegistry,
- "SubscriptionUpdated",
- );
-
- expect(await operatorRegistry.subscriptionOf(editionsDFS.address)).to.equal(initialPlatformExecutor.address);
-
- // all 4 transfer / approval functions should fail for editionsDFS owner now, and succeed for others
- // TODO: transfer functions
-
- editionsDFS = editionsDFS.connect(fan1);
- await expect(editionsDFS.approve(editionsOwner.address, 1)).to.be.revertedWithCustomError(
- operatorRegistry,
- "AddressFiltered",
- );
-
- await expect(editionsDFS.setApprovalForAll(editionsOwner.address, true)).to.be.revertedWithCustomError(
- operatorRegistry,
- "AddressFiltered",
- );
-
- await expect(editionsDFS.approve(editionsMetadataOwner.address, 1)).to.emit(editionsDFS, "Approval");
-
- await expect(editionsDFS.setApprovalForAll(editionsMetadataOwner.address, true)).to.emit(
- editionsDFS,
- "ApprovalForAll",
- );
- });
- });
-});
diff --git a/test/MechanicMintVectorsTest.ts b/test/MechanicMintVectorsTest.ts
deleted file mode 100644
index 0131ffa..0000000
--- a/test/MechanicMintVectorsTest.ts
+++ /dev/null
@@ -1,1139 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- DiscreteDutchAuctionMechanic,
- ERC721EditionsDFS,
- ERC721General,
- ERC721Generative,
- ERC721SingleEditionDFS,
- MinimalForwarder,
- MintManager,
- Observability,
-} from "../types";
-import { SAMPLE_DA_VECTOR } from "./__utils__/data";
-import { Errors } from "./__utils__/data";
-import {
- dutchAuctionUpdateArgs,
- encodeDAVectorData,
- encodeMechanicVectorData,
- produceMechanicVectorId,
- setupGeneral,
- setupGenerative,
- setupMultipleEditionDFS,
- setupSingleEditionDFS,
- setupSystem,
-} from "./__utils__/helpers";
-
-describe("Mechanic mint vectors", () => {
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- generalOwner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let observability: Observability;
- let mintManager: MintManager;
- let auctionManager: AuctionManager;
- let trustedForwarder: MinimalForwarder;
- let dutchAuction: DiscreteDutchAuctionMechanic;
- let editionsDFSImplementation: string;
- let singleEditionDFSImplementation: string;
- let generalImplementation: string;
- let generativeImplementation: string;
-
- let generative: ERC721Generative;
- let editions: ERC721EditionsDFS;
- let singleEdition: ERC721SingleEditionDFS;
- let general: ERC721General;
-
- let generativeVectorId: string;
- let editionsVectorId: string;
- let singleEditionVectorId: string;
- let generalVectorId: string;
-
- const prices1 = ["0.001", "0.0001"];
- const prices2 = ["100", "0.189", "0.09", "0.08", "0.07", "0.06", "0.05", "0.00001"];
- const prices3 = ["0.00000000001", "0.0000000000000001"];
- const prices4: string[] = [];
-
- const mintFeeWei = ethers.BigNumber.from("800000000000000");
-
- before(async () => {
- [
- initialPlatformExecutor,
- mintManagerOwner,
- editionsMetadataOwner,
- platformPaymentAddress,
- editionsOwner,
- generalOwner,
- fan1,
- ] = await ethers.getSigners();
-
- const {
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsDFSImplementationAddress,
- singleEditionDFSImplementationAddress,
- generalImplementationAddress,
- generativeImplementationAddress,
- daMechanic,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- mintManagerOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- observability = observabilityInstance;
- editionsDFSImplementation = editionsDFSImplementationAddress;
- singleEditionDFSImplementation = singleEditionDFSImplementationAddress;
- generalImplementation = generalImplementationAddress;
- generativeImplementation = generativeImplementationAddress;
- dutchAuction = daMechanic;
- });
-
- // in this, validate that contract deployments with mechanic vector registration works
- beforeEach(async function () {
- for (let i = 0; i < 30; i++) {
- prices4[i] = (1 - i * ((1 - 0.08) / 30)).toString();
- }
-
- const vector1 = SAMPLE_DA_VECTOR(dutchAuction.address, {});
- const vector2 = SAMPLE_DA_VECTOR(dutchAuction.address, { prices: prices2 });
- const vector3 = SAMPLE_DA_VECTOR(dutchAuction.address, { prices: prices3 });
- const vector4 = SAMPLE_DA_VECTOR(dutchAuction.address, { prices: prices4, periodDuration: 10000 });
-
- singleEdition = await setupSingleEditionDFS(
- observability.address,
- singleEditionDFSImplementation,
- mintManager.address,
- trustedForwarder.address,
- editionsOwner,
- 5,
- "",
- "NM",
- null,
- vector1,
- );
-
- editions = await setupMultipleEditionDFS(
- observability.address,
- editionsDFSImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- editionsOwner,
- 100,
- "symbol",
- null,
- vector2,
- );
-
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- generalOwner,
- null,
- vector3,
- true,
- );
-
- generative = await setupGenerative(
- observability.address,
- generativeImplementation,
- trustedForwarder.address,
- mintManager.address,
- generalOwner,
- null,
- vector4,
- );
-
- singleEditionVectorId = produceMechanicVectorId(
- singleEdition.address,
- dutchAuction.address,
- parseInt(vector1.seed),
- 0,
- );
- editionsVectorId = produceMechanicVectorId(editions.address, dutchAuction.address, parseInt(vector2.seed), 0);
- generalVectorId = produceMechanicVectorId(general.address, dutchAuction.address, parseInt(vector3.seed));
- generativeVectorId = produceMechanicVectorId(generative.address, dutchAuction.address, parseInt(vector4.seed));
-
- const vectorMeta1 = await mintManager.mechanicVectorMetadata(singleEditionVectorId);
- const vectorMeta2 = await mintManager.mechanicVectorMetadata(editionsVectorId);
- const vectorMeta3 = await mintManager.mechanicVectorMetadata(generalVectorId);
- const vectorMeta4 = await mintManager.mechanicVectorMetadata(generativeVectorId);
- expect(ethers.utils.getAddress(vectorMeta1.contractAddress)).to.equal(
- ethers.utils.getAddress(singleEdition.address),
- );
- expect(ethers.utils.getAddress(vectorMeta2.contractAddress)).to.equal(ethers.utils.getAddress(editions.address));
- expect(ethers.utils.getAddress(vectorMeta3.contractAddress)).to.equal(ethers.utils.getAddress(general.address));
- expect(ethers.utils.getAddress(vectorMeta4.contractAddress)).to.equal(ethers.utils.getAddress(generative.address));
-
- const daState1 = await dutchAuction.getVectorState(singleEditionVectorId);
- const daState2 = await dutchAuction.getVectorState(editionsVectorId);
- const daState3 = await dutchAuction.getVectorState(generalVectorId);
- const daState4 = await dutchAuction.getVectorState(generativeVectorId);
-
- expect(daState1._vector.numPrices.toString()).to.equal(prices1.length.toString());
- expect(
- daState1.prices.map(price => {
- return parseFloat(ethers.utils.formatEther(price));
- }),
- ).to.eql(
- prices1.map(price => {
- return parseFloat(price);
- }),
- );
- expect(daState2._vector.numPrices.toString()).to.equal(prices2.length.toString());
- expect(
- daState2.prices.map(price => {
- return parseFloat(ethers.utils.formatEther(price));
- }),
- ).to.eql(
- prices2.map(price => {
- return parseFloat(price);
- }),
- );
- expect(daState3._vector.numPrices.toString()).to.equal(prices3.length.toString());
- expect(
- daState3.prices.map(price => {
- return parseFloat(ethers.utils.formatEther(price));
- }),
- ).to.eql(
- prices3.map(price => {
- return parseFloat(price);
- }),
- );
- expect(daState4._vector.numPrices.toString()).to.equal(prices4.length.toString());
- expect(
- daState4.prices.map(price => {
- return parseFloat(ethers.utils.formatEther(price));
- }),
- ).to.eql(
- prices4.map(price => {
- return parseFloat(price);
- }),
- );
- });
-
- describe("Mechanic vector management", function () {
- it("Only the owner of a collection can register mechanic mint vectors", async function () {
- const seed = Math.floor(Date.now() / 1000);
- const vectorData = encodeMechanicVectorData(
- mintManager.address,
- fan1.address,
- SAMPLE_DA_VECTOR(dutchAuction.address, {}),
- );
- mintManager = mintManager.connect(fan1);
- await expect(
- mintManager.registerMechanicVector(
- {
- contractAddress: editions.address,
- editionId: 1,
- isChoose: false,
- paused: false,
- mechanic: dutchAuction.address,
- isEditionBased: true,
- },
- seed,
- vectorData,
- ),
- ).to.be.revertedWithCustomError(mintManager, Errors.Unauthorized);
-
- mintManager = mintManager.connect(generalOwner);
- });
-
- it("Only the owner can pause/unpause mechanic mint vectors, which cause the mints to be paused/unpaused", async function () {
- // do with both mechanicMintNum and mechanicMintChoose
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 2, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices4[0])).mul(2),
- }),
- )
- .to.emit(mintManager, "NumTokenMint")
- .withArgs(generativeVectorId, generative.address, true, 2);
- await expect(
- mintManager.mechanicMintChoose(generalVectorId, fan1.address, [1, 2], "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices3[0])).mul(2),
- }),
- )
- .to.emit(mintManager, "ChooseTokenMint")
- .withArgs(generalVectorId, general.address, true, [1, 2]);
-
- await expect(mintManager.setPauseOnMechanicMintVector(generativeVectorId, true)).to.be.not.reverted;
- await expect(mintManager.setPauseOnMechanicMintVector(generalVectorId, true)).to.be.not.reverted;
-
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 2, "0x", {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.MechanicPaused);
- await expect(
- mintManager.mechanicMintChoose(generalVectorId, fan1.address, [3], "0x", {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.MechanicPaused);
- });
-
- it("Cannot try the wrong mint style", async function () {
- await expect(
- mintManager.mechanicMintNum(generalVectorId, fan1.address, 2, "0x", {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidMechanic);
- await expect(
- mintManager.mechanicMintChoose(generativeVectorId, fan1.address, [3], "0x", {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidMechanic);
- });
-
- describe("Dutch auction mechanic vector management", function () {
- it("Can register/create dutch auction mechanic mint vectors with different configurations", async function () {
- mintManager = mintManager.connect(editionsOwner);
- const editionId = 0;
- const seed = 1;
-
- await expect(
- mintManager.registerMechanicVector(
- {
- contractAddress: editions.address,
- editionId,
- isChoose: false,
- paused: false,
- mechanic: dutchAuction.address,
- isEditionBased: true,
- },
- seed,
- encodeDAVectorData(
- SAMPLE_DA_VECTOR(dutchAuction.address, {
- prices: ["0.001", "0.0001", "0.00009"],
- periodDuration: 10,
- maxTotalClaimableViaVector: 20,
- startTimestamp: Math.floor(Date.now() / 1000) + 1000,
- endTimestamp: Math.floor(Date.now() / 1000) + 1021, // 21 sec dutch auction / 2 periods of 10 sec each + 1 last period of 1 sec
- }),
- editionsOwner.address,
- ),
- ),
- )
- .to.emit(dutchAuction, "DiscreteDutchAuctionCreated")
- .withArgs(produceMechanicVectorId(editions.address, dutchAuction.address, seed, editionId));
- });
-
- it("Cannot register/create dutch auction mechanic mint vectors with invalid configurations", async function () {
- const editionId = 0;
- const seed = 1;
-
- await expect(
- mintManager.registerMechanicVector(
- {
- contractAddress: editions.address,
- editionId,
- isChoose: false,
- paused: false,
- mechanic: dutchAuction.address,
- isEditionBased: true,
- },
- seed,
- encodeDAVectorData(
- SAMPLE_DA_VECTOR(dutchAuction.address, {
- prices: ["0.001", "0.0001", "0.00009"],
- periodDuration: 10,
- maxTotalClaimableViaVector: 20,
- startTimestamp: Math.floor(Date.now() / 1000) + 1000,
- endTimestamp: Math.floor(Date.now() / 1000) + 1020, // invalid, no time for last period
- }),
- editionsOwner.address,
- ),
- ),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
-
- await expect(
- mintManager.registerMechanicVector(
- {
- contractAddress: editions.address,
- editionId,
- isChoose: false,
- paused: false,
- mechanic: dutchAuction.address,
- isEditionBased: true,
- },
- seed,
- encodeDAVectorData(SAMPLE_DA_VECTOR(dutchAuction.address, {}), ethers.constants.AddressZero),
- ),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
-
- await expect(
- mintManager.registerMechanicVector(
- {
- contractAddress: editions.address,
- editionId,
- isChoose: false,
- paused: false,
- mechanic: dutchAuction.address,
- isEditionBased: true,
- },
- seed,
- encodeDAVectorData(
- SAMPLE_DA_VECTOR(dutchAuction.address, {
- periodDuration: 0,
- }),
- ethers.constants.AddressZero,
- ),
- ),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
-
- await expect(
- mintManager.registerMechanicVector(
- {
- contractAddress: editions.address,
- editionId,
- isChoose: false,
- paused: false,
- mechanic: dutchAuction.address,
- isEditionBased: true,
- },
- seed,
- encodeDAVectorData(
- SAMPLE_DA_VECTOR(dutchAuction.address, {
- prices: ["0.001"],
- }),
- ethers.constants.AddressZero,
- ),
- ),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
-
- await expect(
- mintManager.registerMechanicVector(
- {
- contractAddress: editions.address,
- editionId,
- isChoose: false,
- paused: false,
- mechanic: dutchAuction.address,
- isEditionBased: true,
- },
- seed,
- encodeDAVectorData(
- SAMPLE_DA_VECTOR(dutchAuction.address, {
- prices: ["0.001", "0.0001", "0.01"],
- }),
- ethers.constants.AddressZero,
- ),
- ),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
-
- await expect(
- mintManager.registerMechanicVector(
- {
- contractAddress: editions.address,
- editionId,
- isChoose: false,
- paused: false,
- mechanic: dutchAuction.address,
- isEditionBased: true,
- },
- seed,
- encodeDAVectorData(
- SAMPLE_DA_VECTOR(dutchAuction.address, {
- prices: ["0.001", "0.0001", "0.0001"],
- }),
- ethers.constants.AddressZero,
- ),
- ),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
- });
-
- it("Non-owner of collection cannot update dutch auction", async function () {
- dutchAuction = dutchAuction.connect(fan1);
- const {
- dutchAuction: dutchAuction1,
- updateConfig: updateConfig1,
- packedPrices: packedPrices1,
- } = dutchAuctionUpdateArgs({
- prices: ["0.1", "0.0001", "0.00001"],
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuction1, packedPrices1, updateConfig1),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.Unauthorized);
- });
-
- it("Can update auction mechanic mint vectors with different configurations", async function () {
- dutchAuction = dutchAuction.connect(generalOwner);
- const {
- numPrices: da1NumPrices,
- bytesPerPrice: da1BytesPerPrice,
- periodDuration: da1PeriodDuration,
- tokenLimitPerTx: da1TokenLimitPerTx,
- endTimestamp: da1EndTimestamp,
- } = (await dutchAuction.getRawVector(generativeVectorId))._vector;
- const {
- dutchAuction: dutchAuction1,
- updateConfig: updateConfig1,
- packedPrices: packedPrices1,
- } = dutchAuctionUpdateArgs({
- prices: ["1000", "0.0001", "0.00001"],
- });
- // none of periodDuration, tokenLimitPerTx, endTimestamp should update
- await expect(
- dutchAuction.updateVector(
- generativeVectorId,
- { ...dutchAuction1, periodDuration: 5, tokenLimitPerTx: 10, endTimestamp: 100 },
- packedPrices1,
- updateConfig1,
- ),
- )
- .to.emit(dutchAuction, "DiscreteDutchAuctionUpdated")
- .withArgs(generativeVectorId);
- const {
- numPrices: da1NewNumPrices,
- bytesPerPrice: da1NewBytesPerPrice,
- periodDuration: da1NewPeriodDuration,
- tokenLimitPerTx: da1NewTokenLimitPerTx,
- endTimestamp: da1NewEndTimestamp,
- } = (await dutchAuction.getRawVector(generativeVectorId))._vector;
- const newPackedPrices = (await dutchAuction.getRawVector(generativeVectorId)).packedPrices;
- expect(da1NumPrices.toString()).to.not.equal(da1NewNumPrices.toString());
- expect(da1NewNumPrices.toString()).to.equal("3");
- expect(da1BytesPerPrice.toString()).to.not.equal(da1NewBytesPerPrice.toString());
- expect(da1PeriodDuration.toString()).to.equal(da1NewPeriodDuration.toString());
- expect(da1TokenLimitPerTx.toString()).to.equal(da1NewTokenLimitPerTx.toString());
- expect(da1EndTimestamp.toString()).to.equal(da1NewEndTimestamp.toString());
- expect(packedPrices1).to.eql(newPackedPrices);
- expect(
- (await dutchAuction.getVectorState(generativeVectorId)).prices.map(price => {
- return ethers.utils.formatEther(price);
- }),
- ).to.eql(["1000.0", "0.0001", "0.00001"]);
-
- const {
- dutchAuction: dutchAuction2,
- updateConfig: updateConfig2,
- packedPrices: packedPrices2,
- } = dutchAuctionUpdateArgs({
- startTimestamp: 10000,
- endTimestamp: 20000,
- periodDuration: 334, // 30 periods, so 334 x 30 = 10020, on limit
- maxUserClaimableViaVector: 5,
- maxTotalClaimableViaVector: 10,
- tokenLimitPerTx: 5,
- paymentRecipient: fan1.address,
- });
- await expect(dutchAuction.updateVector(generativeVectorId, dutchAuction2, packedPrices2, updateConfig2))
- .to.emit(dutchAuction, "DiscreteDutchAuctionUpdated")
- .withArgs(generativeVectorId);
- const {
- startTimestamp: da2NewStartTimestamp,
- endTimestamp: da2NewEndTimestamp,
- periodDuration: da2NewPeriodDuration,
- maxUserClaimableViaVector: da2NewMaxUserClaimableViaVector,
- maxTotalClaimableViaVector: da2NewMaxTotalClaimableViaVector,
- tokenLimitPerTx: da2NewTokenLimitPerTx,
- paymentRecipient: da2NewPaymentRecipient,
- } = (await dutchAuction.getRawVector(generativeVectorId))._vector;
-
- expect(da2NewStartTimestamp.toString()).to.equal("10000");
- expect(da2NewEndTimestamp.toString()).to.equal("20000");
- expect(da2NewPeriodDuration.toString()).to.equal("334");
- expect(da2NewMaxUserClaimableViaVector.toString()).to.equal("5");
- expect(da2NewMaxTotalClaimableViaVector.toString()).to.equal("10");
- expect(da2NewTokenLimitPerTx.toString()).to.equal("5");
- expect(ethers.utils.getAddress(da2NewPaymentRecipient)).to.equal(ethers.utils.getAddress(fan1.address));
- });
-
- it("Cannot update dutch auction to set a time range that exceeds or equals (numPrices - 1) * periodDuration", async function () {
- // cannot set invalid times, given there are 30 prices
- const {
- dutchAuction: dutchAuction1,
- updateConfig: updateConfig1,
- packedPrices: packedPrices1,
- } = dutchAuctionUpdateArgs({
- periodDuration: 10,
- startTimestamp: 20,
- endTimestamp: 310,
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuction1, packedPrices1, updateConfig1),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
- });
-
- it("Cannot update dutch auction with non-decreasing prices", async function () {
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- prices: ["0.001", "0.001"],
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
- });
-
- it("Cannot update dutch auction with the payment recipient as the zero address", async function () {
- const {
- dutchAuction: dutchAuction3,
- updateConfig: updateConfig3,
- packedPrices: packedPrices3,
- } = dutchAuctionUpdateArgs({
- paymentRecipient: ethers.constants.AddressZero,
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuction3, packedPrices3, updateConfig3),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
- });
-
- it("Cannot update dutch auction to make period duration 0", async function () {
- const {
- dutchAuction: dutchAuction3,
- updateConfig: updateConfig3,
- packedPrices: packedPrices3,
- } = dutchAuctionUpdateArgs({
- periodDuration: 0,
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuction3, packedPrices3, updateConfig3),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidVectorConfig);
- });
-
- describe("Cannot update certain fields on dutch auction after first token is minted", function () {
- beforeEach(async function () {
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 2, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")).mul(2),
- }),
- ).to.emit(mintManager, "NumTokenMint");
-
- const { _vector, payeePotentialEscrowedFunds, currentPrice } = await dutchAuction.getVectorState(
- generativeVectorId,
- );
- expect(_vector.lowestPriceSoldAtIndex).to.equal(0);
- expect(_vector.currentSupply).to.equal(2);
- expect(ethers.utils.formatEther(_vector.totalSales)).to.equal("2.0");
- expect(ethers.utils.formatEther(currentPrice)).to.equal("1.0");
- expect(ethers.utils.formatEther(payeePotentialEscrowedFunds)).to.equal("2.0");
- });
-
- it("maxTotalClaimableViaVector", async function () {
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- maxTotalClaimableViaVector: 30,
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidUpdate);
- });
-
- it("prices", async function () {
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- prices: ["0.008", "0.007"],
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidUpdate);
- });
-
- it("periodDuration", async function () {
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- periodDuration: 1001,
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidUpdate);
- });
-
- it("startTimestamp", async function () {
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- startTimestamp: 100,
- });
- await expect(
- dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidUpdate);
- });
- });
- });
- });
-
- describe("Dutch auctions", function () {
- describe("Mints + rebates + escrow funds withdrawal (logic / state / errors)", function () {
- it("Cannot send too low of a mint fee", async function () {
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x"),
- ).to.be.revertedWithCustomError(mintManager, Errors.MintFeeTooLow);
- });
-
- it("Cannot send too low of a fee for the auction", async function () {
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", { value: mintFeeWei }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- });
-
- it("Can only mint within the time bounds of the auction", async function () {
- dutchAuction = dutchAuction.connect(generalOwner);
- const currTime = Math.floor(Date.now() / 1000);
- const startTimestamp = currTime + 1000;
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- startTimestamp,
- endTimestamp: startTimestamp + 300000,
- });
- await expect(dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig)).to.not
- .be.reverted;
-
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")),
- }),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidMint);
- await ethers.provider.send("evm_mine", [currTime + 200000]);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")),
- }),
- ).not.be.reverted;
- await ethers.provider.send("evm_mine", [currTime + 400000]);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")),
- }),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidMint);
-
- const currentPrice = (await dutchAuction.getVectorState(generativeVectorId)).currentPrice;
- const userInfo = await dutchAuction.getUserInfo(generativeVectorId, fan1.address);
- const rebate = userInfo[0];
- const { totalPosted } = userInfo[1];
- expect(ethers.utils.formatEther(totalPosted)).to.equal("1.0");
- expect(totalPosted.sub(currentPrice).eq(rebate)).to.equal(true);
- });
-
- it("Cannot mint over maxUser, maxTotal, and tokenLimitPerTx bounds", async function () {
- dutchAuction = dutchAuction.connect(generalOwner);
- const currTime = Math.floor(Date.now() / 1000);
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- maxTotalClaimableViaVector: 10,
- maxUserClaimableViaVector: 5,
- tokenLimitPerTx: 3,
- startTimestamp: currTime + 1000,
- });
- await expect(dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig)).to.not
- .be.reverted;
-
- // tokenLimitPerTx
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 4, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")).mul(4),
- }),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidMint);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 3, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")).mul(3),
- }),
- ).to.not.be.reverted;
- // maxUserClaimableViaVector
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 3, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")).mul(3),
- }),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidMint);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 2, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")).mul(2),
- }),
- ).to.not.be.reverted;
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, generalOwner.address, 3, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")).mul(3),
- }),
- ).to.not.be.reverted;
- // maxTotalClaimableViaVector
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, editionsOwner.address, 3, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")).mul(3),
- }),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidMint);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, editionsOwner.address, 2, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther("1")).mul(2),
- }),
- ).to.not.be.reverted;
- });
-
- it("State updates properly through multiple mints at different prices", async function () {
- dutchAuction = dutchAuction.connect(generalOwner);
- const currTime = Math.floor(Date.now() / 1000);
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- maxTotalClaimableViaVector: 15,
- startTimestamp: currTime + 1000000,
- });
- await expect(dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig)).to.not
- .be.reverted;
-
- await ethers.provider.send("evm_mine", [currTime + 1000000]);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 3, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices4[0])).mul(3),
- }),
- ).to.not.be.reverted;
- await ethers.provider.send("evm_mine", [currTime + 1010000]);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 3, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices4[1])).mul(3),
- }),
- ).to.not.be.reverted;
- await ethers.provider.send("evm_mine", [currTime + 1030000]);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 3, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices4[3])).mul(3),
- }),
- ).to.not.be.reverted;
-
- const { _vector, currentPrice, collectionSupply, payeePotentialEscrowedFunds, escrowedFundsAmountFinalized } =
- await dutchAuction.getVectorState(generativeVectorId);
- expect(_vector.currentSupply).to.equal(9);
- expect(ethers.utils.formatEther(currentPrice)).to.equal(prices4[3]);
- expect(collectionSupply.toString()).to.equal("9");
- expect(payeePotentialEscrowedFunds.toString()).to.eql(ethers.utils.parseEther(prices4[3]).mul(9).toString());
- expect(escrowedFundsAmountFinalized).to.equal(false);
- expect(_vector.totalSales.toString()).to.equal(
- ethers.utils
- .parseEther(prices4[0])
- .add(ethers.utils.parseEther(prices4[1]))
- .add(ethers.utils.parseEther(prices4[3]))
- .mul(3)
- .toString(),
- );
- await ethers.provider.send("evm_mine", [currTime + 1040000]);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, generalOwner.address, 5, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices4[4])).mul(5),
- }),
- ).to.not.be.reverted;
-
- expect((await dutchAuction.getVectorState(generativeVectorId))._vector.lowestPriceSoldAtIndex).to.equal(4);
-
- await ethers.provider.send("evm_mine", [currTime + 1090000]);
-
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, generalOwner.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices4[9])).mul(1),
- }),
- ).to.not.be.reverted;
-
- expect((await dutchAuction.getVectorState(generativeVectorId))._vector.lowestPriceSoldAtIndex).to.equal(9);
-
- const contractBalance = await ethers.provider.getBalance(dutchAuction.address);
- const collectorBalance = await ethers.provider.getBalance(generalOwner.address);
- const userInfo = await dutchAuction.getUserInfo(generativeVectorId, generalOwner.address);
- expect(userInfo[0].toString()).to.equal(
- ethers.utils.parseEther(prices4[4]).sub(ethers.utils.parseEther(prices4[9])).mul(5).toString(),
- );
- const generalOwnerTotalPosted = ethers.utils
- .parseEther(prices4[4])
- .mul(5)
- .add(ethers.utils.parseEther(prices4[9]));
- expect(userInfo[1].totalPosted.toString()).to.equal(generalOwnerTotalPosted.toString());
-
- // collect rebate
- await expect(dutchAuction.rebateCollector(generativeVectorId, generalOwner.address))
- .to.emit(dutchAuction, "DiscreteDutchAuctionCollectorRebate")
- .withArgs(
- generativeVectorId,
- generalOwner.address,
- userInfo[0],
- (
- await dutchAuction.getVectorState(generativeVectorId)
- ).currentPrice,
- );
-
- // validate 2 balances difference
- expect((await ethers.provider.getBalance(dutchAuction.address)).eq(contractBalance.sub(userInfo[0]))).to.equal(
- true,
- );
- // over 90% of the rebate (consider ether lost to gas)
- const newCollectorBalance = await ethers.provider.getBalance(generalOwner.address);
- expect(newCollectorBalance.lt(collectorBalance.add(userInfo[0]))).to.equal(true);
- expect(newCollectorBalance.gt(collectorBalance.add(userInfo[0]).mul(9).div(10))).to.equal(true);
-
- const state = await dutchAuction.getVectorState(generativeVectorId);
- const [rebateGeneralOwner, newUserInfoGeneralOwner] = await dutchAuction.getUserInfo(
- generativeVectorId,
- generalOwner.address,
- );
- const [rebateFan1, newUserInfoFan1] = await dutchAuction.getUserInfo(generativeVectorId, fan1.address);
- expect(state.escrowedFundsAmountFinalized).to.equal(true);
- expect(state.payeePotentialEscrowedFunds.toString()).to.equal(
- ethers.utils.parseEther(prices4[9]).mul(15).toString(),
- );
- expect(rebateGeneralOwner.eq(0)).to.equal(true);
- expect(newUserInfoGeneralOwner.totalPosted.eq(generalOwnerTotalPosted.sub(userInfo[0]))).to.equal(true); // new totalPosted = old totalPosted - rebate paid out
- expect(newUserInfoGeneralOwner.numRebates).to.equal(1);
- expect(newUserInfoGeneralOwner.numTokensBought).to.equal(6);
- const fan1TotalPosted = ethers.utils
- .parseEther(prices4[0])
- .add(ethers.utils.parseEther(prices4[1]))
- .add(ethers.utils.parseEther(prices4[3]))
- .mul(3);
- expect(newUserInfoFan1.totalPosted.eq(fan1TotalPosted)).to.equal(true);
- expect(rebateFan1.eq(fan1TotalPosted.sub(ethers.utils.parseEther(prices4[9]).mul(9)))).to.equal(true);
- expect(newUserInfoFan1.numRebates).to.equal(0);
- expect(newUserInfoFan1.numTokensBought).to.equal(9);
-
- expect(state._vector.auctionExhausted).to.equal(true);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(state.currentPrice),
- }),
- ).to.be.revertedWithCustomError(dutchAuction, Errors.InvalidMint);
- });
-
- it("Underlying collection is exhausted and we validly withdraws escrowed funds", async function () {
- dutchAuction = dutchAuction.connect(generalOwner);
- const currTime = Math.floor(Date.now() / 1000);
- generative = generative.connect(generalOwner);
- await expect(generative.setLimitSupply(1)).to.not.be.reverted;
-
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- maxTotalClaimableViaVector: 15,
- startTimestamp: currTime + 2000000,
- });
- await expect(dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig)).to.not
- .be.reverted;
-
- await ethers.provider.send("evm_mine", [currTime + 2000000]);
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices4[0])),
- }),
- ).to.not.be.reverted;
-
- // cannot trigger a rebate if user isn't eligible
- expect((await dutchAuction.getUserInfo(generativeVectorId, fan1.address))[0].eq(0)).to.equal(true);
- await expect(dutchAuction.rebateCollector(generativeVectorId, fan1.address)).to.be.revertedWithCustomError(
- dutchAuction,
- Errors.CollectorNotOwedRebate,
- );
-
- const state = await dutchAuction.getVectorState(generativeVectorId);
- expect(state.auctionExhausted).to.equal(true);
- expect(state.escrowedFundsAmountFinalized).to.equal(true);
- expect(state.payeePotentialEscrowedFunds.eq(ethers.utils.parseEther("1"))).to.equal(true);
- expect(state._vector.lowestPriceSoldAtIndex).eq(0);
-
- dutchAuction = dutchAuction.connect(fan1);
- const contractBalance = await ethers.provider.getBalance(dutchAuction.address);
- const payeeBalance = await ethers.provider.getBalance(state._vector.paymentRecipient);
- await expect(dutchAuction.withdrawDPPFunds(generativeVectorId))
- .to.emit(dutchAuction, "DiscreteDutchAuctionDPPFundsWithdrawn")
- .withArgs(generativeVectorId, state._vector.paymentRecipient, ethers.utils.parseEther("1.0"), 1);
- expect(
- (await ethers.provider.getBalance(dutchAuction.address)).eq(
- contractBalance.sub(ethers.utils.parseEther("1")),
- ),
- ).to.equal(true);
- expect(
- (await ethers.provider.getBalance(state._vector.paymentRecipient)).eq(
- payeeBalance.add(ethers.utils.parseEther("1")),
- ),
- ).to.equal(true);
-
- // cannot re-trigger a withdrawal
- expect((await dutchAuction.getVectorState(generativeVectorId))._vector.payeeRevenueHasBeenWithdrawn).to.equal(
- true,
- );
- await expect(dutchAuction.withdrawDPPFunds(generativeVectorId)).to.be.revertedWithCustomError(
- dutchAuction,
- Errors.InvalidDPPFundsWithdrawl,
- );
- });
-
- it("Cannot trigger a rebate for a vector with no tokens minted through it", async function () {
- await expect(dutchAuction.rebateCollector(generativeVectorId, fan1.address)).to.be.revertedWithCustomError(
- dutchAuction,
- Errors.InvalidRebate,
- );
- });
-
- it("Cannot trigger a withdrawal when no tokens have been minted through the vector", async function () {
- await expect(dutchAuction.withdrawDPPFunds(generativeVectorId)).to.be.revertedWithCustomError(
- dutchAuction,
- Errors.InvalidDPPFundsWithdrawl,
- );
- });
-
- it("Cannot trigger a withdrawal if an auction isn't exhausted or in the FPP", async function () {
- dutchAuction = dutchAuction.connect(generalOwner);
- const currTime = Math.floor(Date.now() / 1000);
-
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- maxTotalClaimableViaVector: 15,
- startTimestamp: currTime + 3000000,
- });
- await expect(dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig)).to.not
- .be.reverted;
- await ethers.provider.send("evm_mine", [currTime + 3000000]);
-
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices4[0])),
- }),
- ).to.not.be.reverted;
-
- await expect(dutchAuction.withdrawDPPFunds(generativeVectorId)).to.be.revertedWithCustomError(
- dutchAuction,
- Errors.InvalidDPPFundsWithdrawl,
- );
- });
-
- it("Keep rebating as price drops, down to FPP (with excess amounts sent), withdraw funds, then payments go straight to payee", async function () {
- dutchAuction = dutchAuction.connect(generalOwner);
- const currTime = Math.floor(Date.now() / 1000);
-
- const prices = ["1", "0.8", "0.6", "0.4"];
- const {
- dutchAuction: dutchAuctionData,
- updateConfig,
- packedPrices,
- } = dutchAuctionUpdateArgs({
- maxTotalClaimableViaVector: 4,
- startTimestamp: currTime + 4000000,
- prices,
- });
- await expect(dutchAuction.updateVector(generativeVectorId, dutchAuctionData, packedPrices, updateConfig)).to.not
- .be.reverted;
- await ethers.provider.send("evm_mine", [currTime + 4000000]);
-
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices[0])).add(ethers.utils.parseEther("0.5")),
- }),
- ).to.not.be.reverted;
- await ethers.provider.send("evm_mine", [currTime + 4010000]);
- await expect(dutchAuction.rebateCollector(generativeVectorId, fan1.address))
- .to.emit(dutchAuction, "DiscreteDutchAuctionCollectorRebate")
- .withArgs(
- generativeVectorId,
- fan1.address,
- ethers.utils.parseEther("0.7"),
- ethers.utils.parseEther(prices[1]),
- );
- expect((await dutchAuction.getUserInfo(generativeVectorId, fan1.address))[0].eq(0)).to.equal(true);
-
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices[1])).add(ethers.utils.parseEther("0.5")),
- }),
- ).to.not.be.reverted;
- await ethers.provider.send("evm_mine", [currTime + 4020000]);
- await expect(dutchAuction.rebateCollector(generativeVectorId, fan1.address))
- .to.emit(dutchAuction, "DiscreteDutchAuctionCollectorRebate")
- .withArgs(
- generativeVectorId,
- fan1.address,
- ethers.utils.parseEther("0.9"),
- ethers.utils.parseEther(prices[2]),
- );
- expect((await dutchAuction.getUserInfo(generativeVectorId, fan1.address))[0].eq(0)).to.equal(true);
-
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices[2])).add(ethers.utils.parseEther("0.5")),
- }),
- ).to.not.be.reverted;
- await ethers.provider.send("evm_mine", [currTime + 4030000]);
- await expect(dutchAuction.rebateCollector(generativeVectorId, fan1.address))
- .to.emit(dutchAuction, "DiscreteDutchAuctionCollectorRebate")
- .withArgs(
- generativeVectorId,
- fan1.address,
- ethers.utils.parseEther("1.1"),
- ethers.utils.parseEther(prices[3]),
- );
- expect((await dutchAuction.getUserInfo(generativeVectorId, fan1.address))[0].eq(0)).to.equal(true);
-
- const { _vector, auctionInFPP, auctionExhausted, escrowedFundsAmountFinalized, payeePotentialEscrowedFunds } =
- await dutchAuction.getVectorState(generativeVectorId);
- expect(_vector.currentSupply).to.equal(3);
- expect(escrowedFundsAmountFinalized).to.equal(true);
- expect(auctionExhausted).to.equal(false);
- expect(auctionInFPP).to.equal(true);
- expect(payeePotentialEscrowedFunds.eq(ethers.utils.parseEther("0.4").mul(3)));
-
- dutchAuction = dutchAuction.connect(fan1);
- mintManager = mintManager.connect(fan1);
- const payeeBalance = await ethers.provider.getBalance(_vector.paymentRecipient);
- await expect(dutchAuction.withdrawDPPFunds(generativeVectorId))
- .to.emit(dutchAuction, "DiscreteDutchAuctionDPPFundsWithdrawn")
- .withArgs(generativeVectorId, _vector.paymentRecipient, ethers.utils.parseEther("0.4"), 3);
- const intermediaryPayeeBalance = await ethers.provider.getBalance(_vector.paymentRecipient);
- expect(intermediaryPayeeBalance.sub(payeePotentialEscrowedFunds).eq(payeeBalance));
-
- // payments now go straight to payee
- await expect(
- mintManager.mechanicMintNum(generativeVectorId, fan1.address, 1, "0x", {
- value: mintFeeWei.add(ethers.utils.parseEther(prices[3])).add(ethers.utils.parseEther("0.5")),
- }),
- ).to.not.be.reverted;
- expect(
- (await ethers.provider.getBalance(_vector.paymentRecipient)).eq(
- intermediaryPayeeBalance.add(ethers.utils.parseEther("0.4")),
- ),
- );
-
- // can still collect rebate from overpay in FPP
- expect(
- (await dutchAuction.getUserInfo(generativeVectorId, fan1.address))[0].eq(ethers.utils.parseEther("0.5")),
- );
- await expect(dutchAuction.rebateCollector(generativeVectorId, fan1.address))
- .to.emit(dutchAuction, "DiscreteDutchAuctionCollectorRebate")
- .withArgs(generativeVectorId, fan1.address, ethers.utils.parseEther("0.5"), ethers.utils.parseEther("0.4"));
- });
- });
- });
-});
diff --git a/test/MetaTransactionsTest.ts b/test/MetaTransactionsTest.ts
deleted file mode 100644
index 5843dd4..0000000
--- a/test/MetaTransactionsTest.ts
+++ /dev/null
@@ -1,647 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ERC721Editions,
- ERC721General,
- ERC721SingleEdition,
- EditionsMetadataRenderer,
- MinimalForwarder,
- MintManager,
- Observability,
-} from "../types";
-import {
- DEFAULT_ONCHAIN_MINT_VECTOR,
- setupEditions,
- setupGeneral,
- setupSingleEdition,
- setupSystem,
-} from "./__utils__/helpers";
-import { sign2771MetaTxRequest } from "./__utils__/metaTx";
-
-// have to import this here to not import OpenZeppelin minimal forwarder
-const MinimalForwarderData = {
- _format: "hh-sol-artifact-1",
- contractName: "MinimalForwarder",
- sourceName: "contracts/metatx/MinimalForwarder.sol",
- abi: [
- {
- inputs: [],
- stateMutability: "nonpayable",
- type: "constructor",
- },
- {
- inputs: [
- {
- components: [
- {
- internalType: "address",
- name: "from",
- type: "address",
- },
- {
- internalType: "address",
- name: "to",
- type: "address",
- },
- {
- internalType: "uint256",
- name: "value",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "gas",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "nonce",
- type: "uint256",
- },
- {
- internalType: "bytes",
- name: "data",
- type: "bytes",
- },
- ],
- internalType: "struct MinimalForwarder.ForwardRequest",
- name: "req",
- type: "tuple",
- },
- {
- internalType: "bytes",
- name: "signature",
- type: "bytes",
- },
- ],
- name: "execute",
- outputs: [
- {
- internalType: "bool",
- name: "",
- type: "bool",
- },
- {
- internalType: "bytes",
- name: "",
- type: "bytes",
- },
- ],
- stateMutability: "payable",
- type: "function",
- },
- {
- inputs: [
- {
- internalType: "address",
- name: "from",
- type: "address",
- },
- ],
- name: "getNonce",
- outputs: [
- {
- internalType: "uint256",
- name: "",
- type: "uint256",
- },
- ],
- stateMutability: "view",
- type: "function",
- },
- {
- inputs: [
- {
- components: [
- {
- internalType: "address",
- name: "from",
- type: "address",
- },
- {
- internalType: "address",
- name: "to",
- type: "address",
- },
- {
- internalType: "uint256",
- name: "value",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "gas",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "nonce",
- type: "uint256",
- },
- {
- internalType: "bytes",
- name: "data",
- type: "bytes",
- },
- ],
- internalType: "struct MinimalForwarder.ForwardRequest",
- name: "req",
- type: "tuple",
- },
- {
- internalType: "bytes",
- name: "signature",
- type: "bytes",
- },
- ],
- name: "verify",
- outputs: [
- {
- internalType: "bool",
- name: "",
- type: "bool",
- },
- ],
- stateMutability: "view",
- type: "function",
- },
- ],
- bytecode:
- "0x61014060405234801561001157600080fd5b50604080518082018252601081526f26b4b734b6b0b62337b93bb0b93232b960811b602080830191825283518085019094526005845264302e302e3160d81b908401528151902060e08190527fae209a0b48f21c054280f2455d32cf309387644879d9acbd8ffc1991638118856101008190524660a0529192917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6100fb8184846040805160208101859052908101839052606081018290524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090509392505050565b6080523060c052610120525061011092505050565b60805160a05160c05160e0516101005161012051610b6261015f600039600061050c0152600061055b015260006105360152600061048f015260006104b9015260006104e30152610b626000f3fe6080604052600436106100345760003560e01c80632d0335ab1461003957806347153f8214610082578063bf5d3bdb146100a3575b600080fd5b34801561004557600080fd5b5061006f6100543660046108fd565b6001600160a01b031660009081526020819052604090205490565b6040519081526020015b60405180910390f35b61009561009036600461092d565b6100d3565b604051610079929190610a28565b3480156100af57600080fd5b506100c36100be36600461092d565b61028c565b6040519015158152602001610079565b600060606100e285858561028c565b61014e5760405162461bcd60e51b815260206004820152603260248201527f4d696e696d616c466f727761726465723a207369676e617475726520646f6573604482015271081b9bdd081b585d18da081c995c5d595cdd60721b60648201526084015b60405180910390fd5b61015d60808601356001610a4b565b60008061016d60208901896108fd565b6001600160a01b03166001600160a01b03168152602001908152602001600020819055506000808660200160208101906101a791906108fd565b6001600160a01b0316606088013560408901356101c760a08b018b610a71565b6101d460208d018d6108fd565b6040516020016101e693929190610ab8565b60408051601f198184030181529082905261020091610ade565b600060405180830381858888f193505050503d806000811461023e576040519150601f19603f3d011682016040523d82523d6000602084013e610243565b606091505b50915091508181906102685760405162461bcd60e51b81526004016101459190610afa565b50610278603f6060890135610b0d565b5a1161028057fe5b90969095509350505050565b60008061039f84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061039992507fdd8f4b70b0f4393e889bd39128a30628a78b61816a9eb8199759e7a349657e4891506102fc905060208a018a6108fd565b61030c60408b0160208c016108fd565b60408b013560608c013560808d013561032860a08f018f610a71565b604051610336929190610b2f565b6040805191829003822060208301989098526001600160a01b0396871690820152949093166060850152608084019190915260a083015260c082015260e0810191909152610100016040516020818303038152906040528051906020012061040a565b9061045e565b905060808501356000806103b660208901896108fd565b6001600160a01b03166001600160a01b031681526020019081526020016000205414801561040157506103ec60208601866108fd565b6001600160a01b0316816001600160a01b0316145b95945050505050565b6000610458610417610482565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b92915050565b600080600061046d85856105a9565b9150915061047a81610619565b509392505050565b6000306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480156104db57507f000000000000000000000000000000000000000000000000000000000000000046145b1561050557507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6000808251604114156105e05760208301516040840151606085015160001a6105d4878285856107d7565b94509450505050610612565b82516040141561060a57602083015160408401516105ff8683836108c4565b935093505050610612565b506000905060025b9250929050565b600081600481111561062d5761062d610b3f565b14156106365750565b600181600481111561064a5761064a610b3f565b14156106985760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610145565b60028160048111156106ac576106ac610b3f565b14156106fa5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610145565b600381600481111561070e5761070e610b3f565b14156107675760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610145565b600481600481111561077b5761077b610b3f565b14156107d45760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610145565b50565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561080e57506000905060036108bb565b8460ff16601b1415801561082657508460ff16601c14155b1561083757506000905060046108bb565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561088b573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166108b4576000600192509250506108bb565b9150600090505b94509492505050565b6000806001600160ff1b038316816108e160ff86901c601b610a4b565b90506108ef878288856107d7565b935093505050935093915050565b60006020828403121561090f57600080fd5b81356001600160a01b038116811461092657600080fd5b9392505050565b60008060006040848603121561094257600080fd5b833567ffffffffffffffff8082111561095a57600080fd5b9085019060c0828803121561096e57600080fd5b9093506020850135908082111561098457600080fd5b818601915086601f83011261099857600080fd5b8135818111156109a757600080fd5b8760208285010111156109b957600080fd5b6020830194508093505050509250925092565b60005b838110156109e75781810151838201526020016109cf565b838111156109f6576000848401525b50505050565b60008151808452610a148160208601602086016109cc565b601f01601f19169290920160200192915050565b8215158152604060208201526000610a4360408301846109fc565b949350505050565b60008219821115610a6c57634e487b7160e01b600052601160045260246000fd5b500190565b6000808335601e19843603018112610a8857600080fd5b83018035915067ffffffffffffffff821115610aa357600080fd5b60200191503681900382131561061257600080fd5b8284823760609190911b6bffffffffffffffffffffffff19169101908152601401919050565b60008251610af08184602087016109cc565b9190910192915050565b60208152600061092660208301846109fc565b600082610b2a57634e487b7160e01b600052601260045260246000fd5b500490565b8183823760009101908152919050565b634e487b7160e01b600052602160045260246000fdfea164736f6c634300080a000a",
- deployedBytecode:
- "0x6080604052600436106100345760003560e01c80632d0335ab1461003957806347153f8214610082578063bf5d3bdb146100a3575b600080fd5b34801561004557600080fd5b5061006f6100543660046108fd565b6001600160a01b031660009081526020819052604090205490565b6040519081526020015b60405180910390f35b61009561009036600461092d565b6100d3565b604051610079929190610a28565b3480156100af57600080fd5b506100c36100be36600461092d565b61028c565b6040519015158152602001610079565b600060606100e285858561028c565b61014e5760405162461bcd60e51b815260206004820152603260248201527f4d696e696d616c466f727761726465723a207369676e617475726520646f6573604482015271081b9bdd081b585d18da081c995c5d595cdd60721b60648201526084015b60405180910390fd5b61015d60808601356001610a4b565b60008061016d60208901896108fd565b6001600160a01b03166001600160a01b03168152602001908152602001600020819055506000808660200160208101906101a791906108fd565b6001600160a01b0316606088013560408901356101c760a08b018b610a71565b6101d460208d018d6108fd565b6040516020016101e693929190610ab8565b60408051601f198184030181529082905261020091610ade565b600060405180830381858888f193505050503d806000811461023e576040519150601f19603f3d011682016040523d82523d6000602084013e610243565b606091505b50915091508181906102685760405162461bcd60e51b81526004016101459190610afa565b50610278603f6060890135610b0d565b5a1161028057fe5b90969095509350505050565b60008061039f84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061039992507fdd8f4b70b0f4393e889bd39128a30628a78b61816a9eb8199759e7a349657e4891506102fc905060208a018a6108fd565b61030c60408b0160208c016108fd565b60408b013560608c013560808d013561032860a08f018f610a71565b604051610336929190610b2f565b6040805191829003822060208301989098526001600160a01b0396871690820152949093166060850152608084019190915260a083015260c082015260e0810191909152610100016040516020818303038152906040528051906020012061040a565b9061045e565b905060808501356000806103b660208901896108fd565b6001600160a01b03166001600160a01b031681526020019081526020016000205414801561040157506103ec60208601866108fd565b6001600160a01b0316816001600160a01b0316145b95945050505050565b6000610458610417610482565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b92915050565b600080600061046d85856105a9565b9150915061047a81610619565b509392505050565b6000306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480156104db57507f000000000000000000000000000000000000000000000000000000000000000046145b1561050557507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6000808251604114156105e05760208301516040840151606085015160001a6105d4878285856107d7565b94509450505050610612565b82516040141561060a57602083015160408401516105ff8683836108c4565b935093505050610612565b506000905060025b9250929050565b600081600481111561062d5761062d610b3f565b14156106365750565b600181600481111561064a5761064a610b3f565b14156106985760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610145565b60028160048111156106ac576106ac610b3f565b14156106fa5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610145565b600381600481111561070e5761070e610b3f565b14156107675760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610145565b600481600481111561077b5761077b610b3f565b14156107d45760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610145565b50565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561080e57506000905060036108bb565b8460ff16601b1415801561082657508460ff16601c14155b1561083757506000905060046108bb565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561088b573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166108b4576000600192509250506108bb565b9150600090505b94509492505050565b6000806001600160ff1b038316816108e160ff86901c601b610a4b565b90506108ef878288856107d7565b935093505050935093915050565b60006020828403121561090f57600080fd5b81356001600160a01b038116811461092657600080fd5b9392505050565b60008060006040848603121561094257600080fd5b833567ffffffffffffffff8082111561095a57600080fd5b9085019060c0828803121561096e57600080fd5b9093506020850135908082111561098457600080fd5b818601915086601f83011261099857600080fd5b8135818111156109a757600080fd5b8760208285010111156109b957600080fd5b6020830194508093505050509250925092565b60005b838110156109e75781810151838201526020016109cf565b838111156109f6576000848401525b50505050565b60008151808452610a148160208601602086016109cc565b601f01601f19169290920160200192915050565b8215158152604060208201526000610a4360408301846109fc565b949350505050565b60008219821115610a6c57634e487b7160e01b600052601160045260246000fd5b500190565b6000808335601e19843603018112610a8857600080fd5b83018035915067ffffffffffffffff821115610aa357600080fd5b60200191503681900382131561061257600080fd5b8284823760609190911b6bffffffffffffffffffffffff19169101908152601401919050565b60008251610af08184602087016109cc565b9190910192915050565b60208152600061092660208301846109fc565b600082610b2a57634e487b7160e01b600052601260045260246000fd5b500490565b8183823760009101908152919050565b634e487b7160e01b600052602160045260246000fdfea164736f6c634300080a000a",
- linkReferences: {},
- deployedLinkReferences: {},
-};
-
-describe("MetaTransactions functionality", () => {
- let minimalForwarderFanSigner: MinimalForwarder;
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- owner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let emr: EditionsMetadataRenderer;
- let mintManager: MintManager;
- let auctionManager: AuctionManager;
- let observability: Observability;
- let trustedForwarder: MinimalForwarder;
- let editionsImplementation: string;
- let singleEditionImplementation: string;
- let generalImplementation: string;
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, owner, fan1] =
- await ethers.getSigners();
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- owner,
- );
-
- emr = emrProxy;
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- observability = observabilityInstance;
- auctionManager = auctionManagerProxy;
-
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
-
- mintManager = mintManager.connect(mintManagerOwner);
- minimalForwarderFanSigner = minimalForwarder.connect(fan1);
- });
-
- describe("MinimalForwarder", function () {
- describe("ERC721Editions", function () {
- let editions: ERC721Editions;
-
- beforeEach(async function () {
- editions = await setupEditions(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- owner,
- );
- });
-
- it("Not encoding enough gas for an operation fails the operation", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: editions.address,
- gas: 30000,
- data: await editions.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(true);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith("");
- });
-
- it("Mismatched signers from the from address of a request are not allowed", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: editionsMetadataOwner.address,
- to: editions.address,
- gas: 60000,
- data: await editions.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(false);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith(
- "MinimalForwarder: signature does not match request",
- );
- });
-
- it("Mismatched data in a request is not allowed", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: editions.address,
- gas: 60000,
- data: await editions.interface.encodeFunctionData("freezeMints"),
- });
-
- request.gas = 60001;
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(false);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith(
- "MinimalForwarder: signature does not match request",
- );
- });
-
- it("Sending a request from a non-trusted forwarder does not invoke meta transaction functionality", async function () {
- // have to do this to not import OpenZeppelin minimal forwarder
- const newMinimalForwarder = (await new ethers.ContractFactory(
- MinimalForwarderData.abi,
- MinimalForwarderData.bytecode,
- fan1,
- ).deploy()) as MinimalForwarder;
- await newMinimalForwarder.deployed();
-
- // this operation would have succeeded from the trusted forwarder, but since it's not trusted, it fails
- const { signature, request } = await sign2771MetaTxRequest(owner, newMinimalForwarder, {
- from: owner.address,
- to: editions.address,
- gas: 60000,
- data: await editions.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await newMinimalForwarder.verify(request, signature)).to.equal(true);
- expect(await editions.isTrustedForwarder(newMinimalForwarder.address)).to.equal(false);
- expect(await editions.isTrustedForwarder(minimalForwarderFanSigner.address)).to.equal(true);
-
- // TODO: fix HardhatChaiMatchersDecodingError issue. This reverts with Ownable message as expected
- let reverted = false;
- try {
- await expect(newMinimalForwarder.execute(request, signature)).to.emit(editions, "MintsFrozen");
- } catch (error) {
- reverted = true;
- }
-
- expect(reverted).to.equal(true);
- });
-
- it("A validly encoded msgSender is allowed to perform operations as expected", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: editions.address,
- gas: 60000,
- data: await editions.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(true);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.emit(editions, "MintsFrozen").withArgs();
- });
- });
-
- describe("ERC721SingleEdition", function () {
- let singleEdition: ERC721SingleEdition;
-
- beforeEach(async function () {
- singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- owner,
- 4,
- "name",
- "SYM",
- );
- });
-
- it("Not encoding enough gas for an operation fails the operation", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: singleEdition.address,
- gas: 30000,
- data: await singleEdition.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(true);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith("");
- });
-
- it("Mismatched signers from the from address of a request are not allowed", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: editionsMetadataOwner.address,
- to: singleEdition.address,
- gas: 60000,
- data: await singleEdition.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(false);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith(
- "MinimalForwarder: signature does not match request",
- );
- });
-
- it("Mismatched data in a request is not allowed", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: singleEdition.address,
- gas: 60000,
- data: await singleEdition.interface.encodeFunctionData("freezeMints"),
- });
-
- request.gas = 60001;
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(false);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith(
- "MinimalForwarder: signature does not match request",
- );
- });
-
- it("Sending a request from a non-trusted forwarder does not invoke meta transaction functionality", async function () {
- // have to do this to not import OpenZeppelin minimal forwarder
- const newMinimalForwarder = (await new ethers.ContractFactory(
- MinimalForwarderData.abi,
- MinimalForwarderData.bytecode,
- fan1,
- ).deploy()) as MinimalForwarder;
- await newMinimalForwarder.deployed();
-
- // this operation would have succeeded from the trusted forwarder, but since it's not trusted, it fails
- const { signature, request } = await sign2771MetaTxRequest(owner, newMinimalForwarder, {
- from: owner.address,
- to: singleEdition.address,
- gas: 60000,
- data: await singleEdition.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await newMinimalForwarder.verify(request, signature)).to.equal(true);
- expect(await singleEdition.isTrustedForwarder(newMinimalForwarder.address)).to.equal(false);
- expect(await singleEdition.isTrustedForwarder(minimalForwarderFanSigner.address)).to.equal(true);
-
- // TODO: fix HardhatChaiMatchersDecodingError issue. This reverts with Ownable message as expected
- let reverted = false;
- try {
- await expect(newMinimalForwarder.execute(request, signature)).to.emit(singleEdition, "MintsFrozen");
- } catch (error) {
- reverted = true;
- }
-
- expect(reverted).to.equal(true);
- });
-
- it("A validly encoded msgSender is allowed to perform operations as expected", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: singleEdition.address,
- gas: 60000,
- data: await singleEdition.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(true);
-
- await expect(minimalForwarderFanSigner.execute(request, signature))
- .to.emit(singleEdition, "MintsFrozen")
- .withArgs();
- });
- });
-
- describe("ERC721General", function () {
- let general: ERC721General;
-
- beforeEach(async function () {
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- owner,
- );
- });
-
- it("Not encoding enough gas for an operation fails the operation", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: general.address,
- gas: 30000,
- data: await general.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(true);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith("");
- });
-
- it("Mismatched signers from the from address of a request are not allowed", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: editionsMetadataOwner.address,
- to: general.address,
- gas: 60000,
- data: await general.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(false);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith(
- "MinimalForwarder: signature does not match request",
- );
- });
-
- it("Mismatched data in a request is not allowed", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: general.address,
- gas: 60000,
- data: await general.interface.encodeFunctionData("freezeMints"),
- });
-
- request.gas = 60001;
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(false);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith(
- "MinimalForwarder: signature does not match request",
- );
- });
-
- it("Sending a request from a non-trusted forwarder does not invoke meta transaction functionality", async function () {
- // have to do this to not import OpenZeppelin minimal forwarder
- const newMinimalForwarder = (await new ethers.ContractFactory(
- MinimalForwarderData.abi,
- MinimalForwarderData.bytecode,
- fan1,
- ).deploy()) as MinimalForwarder;
- await newMinimalForwarder.deployed();
-
- // this operation would have succeeded from the trusted forwarder, but since it's not trusted, it fails
- const { signature, request } = await sign2771MetaTxRequest(owner, newMinimalForwarder, {
- from: owner.address,
- to: general.address,
- gas: 60000,
- data: await general.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await newMinimalForwarder.verify(request, signature)).to.equal(true);
- expect(await general.isTrustedForwarder(newMinimalForwarder.address)).to.equal(false);
- expect(await general.isTrustedForwarder(minimalForwarderFanSigner.address)).to.equal(true);
-
- // TODO: fix HardhatChaiMatchersDecodingError issue. This reverts with Ownable message as expected
- let reverted = false;
- try {
- await expect(newMinimalForwarder.execute(request, signature)).to.emit(general, "MintsFrozen");
- } catch (error) {
- reverted = true;
- }
-
- expect(reverted).to.equal(true);
- });
-
- it("A validly encoded msgSender is allowed to perform operations as expected", async function () {
- const { signature, request } = await sign2771MetaTxRequest(owner, minimalForwarderFanSigner, {
- from: owner.address,
- to: general.address,
- gas: 60000,
- data: await general.interface.encodeFunctionData("freezeMints"),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(true);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.emit(general, "MintsFrozen").withArgs();
- });
- });
-
- describe("MintManager", function () {
- it("Not encoding enough gas for an operation fails the operation", async function () {
- const { signature, request } = await sign2771MetaTxRequest(mintManagerOwner, minimalForwarderFanSigner, {
- from: mintManagerOwner.address,
- to: mintManager.address,
- gas: 2000,
- data: await mintManager.interface.encodeFunctionData("transferOwnership", [fan1.address]),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(true);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith("");
- });
-
- it("Mismatched signers from the from address of a request are not allowed", async function () {
- const { signature, request } = await sign2771MetaTxRequest(mintManagerOwner, minimalForwarderFanSigner, {
- from: editionsMetadataOwner.address,
- to: mintManager.address,
- gas: 60000,
- data: await mintManager.interface.encodeFunctionData("transferOwnership", [fan1.address]),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(false);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith(
- "MinimalForwarder: signature does not match request",
- );
- });
-
- it("Mismatched data in a request is not allowed", async function () {
- const { signature, request } = await sign2771MetaTxRequest(mintManagerOwner, minimalForwarderFanSigner, {
- from: mintManagerOwner.address,
- to: mintManager.address,
- gas: 60000,
- data: await mintManager.interface.encodeFunctionData("transferOwnership", [fan1.address]),
- });
-
- request.gas = 60001;
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(false);
-
- await expect(minimalForwarderFanSigner.execute(request, signature)).to.be.revertedWith(
- "MinimalForwarder: signature does not match request",
- );
- });
-
- it("Sending a request from a non-trusted forwarder does not invoke meta transaction functionality", async function () {
- // have to do this to not import OpenZeppelin minimal forwarder
- const newMinimalForwarder = (await new ethers.ContractFactory(
- MinimalForwarderData.abi,
- MinimalForwarderData.bytecode,
- fan1,
- ).deploy()) as MinimalForwarder;
- await newMinimalForwarder.deployed();
-
- // this operation would have succeeded from the trusted forwarder, but since it's not trusted, it fails
- const { signature, request } = await sign2771MetaTxRequest(mintManagerOwner, newMinimalForwarder, {
- from: mintManagerOwner.address,
- to: mintManager.address,
- gas: 60000,
- data: await mintManager.interface.encodeFunctionData("transferOwnership", [fan1.address]),
- });
-
- expect(await newMinimalForwarder.verify(request, signature)).to.equal(true);
- expect(await mintManager.isTrustedForwarder(newMinimalForwarder.address)).to.equal(false);
- expect(await mintManager.isTrustedForwarder(minimalForwarderFanSigner.address)).to.equal(true);
-
- // TODO: fix HardhatChaiMatchersDecodingError issue. This reverts with Ownable message as expected
- let reverted = false;
- try {
- await expect(newMinimalForwarder.execute(request, signature)).to.emit(mintManager, "OwnershipTransferred");
- } catch (error) {
- reverted = true;
- }
-
- expect(reverted).to.equal(true);
- });
-
- it("A validly encoded msgSender is allowed to perform operations as expected", async function () {
- const { signature, request } = await sign2771MetaTxRequest(mintManagerOwner, minimalForwarderFanSigner, {
- from: mintManagerOwner.address,
- to: mintManager.address,
- gas: 60000,
- data: await mintManager.interface.encodeFunctionData("transferOwnership", [fan1.address]),
- });
-
- expect(await minimalForwarderFanSigner.verify(request, signature)).to.equal(true);
-
- await expect(minimalForwarderFanSigner.execute(request, signature))
- .to.emit(mintManager, "OwnershipTransferred")
- .withArgs(mintManagerOwner.address, fan1.address);
- });
- });
- });
-});
diff --git a/test/MintManagerTest.ts b/test/MintManagerTest.ts
deleted file mode 100644
index 84b6a69..0000000
--- a/test/MintManagerTest.ts
+++ /dev/null
@@ -1,2838 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- ERC721Editions,
- ERC721EditionsDFS,
- ERC721General,
- ERC721GeneralSequence,
- ERC721GeneralSequence__factory,
- ERC721Generative,
- ERC721SingleEdition,
- ERC721SingleEditionDFS,
- EditionsMetadataRenderer,
- MinimalForwarder,
- MintManager,
- MintManager__factory,
- Observability,
-} from "../types";
-import { SAMPLE_ABRIDGED_VECTOR, SAMPLE_ABRIDGED_VECTOR_UPDATE_CONFIG } from "./__utils__/data";
-import { Errors } from "./__utils__/data";
-import {
- generateClaim,
- generateClaimWithMetaTxPackets,
- generateSeriesClaim,
- setupEditionsDFS,
- setupGeneral,
- setupGenerative,
- setupMultipleEdition,
- setupSingleEdition,
- setupSingleEditionDFS,
- setupSystem,
-} from "./__utils__/helpers";
-import { getExpiredClaimTimestamp, getValidClaimTimestamp } from "./__utils__/mint";
-
-describe("Mint Manager", () => {
- let initialPlatformExecutor: SignerWithAddress,
- additionalPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- eWETHOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- editionsOwner: SignerWithAddress,
- generalOwner: SignerWithAddress,
- fan1: SignerWithAddress,
- randomEOA: SignerWithAddress;
-
- let emr: EditionsMetadataRenderer;
- let observability: Observability;
- let mintManager: MintManager;
- let auctionManager: AuctionManager;
- let trustedForwarder: MinimalForwarder;
- let editionsImplementation: string;
- let singleEditionImplementation: string;
- let singleEditionDFSImplementation: string;
- let generalImplementation: string;
- let generalSequenceImplementation: string;
- let generativeImplementation: string;
- let editionsDFSImplementation: string;
-
- const mintFeeWei = ethers.BigNumber.from("800000000000000");
-
- before(async () => {
- [
- initialPlatformExecutor,
- additionalPlatformExecutor,
- mintManagerOwner,
- editionsMetadataOwner,
- eWETHOwner,
- platformPaymentAddress,
- editionsOwner,
- generalOwner,
- fan1,
- randomEOA,
- ] = await ethers.getSigners();
- });
-
- describe("Platform Executor", function () {
- it("Should be able to add a new platform executor as Owner", async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- generalSequenceImplementationAddress,
- generativeImplementationAddress,
- singleEditionDFSImplementationAddress,
- editionsDFSImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- observability = observabilityInstance;
- emr = emrProxy;
- editionsImplementation = editionsImplementationAddress;
- editionsDFSImplementation = editionsDFSImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- generalSequenceImplementation = generalSequenceImplementationAddress;
- generativeImplementation = generativeImplementationAddress;
- singleEditionDFSImplementation = singleEditionDFSImplementationAddress;
-
- const mintManagerOwnerBased = mintManager.connect(mintManagerOwner);
-
- await expect(mintManagerOwnerBased.addOrDeprecatePlatformExecutor(additionalPlatformExecutor.address)).to.not.be
- .reverted;
- expect(await mintManagerOwnerBased.isPlatformExecutor(additionalPlatformExecutor.address)).to.be.true;
- });
- it("Should be able to deprecate platform executor as Owner", async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- mintManagerOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- observability = observabilityInstance;
- emr = emrProxy;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
-
- //Add platform executor
- await expect(mintManager.addOrDeprecatePlatformExecutor(additionalPlatformExecutor.address)).to.not.be.reverted;
- expect(await mintManager.isPlatformExecutor(additionalPlatformExecutor.address)).to.be.true;
-
- //deprecate platform executor
- await expect(mintManager.addOrDeprecatePlatformExecutor(additionalPlatformExecutor.address)).to.not.be.reverted;
- expect(await mintManager.isPlatformExecutor(additionalPlatformExecutor.address)).to.be.false;
- });
- it("Should not be able to add Zero address as platform executor", async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- editionsImplementationAddress,
- observability: observabilityInstance,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- mintManagerOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- observability = observabilityInstance;
- emr = emrProxy;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- await expect(
- mintManager.addOrDeprecatePlatformExecutor(ethers.constants.AddressZero),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidExecutorChanged);
- expect(await mintManager.isPlatformExecutor(ethers.constants.AddressZero)).to.be.false;
- });
- it("Should not be able to add a platform executor that already exists", async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- mintManagerOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- emr = emrProxy;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- await expect(mintManager.addOrDeprecatePlatformExecutor(additionalPlatformExecutor.address)).to.not.be.reverted;
- expect(await mintManager.isPlatformExecutor(additionalPlatformExecutor.address)).to.be.true;
- });
- it("Should reject all platform executor changes from non owner", async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- mintManagerOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- emr = emrProxy;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- const mintManagerForFan1 = await mintManager.connect(fan1);
-
- //Add/deprecate
- await expect(
- mintManagerForFan1.addOrDeprecatePlatformExecutor(additionalPlatformExecutor.address),
- ).to.be.revertedWith("Ownable: caller is not the owner");
- expect(await mintManager.isPlatformExecutor(additionalPlatformExecutor.address)).be.false;
- });
- });
-
- describe("Off-Chain Claims", function () {
- let singleEdition: ERC721SingleEdition;
- before(async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- mintManagerOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- observability = observabilityInstance;
- emr = emrProxy;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 10,
- "Test 1",
- "T1",
- );
- });
-
- it("Should return true for valid claim", async function () {
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- );
- expect(await mintManager.verifyClaim(claim, signature, fan1.address)).to.be.true;
- });
- it("should return false for expired timestamp claim", async function () {
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getExpiredClaimTimestamp(),
- );
- expect(await mintManager.verifyClaim(claim, signature, fan1.address)).to.be.false;
- });
- it("Should return false if not PlatformExecutor", async function () {
- const { signature, claim } = await generateClaim(
- randomEOA,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- );
- expect(await mintManager.verifyClaim(claim, signature, fan1.address)).to.be.false;
- });
- it("Should return false if maxPerVector reached", async function () {
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 1,
- );
- expect(await mintManager.verifyClaim(claim, signature, fan1.address)).to.be.true;
- const mintManagerForFan1 = mintManager.connect(fan1);
- const tx = await mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- });
- await tx.wait();
- const { signature: signature2, claim: claim2 } = await generateClaim(
- initialPlatformExecutor,
- mintManagerForFan1.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 1,
- );
- expect(await mintManagerForFan1.verifyClaim(claim2, signature2, fan1.address)).to.be.false;
- });
- it("Should return false if maxPerUser reached", async function () {
- const offChainVectorId = "maxPerUserTestVectorId";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 0,
- 1,
- 0,
- offChainVectorId,
- "maxPerUserClaimNonce",
- );
- expect(await mintManager.verifyClaim(claim, signature, fan1.address)).to.be.true;
- const mintManagerForFan1 = mintManager.connect(fan1);
- const tx = await mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- });
- await tx.wait();
- const { signature: signature2, claim: claim2 } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 0,
- 1,
- 0,
- offChainVectorId,
- "maxPerUserClaimNonce1",
- );
- expect(await mintManager.verifyClaim(claim2, signature2, fan1.address)).to.be.false;
- });
- it("Should return false if claimNonce already used", async function () {
- const claimNonce = "claimNonceUsedClaimNonce";
- const offChainVectorId = "claimNonceUsedVectorId";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 0,
- 0,
- 0,
- offChainVectorId,
- claimNonce,
- );
- expect(await mintManager.verifyClaim(claim, signature, fan1.address)).to.be.true;
- const mintManagerForFan1 = mintManager.connect(fan1);
- const tx = await mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- });
- await tx.wait();
- const { signature: signature2, claim: claim2 } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 0,
- 0,
- 0,
- offChainVectorId,
- claimNonce,
- );
- expect(await mintManager.verifyClaim(claim2, signature2, fan1.address)).to.be.false;
- });
- });
-
- describe("Gated Mints", function () {
- async function deployMetaTxnFixture() {
- const maticWETHFactory = await ethers.getContractFactory("MaticWETH", fan1);
- const maticWETH = await maticWETHFactory.deploy(initialPlatformExecutor.address);
- await maticWETH.deployed();
-
- const eWETHFactory = await ethers.getContractFactory("EthereumWETH", eWETHOwner);
- const eWETH = await eWETHFactory.deploy();
- await eWETH.deployed();
-
- const tx = await eWETH.mint(fan1.address, 100);
- await tx.wait();
-
- return { eWETH, maticWETH };
- }
- describe("General721", function () {
- let general: ERC721General;
- const offChainVectorId = "gatedMintGeneral721VectorId";
- const offchainVectorId2 = "gatedMintGeneral721VectorId2";
- const maxPerVector = 10;
- const maxPerUser = 10;
- before(async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- mintManagerOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- emr = emrProxy;
- observability = observabilityInstance;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- generalOwner,
- null,
- null,
- false,
- false,
- 0,
- ethers.constants.AddressZero,
- ethers.constants.AddressZero,
- 0,
- "Test 1",
- "T1",
- );
- });
- it("Should be able to mint one to one recipient", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce1";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(mintManagerForFan1.gatedSeriesMint(claim, signature, fan1.address)).to.be.revertedWithCustomError(
- mintManager,
- Errors.MintFeeTooLow,
- );
- await expect(
- mintManagerForFan1.gatedSeriesMint(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.emit(general, "Transfer");
- });
-
- it("User limit is based on claimer address, and others can ferry mints", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonceA";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- maxPerVector,
- 1,
- 0,
- offchainVectorId2,
- claimNonce,
- );
- const mintManagerNotForFan = mintManager.connect(generalOwner);
- await expect(
- mintManagerNotForFan.gatedSeriesMint(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.emit(general, "Transfer");
-
- const claimNonce2 = "gatedMintGeneral721ClaimNonceB";
- const { signature: signature2, claim: claim2 } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- maxPerVector,
- 1,
- 0,
- offchainVectorId2,
- claimNonce2,
- );
- const mintManagerForFan = mintManager.connect(fan1);
- await expect(
- mintManagerForFan.gatedSeriesMint(claim2, signature2, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidClaim);
- });
-
- it("Cannot mint with an unsafe mint recipient (tx not sent by claimer and to a recipient that's not the claimer)", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonceC";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerNotForFan = mintManager.connect(generalOwner);
- await expect(
- mintManagerNotForFan.gatedSeriesMint(claim, signature, generalOwner.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.UnsafeMintRecipient);
- });
-
- it("Should be able to mint multiple to one recipient", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce2";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- getValidClaimTimestamp(),
- "0.01",
- 9,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedSeriesMint(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.gatedSeriesMint(claim, signature, fan1.address, {
- value: ethers.utils.parseEther("0.09"),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(mintManagerForFan1.gatedSeriesMint(claim, signature, fan1.address)).to.be.revertedWithCustomError(
- mintManager,
- Errors.InvalidPaymentAmount,
- );
- await expect(
- mintManagerForFan1.gatedSeriesMint(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint).add(ethers.utils.parseEther("0.09")),
- }),
- ).to.emit(general, "Transfer");
- });
- });
- describe("General721 Series claims (choose token)", function () {
- let general: ERC721General;
- const offChainVectorId = "gatedSeriesMintVectorId";
- const maxPerVector = 10;
- const maxPerUser = 10;
- before(async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- mintManagerOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- emr = emrProxy;
- observability = observabilityInstance;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- generalOwner,
- null,
- null,
- false,
- false,
- 10,
- ethers.constants.AddressZero,
- ethers.constants.AddressZero,
- 0,
- "Test 1",
- "T1",
- );
- });
- it("Should be able to mint a chosen token to a recipient", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce1";
- const { signature, claim } = await generateSeriesClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 1,
- getValidClaimTimestamp(),
- "0",
- maxPerVector,
- maxPerUser,
- offChainVectorId,
- claimNonce,
- );
- // expect(await mintManager.verifySeriesClaim(claim, signature, fan1.address, [1])).to.be.true;
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedSeriesMintChooseToken(claim, signature, fan1.address, [1]),
- ).to.be.revertedWithCustomError(mintManager, Errors.MintFeeTooLow);
- await expect(
- mintManagerForFan1.gatedSeriesMintChooseToken(claim, signature, fan1.address, [1], { value: mintFeeWei }),
- ).to.emit(general, "Transfer");
- });
- it("Should be able to mint multiple chosen tokens to one recipient", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce2";
- const { signature, claim } = await generateSeriesClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 2,
- getValidClaimTimestamp(),
- "0.01",
- maxPerVector,
- maxPerUser,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedSeriesMintChooseToken(claim, signature, fan1.address, [2, 3], {
- value: mintFeeWei.mul(2),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.gatedSeriesMintChooseToken(claim, signature, fan1.address, [2, 3], {
- value: ethers.utils.parseEther("0.02"),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.gatedSeriesMintChooseToken(claim, signature, fan1.address, [2, 3]),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.gatedSeriesMintChooseToken(claim, signature, fan1.address, [2, 3], {
- value: mintFeeWei.mul(2).add(ethers.utils.parseEther("0.02")),
- }),
- ).to.emit(general, "Transfer");
- });
-
- it("Should not be able to mint chosen token that's already minted", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce3";
- const { signature, claim } = await generateSeriesClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 2,
- getValidClaimTimestamp(),
- "0",
- maxPerVector,
- maxPerUser,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- // expect(await mintManagerForFan1.verifySeriesClaim(claim, signature, fan1.address, [4, 2])).to.be.false;
- await expect(
- mintManagerForFan1.gatedSeriesMintChooseToken(claim, signature, fan1.address, [4, 2], {
- value: mintFeeWei.mul(2),
- }),
- ).to.be.revertedWith("ERC721: token minted");
- });
-
- /*
- it("Invalid claim signer should fail", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce4";
- const { signature, claim } = await generateSeriesClaim(
- generalOwner,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 2,
- getValidClaimTimestamp(),
- "0",
- maxPerVector,
- maxPerUser,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- // expect(await mintManagerForFan1.verifySeriesClaim(claim, signature, fan1.address, [4])).to.be.false;
- });
-
- it("Hitting the max per user limit should fail", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce5";
- const { signature, claim } = await generateSeriesClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 3,
- getValidClaimTimestamp(),
- "0",
- maxPerVector,
- 4,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- // expect(await mintManagerForFan1.verifySeriesClaim(claim, signature, fan1.address, [4, 5])).to.be.false;
- });
-
- it("Hitting the max per vector limit should fail", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce6";
- const { signature, claim } = await generateSeriesClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 5,
- getValidClaimTimestamp(),
- "0",
- 4,
- maxPerUser,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- // expect(await mintManagerForFan1.verifySeriesClaim(claim, signature, fan1.address, [4, 5])).to.be.false;
- });
-
- it("Expired claim should fail", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce7";
- const { signature, claim } = await generateSeriesClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 1,
- "100",
- "0",
- maxPerVector,
- maxPerUser,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- // expect(await mintManagerForFan1.verifySeriesClaim(claim, signature, fan1.address, [4])).to.be.false;
- });
-
- it("Claim with taken nonce should fail", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce1";
- const { signature, claim } = await generateSeriesClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 1,
- getValidClaimTimestamp(),
- "0",
- maxPerVector,
- maxPerUser,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- // expect(await mintManagerForFan1.verifySeriesClaim(claim, signature, fan1.address, [4])).to.be.false;
- });
- it("Cannot mint more tokens than the maxPerTxn", async function () {
- const claimNonce = "gatedMintGeneral721ClaimNonce9";
- const { signature, claim } = await generateSeriesClaim(
- initialPlatformExecutor,
- mintManager.address,
- general.address,
- fan1.address,
- generalOwner.address,
- 0,
- getValidClaimTimestamp(),
- "0",
- maxPerVector,
- maxPerUser,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- // expect(await mintManagerForFan1.verifySeriesClaim(claim, signature, fan1.address, [4])).to.be.false;
- });
- */
- });
- describe("Edition721", function () {
- describe("Single Edition", function () {
- let singleEdition: ERC721SingleEdition, mintManager: MintManager;
- const offChainVectorId = "gatedMintEditions721VectorId";
- const maxPerVector = 10;
- const maxPerUser = 10;
- const editionSize = 10;
-
- async function deploySingleEditionFixture() {
- //Deploy Minimal Forwarder
- const minimalForwarderFactory = await ethers.getContractFactory("MinimalForwarder");
- const minimalForwarder = await minimalForwarderFactory.deploy();
- await minimalForwarder.deployed();
-
- //Deploy Mint Manager
- const mintManagerFactory = await ethers.getContractFactory("MintManager");
- const mintManager = await mintManagerFactory.deploy();
- await mintManager.deployed();
- const encodedFn = mintManager.interface.encodeFunctionData("initialize", [
- platformPaymentAddress.address,
- mintManagerOwner.address,
- minimalForwarder.address,
- initialPlatformExecutor.address,
- mintFeeWei,
- ]);
-
- const mintManagerProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const mintManagerProxy = await mintManagerProxyFactory.deploy(mintManager.address, encodedFn);
- await mintManagerProxy.deployed();
-
- // deploy AuctionManager
- const auctionManagerFactory = await ethers.getContractFactory("AuctionManager");
- const auctionManager = await auctionManagerFactory.deploy();
- await auctionManager.deployed();
- const amEncodedFn = auctionManager.interface.encodeFunctionData("initialize", [
- platformPaymentAddress.address,
- minimalForwarder.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- ]);
-
- const auctionManagerProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const auctionManagerProxy = await auctionManagerProxyFactory.deploy(auctionManager.address, amEncodedFn);
- await auctionManagerProxy.deployed();
-
- //Deploy EMR
- const emrFactory = await ethers.getContractFactory("EditionsMetadataRenderer");
- const emr = await emrFactory.deploy();
- await emr.deployed();
- const emrEncodedFn = emr.interface.encodeFunctionData("initialize", [editionsMetadataOwner.address]);
-
- const emrProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const emrProxy = await emrProxyFactory.deploy(emr.address, emrEncodedFn);
- await emrProxy.deployed();
-
- //Deploy Editions
- const editionsFactory = await ethers.getContractFactory("ERC721Editions");
- const editionsImpl = await editionsFactory.deploy();
- await editionsImpl.deployed();
-
- //Deploy Single Edition
- const singleEditionFactory = await ethers.getContractFactory("ERC721SingleEdition");
- const singleEditionImpl = await singleEditionFactory.deploy();
- await singleEditionImpl.deployed();
-
- //Deploy General
- const generalFactory = await ethers.getContractFactory("ERC721General");
- const generalImpl = await generalFactory.deploy();
- await generalImpl.deployed();
-
- const mintManagerWithOwner = MintManager__factory.connect(mintManagerProxy.address, mintManagerOwner);
-
- const singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManagerProxy.address,
- minimalForwarder.address,
- emrProxy.address,
- editionsOwner,
- editionSize,
- "name",
- "SYM",
- );
-
- const maticWETHFactory = await ethers.getContractFactory("MaticWETH", fan1);
- const maticWETH = await maticWETHFactory.deploy(initialPlatformExecutor.address);
- await maticWETH.deployed();
-
- return { mintManagerWithOwner, singleEdition, maticWETH };
- }
- before(async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- emr = emrProxy;
- observability = observabilityInstance;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- editionSize,
- "name",
- "SYM",
- );
- });
- it("Should be able to mint one to one recipient", async function () {
- const claimNonce = "gatedMintEdition721ClaimNonce1";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.emit(singleEdition, "Transfer");
- expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(1);
- expect(
- (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- ).to.be.equal(1);
- expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- });
- it("Should be able to mint multiple to one recipient", async function () {
- const claimNonce = "gatedMintEdition721ClaimNonce2";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 9,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.emit(singleEdition, "Transfer");
- expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(10);
- expect(
- (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- ).to.be.equal(10);
- expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- });
- it("Should be able to mint with Native Currency", async function () {
- const { mintManagerWithOwner, singleEdition } = await deploySingleEditionFixture();
- const offChainVectorId = "gatedMintNativeVectorId";
- const claimNonce = "gatedMintNativePay";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManagerWithOwner.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0.00001",
- 1,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManagerWithOwner.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: claim.pricePerToken,
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address),
- ).to.be.revertedWithCustomError(mintManagerForFan1, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint).add(claim.pricePerToken),
- }),
- ).to.emit(singleEdition, "Transfer");
- expect((await mintManagerWithOwner.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(
- 1,
- );
- expect(
- (
- await mintManagerWithOwner.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)
- ).toNumber(),
- ).to.be.equal(1);
- expect(await mintManagerWithOwner.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- });
- it("Should be able to mint with ERC20", async function () {
- const { mintManagerWithOwner, singleEdition, maticWETH } = await deploySingleEditionFixture();
- const offChainVectorId = "gatedMintERC20VectorId";
- const claimNonce = "gatedMintERC20";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManagerWithOwner.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 9,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- maticWETH.address,
- );
- const mintManagerForFan1 = mintManagerWithOwner.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.emit(singleEdition, "Transfer");
- expect((await mintManagerForFan1.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(
- 9,
- );
- expect(
- (
- await mintManagerForFan1.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)
- ).toNumber(),
- ).to.be.equal(9);
- expect(await mintManagerForFan1.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- });
- it.skip("Should be able to mint with meta-tx payment", async function () {
- const { maticWETH } = await deployMetaTxnFixture();
- const claimNonce = "gatedMintPacketClaimNonce2";
- const { signature, claim } = await generateClaimWithMetaTxPackets(
- initialPlatformExecutor,
- fan1,
- mintManager.address,
- singleEdition.address,
- editionsOwner.address,
- maticWETH.address,
- getValidClaimTimestamp(),
- "1",
- 1,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- console.log({ signature, claim, mintManagerForFan1 });
- /*
- await expect(mintManagerForFan1.gatedMintPaymentPacketEdition721(claim, signature, fan1.address)).to.emit(
- singleEdition,
- "Transfer",
- );
- */
- // expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(10);
- // expect(
- // (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- // ).to.be.equal(10);
- // expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- // claim.claimNonce,
- // );
- });
- it("Should not be able to mint if claim nonce already used", async function () {
- const claimNonce = "gatedMintEdition721ClaimNonce2";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 0,
- 0,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(mintManagerForFan1, Errors.InvalidClaim);
- expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(10);
- expect(
- (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- ).to.be.equal(10);
- });
- it("Should not be be able to mint after max per vector reached", async function () {
- const claimNonce = "gatedMintEdition721ClaimNonce3";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 9,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(mintManagerForFan1, Errors.InvalidClaim);
- expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(10);
- expect(
- (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- ).to.be.equal(10);
- expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.not.include(
- claim.claimNonce,
- );
- });
- it("Should not be be able to mint after edition size is reached", async function () {
- const claimNonce = "gatedMintEdition721ClaimNonce4";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- singleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 0,
- 0,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(singleEdition, Errors.SoldOut);
- });
- });
- describe("Multiple Editions", function () {
- let multipleEdition: ERC721Editions, mintManager: MintManager;
- const offChainVectorId = "gatedMintEdition721VectorId";
- const maxPerVector = 10;
- const maxPerUser = 10;
- const editionSize = 10;
-
- async function deployMultipleEditionFixture() {
- //Deploy Minimal Forwarder
- const minimalForwarderFactory = await ethers.getContractFactory("MinimalForwarder");
- const minimalForwarder = await minimalForwarderFactory.deploy();
- await minimalForwarder.deployed();
-
- //Deploy Mint Manager
- const mintManagerFactory = await ethers.getContractFactory("MintManager");
- const mintManager = await mintManagerFactory.deploy();
- await mintManager.deployed();
- const encodedFn = mintManager.interface.encodeFunctionData("initialize", [
- platformPaymentAddress.address,
- mintManagerOwner.address,
- minimalForwarder.address,
- initialPlatformExecutor.address,
- mintFeeWei,
- ]);
-
- const mintManagerProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const mintManagerProxy = await mintManagerProxyFactory.deploy(mintManager.address, encodedFn);
- await mintManagerProxy.deployed();
-
- // deploy AuctionManager
- const auctionManagerFactory = await ethers.getContractFactory("AuctionManager");
- const auctionManager = await auctionManagerFactory.deploy();
- await auctionManager.deployed();
- const amEncodedFn = auctionManager.interface.encodeFunctionData("initialize", [
- platformPaymentAddress.address,
- minimalForwarder.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- ]);
-
- const auctionManagerProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const auctionManagerProxy = await auctionManagerProxyFactory.deploy(auctionManager.address, amEncodedFn);
- await auctionManagerProxy.deployed();
-
- //Deploy EMR
- const emrFactory = await ethers.getContractFactory("EditionsMetadataRenderer");
- const emr = await emrFactory.deploy();
- await emr.deployed();
- const emrEncodedFn = emr.interface.encodeFunctionData("initialize", [editionsMetadataOwner.address]);
-
- const emrProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const emrProxy = await emrProxyFactory.deploy(emr.address, emrEncodedFn);
- await emrProxy.deployed();
-
- //Deploy Editions
- const editionsFactory = await ethers.getContractFactory("ERC721Editions");
- const editionsImpl = await editionsFactory.deploy();
- await editionsImpl.deployed();
-
- //Deploy Single Edition
- const singleEditionFactory = await ethers.getContractFactory("ERC721SingleEdition");
- const singleEditionImpl = await singleEditionFactory.deploy();
- await singleEditionImpl.deployed();
-
- //Deploy General
- const generalFactory = await ethers.getContractFactory("ERC721General");
- const generalImpl = await generalFactory.deploy();
- await generalImpl.deployed();
-
- const mintManagerWithOwner = MintManager__factory.connect(mintManagerProxy.address, mintManagerOwner);
-
- const multipleEdition = await setupMultipleEdition(
- observability.address,
- editionsImplementation,
- mintManagerProxy.address,
- auctionManagerProxy.address,
- minimalForwarder.address,
- emrProxy.address,
- editionsOwner,
- editionSize,
- "Test 1",
- "T1",
- );
-
- const maticWETHFactory = await ethers.getContractFactory("MaticWETH", fan1);
- const maticWETH = await maticWETHFactory.deploy(initialPlatformExecutor.address);
- await maticWETH.deployed();
-
- return { mintManagerWithOwner, multipleEdition, maticWETH };
- }
- before(async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- emr = emrProxy;
- observability = observabilityInstance;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- multipleEdition = await setupMultipleEdition(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 10,
- "Test 1",
- "T1",
- );
- });
- it("Should be able to mint one to one recipient", async function () {
- const claimNonce = "gatedMintEditions721ClaimNonce1";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- multipleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.emit(multipleEdition, "Transfer");
- expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(1);
- expect(
- (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- ).to.be.equal(1);
- expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- });
- it("Should be able to mint multiple to one recipient", async function () {
- const claimNonce = "gatedMintEditions721ClaimNonce2";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- multipleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 9,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.emit(multipleEdition, "Transfer");
- expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(10);
- expect(
- (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- ).to.be.equal(10);
- expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- });
- it("Should be able to mint with Native Currency", async function () {
- const { mintManagerWithOwner, multipleEdition } = await deployMultipleEditionFixture();
- const offChainVectorId = "gatedMintNativeVectorId";
- const claimNonce = "gatedMintNativePay";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManagerWithOwner.address,
- multipleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0.00001",
- 1,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManagerWithOwner.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint).add(claim.pricePerToken),
- }),
- ).to.emit(multipleEdition, "Transfer");
- expect((await mintManagerWithOwner.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(
- 1,
- );
- expect(
- (
- await mintManagerWithOwner.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)
- ).toNumber(),
- ).to.be.equal(1);
- expect(await mintManagerWithOwner.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- });
- it("Should be able to mint with ERC20", async function () {
- const { mintManagerWithOwner, multipleEdition, maticWETH } = await deployMultipleEditionFixture();
- const offChainVectorId = "gatedMintERC20VectorId";
- const claimNonce = "gatedMintERC20";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManagerWithOwner.address,
- multipleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 9,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- maticWETH.address,
- );
- const mintManagerForFan1 = mintManagerWithOwner.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.emit(multipleEdition, "Transfer");
- expect((await mintManagerForFan1.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(
- 9,
- );
- expect(
- (
- await mintManagerForFan1.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)
- ).toNumber(),
- ).to.be.equal(9);
- expect(await mintManagerForFan1.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- });
- it("Should not be able to mint if claim nonce already used", async function () {
- const claimNonce = "gatedMintEditions721ClaimNonce2";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- multipleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 0,
- 0,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.include(
- claim.claimNonce,
- );
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(mintManagerForFan1, Errors.InvalidClaim);
- expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(10);
- expect(
- (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- ).to.be.equal(10);
- });
- it("Should not be able to mint after max per vector reached", async function () {
- const claimNonce = "gatedMintEditions721ClaimNonce3";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- multipleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- maxPerVector,
- maxPerUser,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- expect((await mintManager.offchainVectorsClaimState(claim.offchainVectorId)).toNumber()).to.be.equal(
- claim.maxClaimableViaVector,
- );
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(mintManagerForFan1, Errors.InvalidClaim);
- expect(
- (await mintManager.getNumClaimedPerUserOffchainVector(claim.offchainVectorId, fan1.address)).toNumber(),
- ).to.be.equal(10);
- expect(await mintManager.getClaimNoncesUsedForOffchainVector(claim.offchainVectorId)).to.not.include(
- claim.claimNonce,
- );
- });
- it("Should be not be able to mint after edition size is reached", async function () {
- const claimNonce = "gatedMintEditions721ClaimNonce4";
- const { signature, claim } = await generateClaim(
- initialPlatformExecutor,
- mintManager.address,
- multipleEdition.address,
- fan1.address,
- editionsOwner.address,
- getValidClaimTimestamp(),
- "0",
- 1,
- 0,
- 0,
- 0,
- offChainVectorId,
- claimNonce,
- );
- const mintManagerForFan1 = mintManager.connect(fan1);
- await expect(
- mintManagerForFan1.gatedMintEdition721(claim, signature, fan1.address, {
- value: mintFeeWei.mul(claim.numTokensToMint),
- }),
- ).to.be.revertedWithCustomError(multipleEdition, Errors.SoldOut);
- });
- });
- });
- });
-
- describe("Vectors", function () {
- let singleEdition: ERC721SingleEdition, mintManager: MintManager;
- const editionSize = 10;
- let vectorId = 1;
- before(async () => {
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- auctionManagerProxy,
- observability: observabilityInstance,
- editionsImplementationAddress,
- singleEditionImplementationAddress,
- generalImplementationAddress,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- editionsOwner,
- );
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- auctionManager = auctionManagerProxy;
- emr = emrProxy;
- observability = observabilityInstance;
- editionsImplementation = editionsImplementationAddress;
- singleEditionImplementation = singleEditionImplementationAddress;
- generalImplementation = generalImplementationAddress;
- singleEdition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- editionSize,
- "Test 1",
- "T1",
- );
- });
- it("Should be able to create new vector for contract by Owner", async () => {
- const mintManagerForEditionOwner = await mintManager.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true);
- await expect(mintManagerForEditionOwner.createAbridgedVector(vector)).to.emit(
- mintManagerForEditionOwner,
- "EditionVectorCreated",
- );
- vectorId += 1;
- });
- it.skip("Should not be able to update vector when frozen", async () => {
- const mintManagerForEditionOwner = await mintManager.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true);
- await (await mintManagerForEditionOwner.createAbridgedVector(vector)).wait();
- /*
- await expect(mintManagerForEditionOwner.updateAbridgedVector(vectorId, vector)).to.be.revertedWithCustomError(
- mintManagerForEditionOwner,
- Errors.VectorUpdateActionFrozen,
- );
- */
- vectorId += 1;
- });
- it("Should be able to update vector for contract by Owner", async () => {
- const mintManagerForEditionOwner = await mintManager.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true, 0, 100);
- const vectorUpdateConfig = SAMPLE_ABRIDGED_VECTOR_UPDATE_CONFIG({
- updateMaxTotalClaimableViaVector: true,
- updateTokenLimitPerTx: true,
- });
- await (await mintManagerForEditionOwner.createAbridgedVector(vector)).wait();
- await expect(
- mintManagerForEditionOwner.updateAbridgedVector(
- vectorId,
- { ...vector, maxUserClaimableViaVector: 57, tokenLimitPerTx: 32938 },
- vectorUpdateConfig,
- true,
- 10009,
- ),
- ).to.emit(mintManagerForEditionOwner, "VectorUpdated");
- expect((await mintManagerForEditionOwner.getAbridgedVector(vectorId)).tokenLimitPerTx).to.equal(32938);
- expect((await mintManagerForEditionOwner.getAbridgedVector(vectorId)).maxTotalClaimableViaVector).to.equal(100);
- expect((await mintManagerForEditionOwner.getAbridgedVector(vectorId)).maxUserClaimableViaVector).to.not.equal(57);
- expect((await mintManagerForEditionOwner.getAbridgedVector(vectorId)).maxUserClaimableViaVector).to.equal(0);
- expect(await mintManagerForEditionOwner.getAbridgedVectorMetadata(vectorId)).to.eql([
- false,
- ethers.BigNumber.from(0),
- ]);
- vectorId += 1;
- });
- it.skip("Should not be able to delete vector when frozen for contract by Owner", async () => {
- const mintManagerForEditionOwner = await mintManager.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true);
- await (await mintManagerForEditionOwner.createAbridgedVector(vector)).wait();
- await expect(mintManagerForEditionOwner.deleteAbridgedVector(vectorId)).to.be.revertedWithCustomError(
- mintManagerForEditionOwner,
- Errors.VectorUpdateActionFrozen,
- );
- vectorId += 1;
- });
- it("Should be able to delete vector for contract by Owner", async () => {
- const mintManagerForEditionOwner = await mintManager.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true);
- await (await mintManagerForEditionOwner.createAbridgedVector(vector)).wait();
- await expect(mintManagerForEditionOwner.deleteAbridgedVector(vectorId)).to.emit(
- mintManagerForEditionOwner,
- "VectorDeleted",
- );
- vectorId += 1;
- });
- it.skip("Should not be able to pause vector when frozen for contract by Owner", async () => {
- const mintManagerForEditionOwner = await mintManager.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true);
- await (await mintManagerForEditionOwner.createAbridgedVector(vector)).wait();
- /*
- await expect(mintManagerForEditionOwner.pauseVector(vectorId)).to.be.revertedWithCustomError(
- mintManagerForEditionOwner,
- Errors.VectorUpdateActionFrozen,
- );
- */
- vectorId += 1;
- });
- it.skip("Should be able to pause vector for contract by Owner", async () => {
- const mintManagerForEditionOwner = await mintManager.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true);
- await (await mintManagerForEditionOwner.createAbridgedVector(vector)).wait();
- /*
- await expect(mintManagerForEditionOwner.pauseVector(vectorId)).to.emit(
- mintManagerForEditionOwner,
- "VectorPausedOrUnpaused",
- );
- */
- vectorId += 1;
- });
- it.skip("Should be able to pause vector for contract by Owner", async () => {
- const mintManagerForEditionOwner = await mintManager.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true);
- await (await mintManagerForEditionOwner.createAbridgedVector(vector)).wait();
- /*
- await expect(mintManagerForEditionOwner.unpauseVector(vectorId)).to.emit(
- mintManagerForEditionOwner,
- "VectorPausedOrUnpaused",
- );
- */
- vectorId += 1;
- });
- it("Should reject all vector interactions for contract by non Owner", async () => {
- const vector = SAMPLE_ABRIDGED_VECTOR(singleEdition.address, editionsOwner.address, true, 0, 100);
- const vectorUpdateConfig = SAMPLE_ABRIDGED_VECTOR_UPDATE_CONFIG({ updateMaxTotalClaimableViaVector: true });
- const mintManagerForEditionOwner = mintManager.connect(editionsOwner);
- await (await mintManagerForEditionOwner.createAbridgedVector(vector)).wait();
- mintManager = mintManager.connect(fan1);
- await expect(mintManager.createAbridgedVector(vector)).to.be.revertedWithCustomError(
- mintManager,
- Errors.Unauthorized,
- );
- await expect(
- mintManager.updateAbridgedVector(vectorId, vector, vectorUpdateConfig, false, 0),
- ).to.be.revertedWithCustomError(mintManager, Errors.Unauthorized);
- await expect(mintManager.deleteAbridgedVector(vectorId)).to.be.revertedWithCustomError(
- mintManager,
- Errors.Unauthorized,
- );
-
- mintManager = mintManager.connect(editionsOwner);
- });
- });
-
- describe("Vector Mints", function () {
- async function vectorMintsFixture() {
- //Deploy Minimal Forwarder
- const minimalForwarderFactory = await ethers.getContractFactory("MinimalForwarder");
- const minimalForwarder = await minimalForwarderFactory.deploy();
- await minimalForwarder.deployed();
-
- //Deploy Mint Manager
- const mintManagerFactory = await ethers.getContractFactory("MintManager");
- const mintManager = await mintManagerFactory.deploy();
- await mintManager.deployed();
- const encodedFn = mintManager.interface.encodeFunctionData("initialize", [
- platformPaymentAddress.address,
- mintManagerOwner.address,
- minimalForwarder.address,
- initialPlatformExecutor.address,
- mintFeeWei,
- ]);
-
- const mintManagerProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const mintManagerProxy = await mintManagerProxyFactory.deploy(mintManager.address, encodedFn);
- await mintManagerProxy.deployed();
-
- // deploy AuctionManager
- const auctionManagerFactory = await ethers.getContractFactory("AuctionManager");
- const auctionManager = await auctionManagerFactory.deploy();
- await auctionManager.deployed();
- const amEncodedFn = auctionManager.interface.encodeFunctionData("initialize", [
- platformPaymentAddress.address,
- minimalForwarder.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- ]);
-
- const auctionManagerProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const auctionManagerProxy = await auctionManagerProxyFactory.deploy(auctionManager.address, amEncodedFn);
- await auctionManagerProxy.deployed();
-
- //Deploy EMR
- const emrFactory = await ethers.getContractFactory("EditionsMetadataRenderer");
- const emr = await emrFactory.deploy();
- await emr.deployed();
- const emrEncodedFn = emr.interface.encodeFunctionData("initialize", [editionsMetadataOwner.address]);
-
- const emrProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const emrProxy = await emrProxyFactory.deploy(emr.address, emrEncodedFn);
- await emrProxy.deployed();
-
- //Deploy Editions
- const editionsFactory = await ethers.getContractFactory("ERC721Editions");
- const editionsImpl = await editionsFactory.deploy();
- await editionsImpl.deployed();
-
- //Deploy Single Edition
- const singleEditionFactory = await ethers.getContractFactory("ERC721SingleEdition");
- const singleEditionImpl = await singleEditionFactory.deploy();
- await singleEditionImpl.deployed();
-
- //Deploy General
- const generalFactory = await ethers.getContractFactory("ERC721General");
- const generalImpl = await generalFactory.deploy();
- await generalImpl.deployed();
-
- const mintManagerWithOwner = MintManager__factory.connect(mintManagerProxy.address, mintManagerOwner);
-
- const observabilityFactory = await ethers.getContractFactory("Observability");
- const observability = await observabilityFactory.deploy();
- await observability.deployed();
-
- const generalERC721 = await setupGeneral(
- observability.address,
- generalImplementation,
- minimalForwarder.address,
- mintManagerProxy.address,
- generalOwner,
- null,
- null,
- false,
- false,
- 0,
- ethers.constants.AddressZero,
- ethers.constants.AddressZero,
- 0,
- "Test 1",
- "T1",
- );
-
- const singleEditionERC721 = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManagerProxy.address,
- minimalForwarder.address,
- emr.address,
- editionsOwner,
- 10,
- "Test 1",
- "T1",
- );
-
- const multipleEditionERC721 = await setupMultipleEdition(
- observability.address,
- editionsImplementation,
- mintManagerProxy.address,
- auctionManagerProxy.address,
- minimalForwarder.address,
- emrProxy.address,
- editionsOwner,
- 10,
- "Test 1",
- "T1",
- );
-
- return { mintManagerWithOwner, generalERC721, singleEditionERC721, multipleEditionERC721 };
- }
- describe("Edition721", function () {
- describe("Single Edition", function () {
- it("Should be able to mint one to one recipient", async function () {
- const { mintManagerWithOwner, singleEditionERC721 } = await vectorMintsFixture();
- const mintManagerForEditionOwner = await mintManagerWithOwner.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(
- singleEditionERC721.address,
- editionsOwner.address,
- true,
- 0,
- 5,
- 5,
- 0,
- 0,
- 5,
- ethers.utils.parseEther("0.00000001"),
- );
- await expect(mintManagerForEditionOwner.createAbridgedVector(vector)).to.emit(
- mintManagerForEditionOwner,
- "EditionVectorCreated",
- );
- const mintManagerForFan1 = await mintManagerWithOwner.connect(fan1);
- await expect(mintManagerForFan1.vectorMint721(1, 1, fan1.address)).to.be.revertedWithCustomError(
- mintManagerForFan1,
- Errors.InvalidPaymentAmount,
- );
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, {
- value: ethers.utils.parseEther("0.00000001"),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, { value: mintFeeWei }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, {
- value: mintFeeWei.add(ethers.utils.parseEther("0.00000001")),
- }),
- ).to.emit(singleEditionERC721, "Transfer");
- });
- });
- describe("Multiple Edition", function () {
- let mintManagerForFan1: MintManager;
- let meERC721: ERC721Editions;
-
- it("Sending invalid mint fee should fail", async function () {
- const { mintManagerWithOwner, multipleEditionERC721 } = await vectorMintsFixture();
- const mintManagerForEditionOwner = mintManagerWithOwner.connect(editionsOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(multipleEditionERC721.address, editionsOwner.address, true);
- await expect(mintManagerForEditionOwner.createAbridgedVector(vector)).to.emit(
- mintManagerForEditionOwner,
- "EditionVectorCreated",
- );
- mintManagerForFan1 = await mintManagerWithOwner.connect(fan1);
- meERC721 = multipleEditionERC721;
- await expect(mintManagerForFan1.vectorMint721(1, 1, fan1.address)).to.be.revertedWithCustomError(
- mintManager,
- Errors.MintFeeTooLow,
- );
- });
-
- it("Should be able to mint one to one recipient", async function () {
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, { value: mintFeeWei.sub(1) }),
- ).to.be.revertedWithCustomError(mintManager, Errors.MintFeeTooLow);
-
- const mintManagerForPlatform = mintManagerForFan1.connect(mintManagerOwner);
- await expect(mintManagerForPlatform.updatePlatformAndMintFee(mintManagerOwner.address, mintFeeWei.sub(1))).to
- .not.be.reverted;
-
- await expect(mintManagerForFan1.vectorMint721(1, 1, fan1.address, { value: mintFeeWei.sub(1) })).to.emit(
- meERC721,
- "Transfer",
- );
- });
- });
-
- describe("Mint fee updates", function () {
- it("Non-platform accounts can't update the mint fee", async function () {
- const { mintManagerWithOwner } = await vectorMintsFixture();
-
- let mintManagerUnauthorized = mintManagerWithOwner.connect(fan1);
- await expect(
- mintManagerUnauthorized.updatePlatformAndMintFee(mintManagerOwner.address, 1),
- ).to.be.revertedWith("Ownable: caller is not the owner");
-
- mintManagerUnauthorized = mintManagerUnauthorized.connect(editionsOwner);
- await expect(
- mintManagerUnauthorized.updatePlatformAndMintFee(mintManagerOwner.address, 1),
- ).to.be.revertedWith("Ownable: caller is not the owner");
-
- mintManagerUnauthorized = mintManagerUnauthorized.connect(editionsMetadataOwner);
- await expect(
- mintManagerUnauthorized.updatePlatformAndMintFee(mintManagerOwner.address, 1),
- ).to.be.revertedWith("Ownable: caller is not the owner");
- });
- });
- });
-
- describe("Series vector mint", async function () {
- it("Should be able to mint one to one recipient", async function () {
- const { mintManagerWithOwner, generalERC721 } = await vectorMintsFixture();
- const mintManagerForGeneralOwner = await mintManagerWithOwner.connect(generalOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(
- generalERC721.address,
- generalOwner.address,
- false,
- 0,
- 10,
- 5,
- 0,
- 0,
- 5,
- ethers.utils.parseEther("0.00000001"),
- );
- await expect(mintManagerForGeneralOwner.createAbridgedVector(vector)).to.emit(
- mintManagerForGeneralOwner,
- "SeriesVectorCreated",
- );
- const mintManagerForFan1 = await mintManagerWithOwner.connect(fan1);
- await expect(mintManagerForFan1.vectorMint721(1, 1, fan1.address)).to.be.revertedWithCustomError(
- mintManagerForFan1,
- Errors.InvalidPaymentAmount,
- );
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, {
- value: ethers.utils.parseEther("0.00000001"),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, { value: mintFeeWei }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, {
- value: mintFeeWei.add(ethers.utils.parseEther("0.00000001")),
- }),
- ).to.emit(generalERC721, "Transfer");
- await expect(
- mintManagerForFan1.vectorMint721(1, 4, fan1.address, {
- value: mintFeeWei.mul(4).add(ethers.utils.parseEther("0.00000001").mul(4)),
- }),
- )
- .to.emit(generalERC721, "Transfer")
- .to.emit(generalERC721, "Transfer")
- .to.emit(generalERC721, "Transfer")
- .to.emit(generalERC721, "Transfer");
-
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, {
- value: mintFeeWei.add(ethers.utils.parseEther("0.00000001")),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.OnchainVectorMintGuardFailed);
- });
-
- it("User limit is based on mint recipent", async function () {
- const { mintManagerWithOwner, generalERC721 } = await vectorMintsFixture();
- const mintManagerForGeneralOwner = await mintManagerWithOwner.connect(generalOwner);
- const vector = SAMPLE_ABRIDGED_VECTOR(
- generalERC721.address,
- generalOwner.address,
- false,
- 0,
- 10,
- 1,
- 0,
- 0,
- 5,
- ethers.utils.parseEther("0.00000001"),
- );
- await expect(mintManagerForGeneralOwner.createAbridgedVector(vector)).to.emit(
- mintManagerForGeneralOwner,
- "SeriesVectorCreated",
- );
- await expect(
- mintManagerForGeneralOwner.vectorMint721(1, 1, fan1.address, {
- value: mintFeeWei.add(ethers.utils.parseEther("0.00000001")),
- }),
- ).to.emit(generalERC721, "Transfer");
- await expect(
- mintManagerForGeneralOwner.vectorMint721(1, 1, fan1.address, {
- value: mintFeeWei.add(ethers.utils.parseEther("0.00000001")),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.OnchainVectorMintGuardFailed);
- await expect(
- mintManagerForGeneralOwner.vectorMint721(1, 2, generalOwner.address, {
- value: mintFeeWei.mul(2).add(ethers.utils.parseEther("0.00000002")),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.OnchainVectorMintGuardFailed);
- });
-
- it("Should be able to mint one to one recipient", async function () {
- const { mintManagerWithOwner, generalERC721 } = await vectorMintsFixture();
- const mintManagerForGeneralOwner = await mintManagerWithOwner.connect(generalOwner);
-
- /*
- const allowlistedAddresses = [
- fan1.address,
- editionsOwner.address,
- generalOwner.address,
- editionsMetadataOwner.address,
- ];
- const leaves = allowlistedAddresses.map(x => ethers.utils.keccak256(x));
- const tree = new MerkleTree(leaves, keccak256, { sortPairs: true });
- const root = tree.getRoot().toString("hex");
- const hashedFan1Address = keccak256(fan1.address);
- const proof = tree.getHexProof(hashedFan1Address);
- */
-
- const vector = SAMPLE_ABRIDGED_VECTOR(
- generalERC721.address,
- generalOwner.address,
- false,
- 0,
- 5,
- 5,
- 0,
- 0,
- 5,
- ethers.utils.parseEther("0.00000001"),
- ethers.constants.HashZero,
- );
- await expect(mintManagerForGeneralOwner.createAbridgedVector(vector)).to.emit(
- mintManagerForGeneralOwner,
- "SeriesVectorCreated",
- );
- const mintManagerForFan1 = await mintManagerWithOwner.connect(fan1);
- await expect(mintManagerForFan1.vectorMint721(1, 1, fan1.address)).to.be.revertedWithCustomError(
- mintManagerForFan1,
- Errors.InvalidPaymentAmount,
- );
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, {
- value: ethers.utils.parseEther("0.00000001"),
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, { value: mintFeeWei }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- await expect(
- mintManagerForFan1.vectorMint721(1, 1, fan1.address, {
- value: mintFeeWei.add(ethers.utils.parseEther("0.00000001")),
- }),
- ).to.emit(generalERC721, "Transfer");
-
- const mintManagerForNonAllowlistedAccount = mintManagerForFan1.connect(platformPaymentAddress);
- await expect(
- mintManagerForNonAllowlistedAccount.vectorMint721(1, 1, platformPaymentAddress.address, {
- value: mintFeeWei,
- }),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- });
- });
-
- describe("Direct mint vectors metadata", function () {
- let mintManagerForEditionOwner: MintManager;
- let mintManagerForGeneralOwner: MintManager;
- let sampleVector: any;
-
- beforeEach(async function () {
- const { mintManagerWithOwner, generalERC721, singleEditionERC721 } = await vectorMintsFixture();
- mintManagerForEditionOwner = await mintManagerWithOwner.connect(editionsOwner);
- mintManagerForGeneralOwner = await mintManagerWithOwner.connect(generalOwner);
-
- const vector1 = SAMPLE_ABRIDGED_VECTOR(generalERC721.address, generalOwner.address, false);
- await expect(mintManagerForGeneralOwner.createAbridgedVector(vector1)).to.emit(
- mintManagerForGeneralOwner,
- "SeriesVectorCreated",
- );
- sampleVector = vector1;
-
- const vector2 = SAMPLE_ABRIDGED_VECTOR(singleEditionERC721.address, editionsOwner.address, true, 0);
- await expect(mintManagerForEditionOwner.createAbridgedVector(vector2)).to.emit(
- mintManagerForEditionOwner,
- "EditionVectorCreated",
- );
-
- const vector3 = SAMPLE_ABRIDGED_VECTOR(
- generalERC721.address,
- generalOwner.address,
- false,
- 0,
- 5,
- 5,
- 0,
- 0,
- 5,
- ethers.utils.parseEther("0"),
- ethers.constants.HashZero,
- );
- await expect(mintManagerForGeneralOwner.createAbridgedVector(vector3)).to.emit(
- mintManagerForGeneralOwner,
- "SeriesVectorCreated",
- );
-
- const vector4 = SAMPLE_ABRIDGED_VECTOR(
- singleEditionERC721.address,
- editionsOwner.address,
- true,
- 0,
- 5,
- 5,
- 0,
- 0,
- 5,
- ethers.utils.parseEther("0"),
- ethers.constants.HashZero,
- );
- await expect(mintManagerForEditionOwner.createAbridgedVector(vector4)).to.emit(
- mintManagerForEditionOwner,
- "EditionVectorCreated",
- );
- });
-
- it("Direct mint vector metadata cannot be set by non contract owners", async function () {
- await expect(mintManagerForEditionOwner.setAbridgedVectorMetadata(1, true, 0)).to.be.revertedWithCustomError(
- mintManagerForEditionOwner,
- Errors.Unauthorized,
- );
-
- await expect(mintManagerForEditionOwner.setAbridgedVectorMetadata(3, true, 0)).to.be.revertedWithCustomError(
- mintManagerForEditionOwner,
- Errors.Unauthorized,
- );
-
- await expect(mintManagerForGeneralOwner.setAbridgedVectorMetadata(2, true, 0)).to.be.revertedWithCustomError(
- mintManagerForGeneralOwner,
- Errors.Unauthorized,
- );
-
- await expect(mintManagerForGeneralOwner.setAbridgedVectorMetadata(4, true, 0)).to.be.revertedWithCustomError(
- mintManagerForGeneralOwner,
- Errors.Unauthorized,
- );
- });
-
- it("Direct mint vector metadata can be set (composed) and read (decomposed) correctly", async function () {
- await expect(mintManagerForGeneralOwner.setAbridgedVectorMetadata(1, true, 589384))
- .to.emit(mintManagerForGeneralOwner, "VectorMetadataSet")
- .withArgs(1, true, 589384);
-
- expect(await mintManagerForGeneralOwner.getAbridgedVectorMetadata(1)).to.eql([
- true,
- ethers.BigNumber.from(589384),
- ]);
- });
-
- it("Pausing a direct mint vector pauses all types of direct mints", async function () {
- // mints paused
- const vectorUpdateConfig = SAMPLE_ABRIDGED_VECTOR_UPDATE_CONFIG({
- updateMetadata: true,
- updateMaxUserClaimableViaVector: true,
- });
- await expect(
- mintManagerForGeneralOwner.updateAbridgedVector(
- 1,
- { ...sampleVector, maxUserClaimableViaVector: 57 },
- vectorUpdateConfig,
- true,
- 1908,
- ),
- )
- .to.emit(mintManagerForGeneralOwner, "VectorMetadataSet")
- .withArgs(1, true, 1908);
-
- await expect(mintManagerForGeneralOwner.setAbridgedVectorMetadata(3, true, 0))
- .to.emit(mintManagerForGeneralOwner, "VectorMetadataSet")
- .withArgs(3, true, 0);
-
- await expect(mintManagerForEditionOwner.setAbridgedVectorMetadata(2, true, 0))
- .to.emit(mintManagerForEditionOwner, "VectorMetadataSet")
- .withArgs(2, true, 0);
-
- await expect(mintManagerForEditionOwner.setAbridgedVectorMetadata(4, true, 0))
- .to.emit(mintManagerForEditionOwner, "VectorMetadataSet")
- .withArgs(4, true, 0);
-
- await expect(
- mintManagerForGeneralOwner.vectorMint721(1, 1, fan1.address, { value: mintFeeWei }),
- ).to.be.revertedWithCustomError(mintManagerForGeneralOwner, Errors.MintPaused);
-
- await expect(
- mintManagerForGeneralOwner.vectorMint721(3, 1, fan1.address, {
- value: mintFeeWei,
- }),
- ).to.be.revertedWithCustomError(mintManagerForGeneralOwner, Errors.MintPaused);
-
- await expect(
- mintManagerForEditionOwner.vectorMint721(2, 1, fan1.address, { value: mintFeeWei }),
- ).to.be.revertedWithCustomError(mintManagerForEditionOwner, Errors.MintPaused);
-
- /*
- vectorMint721WithAllowlist DEPRECATED
- await expect(
- mintManagerForEditionOwner.vectorMint721WithAllowlist(4, 1, fan1.address, proofForFan, {
- value: mintFeeWei,
- }),
- ).to.be.revertedWithCustomError(mintManagerForEditionOwner, Errors.MintPaused);
- */
-
- // mints unpaused
- await expect(mintManagerForGeneralOwner.setAbridgedVectorMetadata(1, false, 0))
- .to.emit(mintManagerForGeneralOwner, "VectorMetadataSet")
- .withArgs(1, false, 0);
-
- await expect(
- mintManagerForGeneralOwner.updateAbridgedVector(
- 3,
- sampleVector,
- SAMPLE_ABRIDGED_VECTOR_UPDATE_CONFIG({ updateMetadata: true }),
- false,
- 2023,
- ),
- )
- .to.emit(mintManagerForGeneralOwner, "VectorMetadataSet")
- .withArgs(3, false, 2023);
-
- await expect(mintManagerForEditionOwner.setAbridgedVectorMetadata(2, false, 0))
- .to.emit(mintManagerForEditionOwner, "VectorMetadataSet")
- .withArgs(2, false, 0);
-
- await expect(mintManagerForEditionOwner.setAbridgedVectorMetadata(4, false, 0))
- .to.emit(mintManagerForEditionOwner, "VectorMetadataSet")
- .withArgs(4, false, 0);
-
- await expect(mintManagerForGeneralOwner.vectorMint721(1, 1, fan1.address, { value: mintFeeWei })).to.not.be
- .reverted;
-
- await expect(
- mintManagerForGeneralOwner.vectorMint721(3, 1, fan1.address, {
- value: mintFeeWei,
- }),
- ).to.not.be.reverted;
-
- await expect(mintManagerForEditionOwner.vectorMint721(2, 1, fan1.address, { value: mintFeeWei })).to.not.be
- .reverted;
-
- /*
- vectorMint721WithAllowlist DEPRECATED
- await expect(
- mintManagerForEditionOwner.vectorMint721WithAllowlist(4, 1, fan1.address, proofForFan, {
- value: mintFeeWei,
- }),
- ).to.not.be.reverted;
- */
- });
- });
- });
-
- describe("721a bug", function () {
- describe("Generative", function () {
- let generative: ERC721Generative;
-
- before(async function () {
- generative = await setupGenerative(
- observability.address,
- generativeImplementation,
- trustedForwarder.address,
- mintManager.address,
- generalOwner,
- SAMPLE_ABRIDGED_VECTOR(ethers.constants.AddressZero, generalOwner.address, false),
- null,
- false,
- 0,
- ethers.constants.AddressZero,
- ethers.constants.AddressZero,
- 0,
- "Test 1",
- "T1",
- );
- });
-
- it("Transfer bug is non-existent", async function () {
- mintManager = mintManager.connect(fan1);
- await expect(mintManager.vectorMint721(1, 1, fan1.address, { value: ethers.utils.parseEther("0.0008") })).to.not
- .be.reverted;
- await expect(
- mintManager.vectorMint721(1, 2, generalOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.not.be.reverted;
-
- expect(await generative.ownerOf(1)).to.equal(fan1.address);
- expect(await generative.ownerOf(2)).to.equal(generalOwner.address);
- expect(await generative.ownerOf(3)).to.equal(generalOwner.address);
-
- mintManager = mintManager.connect(generalOwner);
- await expect(generative.transferFrom(generalOwner.address, fan1.address, 3)).to.not.be.reverted;
-
- expect(await generative.ownerOf(3)).to.equal(fan1.address);
-
- // can still mint after the last transfer
- await expect(
- mintManager.vectorMint721(1, 2, generalOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.not.be.reverted;
-
- expect(await generative.ownerOf(4)).to.equal(generalOwner.address);
- });
-
- it("Parallel minting bug is non-existent", async function () {
- const signers = (await ethers.getSigners()).filter(
- signer => signer.address != fan1.address && signer.address != generalOwner.address,
- );
-
- await Promise.all(
- signers.map(async signer => {
- for (let i = 1; i <= 5; i++) {
- await expect(
- mintManager.vectorMint721(1, i, signer.address, {
- value: ethers.utils.parseEther("0.0008").mul(i),
- }),
- ).to.not.be.reverted;
- }
- }),
- );
-
- await Promise.all(
- signers.map(async signer => {
- expect((await generative.balanceOf(signer.address)).toNumber()).to.equal(15);
- }),
- );
- });
- });
-
- describe("General Sequence", function () {
- let general: ERC721GeneralSequence;
-
- before(async function () {
- general = ERC721GeneralSequence__factory.connect(
- (
- await setupGeneral(
- observability.address,
- generalSequenceImplementation,
- trustedForwarder.address,
- mintManager.address,
- generalOwner,
- SAMPLE_ABRIDGED_VECTOR(ethers.constants.AddressZero, generalOwner.address, false),
- )
- ).address,
- generalOwner,
- );
- });
-
- it("Transfer bug is non-existent", async function () {
- mintManager = mintManager.connect(fan1);
- await expect(mintManager.vectorMint721(2, 1, fan1.address, { value: ethers.utils.parseEther("0.0008") })).to.not
- .be.reverted;
- await expect(
- mintManager.vectorMint721(2, 2, generalOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.not.be.reverted;
-
- expect(await general.ownerOf(1)).to.equal(fan1.address);
- expect(await general.ownerOf(2)).to.equal(generalOwner.address);
- expect(await general.ownerOf(3)).to.equal(generalOwner.address);
-
- mintManager = mintManager.connect(generalOwner);
- await expect(general.transferFrom(generalOwner.address, fan1.address, 3)).to.not.be.reverted;
-
- expect(await general.ownerOf(3)).to.equal(fan1.address);
-
- // can still mint after the last transfer
- await expect(
- mintManager.vectorMint721(2, 2, generalOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.not.be.reverted;
-
- expect(await general.ownerOf(4)).to.equal(generalOwner.address);
- });
-
- it("Parallel minting bug is non-existent", async function () {
- const signers = (await ethers.getSigners()).filter(
- signer => signer.address != fan1.address && signer.address != generalOwner.address,
- );
-
- await Promise.all(
- signers.map(async signer => {
- for (let i = 1; i <= 5; i++) {
- await expect(
- mintManager.vectorMint721(2, i, signer.address, {
- value: ethers.utils.parseEther("0.0008").mul(i),
- }),
- ).to.not.be.reverted;
- }
- }),
- );
-
- await Promise.all(
- signers.map(async signer => {
- expect((await general.balanceOf(signer.address)).toNumber()).to.equal(15);
- }),
- );
- });
- });
-
- describe("Open Edition", function () {
- let edition: ERC721SingleEdition;
-
- before(async function () {
- edition = await setupSingleEdition(
- observability.address,
- singleEditionImplementation,
- mintManager.address,
- trustedForwarder.address,
- emr.address,
- editionsOwner,
- 0,
- "Test 1",
- "T1",
- SAMPLE_ABRIDGED_VECTOR(ethers.constants.AddressZero, editionsOwner.address, true),
- );
- });
-
- it("Transfer bug is non-existent", async function () {
- mintManager = mintManager.connect(fan1);
- await expect(mintManager.vectorMint721(3, 1, fan1.address, { value: ethers.utils.parseEther("0.0008") })).to.not
- .be.reverted;
- await expect(
- mintManager.vectorMint721(3, 2, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.not.be.reverted;
-
- expect(await edition.ownerOf(1)).to.equal(fan1.address);
- expect(await edition.ownerOf(2)).to.equal(editionsOwner.address);
- expect(await edition.ownerOf(3)).to.equal(editionsOwner.address);
-
- mintManager = mintManager.connect(editionsOwner);
- await expect(edition.transferFrom(editionsOwner.address, fan1.address, 3)).to.not.be.reverted;
-
- expect(await edition.ownerOf(3)).to.equal(fan1.address);
-
- // can still mint after the last transfer
- await expect(
- mintManager.vectorMint721(3, 2, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.not.be.reverted;
-
- expect(await edition.ownerOf(4)).to.equal(editionsOwner.address);
- });
-
- it("Parallel minting bug is non-existent", async function () {
- const signers = (await ethers.getSigners()).filter(
- signer => signer.address != fan1.address && signer.address != editionsOwner.address,
- );
-
- await Promise.all(
- signers.map(async signer => {
- for (let i = 1; i <= 5; i++) {
- await expect(
- mintManager.vectorMint721(3, i, signer.address, {
- value: ethers.utils.parseEther("0.0008").mul(i),
- }),
- ).to.not.be.reverted;
- }
- }),
- );
-
- await Promise.all(
- signers.map(async signer => {
- expect((await edition.balanceOf(signer.address)).toNumber()).to.equal(15);
- }),
- );
- });
- });
-
- describe("Open Edition DFS", function () {
- let edition: ERC721SingleEditionDFS;
-
- before(async function () {
- edition = await setupSingleEditionDFS(
- observability.address,
- singleEditionDFSImplementation,
- mintManager.address,
- trustedForwarder.address,
- editionsOwner,
- 0,
- "Test 1",
- "T1",
- SAMPLE_ABRIDGED_VECTOR(ethers.constants.AddressZero, editionsOwner.address, true),
- );
- });
-
- it("Transfer bug is non-existent", async function () {
- mintManager = mintManager.connect(fan1);
- await expect(mintManager.vectorMint721(4, 1, fan1.address, { value: ethers.utils.parseEther("0.0008") })).to.not
- .be.reverted;
- await expect(
- mintManager.vectorMint721(4, 2, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.not.be.reverted;
-
- expect(await edition.ownerOf(1)).to.equal(fan1.address);
- expect(await edition.ownerOf(2)).to.equal(editionsOwner.address);
- expect(await edition.ownerOf(3)).to.equal(editionsOwner.address);
-
- mintManager = mintManager.connect(editionsOwner);
- await expect(edition.transferFrom(editionsOwner.address, fan1.address, 3)).to.not.be.reverted;
-
- expect(await edition.ownerOf(3)).to.equal(fan1.address);
-
- // can still mint after the last transfer
- await expect(
- mintManager.vectorMint721(4, 2, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- ).to.not.be.reverted;
-
- expect(await edition.ownerOf(4)).to.equal(editionsOwner.address);
- });
-
- it("Parallel minting bug is non-existent", async function () {
- const signers = (await ethers.getSigners()).filter(
- signer => signer.address != fan1.address && signer.address != editionsOwner.address,
- );
-
- await Promise.all(
- signers.map(async signer => {
- for (let i = 1; i <= 5; i++) {
- await expect(
- mintManager.vectorMint721(4, i, signer.address, {
- value: ethers.utils.parseEther("0.0008").mul(i),
- }),
- ).to.not.be.reverted;
- }
- }),
- );
-
- await Promise.all(
- signers.map(async signer => {
- expect((await edition.balanceOf(signer.address)).toNumber()).to.equal(15);
- }),
- );
- });
- });
- });
-
- describe("Creator reserve mints", function () {
- describe("Series based", function () {
- let generative: ERC721Generative;
- let general: ERC721General;
-
- before(async function () {
- generative = await setupGenerative(
- observability.address,
- generativeImplementation,
- trustedForwarder.address,
- mintManager.address,
- generalOwner,
- SAMPLE_ABRIDGED_VECTOR(ethers.constants.AddressZero, generalOwner.address, false),
- null,
- false,
- 0,
- ethers.constants.AddressZero,
- ethers.constants.AddressZero,
- 0,
- "Test 1",
- "T1",
- );
-
- general = await setupGeneral(
- observability.address,
- generalImplementation,
- trustedForwarder.address,
- mintManager.address,
- generalOwner,
- );
- });
-
- it("Non-owner cannot mint creator reserves", async function () {
- mintManager = mintManager.connect(fan1);
- await expect(
- mintManager.creatorReservesMint(generative.address, false, 0, 3, [], false, generalOwner.address),
- ).to.be.revertedWithCustomError(mintManager, Errors.Unauthorized);
-
- await expect(
- mintManager.creatorReservesMint(general.address, false, 0, 0, [4, 7], true, generalOwner.address),
- ).to.be.revertedWithCustomError(mintManager, Errors.Unauthorized);
- });
-
- it("Cannot mint creator reserves with invalid mint fee", async function () {
- mintManager = mintManager.connect(generalOwner);
- await expect(
- mintManager.creatorReservesMint(generative.address, false, 0, 3, [], false, generalOwner.address),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
-
- await expect(
- mintManager.creatorReservesMint(general.address, false, 0, 0, [4, 7], true, generalOwner.address),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- });
-
- it("Owner can validly mint creator reserves multiple times", async function () {
- mintManager = mintManager.connect(generalOwner);
- await expect(
- mintManager.creatorReservesMint(generative.address, false, 0, 3, [], false, generalOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(3),
- }),
- )
- .to.emit(mintManager, "CreatorReservesNumMint")
- .withArgs(generative.address, false, 0, 3)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, generalOwner.address, 1)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, generalOwner.address, 2)
- .to.emit(generative, "Transfer")
- .withArgs(ethers.constants.AddressZero, generalOwner.address, 3);
-
- await expect(
- mintManager.creatorReservesMint(general.address, false, 0, 0, [4, 7], true, generalOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(2),
- }),
- )
- .to.emit(mintManager, "CreatorReservesChooseMint")
- .withArgs(general.address, [4, 7])
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, generalOwner.address, 4)
- .to.emit(general, "Transfer")
- .withArgs(ethers.constants.AddressZero, generalOwner.address, 7);
- });
- });
-
- describe("Editions based", function () {
- let editions: ERC721EditionsDFS;
-
- before(async function () {
- editions = await setupEditionsDFS(
- observability.address,
- editionsDFSImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- editionsOwner,
- );
- editions = editions.connect(editionsOwner);
- await expect(
- editions.createEdition(
- "uri",
- 100,
- ethers.constants.AddressZero,
- {
- royaltyPercentageBPS: 0,
- recipientAddress: ethers.constants.AddressZero,
- },
- "0x",
- ),
- ).to.not.be.reverted;
-
- await expect(
- editions.createEdition(
- "uri",
- 100,
- ethers.constants.AddressZero,
- {
- royaltyPercentageBPS: 0,
- recipientAddress: ethers.constants.AddressZero,
- },
- "0x",
- ),
- ).to.not.be.reverted;
- });
-
- it("Non-owner cannot mint creator reserves", async function () {
- mintManager = mintManager.connect(fan1);
- await expect(
- mintManager.creatorReservesMint(editions.address, true, 0, 3, [], false, editionsOwner.address),
- ).to.be.revertedWithCustomError(mintManager, Errors.Unauthorized);
- });
-
- it("Cannot mint creator reserves with invalid mint fee", async function () {
- mintManager = mintManager.connect(editionsOwner);
- await expect(
- mintManager.creatorReservesMint(editions.address, true, 0, 3, [], false, editionsOwner.address),
- ).to.be.revertedWithCustomError(mintManager, Errors.InvalidPaymentAmount);
- });
-
- it("Owner can validly mint creator reserves multiple times on multiple editions on a contract", async function () {
- mintManager = mintManager.connect(editionsOwner);
- await expect(
- mintManager.creatorReservesMint(editions.address, true, 0, 3, [], false, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(3),
- }),
- )
- .to.emit(mintManager, "CreatorReservesNumMint")
- .withArgs(editions.address, true, 0, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 1)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 2)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 3);
-
- await expect(
- mintManager.creatorReservesMint(editions.address, true, 1, 3, [], false, editionsOwner.address, {
- value: ethers.utils.parseEther("0.0008").mul(3),
- }),
- )
- .to.emit(mintManager, "CreatorReservesNumMint")
- .withArgs(editions.address, true, 1, 3)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 101)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 102)
- .to.emit(editions, "Transfer")
- .withArgs(ethers.constants.AddressZero, editionsOwner.address, 103);
- });
- });
- });
-});
diff --git a/test/Test.ts b/test/Test.ts
new file mode 100644
index 0000000..c0985bf
--- /dev/null
+++ b/test/Test.ts
@@ -0,0 +1,7 @@
+// import helpful utilities from __utils__
+
+describe("Test", () => {
+ it("Test 1", async function () {
+ console.log("Starting test 1...");
+ })
+});
\ No newline at end of file
diff --git a/test/UpgradesTest.ts b/test/UpgradesTest.ts
deleted file mode 100644
index a786989..0000000
--- a/test/UpgradesTest.ts
+++ /dev/null
@@ -1,262 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager,
- DiscreteDutchAuctionMechanic,
- ERC721Editions,
- EditionsMetadataRenderer,
- MinimalForwarder,
- MintManager,
- Observability,
- TestDiscreteDutchAuctionMechanic,
- TestEditionsMetadataRenderer,
- TestMintManager,
-} from "../types";
-import { SAMPLE_ABRIDGED_VECTOR } from "./__utils__/data";
-import { setupEditions, setupSystem } from "./__utils__/helpers";
-
-const defaultEditionInfo = ethers.utils.defaultAbiCoder.encode(
- ["tuple(string, string, string, string, string, string)"],
- [["name", "description", "imageUrl", "animationUrl", "externalUrl", "attributes"]],
-);
-
-describe("Upgrades functionality", () => {
- let initialPlatformExecutor: SignerWithAddress,
- mintManagerOwner: SignerWithAddress,
- editionsMetadataOwner: SignerWithAddress,
- platformPaymentAddress: SignerWithAddress,
- owner: SignerWithAddress,
- fan1: SignerWithAddress;
-
- let emr: EditionsMetadataRenderer;
- let mintManager: MintManager;
- let dutchAuction: DiscreteDutchAuctionMechanic;
- let observability: Observability;
- let auctionManager: AuctionManager;
- let trustedForwarder: MinimalForwarder;
- let editionsImplementation: string;
-
- const zeroRoyalty = {
- recipientAddress: ethers.constants.AddressZero,
- royaltyPercentageBPS: 0,
- };
-
- before(async () => {
- [initialPlatformExecutor, mintManagerOwner, editionsMetadataOwner, platformPaymentAddress, owner, fan1] =
- await ethers.getSigners();
- const {
- emrProxy,
- mintManagerProxy,
- minimalForwarder,
- observability: observabilityInstance,
- auctionManagerProxy,
- editionsImplementationAddress,
- daMechanic,
- } = await setupSystem(
- platformPaymentAddress.address,
- mintManagerOwner.address,
- initialPlatformExecutor.address,
- editionsMetadataOwner.address,
- owner,
- );
-
- emr = emrProxy;
- dutchAuction = daMechanic;
- mintManager = mintManagerProxy;
- trustedForwarder = minimalForwarder;
- observability = observabilityInstance;
- auctionManager = auctionManagerProxy;
- editionsImplementation = editionsImplementationAddress;
-
- mintManager = mintManager.connect(mintManagerOwner);
- emr = emr.connect(editionsMetadataOwner);
- });
-
- describe("MintManager", function () {
- let testMintManager: TestMintManager;
- let editions: ERC721Editions;
-
- beforeEach(async function () {
- editions = await setupEditions(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- mintManagerOwner,
- );
- });
-
- it("Non owner cannot upgrade MintManager", async function () {
- testMintManager = await (await ethers.getContractFactory("TestMintManager")).deploy();
- await testMintManager.deployed();
-
- mintManager = mintManager.connect(owner);
-
- await expect(mintManager.upgradeTo(testMintManager.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- mintManager = mintManager.connect(editionsMetadataOwner);
-
- await expect(mintManager.upgradeTo(testMintManager.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- mintManager = mintManager.connect(fan1);
-
- await expect(mintManager.upgradeTo(testMintManager.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
-
- it("Upgrade to TestMintManager retains original data and introduces new functionality", async function () {
- mintManager = mintManager.connect(mintManagerOwner);
-
- await expect(
- mintManager.createAbridgedVector(SAMPLE_ABRIDGED_VECTOR(editions.address, owner.address, true)),
- ).to.emit(mintManager, "EditionVectorCreated");
-
- // data before upgrade
- const vectorOnOldImpl = await mintManager.vectors(1);
- const ownerOnOldImpl = await mintManager.owner();
-
- testMintManager = await (await ethers.getContractFactory("TestMintManager")).deploy();
- await testMintManager.deployed();
-
- await expect(mintManager.upgradeTo(testMintManager.address))
- .to.emit(mintManager, "Upgraded")
- .withArgs(testMintManager.address);
-
- const newMintManager = new ethers.Contract(mintManager.address, testMintManager.interface, mintManagerOwner);
- expect(await newMintManager.test()).to.equal(true);
-
- // data after upgrade
- expect(await newMintManager.vectors(1)).to.eql(vectorOnOldImpl);
- expect(await newMintManager.owner()).to.equal(ownerOnOldImpl);
- });
- });
-
- describe("EditionsMetadataRenderer", function () {
- let testEditionsMetadataRenderer: TestEditionsMetadataRenderer;
- let editions: ERC721Editions;
-
- beforeEach(async function () {
- editions = await setupEditions(
- observability.address,
- editionsImplementation,
- mintManager.address,
- auctionManager.address,
- trustedForwarder.address,
- emr.address,
- mintManagerOwner,
- );
- await expect(editions.createEdition(defaultEditionInfo, 4, ethers.constants.AddressZero, zeroRoyalty, "0x"));
- });
-
- it("Non owner cannot upgrade EditionsMetadataRenderer", async function () {
- testEditionsMetadataRenderer = await (await ethers.getContractFactory("TestEditionsMetadataRenderer")).deploy();
- await testEditionsMetadataRenderer.deployed();
-
- mintManager = mintManager.connect(owner);
-
- await expect(mintManager.upgradeTo(testEditionsMetadataRenderer.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- mintManager = mintManager.connect(editionsMetadataOwner);
-
- await expect(mintManager.upgradeTo(testEditionsMetadataRenderer.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- mintManager = mintManager.connect(fan1);
-
- await expect(mintManager.upgradeTo(testEditionsMetadataRenderer.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
-
- it("Upgrade to TestEditionsMetadataRenderer retains original data and introduces new functionality", async function () {
- emr = emr.connect(editionsMetadataOwner);
- // data before upgrade
- // const editionOnOldEMR = await emr.editionInfo(editions.address, 0);
- const ownerOnOldEMR = await emr.owner();
-
- testEditionsMetadataRenderer = await (await ethers.getContractFactory("TestEditionsMetadataRenderer")).deploy();
- await testEditionsMetadataRenderer.deployed();
-
- await expect(emr.upgradeTo(testEditionsMetadataRenderer.address))
- .to.emit(emr, "Upgraded")
- .withArgs(testEditionsMetadataRenderer.address);
-
- const newEditionsMetadataRenderer = new ethers.Contract(
- emr.address,
- testEditionsMetadataRenderer.interface,
- editionsMetadataOwner,
- );
- expect(await newEditionsMetadataRenderer.test()).to.equal("test");
-
- // data after upgrade
- // expect(await newEditionsMetadataRenderer.editionInfo(editions.address, 0)).to.equal(editionOnOldEMR);
- expect(await newEditionsMetadataRenderer.owner()).to.equal(ownerOnOldEMR);
- });
- });
-
- describe("DiscreteDutchAuctionMechanic", function () {
- let testDiscreteDutchAuctionMechanic: TestDiscreteDutchAuctionMechanic;
-
- it("Non owner cannot upgrade DiscreteDutchAuctionMechanic", async function () {
- testDiscreteDutchAuctionMechanic = await (
- await ethers.getContractFactory("TestDiscreteDutchAuctionMechanic")
- ).deploy();
- await testDiscreteDutchAuctionMechanic.deployed();
-
- dutchAuction = dutchAuction.connect(owner);
-
- await expect(dutchAuction.upgradeTo(testDiscreteDutchAuctionMechanic.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- dutchAuction = dutchAuction.connect(editionsMetadataOwner);
-
- await expect(dutchAuction.upgradeTo(testDiscreteDutchAuctionMechanic.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
-
- dutchAuction = dutchAuction.connect(fan1);
-
- await expect(dutchAuction.upgradeTo(testDiscreteDutchAuctionMechanic.address)).to.be.revertedWith(
- "Ownable: caller is not the owner",
- );
- });
-
- it("Upgrade to TestDiscreteDutchAuctionMechanic retains original data and introduces new functionality", async function () {
- dutchAuction = dutchAuction.connect(mintManagerOwner);
- // data before upgrade
- const ownerOnOldMechanic = await dutchAuction.owner();
-
- testDiscreteDutchAuctionMechanic = await (
- await ethers.getContractFactory("TestDiscreteDutchAuctionMechanic")
- ).deploy();
- await testDiscreteDutchAuctionMechanic.deployed();
-
- await expect(dutchAuction.upgradeTo(testDiscreteDutchAuctionMechanic.address))
- .to.emit(dutchAuction, "Upgraded")
- .withArgs(testDiscreteDutchAuctionMechanic.address);
-
- const newDiscreteDutchAuctionMechanic = new ethers.Contract(
- dutchAuction.address,
- testDiscreteDutchAuctionMechanic.interface,
- mintManagerOwner,
- );
- expect(await newDiscreteDutchAuctionMechanic.test()).to.equal(true);
-
- // data after upgrade
- expect(await newDiscreteDutchAuctionMechanic.owner()).to.equal(ownerOnOldMechanic);
- });
- });
-});
diff --git a/test/__utils__/EIP712.ts b/test/__utils__/EIP712.ts
deleted file mode 100644
index 8f3c6e5..0000000
--- a/test/__utils__/EIP712.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { TypedDataDomain, TypedDataField, ethers } from "ethers";
-
-export type EIP712Types = Record>;
-
-export class EIP712 {
- contract: ethers.Contract;
- signer: SignerWithAddress;
- message: Record;
- types: EIP712Types;
- constructor(contract: ethers.Contract, signer: SignerWithAddress, message: Record, types: EIP712Types) {
- this.contract = contract;
- this.signer = signer;
- this.message = message;
- this.types = types;
- }
-
- static buildDomain(
- name: string,
- version: string,
- verifyingContract: string,
- chainId?: number,
- salt?: string,
- ): TypedDataDomain {
- const domain: TypedDataDomain = {
- name,
- version,
- verifyingContract,
- };
- if (salt) domain.salt = salt;
- if (chainId) domain.chainId = chainId;
- return domain;
- }
-
- buildTypedData(domain: TypedDataDomain) {
- return {
- types: this.types,
- domain,
- message: this.message,
- };
- }
-
- async sign(domain: TypedDataDomain): Promise {
- const typedData = this.buildTypedData(domain);
- return await this.signer._signTypedData(typedData.domain, typedData.types, typedData.message);
- }
-}
diff --git a/test/__utils__/auction.ts b/test/__utils__/auction.ts
deleted file mode 100644
index ea32cad..0000000
--- a/test/__utils__/auction.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { ethers } from "ethers";
-
-import { EIP712 } from "./EIP712";
-
-export type Claim = {
- auctionId: string;
- bidPrice: string;
- reservePrice: string;
- maxClaimsPerAccount: number;
- claimExpiryTimestamp: string;
- buffer: number;
- minimumIncrementPerBidPctBPS: number;
- claimer: string;
-};
-
-const Claim = [
- { name: "auctionId", type: "bytes32" },
- { name: "bidPrice", type: "uint256" },
- { name: "reservePrice", type: "uint256" },
- { name: "maxClaimsPerAccount", type: "uint256" },
- { name: "claimExpiryTimestamp", type: "uint256" },
- { name: "buffer", type: "uint256" },
- { name: "minimumIncrementPerBidPctBPS", type: "uint256" },
- { name: "claimer", type: "address" },
-];
-
-function buildClaim(input: Claim): Claim {
- input.auctionId = ethers.utils.formatBytes32String(input.auctionId);
- input.bidPrice = ethers.utils.parseEther(input.bidPrice).toString();
- input.reservePrice = ethers.utils.parseEther(input.reservePrice).toString();
- if (input.claimExpiryTimestamp == "0") {
- input.claimExpiryTimestamp = getValidClaimTimestamp();
- }
-
- return input;
-}
-
-export async function signGatedBid(signer: SignerWithAddress, auctionManager: ethers.Contract, claimInput: Claim) {
- const chainId = await auctionManager.provider.getNetwork().then(n => n.chainId);
- const claim = buildClaim(claimInput) as Claim;
- const eip712 = new EIP712(auctionManager, signer, claim, { Claim });
- const signature = await eip712.sign(EIP712.buildDomain("AuctionManager", "1.0.0", auctionManager.address, chainId));
- return { signature, claim };
-}
-
-export const getValidClaimTimestamp = () => (Math.floor(Date.now() / 1000) + 360000).toString();
diff --git a/test/__utils__/data.ts b/test/__utils__/data.ts
deleted file mode 100644
index 00f91fa..0000000
--- a/test/__utils__/data.ts
+++ /dev/null
@@ -1,194 +0,0 @@
-import { BigNumber } from "@ethersproject/contracts/node_modules/@ethersproject/bignumber";
-import { ethers } from "hardhat";
-
-import { OnchainDutchAuctionParams } from "./helpers";
-
-export const SAMPLE_VECTOR_1 = (
- address: string,
- paymentRecipient: string,
- maxTotalClaimableViaVector = 10,
- maxUserClaimableViaVector = 5,
- pricePerToken = 0,
- tokenLimitPerTx = 1,
- start = 0,
- end = 0,
- paused = 0,
- allowlistRoot = "",
-) => {
- return {
- contractAddress: address,
- currency: ethers.constants.AddressZero,
- paymentRecipient: paymentRecipient,
- startTimestamp: start,
- endTimestamp: end,
- pricePerToken,
- tokenLimitPerTx,
- maxTotalClaimableViaVector,
- maxUserClaimableViaVector,
- totalClaimedViaVector: 0,
- allowlistRoot: ethers.utils.formatBytes32String(allowlistRoot),
- paused,
- };
-};
-
-export const SAMPLE_ABRIDGED_VECTOR = (
- contractAddress: string,
- paymentRecipient: string,
- editionBasedCollection: boolean,
- editionId: number = 0,
- maxTotalClaimableViaVector: number = 0,
- maxUserClaimableViaVector: number = 0,
- startTimestamp: number = 0,
- endTimestamp: number = 0,
- tokenLimitPerTx: number = 0,
- pricePerToken: BigNumber = ethers.utils.parseEther("0"),
- allowlistRoot: string = ethers.constants.HashZero,
- requireDirectEOA: boolean = false,
-) => {
- return {
- contractAddress,
- currency: ethers.constants.AddressZero,
- totalClaimedViaVector: 0,
- startTimestamp,
- endTimestamp,
- paymentRecipient,
- maxTotalClaimableViaVector,
- tokenLimitPerTx,
- maxUserClaimableViaVector,
- pricePerToken,
- editionId,
- editionBasedCollection,
- requireDirectEOA,
- allowlistRoot,
- };
-};
-
-export const SAMPLE_ABRIDGED_VECTOR_UPDATE_CONFIG = ({
- updateMaxTotalClaimableViaVector,
- updateStartTimestamp,
- updateEndTimestamp,
- updatePaymentRecipient,
- updateTokenLimitPerTx,
- updateMaxUserClaimableViaVector,
- updatePricePerToken,
- updateAllowlistRoot,
- updateRequireDirectEOA,
- updateMetadata,
-}: {
- updateMaxTotalClaimableViaVector?: boolean;
- updateStartTimestamp?: boolean;
- updateEndTimestamp?: boolean;
- updatePaymentRecipient?: boolean;
- updateTokenLimitPerTx?: boolean;
- updateMaxUserClaimableViaVector?: boolean;
- updatePricePerToken?: boolean;
- updateAllowlistRoot?: boolean;
- updateRequireDirectEOA?: boolean;
- updateMetadata?: boolean;
-}) => {
- return {
- updateStartTimestamp: updateStartTimestamp ? 1 : 0,
- updateEndTimestamp: updateEndTimestamp ? 1 : 0,
- updatePaymentRecipient: updatePaymentRecipient ? 1 : 0,
- updateMaxTotalClaimableViaVector: updateMaxTotalClaimableViaVector ? 1 : 0,
- updateTokenLimitPerTx: updateTokenLimitPerTx ? 1 : 0,
- updateMaxUserClaimableViaVector: updateMaxUserClaimableViaVector ? 1 : 0,
- updatePricePerToken: updatePricePerToken ? 1 : 0,
- updateAllowlistRoot: updateAllowlistRoot ? 1 : 0,
- updateRequireDirectEOA: updateRequireDirectEOA ? 1 : 0,
- updateMetadata: updateMetadata ? 1 : 0,
- };
-};
-
-export const SAMPLE_DA_VECTOR = (
- mechanicAddress: string,
- input: {
- prices?: string[];
- periodDuration?: number;
- maxTotalClaimableViaVector?: number;
- maxUserClaimableViaVector?: number;
- startTimestamp?: number;
- endTimestamp?: number;
- tokenLimitPerTx?: number;
- seed?: string;
- },
-): OnchainDutchAuctionParams => {
- return {
- mechanicAddress,
- prices: input.prices ?? ["0.001", "0.0001"],
- periodDuration: input.periodDuration ?? 100,
- maxTotalClaimableViaVector: input.maxTotalClaimableViaVector ?? 0,
- maxUserClaimableViaVector: input.maxUserClaimableViaVector ?? 0,
- startTimestamp: input.startTimestamp ?? Math.floor(Date.now() / 1000),
- endTimestamp: input.endTimestamp ?? 0,
- tokenLimitPerTx: input.tokenLimitPerTx ?? 0,
- seed: input.seed ?? Math.floor(Date.now() / 1000).toString(),
- };
-};
-
-export type DutchAuctionUpdateValues = {
- prices?: string[];
- periodDuration?: number;
- maxTotalClaimableViaVector?: number;
- maxUserClaimableViaVector?: number;
- startTimestamp?: number;
- endTimestamp?: number;
- tokenLimitPerTx?: number;
- paymentRecipient?: string;
-};
-
-export const SAMPLE_VECTOR_MUTABILITY_1 = (deleteFrozen = 0, pausesFrozen = 0, updatesFrozen = 0) => {
- return {
- deleteFrozen,
- pausesFrozen,
- updatesFrozen,
- };
-};
-
-export enum Errors {
- InvalidManager = "InvalidManager",
- ManagerDoesNotExist = "ManagerDoesNotExist",
- Unauthorized = "Unauthorized",
- NotMinter = "NotMinter",
- ManagerSwapBlocked = "ManagerSwapBlocked",
- ManagerRemoveBlocked = "ManagerRemoveBlocked",
- RoyaltySetBlocked = "RoyaltySetBlocked",
- RoyaltyBPSInvalid = "RoyaltyBPSInvalid",
- MinterRegistrationInvalid = "MinterRegistrationInvalid",
- EditionDoesNotExist = "EditionDoesNotExist",
- TokenDoesNotExist = "TokenDoesNotExist",
- MintFrozen = "MintFrozen",
- SoldOut = "SoldOut",
- InvalidSize = "InvalidSize",
- InvalidEditionIdsLength = "InvalidEditionIdsLength",
- TokenNotInRange = "TokenNotInRange",
- OverLimitSupply = "OverLimitSupply",
- MismatchedArrayLengths = "MismatchedArrayLengths",
- MetadataUpdateBlocked = "MetadataUpdateBlocked",
- InvalidEditionId = "InvalidEditionId",
- InvalidExecutorChanged = "InvalidExecutorChanged",
- VectorUpdateActionFrozen = "VectorUpdateActionFrozen",
- InvalidTotalClaimed = "InvalidTotalClaimed",
- AllowlistInvalid = "AllowlistInvalid",
- CurrencyTypeInvalid = "CurrencyTypeInvalid",
- MintFeeTooLow = "MintFeeTooLow",
- EtherSendFailed = "EtherSendFailed",
- SenderNotClaimer = "SenderNotClaimer",
- InvalidClaim = "InvalidClaim",
- InvalidPaymentAmount = "InvalidPaymentAmount",
- OnchainVectorMintGuardFailed = "OnchainVectorMintGuardFailed",
- EmptyString = "EmptyString",
- OwnerQueryForNonexistentToken = "OwnerQueryForNonexistentToken",
- TransferFromIncorrectOwner = "TransferFromIncorrectOwner",
- TokenMintedAlready = "TokenMintedAlready",
- UnsafeMintRecipient = "UnsafeMintRecipient",
- MintPaused = "MintPaused",
- MechanicPaused = "MechanicPaused",
- InvalidMechanic = "InvalidMechanic",
- InvalidVectorConfig = "InvalidVectorConfig",
- InvalidUpdate = "InvalidUpdate",
- InvalidMint = "InvalidMint",
- InvalidRebate = "InvalidRebate",
- CollectorNotOwedRebate = "CollectorNotOwedRebate",
- InvalidDPPFundsWithdrawl = "InvalidDPPFundsWithdrawl",
-}
diff --git a/test/__utils__/helpers.ts b/test/__utils__/helpers.ts
deleted file mode 100644
index 5fd559b..0000000
--- a/test/__utils__/helpers.ts
+++ /dev/null
@@ -1,1167 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { BigNumber, BytesLike } from "ethers";
-import { ethers } from "hardhat";
-
-import {
- AuctionManager__factory,
- DiscreteDutchAuctionMechanic,
- DiscreteDutchAuctionMechanic__factory,
- ERC721Editions,
- ERC721EditionsDFS,
- ERC721EditionsDFS__factory,
- ERC721Editions__factory,
- ERC721General,
- ERC721General__factory,
- ERC721Generative__factory,
- ERC721SingleEdition,
- ERC721SingleEditionDFS,
- ERC721SingleEditionDFS__factory,
- ERC721SingleEdition__factory,
- EditionsMetadataRenderer__factory,
- IERC20__factory,
- MinimalForwarder__factory,
- MintManager__factory,
- NativeMetaTransaction__factory,
- Observability__factory,
-} from "../../types";
-import { DutchAuctionUpdateValues } from "./data";
-import { signGatedMint, signGatedMintWithMetaTxPacket, signGatedSeriesMint, signWETHMetaTxRequest } from "./mint";
-
-export type OnchainMintVectorParams = {
- startTimestamp: number;
- endTimestamp: number;
- pricePerToken: BigNumber;
- tokenLimitPerTx: number;
- maxTotalClaimableViaVector: number;
- maxUserClaimableViaVector: number;
- allowlistRoot: string;
- editionId?: number;
-};
-
-export type OnchainDutchAuctionParams = {
- startTimestamp: number;
- endTimestamp: number;
- prices: string[];
- periodDuration: number;
- tokenLimitPerTx: number;
- maxTotalClaimableViaVector: number;
- maxUserClaimableViaVector: number;
- mechanicAddress: string;
- seed: string;
-};
-
-export const DEFAULT_ONCHAIN_MINT_VECTOR: OnchainMintVectorParams = {
- startTimestamp: 0,
- endTimestamp: 0,
- pricePerToken: ethers.utils.parseEther("0"),
- tokenLimitPerTx: 0,
- maxTotalClaimableViaVector: 0,
- maxUserClaimableViaVector: 0,
- allowlistRoot: ethers.constants.HashZero,
-};
-
-export const setupSingleEdition = async (
- observabilityAddress: string,
- singleImplementationAddress: string,
- mintManagerAddress: string,
- trustedForwarderAddress: string,
- emrAddress: string,
- creator: SignerWithAddress,
- size: number,
- name: string,
- symbol: string,
- directMint: OnchainMintVectorParams | null = null,
- mechanicMint: OnchainDutchAuctionParams | null = null,
- useMarketplaceFilter = false,
- defaultTokenManager = ethers.constants.AddressZero,
- royaltyRecipient = ethers.constants.AddressZero,
- royaltyPercentage = 0,
- contractUri = "",
- description = "",
- imageUrl = "",
- animationUrl = "",
- externalUrl = "",
- attributes = "",
-): Promise => {
- const editionInfo = ethers.utils.defaultAbiCoder.encode(
- ["tuple(string, string, string, string, string, string)"],
- [[name, description, imageUrl, animationUrl, externalUrl, attributes]],
- );
-
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- [
- "address",
- "tuple(address, uint16)",
- "address",
- "string",
- "string",
- "string",
- "uint256",
- "address",
- "address",
- "address",
- "bool",
- "bytes",
- ],
- [
- creator.address,
- [royaltyRecipient, royaltyPercentage],
- defaultTokenManager,
- contractUri,
- name,
- symbol,
- size,
- emrAddress,
- trustedForwarderAddress,
- mintManagerAddress,
- useMarketplaceFilter,
- editionInfo,
- ],
- );
-
- const mintVectorData = directMint
- ? ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintManagerAddress,
- creator.address,
- directMint.startTimestamp,
- directMint.endTimestamp,
- directMint.pricePerToken,
- directMint.tokenLimitPerTx,
- directMint.maxTotalClaimableViaVector,
- directMint.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- )
- : "0x";
-
- const SingleEdition = await (
- await ethers.getContractFactory("SingleEdition")
- ).deploy(
- singleImplementationAddress,
- initializeData,
- mintVectorData,
- encodeMechanicVectorData(mintManagerAddress, creator.address, mechanicMint),
- observabilityAddress,
- );
- const singleEdition = await SingleEdition.deployed();
- return ERC721SingleEdition__factory.connect(singleEdition.address, creator);
-};
-
-export const setupSingleEditionDFS = async (
- observabilityAddress: string,
- singleEditionDFSImplementationAddress: string,
- mintManagerAddress: string,
- trustedForwarderAddress: string,
- creator: SignerWithAddress,
- size: number,
- name: string,
- symbol: string,
- directMint: OnchainMintVectorParams | null = null,
- mechanicMint: OnchainDutchAuctionParams | null = null,
- useMarketplaceFilter = false,
- defaultTokenManager = ethers.constants.AddressZero,
- royaltyRecipient = ethers.constants.AddressZero,
- royaltyPercentage = 0,
- contractUri = "",
- editionUri = "editionUri",
-): Promise => {
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- [
- "address",
- "tuple(address, uint16)",
- "address",
- "string",
- "string",
- "string",
- "uint256",
- "address",
- "address",
- "bool",
- "string",
- ],
- [
- creator.address,
- [royaltyRecipient, royaltyPercentage],
- defaultTokenManager,
- contractUri,
- name,
- symbol,
- size,
- trustedForwarderAddress,
- mintManagerAddress,
- useMarketplaceFilter,
- editionUri,
- ],
- );
-
- const mintVectorData = directMint
- ? ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintManagerAddress,
- creator.address,
- directMint.startTimestamp,
- directMint.endTimestamp,
- directMint.pricePerToken,
- directMint.tokenLimitPerTx,
- directMint.maxTotalClaimableViaVector,
- directMint.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- )
- : "0x";
-
- const SingleEditionDFS = await (
- await ethers.getContractFactory("SingleEditionDFS")
- ).deploy(
- singleEditionDFSImplementationAddress,
- initializeData,
- mintVectorData,
- encodeMechanicVectorData(mintManagerAddress, creator.address, mechanicMint),
- observabilityAddress,
- );
- const singleEditionDFS = await SingleEditionDFS.deployed();
- return ERC721SingleEditionDFS__factory.connect(singleEditionDFS.address, creator);
-};
-
-// sets up MultipleEditions without first edition
-export const setupEditions = async (
- observabilityAddress: string,
- editionsImplementationAddress: string,
- mintManagerAddress: string,
- auctionManagerAddress: string,
- trustedForwarderAddress: string,
- emrAddress: string,
- creator: SignerWithAddress,
- directMint: OnchainMintVectorParams | null = null,
- mechanicMint: OnchainDutchAuctionParams | null = null,
- defaultTokenManager = ethers.constants.AddressZero,
- royaltyRecipient = ethers.constants.AddressZero,
- royaltyPercentage = 0,
- useMarketplaceFilter = false,
- name = "dummy",
- symbol = "DMY",
- contractUri = "dummyContractMetadata",
-): Promise => {
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- ["address", "string", "string", "string", "address", "address", "address[]", "bool", "address"],
- [
- creator.address,
- contractUri,
- name,
- symbol,
- emrAddress,
- trustedForwarderAddress,
- [mintManagerAddress, auctionManagerAddress],
- useMarketplaceFilter,
- observabilityAddress,
- ],
- );
-
- const mintVectorData = directMint
- ? ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintManagerAddress,
- creator.address,
- directMint.startTimestamp,
- directMint.endTimestamp,
- directMint.pricePerToken,
- directMint.tokenLimitPerTx,
- directMint.maxTotalClaimableViaVector,
- directMint.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- )
- : "0x";
-
- const MultipleEditions = await (
- await ethers.getContractFactory("MultipleEditions", creator)
- ).deploy(
- editionsImplementationAddress,
- initializeData,
- ethers.utils.arrayify("0x"),
- 0,
- ethers.constants.AddressZero,
- {
- recipientAddress: royaltyRecipient,
- royaltyPercentageBPS: royaltyPercentage,
- },
- ethers.utils.arrayify("0x"),
- mintVectorData,
- encodeMechanicVectorData(mintManagerAddress, creator.address, mechanicMint),
- );
- const multipleEditions = await MultipleEditions.deployed();
-
- const multipleEditionsCreator = ERC721Editions__factory.connect(multipleEditions.address, creator);
-
- if (defaultTokenManager != ethers.constants.AddressZero) {
- const tx = await multipleEditionsCreator.setDefaultTokenManager(defaultTokenManager);
- await tx.wait();
- }
-
- return multipleEditionsCreator;
-};
-
-// sets up MultipleEditionsDFS without first edition
-export const setupEditionsDFS = async (
- observabilityAddress: string,
- editionsDFSImplementationAddress: string,
- mintManagerAddress: string,
- auctionManagerAddress: string,
- trustedForwarderAddress: string,
- creator: SignerWithAddress,
- directMint: OnchainMintVectorParams | null = null,
- mechanicMint: OnchainDutchAuctionParams | null = null,
- editionUri = "",
- defaultTokenManager = ethers.constants.AddressZero,
- royaltyRecipient = ethers.constants.AddressZero,
- royaltyPercentage = 0,
- useMarketplaceFilter = false,
- name = "dummy",
- symbol = "DMY",
- contractUri = "dummyContractMetadata",
-): Promise => {
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- ["address", "string", "string", "string", "address", "address[]", "bool", "address"],
- [
- creator.address,
- contractUri,
- name,
- symbol,
- trustedForwarderAddress,
- [mintManagerAddress, auctionManagerAddress],
- useMarketplaceFilter,
- observabilityAddress,
- ],
- );
-
- const mintVectorData = directMint
- ? ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintManagerAddress,
- creator.address,
- directMint.startTimestamp,
- directMint.endTimestamp,
- directMint.pricePerToken,
- directMint.tokenLimitPerTx,
- directMint.maxTotalClaimableViaVector,
- directMint.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- )
- : "0x";
-
- const MultipleEditionsDFS = await (
- await ethers.getContractFactory("MultipleEditionsDFS", creator)
- ).deploy(
- editionsDFSImplementationAddress,
- initializeData,
- editionUri,
- 0,
- ethers.constants.AddressZero,
- {
- recipientAddress: royaltyRecipient,
- royaltyPercentageBPS: royaltyPercentage,
- },
- ethers.utils.arrayify("0x"),
- mintVectorData,
- encodeMechanicVectorData(mintManagerAddress, creator.address, mechanicMint),
- );
- const multipleEditionsDFS = await MultipleEditionsDFS.deployed();
-
- const multipleEditionsCreator = ERC721EditionsDFS__factory.connect(multipleEditionsDFS.address, creator);
-
- if (defaultTokenManager != ethers.constants.AddressZero) {
- const tx = await multipleEditionsCreator.setDefaultTokenManager(defaultTokenManager);
- await tx.wait();
- }
-
- return multipleEditionsCreator;
-};
-
-// sets up MultipleEditions with first edition
-export const setupMultipleEdition = async (
- observabilityAddress: string,
- editionsImplementationAddress: string,
- mintVectorAddress: string,
- auctionManagerAddress: string,
- trustedForwarderAddress: string,
- emrAddress: string,
- creator: SignerWithAddress,
- size: number,
- name: string,
- symbol: string,
- directMint: OnchainMintVectorParams | null = null,
- mechanicMint: OnchainDutchAuctionParams | null = null,
- useMarketplaceFilter = false,
- contractName = "contractName",
- royaltyPercentage = 0,
- royaltyRecipient = ethers.constants.AddressZero,
- contractUri = "",
- description = "",
- imageUrl = "",
- animationUrl = "",
- externalUrl = "",
- attributes = "",
-): Promise => {
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- ["address", "string", "string", "string", "address", "address", "address[]", "bool", "address"],
- [
- creator.address,
- contractUri,
- contractName,
- symbol,
- emrAddress,
- trustedForwarderAddress,
- [mintVectorAddress, auctionManagerAddress],
- useMarketplaceFilter,
- observabilityAddress,
- ],
- );
-
- const defaultEditionInfo = ethers.utils.defaultAbiCoder.encode(
- ["tuple(string, string, string, string, string, string)"],
- [[name, description, imageUrl, animationUrl, externalUrl, attributes]],
- );
-
- const mintVectorData = directMint
- ? ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintVectorAddress,
- creator.address,
- directMint.startTimestamp,
- directMint.endTimestamp,
- directMint.pricePerToken,
- directMint.tokenLimitPerTx,
- directMint.maxTotalClaimableViaVector,
- directMint.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- )
- : "0x";
-
- const MultipleEditions = await (
- await ethers.getContractFactory("MultipleEditions", creator)
- ).deploy(
- editionsImplementationAddress,
- initializeData,
- defaultEditionInfo,
- size,
- ethers.constants.AddressZero,
- {
- recipientAddress: royaltyRecipient,
- royaltyPercentageBPS: royaltyPercentage,
- },
- ethers.utils.arrayify("0x"),
- mintVectorData,
- encodeMechanicVectorData(mintVectorAddress, creator.address, mechanicMint),
- );
- const multipleEditions = await MultipleEditions.deployed();
-
- return ERC721Editions__factory.connect(multipleEditions.address, creator);
-};
-
-// sets up MultipleEditionsDFS with first edition
-export const setupMultipleEditionDFS = async (
- observabilityAddress: string,
- editionsDFSImplementationAddress: string,
- mintVectorAddress: string,
- auctionManagerAddress: string,
- trustedForwarderAddress: string,
- creator: SignerWithAddress,
- size: number,
- symbol: string,
- directMint: OnchainMintVectorParams | null = null,
- mechanicMint: OnchainDutchAuctionParams | null = null,
- editionUri: string = "uri",
- useMarketplaceFilter = false,
- contractName = "contractName",
- royaltyPercentage = 0,
- royaltyRecipient = ethers.constants.AddressZero,
- contractUri = "",
-): Promise => {
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- ["address", "string", "string", "string", "address", "address[]", "bool", "address"],
- [
- creator.address,
- contractUri,
- contractName,
- symbol,
- trustedForwarderAddress,
- [mintVectorAddress, auctionManagerAddress],
- useMarketplaceFilter,
- observabilityAddress,
- ],
- );
-
- const mintVectorData = directMint
- ? ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintVectorAddress,
- creator.address,
- directMint.startTimestamp,
- directMint.endTimestamp,
- directMint.pricePerToken,
- directMint.tokenLimitPerTx,
- directMint.maxTotalClaimableViaVector,
- directMint.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- )
- : "0x";
-
- const MultipleEditionsDFS = await (
- await ethers.getContractFactory("MultipleEditionsDFS", creator)
- ).deploy(
- editionsDFSImplementationAddress,
- initializeData,
- editionUri,
- size,
- ethers.constants.AddressZero,
- {
- recipientAddress: royaltyRecipient,
- royaltyPercentageBPS: royaltyPercentage,
- },
- ethers.utils.arrayify("0x"),
- mintVectorData,
- encodeMechanicVectorData(mintVectorAddress, creator.address, mechanicMint),
- );
- const multipleEditionsDFS = await MultipleEditionsDFS.deployed();
-
- return ERC721EditionsDFS__factory.connect(multipleEditionsDFS.address, creator);
-};
-
-export const generateClaim = async (
- mintManagerOwner: SignerWithAddress,
- mintManagerAddress: string,
- contractAddress: string,
- claimer: string,
- paymentRecipient: string,
- claimExpiryTimestamp: string = (Math.floor(Date.now() / 1000) + 360000).toString(),
- pricePerToken = "0",
- numTokensToMint = 1,
- maxClaimableViaVector = 0,
- maxClaimablePerUser = 0,
- editionId = 0,
- offchainVectorId = "randomVectorId",
- claimNonce = "randomClaimNonce",
- currency: string = ethers.constants.AddressZero,
-) => {
- const mintManager = await MintManager__factory.connect(mintManagerAddress, mintManagerOwner);
-
- return await signGatedMint(
- mintManagerOwner,
- mintManager,
- {
- currency,
- contractAddress,
- claimer,
- paymentRecipient,
- pricePerToken: ethers.utils.parseEther(pricePerToken).toString(),
- numTokensToMint,
- maxClaimableViaVector,
- maxClaimablePerUser,
- editionId,
- },
- offchainVectorId,
- claimNonce,
- claimExpiryTimestamp,
- );
-};
-
-export const generateSeriesClaim = async (
- mintManagerOwner: SignerWithAddress,
- mintManagerAddress: string,
- contractAddress: string,
- claimer: string,
- paymentRecipient: string,
- maxPerTxn: number,
- claimExpiryTimestamp: string = (Math.floor(Date.now() / 1000) + 360000).toString(),
- pricePerToken = "0",
- maxClaimableViaVector = 0,
- maxClaimablePerUser = 0,
- offchainVectorId = "randomVectorId",
- claimNonce = "randomClaimNonce",
- currency: string = ethers.constants.AddressZero,
-) => {
- const mintManager = await MintManager__factory.connect(mintManagerAddress, mintManagerOwner);
-
- return await signGatedSeriesMint(
- mintManagerOwner,
- mintManager,
- {
- currency,
- contractAddress,
- claimer,
- paymentRecipient,
- pricePerToken: ethers.utils.parseEther(pricePerToken).toString(),
- maxPerTxn,
- maxClaimableViaVector,
- maxClaimablePerUser,
- },
- offchainVectorId,
- claimNonce,
- claimExpiryTimestamp,
- );
-};
-
-export const generateClaimWithMetaTxPackets = async (
- mintManagerOwner: SignerWithAddress,
- claimer: SignerWithAddress,
- mintManagerAddress: string,
- contractAddress: string,
- paymentRecipient: string,
- currency: string,
- claimExpiryTimestamp: string = (Math.floor(Date.now() / 1000) + 3600).toString(),
- pricePerToken = "0",
- numTokensToMint = 1,
- maxClaimableViaVector = 10,
- maxClaimablePerUser = 10,
- editionId = 0,
- offchainVectorId = "randomVectorId",
- claimNonce = "randomClaimNonce",
-) => {
- const wETHWei = ethers.utils.parseUnits(pricePerToken, 18).mul(BigNumber.from(numTokensToMint));
- const wETHWeiToCreator = wETHWei.mul(95).div(100);
- const wETHWeiToPlatform = wETHWei.mul(5).div(100);
-
- const erc20 = await IERC20__factory.connect(currency, mintManagerOwner);
- const transferToCreatorData = erc20.interface.encodeFunctionData("transfer", [paymentRecipient, wETHWeiToCreator]);
- const transferToPlatformData = erc20.interface.encodeFunctionData("transfer", [
- mintManagerAddress,
- wETHWeiToPlatform,
- ]);
- const metaTxContract = await NativeMetaTransaction__factory.connect(currency, mintManagerOwner);
- const nonce = await metaTxContract.getNonce(claimer.address);
- const purchaseToCreatorPacket = await signWETHMetaTxRequest(claimer, erc20, {
- from: claimer.address,
- functionSignature: transferToCreatorData,
- nonce: nonce.toString(),
- });
- const purchaseToPlatformPacket = await signWETHMetaTxRequest(claimer, erc20, {
- from: claimer.address,
- functionSignature: transferToPlatformData,
- nonce: nonce.add(1).toString(),
- });
-
- const mintManager = await MintManager__factory.connect(mintManagerAddress, mintManagerOwner);
-
- return await signGatedMintWithMetaTxPacket(
- mintManagerOwner,
- mintManager,
- {
- currency,
- contractAddress,
- claimer: claimer.address,
- purchaseToCreatorPacket,
- purchaseToPlatformPacket,
- pricePerToken: ethers.utils.parseEther(pricePerToken).toString(),
- numTokensToMint,
- maxClaimableViaVector,
- maxClaimablePerUser,
- editionId,
- },
- offchainVectorId,
- claimNonce,
- claimExpiryTimestamp,
- );
-};
-
-export const setupGeneral = async (
- observabilityAddress: string,
- generalImplementationAddress: string,
- trustedForwarderAddress: string,
- mintManagerAddress: string,
- creator: SignerWithAddress,
- directMint: OnchainMintVectorParams | null = null,
- mechanicMint: OnchainDutchAuctionParams | null = null,
- isCollectorsChoice: boolean = false,
- useMarketplaceFilter = false,
- limitSupply = 0,
- defaultTokenManager = ethers.constants.AddressZero,
- royaltyRecipient = ethers.constants.AddressZero,
- royaltyPercentage = 0,
- name = "dummy",
- symbol = "DMY",
- baseUri = "baseUri",
- contractUri = "dummyContractMetadata",
-): Promise => {
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- [
- "address",
- "string",
- "tuple(address, uint16)",
- "address",
- "string",
- "string",
- "address",
- "address",
- "string",
- "uint256",
- "bool",
- "address",
- ],
- [
- creator.address,
- contractUri,
- [royaltyRecipient, royaltyPercentage],
- defaultTokenManager,
- name,
- symbol,
- trustedForwarderAddress,
- mintManagerAddress,
- baseUri,
- limitSupply,
- useMarketplaceFilter,
- observabilityAddress,
- ],
- );
-
- const vectorData = directMint
- ? ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintManagerAddress,
- creator.address,
- directMint.startTimestamp,
- directMint.endTimestamp,
- directMint.pricePerToken,
- directMint.tokenLimitPerTx,
- directMint.maxTotalClaimableViaVector,
- directMint.maxUserClaimableViaVector,
- ethers.constants.HashZero,
- ],
- )
- : "0x";
-
- const Series = await (
- await ethers.getContractFactory("Series", creator)
- ).deploy(
- generalImplementationAddress,
- initializeData,
- vectorData,
- encodeMechanicVectorData(mintManagerAddress, creator.address, mechanicMint),
- isCollectorsChoice,
- );
- const series = await Series.deployed();
-
- return ERC721General__factory.connect(series.address, creator);
-};
-
-export const setupGenerative = async (
- observabilityAddress: string,
- generalImplementationAddress: string,
- trustedForwarderAddress: string,
- mintManagerAddress: string,
- creator: SignerWithAddress,
- directMint: OnchainMintVectorParams | null = null,
- mechanicMint: OnchainDutchAuctionParams | null = null,
- useMarketplaceFilter = false,
- limitSupply = 0,
- defaultTokenManager = ethers.constants.AddressZero,
- royaltyRecipient = ethers.constants.AddressZero,
- royaltyPercentage = 0,
- name = "dummy",
- symbol = "DMY",
- baseUri = "baseUri",
- contractUri = "dummyContractMetadata",
- codeUri = "codeUri",
-): Promise => {
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- [
- "address",
- "string",
- "tuple(address, uint16)",
- "address",
- "string",
- "string",
- "address",
- "address",
- "string",
- "string",
- "uint256",
- "bool",
- ],
- [
- creator.address,
- contractUri,
- [royaltyRecipient, royaltyPercentage],
- defaultTokenManager,
- name,
- symbol,
- trustedForwarderAddress,
- mintManagerAddress,
- codeUri,
- baseUri,
- limitSupply,
- useMarketplaceFilter,
- ],
- );
-
- const vectorData = directMint
- ? ethers.utils.defaultAbiCoder.encode(
- ["address", "address", "uint48", "uint48", "uint192", "uint48", "uint48", "uint48", "bytes32"],
- [
- mintManagerAddress,
- creator.address,
- directMint.startTimestamp,
- directMint.endTimestamp,
- directMint.pricePerToken,
- directMint.tokenLimitPerTx,
- directMint.maxTotalClaimableViaVector,
- directMint.maxUserClaimableViaVector,
- directMint.allowlistRoot,
- ],
- )
- : "0x";
-
- const GenerativeSeries = await (
- await ethers.getContractFactory("GenerativeSeries", creator)
- ).deploy(
- generalImplementationAddress,
- initializeData,
- vectorData,
- encodeMechanicVectorData(mintManagerAddress, creator.address, mechanicMint),
- observabilityAddress,
- );
- const generativeSeries = await GenerativeSeries.deployed();
-
- return ERC721Generative__factory.connect(generativeSeries.address, creator);
-};
-
-export const setupEtherAuctionWithNewToken = async (
- observabilityAddress: string,
- editionsImplementationAddress: string,
- mintManagerAddress: string,
- auctionManagerAddress: string,
- emrAddress: string,
- trustedForwarderAddress: string,
- creator: SignerWithAddress,
- auctionId: string,
- auctionEndTime: number,
- auctionPaymentRecipient: string,
- useMarketplaceFilter: boolean = false,
- auctionCurrency: string = ethers.constants.AddressZero,
- defaultTokenManager = ethers.constants.AddressZero,
- royaltyRecipient = ethers.constants.AddressZero,
- royaltyPercentage = 0,
- contractName = "contractName",
- symbol = "DMY",
- name = "dummy",
- description = "description",
- imageUrl = "imageUrl",
- animationUrl = "animationUrl",
- externalUrl = "externalUrl",
- attributes = "attributes",
- contractUri = "dummyContractMetadata",
-): Promise => {
- const initializeData = ethers.utils.defaultAbiCoder.encode(
- ["address", "string", "string", "string", "address", "address", "address[]", "bool", "address"],
- [
- creator.address,
- contractUri,
- contractName,
- symbol,
- emrAddress,
- trustedForwarderAddress,
- [mintManagerAddress, auctionManagerAddress],
- useMarketplaceFilter,
- observabilityAddress,
- ],
- );
-
- const defaultEditionInfo = ethers.utils.defaultAbiCoder.encode(
- ["tuple(string, string, string, string, string, string)"],
- [[name, description, imageUrl, animationUrl, externalUrl, attributes]],
- );
-
- const auctionData = ethers.utils.defaultAbiCoder.encode(
- ["address", "bytes32", "address", "address", "uint256"],
- [
- auctionManagerAddress,
- ethers.utils.formatBytes32String(auctionId),
- auctionCurrency,
- auctionPaymentRecipient,
- auctionEndTime,
- ],
- );
-
- const mintVectorData = "0x";
-
- const MultipleEditions = await (
- await ethers.getContractFactory("MultipleEditions", creator)
- ).deploy(
- editionsImplementationAddress,
- initializeData,
- defaultEditionInfo,
- 1,
- defaultTokenManager,
- { recipientAddress: royaltyRecipient, royaltyPercentageBPS: royaltyPercentage },
- auctionData,
- mintVectorData,
- "0x",
- );
- const multipleEditions = await MultipleEditions.deployed();
-
- return ERC721Editions__factory.connect(multipleEditions.address, creator);
-};
-
-export async function setupSystem(
- platformPaymentAddress: string,
- mintManagerOwnerAddress: string,
- initialPlatformExecutorAddress: string,
- editionsMetadataOwnerAddress: string,
- signer: SignerWithAddress,
- mintFee: string = "0.0008",
-) {
- const minimalForwarderFactory = await ethers.getContractFactory("MinimalForwarder");
- const minimalForwarder = await minimalForwarderFactory.deploy();
- await minimalForwarder.deployed();
-
- const mintManagerFactory = await ethers.getContractFactory("MintManager");
- const mintManager = await mintManagerFactory.deploy();
- await mintManager.deployed();
- const encodedFn = mintManager.interface.encodeFunctionData("initialize", [
- platformPaymentAddress,
- mintManagerOwnerAddress,
- minimalForwarder.address,
- initialPlatformExecutorAddress,
- ethers.utils.parseEther(mintFee),
- ]);
-
- const mintManagerProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const mintManagerProxy = await mintManagerProxyFactory.deploy(mintManager.address, encodedFn);
- await mintManagerProxy.deployed();
-
- // deploy AuctionManager
- const auctionManagerFactory = await ethers.getContractFactory("AuctionManager");
- const auctionManager = await auctionManagerFactory.deploy();
- await auctionManager.deployed();
- const amEncodedFn = auctionManager.interface.encodeFunctionData("initialize", [
- platformPaymentAddress,
- minimalForwarder.address,
- mintManagerOwnerAddress,
- initialPlatformExecutorAddress,
- ]);
-
- const auctionManagerProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const auctionManagerProxy = await auctionManagerProxyFactory.deploy(auctionManager.address, amEncodedFn);
- await auctionManagerProxy.deployed();
-
- //Deploy EMR
- const emrFactory = await ethers.getContractFactory("EditionsMetadataRenderer");
- const emr = await emrFactory.deploy();
- await emr.deployed();
- const emrEncodedFn = emr.interface.encodeFunctionData("initialize", [editionsMetadataOwnerAddress]);
-
- const emrProxyFactory = await ethers.getContractFactory("ERC1967Proxy");
- const emrProxy = await emrProxyFactory.deploy(emr.address, emrEncodedFn);
- await emrProxy.deployed();
-
- //Deploy Editions
- const editionsFactory = await ethers.getContractFactory("ERC721Editions");
- const editions = await editionsFactory.deploy();
- await editions.deployed();
-
- //Deploy EditionsDFS
- const editionsDFSFactory = await ethers.getContractFactory("ERC721EditionsDFS");
- const editionsDFS = await editionsDFSFactory.deploy();
- await editionsDFS.deployed();
-
- //Deploy Single Edition
- const singleEditionFactory = await ethers.getContractFactory("ERC721SingleEdition");
- const singleEdition = await singleEditionFactory.deploy();
- await singleEdition.deployed();
-
- //Deploy Single Edition DFS
- const singleEditionDFSFactory = await ethers.getContractFactory("ERC721SingleEditionDFS");
- const singleEditionDFS = await singleEditionDFSFactory.deploy();
- await singleEditionDFS.deployed();
-
- //Deploy General
- const generalFactory = await ethers.getContractFactory("ERC721General");
- const general = await generalFactory.deploy();
- await general.deployed();
-
- //Deploy GeneralSequence
- const generalSequenceFactory = await ethers.getContractFactory("ERC721GeneralSequence");
- const generalSequence = await generalSequenceFactory.deploy();
- await generalSequence.deployed();
-
- //Deploy Editions
- const generativeFactory = await ethers.getContractFactory("ERC721GenerativeOnchain");
- const generative = await generativeFactory.deploy();
- await generative.deployed();
-
- const observabilityFactory = await ethers.getContractFactory("Observability");
- const observability = await observabilityFactory.deploy();
- await observability.deployed();
-
- const dutchAuctionImplFactory = await ethers.getContractFactory("DiscreteDutchAuctionMechanic");
- const dutchAuctionImpl = await dutchAuctionImplFactory.deploy();
- await dutchAuctionImpl.deployed();
-
- const dutchAuctionEncodedFn = dutchAuctionImpl.interface.encodeFunctionData("initialize", [
- mintManagerProxy.address,
- mintManagerOwnerAddress,
- ]);
- const dutchAuctionFactory = await ethers.getContractFactory("ERC1967Proxy");
- const dutchAuction = await dutchAuctionFactory.deploy(dutchAuctionImpl.address, dutchAuctionEncodedFn);
- await dutchAuction.deployed();
-
- return {
- emrProxy: EditionsMetadataRenderer__factory.connect(emrProxy.address, signer),
- mintManagerProxy: MintManager__factory.connect(mintManagerProxy.address, signer),
- auctionManagerProxy: AuctionManager__factory.connect(auctionManagerProxy.address, signer),
- minimalForwarder: MinimalForwarder__factory.connect(minimalForwarder.address, signer),
- observability: Observability__factory.connect(observability.address, signer),
- daMechanic: DiscreteDutchAuctionMechanic__factory.connect(dutchAuction.address, signer),
- generalImplementationAddress: general.address,
- generalSequenceImplementationAddress: generalSequence.address,
- generativeImplementationAddress: generative.address,
- editionsImplementationAddress: editions.address,
- editionsDFSImplementationAddress: editionsDFS.address,
- singleEditionImplementationAddress: singleEdition.address,
- singleEditionDFSImplementationAddress: singleEditionDFS.address,
- };
-}
-
-export const encodeMechanicVectorData = (
- mintManagerAddress: string,
- paymentRecipient: string,
- mechanicMint: OnchainDutchAuctionParams | null,
-): BytesLike => {
- let mechanicVectorData = "0x";
- if (mechanicMint) {
- const dutchAuctionData = encodeDAVectorData(mechanicMint, paymentRecipient);
-
- mechanicVectorData = ethers.utils.defaultAbiCoder.encode(
- ["uint96", "address", "address", "bytes"],
- [mechanicMint.seed, mechanicMint.mechanicAddress, mintManagerAddress, dutchAuctionData],
- );
- }
-
- return mechanicVectorData;
-};
-
-export const encodeDAVectorData = (mechanicMint: OnchainDutchAuctionParams, paymentRecipient: string): BytesLike => {
- const { packedPrices, numPrices, bytesPerPrice } = encodeDutchAuctionPriceData(mechanicMint.prices);
-
- return ethers.utils.defaultAbiCoder.encode(
- ["uint48", "uint48", "uint32", "uint32", "uint48", "uint32", "uint32", "uint8", "address", "bytes"],
- [
- mechanicMint.startTimestamp,
- mechanicMint.endTimestamp,
- mechanicMint.periodDuration,
- mechanicMint.maxUserClaimableViaVector,
- mechanicMint.maxTotalClaimableViaVector,
- mechanicMint.tokenLimitPerTx,
- numPrices,
- bytesPerPrice,
- paymentRecipient,
- packedPrices,
- ],
- );
-};
-
-export const encodeDutchAuctionPriceData = (
- prices: string[],
-): { packedPrices: BytesLike; numPrices: number; bytesPerPrice: number } => {
- if (prices.length == 0) {
- return { packedPrices: "0x", numPrices: 0, bytesPerPrice: 0 };
- }
-
- // expect in ether, expect 10^18, convert to wei
- let biggestPrice = ethers.utils.parseEther(prices[0]);
- for (const price of prices) {
- if (ethers.utils.parseEther(price).gt(biggestPrice)) {
- biggestPrice = ethers.utils.parseEther(price);
- }
- }
-
- const bytesPerPrice = numBytesNeeded(biggestPrice);
- const packedPrices = ethers.utils.solidityPack(
- new Array(prices.length).fill(`uint${bytesPerPrice * 8}`),
- prices.map(price => {
- return ethers.utils.parseEther(price);
- }),
- );
-
- return {
- packedPrices,
- numPrices: prices.length,
- bytesPerPrice,
- };
-};
-
-export const dutchAuctionUpdateArgs = (
- updateValues: DutchAuctionUpdateValues,
-): {
- dutchAuction: DiscreteDutchAuctionMechanic.DutchAuctionVectorStruct;
- updateConfig: DiscreteDutchAuctionMechanic.DutchAuctionVectorUpdateConfigStruct;
- packedPrices: BytesLike;
-} => {
- // if prices isn't updated, this returns 0 values for each field
- const { numPrices, bytesPerPrice, packedPrices } = encodeDutchAuctionPriceData(updateValues.prices ?? []);
-
- const dutchAuction = {
- startTimestamp: updateValues.startTimestamp ?? 0,
- endTimestamp: updateValues.endTimestamp ?? 0,
- periodDuration: updateValues.periodDuration ?? 0,
- maxUserClaimableViaVector: updateValues.maxUserClaimableViaVector ?? 0,
- maxTotalClaimableViaVector: updateValues.maxTotalClaimableViaVector ?? 0,
- currentSupply: 0,
- lowestPriceSoldAtIndex: 0,
- tokenLimitPerTx: updateValues.tokenLimitPerTx ?? 0,
- numPrices,
- paymentRecipient: updateValues.paymentRecipient ?? ethers.constants.AddressZero,
- totalSales: 0,
- bytesPerPrice,
- auctionExhausted: false,
- payeeRevenueHasBeenWithdrawn: false,
- };
- const updateConfig = {
- updateStartTimestamp: updateValues.startTimestamp != undefined,
- updateEndTimestamp: updateValues.endTimestamp != undefined,
- updatePaymentRecipient: updateValues.paymentRecipient != undefined,
- updateMaxTotalClaimableViaVector: updateValues.maxTotalClaimableViaVector != undefined,
- updateTokenLimitPerTx: updateValues.tokenLimitPerTx != undefined,
- updateMaxUserClaimableViaVector: updateValues.maxUserClaimableViaVector != undefined,
- updatePrices: updateValues.prices != undefined,
- updatePeriodDuration: updateValues.periodDuration != undefined,
- };
-
- return {
- dutchAuction,
- updateConfig,
- packedPrices,
- };
-};
-
-const numBytesNeeded = (num: BigNumber) => {
- const log10 = num.toString().length - 1;
- const log2 = log10 / Math.log10(2); // convert log10 to log2 using the base change formula
- return Math.floor(log2 / 8) + 1;
-};
-
-export const produceMechanicVectorId = (
- contractAddress: string,
- mechanicAddress: string,
- seed: number,
- editionId?: number,
-): string => {
- return ethers.utils.solidityKeccak256(
- ["address", "uint96", "address", "bool", "uint96"],
- [contractAddress, editionId ?? 0, mechanicAddress, editionId != undefined, seed],
- );
-};
-
-export const hourFromNow = () => {
- return Math.floor(Date.now() / 1000 + 3600);
-};
diff --git a/test/__utils__/metaTx.ts b/test/__utils__/metaTx.ts
deleted file mode 100644
index 6b72198..0000000
--- a/test/__utils__/metaTx.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-
-import { MinimalForwarder } from "../../types";
-
-type Input2771 = {
- from: string; // signer address
- to: string; // contract being called
- gas: number; // expected gas units for operation
- data: string; // encoded function call on contract with arguments
-};
-
-type ForwardRequest = {
- from: string;
- to: string;
- value: number;
- gas: number;
- nonce: string;
- data: string;
-};
-
-const MINIMAL_FORWARDER_GAS_UNIT_CONSUMPTION = 60000;
-
-const ForwardRequest = [
- { name: "from", type: "address" },
- { name: "to", type: "address" },
- { name: "value", type: "uint256" },
- { name: "gas", type: "uint256" },
- { name: "nonce", type: "uint256" },
- { name: "data", type: "bytes" },
-];
-
-function get2771MetaTxTypeData(chainId: number, verifyingContract: string) {
- return {
- types: {
- // EIP712Domain, do not pass EIP712Domain type into ethers, it will pre-compute for us
- ForwardRequest,
- },
- domain: {
- name: "MinimalForwarder",
- version: "0.0.1",
- chainId,
- verifyingContract,
- },
- primaryType: "ForwardRequest",
- };
-}
-
-async function build2771Request(forwarder: MinimalForwarder, input: Input2771, overrideNonce?: number) {
- const nonce = await forwarder.getNonce(input.from);
- return { value: 0, nonce: overrideNonce == null ? nonce.toString() : overrideNonce.toString(), ...input };
-}
-
-async function build2771TypedData(forwarder: MinimalForwarder, request: ForwardRequest) {
- const chainId = await forwarder.provider.getNetwork().then(n => n.chainId);
- const typeData = get2771MetaTxTypeData(chainId, forwarder.address);
- return { ...typeData, message: request };
-}
-
-export async function sign2771MetaTxRequest(
- signer: SignerWithAddress,
- forwarder: MinimalForwarder,
- input: Input2771,
- overrideNonce?: number,
-) {
- const request = await build2771Request(forwarder, input, overrideNonce);
- const toSign = await build2771TypedData(forwarder, request);
- const signature = await signer._signTypedData(toSign.domain, toSign.types, toSign.message);
- return { signature, request };
-}
diff --git a/test/__utils__/mint.ts b/test/__utils__/mint.ts
deleted file mode 100644
index 2718665..0000000
--- a/test/__utils__/mint.ts
+++ /dev/null
@@ -1,224 +0,0 @@
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { ethers } from "ethers";
-
-import { EIP712 } from "./EIP712";
-
-type ClaimInput = {
- currency: string;
- contractAddress: string;
- claimer: string;
- paymentRecipient: string;
- pricePerToken: string;
- numTokensToMint: number;
- maxClaimableViaVector: number;
- maxClaimablePerUser: number;
- editionId: number;
-};
-
-type SeriesClaimInput = {
- currency: string;
- contractAddress: string;
- claimer: string;
- paymentRecipient: string;
- pricePerToken: string;
- maxPerTxn: number;
- maxClaimableViaVector: number;
- maxClaimablePerUser: number;
-};
-
-type ClaimWithMetaTxPacketInput = {
- currency: string;
- contractAddress: string;
- claimer: string;
- pricePerToken: string;
- numTokensToMint: number;
- purchaseToCreatorPacket: PurchaserMetaTxPacket;
- purchaseToPlatformPacket: PurchaserMetaTxPacket;
- maxClaimableViaVector: number;
- maxClaimablePerUser: number;
- editionId: number;
-};
-
-type PurchaserMetaTxPacket = {
- functionSignature: string;
- sigR: string;
- sigS: string;
- sigV: number;
-};
-
-const MetaTransaction = [
- { name: "nonce", type: "uint256" },
- { name: "from", type: "address" },
- { name: "functionSignature", type: "bytes" },
-];
-
-export type Claim = ClaimInput & {
- claimExpiryTimestamp: string;
- claimNonce: string;
- offchainVectorId: string;
-};
-
-export type ClaimWithMetaTxPacket = ClaimWithMetaTxPacketInput & {
- claimExpiryTimestamp: string;
- claimNonce: string;
- offchainVectorId: string;
-};
-
-export type SeriesClaim = SeriesClaimInput & {
- claimExpiryTimestamp: string;
- claimNonce: string;
- offchainVectorId: string;
-};
-
-const Claim = [
- { name: "currency", type: "address" },
- { name: "contractAddress", type: "address" },
- { name: "claimer", type: "address" },
- { name: "paymentRecipient", type: "address" },
- { name: "pricePerToken", type: "uint256" },
- { name: "numTokensToMint", type: "uint64" },
- { name: "maxClaimableViaVector", type: "uint256" },
- { name: "maxClaimablePerUser", type: "uint256" },
- { name: "editionId", type: "uint256" },
- { name: "claimExpiryTimestamp", type: "uint256" },
- { name: "claimNonce", type: "bytes32" },
- { name: "offchainVectorId", type: "bytes32" },
-];
-
-const SeriesClaim = [
- { name: "currency", type: "address" },
- { name: "contractAddress", type: "address" },
- { name: "claimer", type: "address" },
- { name: "paymentRecipient", type: "address" },
- { name: "pricePerToken", type: "uint256" },
- { name: "maxPerTxn", type: "uint64" },
- { name: "maxClaimableViaVector", type: "uint64" },
- { name: "maxClaimablePerUser", type: "uint64" },
- { name: "claimExpiryTimestamp", type: "uint64" },
- { name: "claimNonce", type: "bytes32" },
- { name: "offchainVectorId", type: "bytes32" },
-];
-
-const ClaimWithMetaTxPacket = [
- { name: "currency", type: "address" },
- { name: "contractAddress", type: "address" },
- { name: "claimer", type: "address" },
- { name: "pricePerToken", type: "uint256" },
- { name: "numTokensToMint", type: "uint64" },
- { name: "purchaseToCreatorPacket", type: "PurchaserMetaTxPacket" },
- { name: "purchaseToPlatformPacket", type: "PurchaserMetaTxPacket" },
- { name: "maxClaimableViaVector", type: "uint256" },
- { name: "maxClaimablePerUser", type: "uint256" },
- { name: "editionId", type: "uint256" },
- { name: "claimExpiryTimestamp", type: "uint256" },
- { name: "claimNonce", type: "bytes32" },
- { name: "offchainVectorId", type: "bytes32" },
-];
-
-const PurchaserMetaTxPacket = [
- { name: "functionSignature", type: "bytes" },
- { name: "sigR", type: "bytes32" },
- { name: "sigS", type: "bytes32" },
- { name: "sigV", type: "uint8" },
-];
-
-function getWETHMetaTxTypeData(salt: string, verifyingContract: string) {
- return {
- types: {
- // EIP712Domain, do not pass EIP712Domain type into ethers, it will pre-compute for us
- MetaTransaction,
- },
- domain: {
- name: "Wrapped Ether",
- version: "1",
- verifyingContract,
- salt,
- },
- primaryType: "MetaTransaction",
- };
-}
-
-function buildClaim(
- input: ClaimInput | SeriesClaimInput | ClaimWithMetaTxPacketInput,
- offchainVectorId: string,
- claimNonce: string,
- claimExpiryTimestamp?: string,
-): Claim | SeriesClaim | ClaimWithMetaTxPacket {
- offchainVectorId = ethers.utils.formatBytes32String(offchainVectorId);
- claimNonce = ethers.utils.formatBytes32String(claimNonce);
-
- return {
- ...input,
- claimExpiryTimestamp: claimExpiryTimestamp ?? (Math.floor(Date.now() / 1000) + 3600).toString(),
- claimNonce,
- offchainVectorId,
- }; // give users 1 hr to use
-}
-
-async function buildWETHTypedData(metaTx: any, contract: ethers.Contract) {
- const chainIdBytes = ethers.utils.hexZeroPad(
- ethers.utils.hexlify(await contract.provider.getNetwork().then(n => n.chainId)),
- 32,
- );
- const typeData = getWETHMetaTxTypeData(chainIdBytes, contract.address);
- return { ...typeData, message: metaTx };
-}
-
-export async function signGatedMint(
- signer: SignerWithAddress,
- contract: ethers.Contract,
- value: ClaimInput,
- offchainVectorId: string,
- claimNonce: string,
- claimExpiryTimestamp?: string,
-) {
- const chainId = await contract.provider.getNetwork().then(n => n.chainId);
- const claim = buildClaim(value, offchainVectorId, claimNonce, claimExpiryTimestamp) as Claim;
- const eip712 = new EIP712(contract, signer, claim, { Claim });
- const signature = await eip712.sign(EIP712.buildDomain("MintManager", "1.0.0", contract.address, chainId));
- return { signature, claim };
-}
-
-export async function signGatedSeriesMint(
- signer: SignerWithAddress,
- contract: ethers.Contract,
- value: SeriesClaimInput,
- offchainVectorId: string,
- claimNonce: string,
- claimExpiryTimestamp?: string,
-) {
- const chainId = await contract.provider.getNetwork().then(n => n.chainId);
- const claim = buildClaim(value, offchainVectorId, claimNonce, claimExpiryTimestamp) as SeriesClaim;
- const eip712 = new EIP712(contract, signer, claim, { SeriesClaim });
- const signature = await eip712.sign(EIP712.buildDomain("MintManager", "1.0.0", contract.address, chainId));
- return { signature, claim };
-}
-
-export async function signGatedMintWithMetaTxPacket(
- signer: SignerWithAddress,
- contract: ethers.Contract,
- value: ClaimWithMetaTxPacketInput,
- offchainVectorId: string,
- claimNonce: string,
- claimExpiryTimestamp?: string,
-) {
- const chainId = await contract.provider.getNetwork().then(n => n.chainId);
- const claim = buildClaim(value, offchainVectorId, claimNonce, claimExpiryTimestamp) as ClaimWithMetaTxPacket;
- const eip712 = new EIP712(contract, signer, claim, { PurchaserMetaTxPacket, ClaimWithMetaTxPacket });
- const signature = await eip712.sign(EIP712.buildDomain("MintManager", "1.0.0", contract.address, chainId));
- return { signature, claim };
-}
-
-export async function signWETHMetaTxRequest(
- signer: SignerWithAddress,
- contract: ethers.Contract,
- value: Record,
-) {
- const toSign = await buildWETHTypedData(value, contract);
- const signature = await signer._signTypedData(toSign.domain, toSign.types, toSign.message);
- const { r, s, v } = ethers.utils.splitSignature(signature);
- return { functionSignature: value.functionSignature, sigR: r, sigS: s, sigV: v };
-}
-
-export const getValidClaimTimestamp = () => (Math.floor(Date.now() / 1000) + 360000).toString();
-export const getExpiredClaimTimestamp = () => (Math.floor(Date.now() / 1000) - 360000).toString();
diff --git a/yarn.lock b/yarn.lock
index 2e0363a..285ea39 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7,6 +7,11 @@
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
+"@adraffy/ens-normalize@1.10.1":
+ version "1.10.1"
+ resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069"
+ integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==
+
"@ampproject/remapping@^2.1.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
@@ -174,6 +179,13 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.11.tgz#becf8ee33aad2a35ed5607f521fe6e72a615f905"
integrity sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==
+"@babel/runtime@^7.4.4":
+ version "7.24.4"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd"
+ integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==
+ dependencies:
+ regenerator-runtime "^0.14.0"
+
"@babel/template@^7.16.7", "@babel/template@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
@@ -232,6 +244,16 @@
"@babel/helper-validator-identifier" "^7.22.5"
to-fast-properties "^2.0.0"
+"@chainlink/contracts@^0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@chainlink/contracts/-/contracts-0.8.0.tgz#4050c83c8b1603ffb0fd6ab99f1d9ea9db2c37de"
+ integrity sha512-nUv1Uxw5Mn92wgLs2bgPYmo8hpdQ3s9jB/lcbdU0LmNOVu0hbfmouVnqwRLa28Ll50q6GczUA+eO0ikNIKLZsA==
+ dependencies:
+ "@eth-optimism/contracts" "^0.5.21"
+ "@openzeppelin/contracts" "~4.3.3"
+ "@openzeppelin/contracts-upgradeable-4.7.3" "npm:@openzeppelin/contracts-upgradeable@v4.7.3"
+ "@openzeppelin/contracts-v0.7" "npm:@openzeppelin/contracts@v3.4.2"
+
"@chainsafe/as-sha256@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9"
@@ -441,6 +463,49 @@
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
+"@ensdomains/address-encoder@^0.1.7":
+ version "0.1.9"
+ resolved "https://registry.yarnpkg.com/@ensdomains/address-encoder/-/address-encoder-0.1.9.tgz#f948c485443d9ef7ed2c0c4790e931c33334d02d"
+ integrity sha512-E2d2gP4uxJQnDu2Kfg1tHNspefzbLT8Tyjrm5sEuim32UkU2sm5xL4VXtgc2X33fmPEw9+jUMpGs4veMbf+PYg==
+ dependencies:
+ bech32 "^1.1.3"
+ blakejs "^1.1.0"
+ bn.js "^4.11.8"
+ bs58 "^4.0.1"
+ crypto-addr-codec "^0.1.7"
+ nano-base32 "^1.0.1"
+ ripemd160 "^2.0.2"
+
+"@ensdomains/ens@0.4.5":
+ version "0.4.5"
+ resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.5.tgz#e0aebc005afdc066447c6e22feb4eda89a5edbfc"
+ integrity sha512-JSvpj1iNMFjK6K+uVl4unqMoa9rf5jopb8cya5UGBWz23Nw8hSNT7efgUx4BTlAPAgpNlEioUfeTyQ6J9ZvTVw==
+ dependencies:
+ bluebird "^3.5.2"
+ eth-ens-namehash "^2.0.8"
+ solc "^0.4.20"
+ testrpc "0.0.1"
+ web3-utils "^1.0.0-beta.31"
+
+"@ensdomains/ensjs@^2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@ensdomains/ensjs/-/ensjs-2.1.0.tgz#0a7296c1f3d735ef019320d863a7846a0760c460"
+ integrity sha512-GRbGPT8Z/OJMDuxs75U/jUNEC0tbL0aj7/L/QQznGYKm/tiasp+ndLOaoULy9kKJFC0TBByqfFliEHDgoLhyog==
+ dependencies:
+ "@babel/runtime" "^7.4.4"
+ "@ensdomains/address-encoder" "^0.1.7"
+ "@ensdomains/ens" "0.4.5"
+ "@ensdomains/resolver" "0.2.4"
+ content-hash "^2.5.2"
+ eth-ens-namehash "^2.0.8"
+ ethers "^5.0.13"
+ js-sha3 "^0.8.0"
+
+"@ensdomains/resolver@0.2.4":
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89"
+ integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA==
+
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -473,6 +538,37 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.48.0.tgz#642633964e217905436033a2bd08bf322849b7fb"
integrity sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==
+"@eth-optimism/contracts@^0.5.21":
+ version "0.5.40"
+ resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.5.40.tgz#d13a04a15ea947a69055e6fc74d87e215d4c936a"
+ integrity sha512-MrzV0nvsymfO/fursTB7m/KunkPsCndltVgfdHaT1Aj5Vi6R/doKIGGkOofHX+8B6VMZpuZosKCMQ5lQuqjt8w==
+ dependencies:
+ "@eth-optimism/core-utils" "0.12.0"
+ "@ethersproject/abstract-provider" "^5.7.0"
+ "@ethersproject/abstract-signer" "^5.7.0"
+
+"@eth-optimism/core-utils@0.12.0":
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.12.0.tgz#6337e4599a34de23f8eceb20378de2a2de82b0ea"
+ integrity sha512-qW+7LZYCz7i8dRa7SRlUKIo1VBU8lvN0HeXCxJR+z+xtMzMQpPds20XJNCMclszxYQHkXY00fOT6GvFw9ZL6nw==
+ dependencies:
+ "@ethersproject/abi" "^5.7.0"
+ "@ethersproject/abstract-provider" "^5.7.0"
+ "@ethersproject/address" "^5.7.0"
+ "@ethersproject/bignumber" "^5.7.0"
+ "@ethersproject/bytes" "^5.7.0"
+ "@ethersproject/constants" "^5.7.0"
+ "@ethersproject/contracts" "^5.7.0"
+ "@ethersproject/hash" "^5.7.0"
+ "@ethersproject/keccak256" "^5.7.0"
+ "@ethersproject/properties" "^5.7.0"
+ "@ethersproject/providers" "^5.7.0"
+ "@ethersproject/rlp" "^5.7.0"
+ "@ethersproject/transactions" "^5.7.0"
+ "@ethersproject/web" "^5.7.0"
+ bufio "^1.0.7"
+ chai "^4.3.4"
+
"@ethereumjs/common@2.5.0":
version "2.5.0"
resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.5.0.tgz#ec61551b31bef7a69d1dc634d8932468866a4268"
@@ -481,7 +577,7 @@
crc-32 "^1.2.0"
ethereumjs-util "^7.1.1"
-"@ethereumjs/common@^2.5.0", "@ethereumjs/common@^2.6.4":
+"@ethereumjs/common@2.6.5", "@ethereumjs/common@^2.5.0", "@ethereumjs/common@^2.6.4":
version "2.6.5"
resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30"
integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA==
@@ -502,7 +598,7 @@
"@ethereumjs/common" "^2.5.0"
ethereumjs-util "^7.1.2"
-"@ethereumjs/tx@^3.3.2":
+"@ethereumjs/tx@3.5.2", "@ethereumjs/tx@^3.3.2":
version "3.5.2"
resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.2.tgz#197b9b6299582ad84f9527ca961466fce2296c1c"
integrity sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw==
@@ -607,7 +703,7 @@
dependencies:
"@ethersproject/bignumber" "^5.7.0"
-"@ethersproject/contracts@5.7.0":
+"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e"
integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==
@@ -710,7 +806,7 @@
dependencies:
"@ethersproject/logger" "^5.7.0"
-"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.6.8", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2":
+"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.0.10", "@ethersproject/providers@^5.6.8", "@ethersproject/providers@^5.7.0", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2":
version "5.7.2"
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb"
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
@@ -920,6 +1016,25 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
+"@manifoldxyz/creator-core-solidity@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@manifoldxyz/creator-core-solidity/-/creator-core-solidity-3.0.0.tgz#c7898e3e91d30a32067a505659be5491a2865ac4"
+ integrity sha512-bVKxdOJ6sLF2u20mxe1ltwKBBf+aVxyFzZEnFobxuBI04cly1SQ88KxrSJB/DuFKzCeimAfA5qyCY7NZoNhUCQ==
+ dependencies:
+ "@manifoldxyz/libraries-solidity" "^1.0.4"
+ "@openzeppelin/contracts" "^4.8.1"
+ "@openzeppelin/contracts-upgradeable" "^4.8.1"
+ "@openzeppelin/test-helpers" "^0.5.16"
+ truffle-contract-size "^2.0.1"
+
+"@manifoldxyz/libraries-solidity@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@manifoldxyz/libraries-solidity/-/libraries-solidity-1.0.4.tgz#d2b7d7ead11e8d85a27b0f59a8ff0e7b32479866"
+ integrity sha512-ibU4TgJ6DBp27j/P4KHKb3Sd1kILp1Q7awhU3To3RlL7ytK3G59B8C5YCUIH4sVzUFghs94FWZYmHvWjzW6f5Q==
+ dependencies:
+ "@openzeppelin/contracts" "4.7.3"
+ "@openzeppelin/contracts-upgradeable" "4.7.3"
+
"@metamask/eth-sig-util@^4.0.0":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088"
@@ -931,6 +1046,13 @@
tweetnacl "^1.0.3"
tweetnacl-util "^0.15.1"
+"@mongodb-js/saslprep@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz#022fa36620a7287d17acd05c4aae1e5f390d250d"
+ integrity sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==
+ dependencies:
+ sparse-bitfield "^3.0.3"
+
"@noble/curves@1.1.0", "@noble/curves@~1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d"
@@ -938,6 +1060,13 @@
dependencies:
"@noble/hashes" "1.3.1"
+"@noble/curves@1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35"
+ integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==
+ dependencies:
+ "@noble/hashes" "1.3.2"
+
"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12"
@@ -948,7 +1077,7 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9"
integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==
-"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1":
+"@noble/hashes@1.3.2", "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==
@@ -1124,6 +1253,14 @@
deep-eql "^4.0.1"
ordinal "^1.0.3"
+"@nomicfoundation/hardhat-ethers@^3.0.6":
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.6.tgz#e8ba7f9719de360c03501b85dae4999bb3a7e1c5"
+ integrity sha512-/xzkFQAaHQhmIAYOQmvHBPwL+NkwLzT9gRZBsgWUYeV+E6pzXsBQsHfRYbAZ3XEYare+T7S+5Tg/1KDJgepSkA==
+ dependencies:
+ debug "^4.1.1"
+ lodash.isequal "^4.5.0"
+
"@nomicfoundation/hardhat-network-helpers@^1.0.9":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.9.tgz#767449e8a2acda79306ac84626117583d95d25aa"
@@ -1223,16 +1360,71 @@
table "^6.8.0"
undici "^5.14.0"
+"@openzeppelin/contract-loader@^0.6.2":
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contract-loader/-/contract-loader-0.6.3.tgz#61a7b44de327e40b7d53f39e0fb59bbf847335c3"
+ integrity sha512-cOFIjBjwbGgZhDZsitNgJl0Ye1rd5yu/Yx5LMgeq3u0ZYzldm4uObzHDFq4gjDdoypvyORjjJa3BlFA7eAnVIg==
+ dependencies:
+ find-up "^4.1.0"
+ fs-extra "^8.1.0"
+
+"@openzeppelin/contracts-upgradeable-4.7.3@npm:@openzeppelin/contracts-upgradeable@v4.7.3", "@openzeppelin/contracts-upgradeable@4.7.3":
+ name "@openzeppelin/contracts-upgradeable-4.7.3"
+ version "4.7.3"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.7.3.tgz#f1d606e2827d409053f3e908ba4eb8adb1dd6995"
+ integrity sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A==
+
"@openzeppelin/contracts-upgradeable@^4.7.3":
version "4.9.3"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.3.tgz#ff17a80fb945f5102571f8efecb5ce5915cc4811"
integrity sha512-jjaHAVRMrE4UuZNfDwjlLGDxTHWIOwTJS2ldnc278a0gevfXfPr8hxKEVBGFBE96kl2G3VHDZhUimw/+G3TG2A==
+"@openzeppelin/contracts-upgradeable@^4.8.1":
+ version "4.9.6"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.6.tgz#38b21708a719da647de4bb0e4802ee235a0d24df"
+ integrity sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA==
+
+"@openzeppelin/contracts-v0.7@npm:@openzeppelin/contracts@v3.4.2":
+ version "3.4.2"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527"
+ integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA==
+
+"@openzeppelin/contracts@4.7.3":
+ version "4.7.3"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.3.tgz#939534757a81f8d69cc854c7692805684ff3111e"
+ integrity sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw==
+
"@openzeppelin/contracts@^4.7.2":
version "4.9.3"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.3.tgz#00d7a8cf35a475b160b3f0293a6403c511099364"
integrity sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg==
+"@openzeppelin/contracts@^4.8.1":
+ version "4.9.6"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677"
+ integrity sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==
+
+"@openzeppelin/contracts@~4.3.3":
+ version "4.3.3"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.3.tgz#ff6ee919fc2a1abaf72b22814bfb72ed129ec137"
+ integrity sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g==
+
+"@openzeppelin/test-helpers@^0.5.16":
+ version "0.5.16"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/test-helpers/-/test-helpers-0.5.16.tgz#2c9054f85069dfbfb5e8cef3ed781e8caf241fb3"
+ integrity sha512-T1EvspSfH1qQO/sgGlskLfYVBbqzJR23SZzYl/6B2JnT4EhThcI85UpvDk0BkLWKaDScQTabGHt4GzHW+3SfZg==
+ dependencies:
+ "@openzeppelin/contract-loader" "^0.6.2"
+ "@truffle/contract" "^4.0.35"
+ ansi-colors "^3.2.3"
+ chai "^4.2.0"
+ chai-bn "^0.2.1"
+ ethjs-abi "^0.2.1"
+ lodash.flatten "^4.4.0"
+ semver "^5.6.0"
+ web3 "^1.2.5"
+ web3-utils "^1.2.5"
+
"@scure/base@~1.1.0":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938"
@@ -1399,11 +1591,94 @@
javascript-natural-sort "0.7.1"
lodash "4.17.21"
+"@truffle/abi-utils@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@truffle/abi-utils/-/abi-utils-1.0.3.tgz#9f0df7a8aaf5e815bee47e0ad26bd4c91e4045f2"
+ integrity sha512-AWhs01HCShaVKjml7Z4AbVREr/u4oiWxCcoR7Cktm0mEvtT04pvnxW5xB/cI4znRkrbPdFQlFt67kgrAjesYkw==
+ dependencies:
+ change-case "3.0.2"
+ fast-check "3.1.1"
+ web3-utils "1.10.0"
+
+"@truffle/blockchain-utils@^0.1.9":
+ version "0.1.9"
+ resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.1.9.tgz#d9b55bd23a134578e4217bae55a6dfbbb038d6dc"
+ integrity sha512-RHfumgbIVo68Rv9ofDYfynjnYZIfP/f1vZy4RoqkfYAO+fqfc58PDRzB1WAGq2U6GPuOnipOJxQhnqNnffORZg==
+
+"@truffle/codec@^0.17.3":
+ version "0.17.3"
+ resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.17.3.tgz#94057e56e1a947594b35eba498d96915df3861d2"
+ integrity sha512-Ko/+dsnntNyrJa57jUD9u4qx9nQby+H4GsUO6yjiCPSX0TQnEHK08XWqBSg0WdmCH2+h0y1nr2CXSx8gbZapxg==
+ dependencies:
+ "@truffle/abi-utils" "^1.0.3"
+ "@truffle/compile-common" "^0.9.8"
+ big.js "^6.0.3"
+ bn.js "^5.1.3"
+ cbor "^5.2.0"
+ debug "^4.3.1"
+ lodash "^4.17.21"
+ semver "^7.5.4"
+ utf8 "^3.0.0"
+ web3-utils "1.10.0"
+
+"@truffle/compile-common@^0.9.8":
+ version "0.9.8"
+ resolved "https://registry.yarnpkg.com/@truffle/compile-common/-/compile-common-0.9.8.tgz#f91507c895852289a17bf401eefebc293c4c69f0"
+ integrity sha512-DTpiyo32t/YhLI1spn84D3MHYHrnoVqO+Gp7ZHrYNwDs86mAxtNiH5lsVzSb8cPgiqlvNsRCU9nm9R0YmKMTBQ==
+ dependencies:
+ "@truffle/error" "^0.2.2"
+ colors "1.4.0"
+
+"@truffle/contract-schema@^3.4.16":
+ version "3.4.16"
+ resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.4.16.tgz#c529c3f230db407b2f03290373b20b7366f2d37e"
+ integrity sha512-g0WNYR/J327DqtJPI70ubS19K1Fth/1wxt2jFqLsPmz5cGZVjCwuhiie+LfBde4/Mc9QR8G+L3wtmT5cyoBxAg==
+ dependencies:
+ ajv "^6.10.0"
+ debug "^4.3.1"
+
+"@truffle/contract@^4.0.35":
+ version "4.6.31"
+ resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.6.31.tgz#75cb059689ce73b365675d9650718908c01b6b58"
+ integrity sha512-s+oHDpXASnZosiCdzu+X1Tx5mUJUs1L1CYXIcgRmzMghzqJkaUFmR6NpNo7nJYliYbO+O9/aW8oCKqQ7rCHfmQ==
+ dependencies:
+ "@ensdomains/ensjs" "^2.1.0"
+ "@truffle/blockchain-utils" "^0.1.9"
+ "@truffle/contract-schema" "^3.4.16"
+ "@truffle/debug-utils" "^6.0.57"
+ "@truffle/error" "^0.2.2"
+ "@truffle/interface-adapter" "^0.5.37"
+ bignumber.js "^7.2.1"
+ debug "^4.3.1"
+ ethers "^4.0.32"
+ web3 "1.10.0"
+ web3-core-helpers "1.10.0"
+ web3-core-promievent "1.10.0"
+ web3-eth-abi "1.10.0"
+ web3-utils "1.10.0"
+
+"@truffle/debug-utils@^6.0.57":
+ version "6.0.57"
+ resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-6.0.57.tgz#4e9a1051221c5f467daa398b0ca638d8b6408a82"
+ integrity sha512-Q6oI7zLaeNLB69ixjwZk2UZEWBY6b2OD1sjLMGDKBGR7GaHYiw96GLR2PFgPH1uwEeLmV4N78LYaQCrDsHbNeA==
+ dependencies:
+ "@truffle/codec" "^0.17.3"
+ "@trufflesuite/chromafi" "^3.0.0"
+ bn.js "^5.1.3"
+ chalk "^2.4.2"
+ debug "^4.3.1"
+ highlightjs-solidity "^2.0.6"
+
"@truffle/error@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.1.1.tgz#e52026ac8ca7180d83443dca73c03e07ace2a301"
integrity sha512-sE7c9IHIGdbK4YayH4BC8i8qMjoAOeg6nUXUDZZp8wlU21/EMpaG+CLx+KqcIPyR+GSWIW3Dm0PXkr2nlggFDA==
+"@truffle/error@^0.2.2":
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.2.2.tgz#1b4c4237c14dda792f20bd4f19ff4e4585b47796"
+ integrity sha512-TqbzJ0O8DHh34cu8gDujnYl4dUl6o2DE4PR6iokbybvnIm/L2xl6+Gv1VC+YJS45xfH83Yo3/Zyg/9Oq8/xZWg==
+
"@truffle/interface-adapter@^0.5.25":
version "0.5.35"
resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.35.tgz#f0eb1c4a2803190ca249143f545029a8b641fe96"
@@ -1413,6 +1688,15 @@
ethers "^4.0.32"
web3 "1.10.0"
+"@truffle/interface-adapter@^0.5.37":
+ version "0.5.37"
+ resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.37.tgz#95d249c1912d2baaa63c54e8a138d3f476a1181a"
+ integrity sha512-lPH9MDgU+7sNDlJSClwyOwPCfuOimqsCx0HfGkznL3mcFRymc1pukAR1k17zn7ErHqBwJjiKAZ6Ri72KkS+IWw==
+ dependencies:
+ bn.js "^5.1.3"
+ ethers "^4.0.32"
+ web3 "1.10.0"
+
"@truffle/provider@^0.2.24":
version "0.2.64"
resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.64.tgz#7dd55117307fd019dcf81d08db5dc2bc5728f51c"
@@ -1423,6 +1707,20 @@
debug "^4.3.1"
web3 "1.7.4"
+"@trufflesuite/chromafi@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-3.0.0.tgz#f6956408c1af6a38a6ed1657783ce59504a1eb8b"
+ integrity sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==
+ dependencies:
+ camelcase "^4.1.0"
+ chalk "^2.3.2"
+ cheerio "^1.0.0-rc.2"
+ detect-indent "^5.0.0"
+ highlight.js "^10.4.1"
+ lodash.merge "^4.6.2"
+ strip-ansi "^4.0.0"
+ strip-indent "^2.0.0"
+
"@tsconfig/node10@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
@@ -1451,6 +1749,14 @@
lodash "^4.17.15"
ts-essentials "^7.0.1"
+"@typechain/ethers-v6@^0.5.1":
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz#42fe214a19a8b687086c93189b301e2b878797ea"
+ integrity sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==
+ dependencies:
+ lodash "^4.17.15"
+ ts-essentials "^7.0.1"
+
"@typechain/hardhat@^6.1.2":
version "6.1.6"
resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-6.1.6.tgz#1a749eb35e5054c80df531cf440819cb347c62ea"
@@ -1572,6 +1878,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.6.tgz#5e9aaa86be03a09decafd61b128d6cec64a5fe40"
integrity sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==
+"@types/node@18.15.13":
+ version "18.15.13"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469"
+ integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==
+
"@types/node@20.4.7":
version "20.4.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.7.tgz#74d323a93f1391a63477b27b9aec56669c98b2ab"
@@ -1653,6 +1964,19 @@
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a"
integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==
+"@types/webidl-conversions@*":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz#2b8e60e33906459219aa587e9d1a612ae994cfe7"
+ integrity sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==
+
+"@types/whatwg-url@^8.2.1":
+ version "8.2.2"
+ resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63"
+ integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==
+ dependencies:
+ "@types/node" "*"
+ "@types/webidl-conversions" "*"
+
"@typescript-eslint/eslint-plugin@^5.30.7":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db"
@@ -1737,6 +2061,11 @@
"@typescript-eslint/types" "5.62.0"
eslint-visitor-keys "^3.3.0"
+"@uniswap/v3-core@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.1.tgz#b6d2bdc6ba3c3fbd610bdc502395d86cd35264a0"
+ integrity sha512-7pVk4hEm00j9tc71Y9+ssYpO6ytkeI0y7WE9P6UcmNzhxPePwyAxImuhVsTqWK9YFvzgtvzJHi64pBl4jUzKMQ==
+
"@vue/compiler-core@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.4.tgz#7fbf591c1c19e1acd28ffd284526e98b4f581128"
@@ -1813,7 +2142,7 @@ abbrev@1.0.x:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==
-abortcontroller-polyfill@^1.7.3:
+abortcontroller-polyfill@^1.7.3, abortcontroller-polyfill@^1.7.5:
version "1.7.5"
resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed"
integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==
@@ -1869,6 +2198,11 @@ aes-js@3.0.0:
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d"
integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==
+aes-js@4.0.0-beta.5:
+ version "4.0.0-beta.5"
+ resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873"
+ integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==
+
agent-base@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -1884,7 +2218,7 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"
-ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.6:
+ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.6:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -1919,6 +2253,11 @@ ansi-colors@4.1.1:
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
+ansi-colors@^3.2.3:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
+ integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
+
ansi-colors@^4.1.1:
version "4.1.3"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
@@ -1938,6 +2277,11 @@ ansi-escapes@^5.0.0:
dependencies:
type-fest "^1.0.2"
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+ integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==
+
ansi-regex@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1"
@@ -2188,16 +2532,31 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
-bech32@1.1.4:
+bech32@1.1.4, bech32@^1.1.3:
version "1.1.4"
resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9"
integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==
+big-integer@1.6.36:
+ version "1.6.36"
+ resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.36.tgz#78631076265d4ae3555c04f85e7d9d2f3a071a36"
+ integrity sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==
+
+big.js@^6.0.3:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f"
+ integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==
+
bigint-crypto-utils@^3.0.23:
version "3.3.0"
resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.3.0.tgz#72ad00ae91062cf07f2b1def9594006c279c1d77"
integrity sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg==
+bignumber.js@^7.2.1:
+ version "7.2.1"
+ resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f"
+ integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==
+
bignumber.js@^9.0.0, bignumber.js@^9.0.1:
version "9.1.1"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6"
@@ -2222,7 +2581,7 @@ blakejs@^1.1.0:
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814"
integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==
-bluebird@^3.5.0, bluebird@^3.7.2:
+bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@@ -2278,6 +2637,11 @@ body-parser@^1.16.0:
type-is "~1.6.18"
unpipe "1.0.0"
+boolbase@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+ integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -2384,7 +2748,7 @@ browserslist@^4.21.9:
node-releases "^2.0.13"
update-browserslist-db "^1.0.11"
-bs58@^4.0.0:
+bs58@^4.0.0, bs58@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==
@@ -2400,6 +2764,11 @@ bs58check@^2.1.2:
create-hash "^1.1.0"
safe-buffer "^5.1.2"
+bson@^5.4.0:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/bson/-/bson-5.4.0.tgz#0eea77276d490953ad8616b483298dbff07384c6"
+ integrity sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA==
+
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
@@ -2420,6 +2789,14 @@ buffer-xor@^1.0.3:
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==
+buffer@6.0.3, buffer@^6.0.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
+ integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.2.1"
+
buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@@ -2428,14 +2805,6 @@ buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0:
base64-js "^1.3.1"
ieee754 "^1.1.13"
-buffer@^6.0.3:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
- integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
- dependencies:
- base64-js "^1.3.1"
- ieee754 "^1.2.1"
-
bufferutil@^4.0.1:
version "4.0.7"
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad"
@@ -2443,6 +2812,11 @@ bufferutil@^4.0.1:
dependencies:
node-gyp-build "^4.3.0"
+bufio@^1.0.7:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.2.1.tgz#8d4ab3ddfcd5faa90f996f922f9397d41cbaf2de"
+ integrity sha512-9oR3zNdupcg/Ge2sSHQF3GX+kmvL/fTPvD0nd5AGLq8SjUYnTz+SlFjK/GXidndbZtIj+pVKXiWeR9w6e9wKCA==
+
busboy@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
@@ -2509,6 +2883,14 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+camel-case@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
+ integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==
+ dependencies:
+ no-case "^2.2.0"
+ upper-case "^1.1.1"
+
camelcase-keys@^6.2.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0"
@@ -2518,6 +2900,16 @@ camelcase-keys@^6.2.2:
map-obj "^4.0.0"
quick-lru "^4.0.1"
+camelcase@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
+ integrity sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==
+
+camelcase@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
+ integrity sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==
+
camelcase@^5.0.0, camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
@@ -2548,6 +2940,14 @@ catering@^2.1.0, catering@^2.1.1:
resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510"
integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==
+cbor@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c"
+ integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==
+ dependencies:
+ bignumber.js "^9.0.1"
+ nofilter "^1.0.4"
+
cbor@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5"
@@ -2562,6 +2962,37 @@ chai-as-promised@^7.1.1:
dependencies:
check-error "^1.0.2"
+chai-bn@^0.2.1:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/chai-bn/-/chai-bn-0.2.2.tgz#4dcf30dbc79db2378a00781693bc749c972bf34f"
+ integrity sha512-MzjelH0p8vWn65QKmEq/DLBG1Hle4WeyqT79ANhXZhn/UxRWO0OogkAxi5oGGtfzwU9bZR8mvbvYdoqNVWQwFg==
+
+chai@^4.2.0:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1"
+ integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==
+ dependencies:
+ assertion-error "^1.1.0"
+ check-error "^1.0.3"
+ deep-eql "^4.1.3"
+ get-func-name "^2.0.2"
+ loupe "^2.3.6"
+ pathval "^1.1.1"
+ type-detect "^4.0.8"
+
+chai@^4.3.4:
+ version "4.3.10"
+ resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384"
+ integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==
+ dependencies:
+ assertion-error "^1.1.0"
+ check-error "^1.0.3"
+ deep-eql "^4.1.3"
+ get-func-name "^2.0.2"
+ loupe "^2.3.6"
+ pathval "^1.1.1"
+ type-detect "^4.0.8"
+
chai@^4.3.6:
version "4.3.8"
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.8.tgz#40c59718ad6928da6629c70496fe990b2bb5b17c"
@@ -2580,7 +3011,7 @@ chalk@5.3.0:
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==
-chalk@^2.4.1, chalk@^2.4.2:
+chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -2597,6 +3028,30 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
+change-case@3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.0.2.tgz#fd48746cce02f03f0a672577d1d3a8dc2eceb037"
+ integrity sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA==
+ dependencies:
+ camel-case "^3.0.0"
+ constant-case "^2.0.0"
+ dot-case "^2.1.0"
+ header-case "^1.0.0"
+ is-lower-case "^1.1.0"
+ is-upper-case "^1.1.0"
+ lower-case "^1.1.1"
+ lower-case-first "^1.0.0"
+ no-case "^2.3.2"
+ param-case "^2.1.0"
+ pascal-case "^2.0.0"
+ path-case "^2.1.0"
+ sentence-case "^2.1.0"
+ snake-case "^2.1.0"
+ swap-case "^1.1.0"
+ title-case "^2.1.0"
+ upper-case "^1.1.1"
+ upper-case-first "^1.1.0"
+
chardet@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -2612,6 +3067,38 @@ check-error@^1.0.2:
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==
+check-error@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694"
+ integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==
+ dependencies:
+ get-func-name "^2.0.2"
+
+cheerio-select@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4"
+ integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==
+ dependencies:
+ boolbase "^1.0.0"
+ css-select "^5.1.0"
+ css-what "^6.1.0"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+ domutils "^3.0.1"
+
+cheerio@^1.0.0-rc.2:
+ version "1.0.0-rc.12"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683"
+ integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==
+ dependencies:
+ cheerio-select "^2.1.0"
+ dom-serializer "^2.0.0"
+ domhandler "^5.0.3"
+ domutils "^3.0.1"
+ htmlparser2 "^8.0.1"
+ parse5 "^7.0.0"
+ parse5-htmlparser2-tree-adapter "^7.0.0"
+
chokidar@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6"
@@ -2741,6 +3228,13 @@ cli-table3@^0.6.0:
optionalDependencies:
"@colors/colors" "1.5.0"
+cli-table@^0.3.1:
+ version "0.3.11"
+ resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.11.tgz#ac69cdecbe81dccdba4889b9a18b7da312a9d3ee"
+ integrity sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==
+ dependencies:
+ colors "1.0.3"
+
cli-truncate@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389"
@@ -2754,6 +3248,15 @@ cli-width@^3.0.0:
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
+cliui@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+ integrity sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wrap-ansi "^2.0.0"
+
cliui@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
@@ -2763,6 +3266,15 @@ cliui@^5.0.0:
strip-ansi "^5.2.0"
wrap-ansi "^5.1.0"
+cliui@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
+ integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^6.2.0"
+
cliui@^7.0.2:
version "7.0.4"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
@@ -2793,6 +3305,11 @@ clone@^1.0.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+ integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==
+
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@@ -2822,6 +3339,11 @@ colorette@^2.0.20:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
+colors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
+ integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==
+
colors@1.4.0, colors@^1.1.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
@@ -2929,6 +3451,14 @@ console-table-printer@^2.9.0:
dependencies:
simple-wcswidth "^1.0.1"
+constant-case@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46"
+ integrity sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==
+ dependencies:
+ snake-case "^2.1.0"
+ upper-case "^1.1.1"
+
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
@@ -3092,6 +3622,13 @@ cross-fetch@^3.1.4:
dependencies:
node-fetch "^2.6.12"
+cross-fetch@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983"
+ integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==
+ dependencies:
+ node-fetch "^2.6.12"
+
cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -3106,6 +3643,19 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
+crypto-addr-codec@^0.1.7:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/crypto-addr-codec/-/crypto-addr-codec-0.1.8.tgz#45c4b24e2ebce8e24a54536ee0ca25b65787b016"
+ integrity sha512-GqAK90iLLgP3FvhNmHbpT3wR6dEdaM8hZyZtLX29SPardh3OA13RFLHDR6sntGCgRWOfiHqW6sIyohpNqOtV/g==
+ dependencies:
+ base-x "^3.0.8"
+ big-integer "1.6.36"
+ blakejs "^1.1.0"
+ bs58 "^4.0.1"
+ ripemd160-min "0.0.6"
+ safe-buffer "^5.2.0"
+ sha3 "^2.1.1"
+
crypto-browserify@3.12.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -3128,6 +3678,22 @@ crypto-js@^3.1.9-1:
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
+css-select@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
+ integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
+ dependencies:
+ boolbase "^1.0.0"
+ css-what "^6.1.0"
+ domhandler "^5.0.2"
+ domutils "^3.0.1"
+ nth-check "^2.0.1"
+
+css-what@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+ integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
csv-parse@^5.5.1:
version "5.5.1"
resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-5.5.1.tgz#ed08dc538c1b009c77428087470356830e6bbb41"
@@ -3201,7 +3767,7 @@ decamelize-keys@^1.1.0:
decamelize "^1.1.0"
map-obj "^1.0.0"
-decamelize@^1.1.0, decamelize@^1.2.0:
+decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
@@ -3235,7 +3801,7 @@ dedent@0.7.0:
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==
-deep-eql@^4.0.1, deep-eql@^4.1.2:
+deep-eql@^4.0.1, deep-eql@^4.1.2, deep-eql@^4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==
@@ -3310,6 +3876,11 @@ detect-indent@6.1.0:
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"
integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==
+detect-indent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
+ integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==
+
detect-port@^1.3.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b"
@@ -3356,11 +3927,48 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dom-serializer@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
+ integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.2"
+ entities "^4.2.0"
+
dom-walk@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
+domelementtype@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+ integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^5.0.2, domhandler@^5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
+ integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
+ dependencies:
+ domelementtype "^2.3.0"
+
+domutils@^3.0.1:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e"
+ integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==
+ dependencies:
+ dom-serializer "^2.0.0"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+
+dot-case@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee"
+ integrity sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==
+ dependencies:
+ no-case "^2.2.0"
+
dot-prop@^5.1.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
@@ -3449,12 +4057,17 @@ enquirer@^2.3.0:
ansi-colors "^4.1.1"
strip-ansi "^6.0.1"
+entities@^4.2.0, entities@^4.4.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
+ integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
env-paths@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
-error-ex@^1.3.1:
+error-ex@^1.2.0, error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
@@ -3734,7 +4347,7 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
-eth-ens-namehash@2.0.8:
+eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf"
integrity sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==
@@ -3832,6 +4445,14 @@ ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2:
"@scure/bip32" "1.3.1"
"@scure/bip39" "1.2.1"
+ethereum-multicall@^2.24.0:
+ version "2.24.0"
+ resolved "https://registry.yarnpkg.com/ethereum-multicall/-/ethereum-multicall-2.24.0.tgz#b6ff351a632cae7882ca24224bd3ea2426b6e213"
+ integrity sha512-+xWNmtWdpL4zOdAF2zeLYa1ZsrHLeU7KLmBAsc0el140qdQmF9ubyM9SwKHzy+xztTHi+eCnmCjknqhEU25gFA==
+ dependencies:
+ "@ethersproject/providers" "^5.0.10"
+ ethers "^5.0.15"
+
ethereumjs-abi@^0.6.8:
version "0.6.8"
resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae"
@@ -3879,7 +4500,7 @@ ethers@^4.0.32, ethers@^4.0.40:
uuid "2.0.1"
xmlhttprequest "1.8.0"
-ethers@^5.7.1:
+ethers@^5.0.13, ethers@^5.0.15, ethers@^5.7.1:
version "5.7.2"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
@@ -3915,6 +4536,28 @@ ethers@^5.7.1:
"@ethersproject/web" "5.7.1"
"@ethersproject/wordlists" "5.7.0"
+ethers@^6.13.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.0.tgz#f342958d0f622cf06559f59fbccdc1d30fa64f50"
+ integrity sha512-+yyQQQWEntY5UVbCv++guA14RRVFm1rSnO1GoLFdrK7/XRWMoktNgyG9UjwxrQqGBfGyFKknNZ81YpUS2emCgg==
+ dependencies:
+ "@adraffy/ens-normalize" "1.10.1"
+ "@noble/curves" "1.2.0"
+ "@noble/hashes" "1.3.2"
+ "@types/node" "18.15.13"
+ aes-js "4.0.0-beta.5"
+ tslib "2.4.0"
+ ws "8.5.0"
+
+ethjs-abi@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/ethjs-abi/-/ethjs-abi-0.2.1.tgz#e0a7a93a7e81163a94477bad56ede524ab6de533"
+ integrity sha512-g2AULSDYI6nEJyJaEVEXtTimRY2aPC2fi7ddSy0W+LXvEVL8Fe1y76o43ecbgdUKwZD+xsmEgX1yJr1Ia3r1IA==
+ dependencies:
+ bn.js "4.11.6"
+ js-sha3 "0.5.5"
+ number-to-bn "1.7.0"
+
ethjs-unit@0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699"
@@ -4062,6 +4705,13 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07"
integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==
+fast-check@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-3.1.1.tgz#72c5ae7022a4e86504762e773adfb8a5b0b01252"
+ integrity sha512-3vtXinVyuUKCKFKYcwXhGE6NtGWkqF8Yh3rvMZNzmwz8EPrgoc/v4pDdLHyLnCyCI5MZpZZkDEwFyXyEONOxpA==
+ dependencies:
+ pure-rand "^5.0.1"
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -4169,6 +4819,14 @@ find-up@5.0.0, find-up@^5.0.0:
locate-path "^6.0.0"
path-exists "^4.0.0"
+find-up@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==
+ dependencies:
+ path-exists "^2.0.0"
+ pinkie-promise "^2.0.0"
+
find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
@@ -4416,6 +5074,11 @@ gensync@^1.0.0-beta.2:
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+get-caller-file@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
+ integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
+
get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@@ -4426,6 +5089,11 @@ get-func-name@^2.0.0:
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==
+get-func-name@^2.0.1, get-func-name@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
+ integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
+
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82"
@@ -4939,6 +5607,24 @@ he@1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+header-case@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d"
+ integrity sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==
+ dependencies:
+ no-case "^2.2.0"
+ upper-case "^1.1.3"
+
+highlight.js@^10.4.1:
+ version "10.7.3"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
+ integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
+
+highlightjs-solidity@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-2.0.6.tgz#e7a702a2b05e0a97f185e6ba39fd4846ad23a990"
+ integrity sha512-DySXWfQghjm2l6a/flF+cteroJqD4gI8GSdL4PtvxZSsAHie8m3yVe2JFoRg03ROKT6hp2Lc/BxXkqerNmtQYg==
+
hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -4967,6 +5653,16 @@ hosted-git-info@^4.0.1:
dependencies:
lru-cache "^6.0.0"
+htmlparser2@^8.0.1:
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21"
+ integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+ domutils "^3.0.1"
+ entities "^4.4.0"
+
http-basic@^8.1.1:
version "8.1.3"
resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf"
@@ -5153,6 +5849,11 @@ interpret@^1.0.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
+invert-kv@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+ integrity sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==
+
io-ts@1.10.4:
version "1.10.4"
resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2"
@@ -5160,6 +5861,11 @@ io-ts@1.10.4:
dependencies:
fp-ts "^1.0.0"
+ip@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
+ integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
+
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
@@ -5238,6 +5944,13 @@ is-extglob@^2.1.1:
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==
+ dependencies:
+ number-is-nan "^1.0.0"
+
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
@@ -5282,8 +5995,15 @@ is-interactive@^1.0.0:
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
-is-negative-zero@^2.0.2:
- version "2.0.2"
+is-lower-case@^1.1.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393"
+ integrity sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==
+ dependencies:
+ lower-case "^1.1.0"
+
+is-negative-zero@^2.0.2:
+ version "2.0.2"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
@@ -5387,7 +6107,14 @@ is-unicode-supported@^0.1.0:
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
-is-utf8@^0.2.1:
+is-upper-case@^1.1.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f"
+ integrity sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==
+ dependencies:
+ upper-case "^1.1.0"
+
+is-utf8@^0.2.0, is-utf8@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==
@@ -5434,6 +6161,11 @@ js-sdsl@^4.1.4:
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.2.tgz#2e3c031b1f47d3aca8b775532e3ebb0818e7f847"
integrity sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==
+js-sha3@0.5.5:
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.5.tgz#baf0c0e8c54ad5903447df96ade7a4a1bca79a4a"
+ integrity sha512-yLLwn44IVeunwjpDVTDZmQeVbB0h+dZpY2eO68B/Zik8hu6dH+rKeLxwua79GGIvW6xr8NBAcrtiUbYrTjEFTA==
+
js-sha3@0.5.7, js-sha3@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
@@ -5614,6 +6346,13 @@ klaw@^1.0.0:
optionalDependencies:
graceful-fs "^4.1.9"
+lcid@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
+ integrity sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==
+ dependencies:
+ invert-kv "^1.0.0"
+
level-supports@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a"
@@ -5689,6 +6428,17 @@ listr2@6.6.1:
rfdc "^1.3.0"
wrap-ansi "^8.1.0"
+load-json-file@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+ integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ strip-bom "^2.0.0"
+
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -5719,11 +6469,26 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash.assign@^4.0.3, lodash.assign@^4.0.6:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
+ integrity sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==
+
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
+lodash.flatten@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+ integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==
+
+lodash.isequal@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+ integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
+
lodash.isfunction@^3.0.9:
version "3.0.9"
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
@@ -5822,6 +6587,25 @@ loupe@^2.3.1:
dependencies:
get-func-name "^2.0.0"
+loupe@^2.3.6:
+ version "2.3.7"
+ resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"
+ integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==
+ dependencies:
+ get-func-name "^2.0.1"
+
+lower-case-first@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1"
+ integrity sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==
+ dependencies:
+ lower-case "^1.1.2"
+
+lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
+ integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==
+
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@@ -5932,6 +6716,11 @@ memory-level@^1.0.0:
functional-red-black-tree "^1.0.1"
module-error "^1.0.1"
+memory-pager@^1.0.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
+ integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
+
memorystream@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2"
@@ -6237,6 +7026,25 @@ module-error@^1.0.1, module-error@^1.0.2:
resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86"
integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==
+mongodb-connection-string-url@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf"
+ integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==
+ dependencies:
+ "@types/whatwg-url" "^8.2.1"
+ whatwg-url "^11.0.0"
+
+mongodb@^5.7.0:
+ version "5.8.1"
+ resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.8.1.tgz#dc201adfbd6c6d73401cdcf12ebdb75f14771faf"
+ integrity sha512-wKyh4kZvm6NrCPH8AxyzXm3JBoEf4Xulo0aUWh3hCgwgYJxyQ1KLST86ZZaSWdj6/kxYUA3+YZuyADCE61CMSg==
+ dependencies:
+ bson "^5.4.0"
+ mongodb-connection-string-url "^2.6.0"
+ socks "^2.7.1"
+ optionalDependencies:
+ "@mongodb-js/saslprep" "^1.1.0"
+
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -6302,6 +7110,11 @@ mute-stream@0.0.8:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+nano-base32@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/nano-base32/-/nano-base32-1.0.1.tgz#ba548c879efcfb90da1c4d9e097db4a46c9255ef"
+ integrity sha512-sxEtoTqAPdjWVGv71Q17koMFGsOMSiHsIFEvzOM7cNp8BXB4AnEwmDabm5dorusJf/v1z7QxaZYxUorU9RKaAw==
+
nano-json-stream-parser@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f"
@@ -6347,6 +7160,13 @@ next-tick@1, next-tick@^1.1.0:
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
+no-case@^2.2.0, no-case@^2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
+ integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+ dependencies:
+ lower-case "^1.1.1"
+
node-addon-api@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
@@ -6384,6 +7204,11 @@ node-releases@^2.0.13:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==
+nofilter@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e"
+ integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==
+
nofilter@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66"
@@ -6396,7 +7221,7 @@ nopt@3.x:
dependencies:
abbrev "1"
-normalize-package-data@^2.5.0:
+normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
@@ -6445,6 +7270,18 @@ npm-run-path@^5.1.0:
dependencies:
path-key "^4.0.0"
+nth-check@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+ integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+ dependencies:
+ boolbase "^1.0.0"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+ integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==
+
number-to-bn@1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0"
@@ -6588,6 +7425,13 @@ ordinal@^1.0.3:
resolved "https://registry.yarnpkg.com/ordinal/-/ordinal-1.0.3.tgz#1a3c7726a61728112f50944ad7c35c06ae3a0d4d"
integrity sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==
+os-locale@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
+ integrity sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==
+ dependencies:
+ lcid "^1.0.0"
+
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@@ -6674,6 +7518,13 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+param-case@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
+ integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==
+ dependencies:
+ no-case "^2.2.0"
+
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -6702,6 +7553,13 @@ parse-headers@^2.0.0:
resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9"
integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==
+parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==
+ dependencies:
+ error-ex "^1.2.0"
+
parse-json@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@@ -6717,11 +7575,48 @@ parse-passwd@^1.0.0:
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==
+parse5-htmlparser2-tree-adapter@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1"
+ integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==
+ dependencies:
+ domhandler "^5.0.2"
+ parse5 "^7.0.0"
+
+parse5@^7.0.0:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32"
+ integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==
+ dependencies:
+ entities "^4.4.0"
+
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+pascal-case@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e"
+ integrity sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==
+ dependencies:
+ camel-case "^3.0.0"
+ upper-case-first "^1.1.0"
+
+path-case@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5"
+ integrity sha512-Ou0N05MioItesaLr9q8TtHVWmJ6fxWdqKB2RohFmNWVyJ+2zeKIeDNWAN6B/Pe7wpzWChhZX6nONYmOnMeJQ/Q==
+ dependencies:
+ no-case "^2.2.0"
+
+path-exists@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==
+ dependencies:
+ pinkie-promise "^2.0.0"
+
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
@@ -6757,6 +7652,15 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+path-type@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+ integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==
+ dependencies:
+ graceful-fs "^4.1.2"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@@ -6798,11 +7702,28 @@ pidtree@0.6.0:
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
+pify@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
+
pify@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+ integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
+
pinst@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pinst/-/pinst-3.0.0.tgz#80dec0a85f1f993c6084172020f3dbf512897eec"
@@ -6923,6 +7844,11 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
+pure-rand@^5.0.1:
+ version "5.0.5"
+ resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.5.tgz#bda2a7f6a1fc0f284d78d78ca5902f26f2ad35cf"
+ integrity sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==
+
qs@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
@@ -7006,6 +7932,14 @@ raw-body@2.5.2, raw-body@^2.4.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
+read-pkg-up@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+ integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==
+ dependencies:
+ find-up "^1.0.0"
+ read-pkg "^1.0.0"
+
read-pkg-up@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
@@ -7015,6 +7949,15 @@ read-pkg-up@^7.0.1:
read-pkg "^5.2.0"
type-fest "^0.8.1"
+read-pkg@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+ integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==
+ dependencies:
+ load-json-file "^1.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^1.0.0"
+
read-pkg@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
@@ -7088,6 +8031,11 @@ reduce-flatten@^2.0.0:
resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27"
integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==
+regenerator-runtime@^0.14.0:
+ version "0.14.1"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
+ integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
+
regexp.prototype.flags@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb"
@@ -7158,11 +8106,21 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+require-from-string@^1.1.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
+ integrity sha512-H7AkJWMobeskkttHyhTVtS0fxpFLjxhbfMa6Bk3wimP7sdPRGL3EyCg3sAQenFfAe+xQ+oAc85Nmtvq0ROM83Q==
+
require-from-string@^2.0.0, require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+require-main-filename@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+ integrity sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==
+
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
@@ -7278,7 +8236,12 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
-ripemd160@^2.0.0, ripemd160@^2.0.1:
+ripemd160-min@0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/ripemd160-min/-/ripemd160-min-0.0.6.tgz#a904b77658114474d02503e819dcc55853b67e62"
+ integrity sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==
+
+ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
@@ -7397,7 +8360,7 @@ secp256k1@^4.0.1:
node-addon-api "^2.0.0"
node-gyp-build "^4.2.0"
-"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.7.0:
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0:
version "5.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
@@ -7414,6 +8377,13 @@ semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+semver@^7.5.4:
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
+ integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
+ dependencies:
+ lru-cache "^6.0.0"
+
send@0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
@@ -7433,6 +8403,14 @@ send@0.18.0:
range-parser "~1.2.1"
statuses "2.0.1"
+sentence-case@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4"
+ integrity sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==
+ dependencies:
+ no-case "^2.2.0"
+ upper-case-first "^1.1.2"
+
serialize-javascript@6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
@@ -7497,6 +8475,13 @@ sha1@^1.1.1:
charenc ">= 0.0.1"
crypt ">= 0.0.1"
+sha3@^2.1.1:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/sha3/-/sha3-2.1.4.tgz#000fac0fe7c2feac1f48a25e7a31b52a6492cc8f"
+ integrity sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==
+ dependencies:
+ buffer "6.0.3"
+
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -7581,6 +8566,26 @@ slice-ansi@^5.0.0:
ansi-styles "^6.0.0"
is-fullwidth-code-point "^4.0.0"
+smart-buffer@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
+ integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
+
+snake-case@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
+ integrity sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==
+ dependencies:
+ no-case "^2.2.0"
+
+socks@^2.7.1:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
+ integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
+ dependencies:
+ ip "^2.0.0"
+ smart-buffer "^4.2.0"
+
sol-merger@^4.1.1:
version "4.4.0"
resolved "https://registry.yarnpkg.com/sol-merger/-/sol-merger-4.4.0.tgz#d637cb50d1553dde1f9fb19833bf6ea01ba2da11"
@@ -7609,6 +8614,17 @@ solc@0.7.3:
semver "^5.5.0"
tmp "0.0.33"
+solc@^0.4.20:
+ version "0.4.26"
+ resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.26.tgz#5390a62a99f40806b86258c737c1cf653cc35cb5"
+ integrity sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA==
+ dependencies:
+ fs-extra "^0.30.0"
+ memorystream "^0.3.1"
+ require-from-string "^1.1.0"
+ semver "^5.3.0"
+ yargs "^4.7.1"
+
solhint-plugin-prettier@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b"
@@ -7700,6 +8716,13 @@ source-map@~0.2.0:
dependencies:
amdefine ">=0.0.4"
+sparse-bitfield@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
+ integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==
+ dependencies:
+ memory-pager "^1.0.2"
+
spdx-correct@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c"
@@ -7797,6 +8820,15 @@ string-format@^2.0.0:
resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b"
integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==
+string-width@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
"string-width@^1.0.2 || 2", string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
@@ -7873,6 +8905,13 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==
+ dependencies:
+ ansi-regex "^2.0.0"
+
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
@@ -7906,6 +8945,13 @@ strip-bom@4.0.0:
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
+strip-bom@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+ integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==
+ dependencies:
+ is-utf8 "^0.2.0"
+
strip-final-newline@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
@@ -7923,6 +8969,11 @@ strip-hex-prefix@1.0.0:
dependencies:
is-hex-prefixed "1.0.0"
+strip-indent@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
+ integrity sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==
+
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
@@ -7980,6 +9031,14 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+swap-case@^1.1.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3"
+ integrity sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==
+ dependencies:
+ lower-case "^1.1.1"
+ upper-case "^1.1.1"
+
swarm-js@^0.1.40:
version "0.1.42"
resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.42.tgz#497995c62df6696f6e22372f457120e43e727979"
@@ -8047,6 +9106,11 @@ tar@^4.0.2:
safe-buffer "^5.2.1"
yallist "^3.1.1"
+testrpc@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/testrpc/-/testrpc-0.0.1.tgz#83e2195b1f5873aec7be1af8cbe6dcf39edb7aed"
+ integrity sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA==
+
text-extensions@^1.0.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26"
@@ -8099,6 +9163,14 @@ timers-ext@^0.1.7:
es5-ext "~0.10.46"
next-tick "1"
+title-case@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa"
+ integrity sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==
+ dependencies:
+ no-case "^2.2.0"
+ upper-case "^1.0.3"
+
tmp@0.0.33, tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -8136,6 +9208,13 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
+tr46@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
+ integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
+ dependencies:
+ punycode "^2.1.1"
+
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@@ -8151,6 +9230,14 @@ trim-newlines@^3.0.0:
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
+truffle-contract-size@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/truffle-contract-size/-/truffle-contract-size-2.0.1.tgz#9ec1b078b0b3221cc1612400bd6f5df83c7a3a09"
+ integrity sha512-AIKPwHPC/1pZwtVjgUcgcK23k6gWxKhn4ZnKLr339uieb94UgAUeIwGUkfc87T+0lqgC6ePY7YhsFeoZK2YEsA==
+ dependencies:
+ cli-table "^0.3.1"
+ yargs "^15.3.1"
+
ts-command-line-args@^2.2.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0"
@@ -8205,6 +9292,11 @@ ts-node@^10.8.1, ts-node@^10.9.1:
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
+tslib@2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
+ integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+
tslib@^1.8.1, tslib@^1.9.3:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
@@ -8263,7 +9355,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
-type-detect@^4.0.0, type-detect@^4.0.5:
+type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
@@ -8458,6 +9550,18 @@ update-browserslist-db@^1.0.11:
escalade "^3.1.1"
picocolors "^1.0.0"
+upper-case-first@^1.1.0, upper-case-first@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
+ integrity sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==
+ dependencies:
+ upper-case "^1.1.1"
+
+upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
+ integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==
+
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
@@ -8484,7 +9588,7 @@ utf-8-validate@^5.0.2:
dependencies:
node-gyp-build "^4.3.0"
-utf8@3.0.0:
+utf8@3.0.0, utf8@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==
@@ -8583,6 +9687,15 @@ web3-bzz@1.10.0:
got "12.1.0"
swarm-js "^0.1.40"
+web3-bzz@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.10.4.tgz#dcc787970767d9004c73d11d0eeef774ce16b880"
+ integrity sha512-ZZ/X4sJ0Uh2teU9lAGNS8EjveEppoHNQiKlOXAjedsrdWuaMErBPdLQjXfcrYvN6WM6Su9PMsAxf3FXXZ+HwQw==
+ dependencies:
+ "@types/node" "^12.12.6"
+ got "12.1.0"
+ swarm-js "^0.1.40"
+
web3-bzz@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.7.4.tgz#9419e606e38a9777443d4ce40506ebd796e06075"
@@ -8600,6 +9713,14 @@ web3-core-helpers@1.10.0:
web3-eth-iban "1.10.0"
web3-utils "1.10.0"
+web3-core-helpers@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.10.4.tgz#bd2b4140df2016d5dd3bb2b925fc29ad8678677c"
+ integrity sha512-r+L5ylA17JlD1vwS8rjhWr0qg7zVoVMDvWhajWA5r5+USdh91jRUYosp19Kd1m2vE034v7Dfqe1xYRoH2zvG0g==
+ dependencies:
+ web3-eth-iban "1.10.4"
+ web3-utils "1.10.4"
+
web3-core-helpers@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.7.4.tgz#f8f808928560d3e64e0c8d7bdd163aa4766bcf40"
@@ -8619,6 +9740,17 @@ web3-core-method@1.10.0:
web3-core-subscriptions "1.10.0"
web3-utils "1.10.0"
+web3-core-method@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.10.4.tgz#566b52f006d3cbb13b21b72b8d2108999bf5d6bf"
+ integrity sha512-uZTb7flr+Xl6LaDsyTeE2L1TylokCJwTDrIVfIfnrGmnwLc6bmTWCCrm71sSrQ0hqs6vp/MKbQYIYqUN0J8WyA==
+ dependencies:
+ "@ethersproject/transactions" "^5.6.2"
+ web3-core-helpers "1.10.4"
+ web3-core-promievent "1.10.4"
+ web3-core-subscriptions "1.10.4"
+ web3-utils "1.10.4"
+
web3-core-method@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.7.4.tgz#3873c6405e1a0a8a1efc1d7b28de8b7550b00c15"
@@ -8637,6 +9769,13 @@ web3-core-promievent@1.10.0:
dependencies:
eventemitter3 "4.0.4"
+web3-core-promievent@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.10.4.tgz#629b970b7934430b03c5033c79f3bb3893027e22"
+ integrity sha512-2de5WnJQ72YcIhYwV/jHLc4/cWJnznuoGTJGD29ncFQHAfwW/MItHFSVKPPA5v8AhJe+r6y4Y12EKvZKjQVBvQ==
+ dependencies:
+ eventemitter3 "4.0.4"
+
web3-core-promievent@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.7.4.tgz#80a75633fdfe21fbaae2f1e38950edb2f134868c"
@@ -8655,6 +9794,17 @@ web3-core-requestmanager@1.10.0:
web3-providers-ipc "1.10.0"
web3-providers-ws "1.10.0"
+web3-core-requestmanager@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.10.4.tgz#eb1f147e6b9df84e3a37e602162f8925bdb4bb9a"
+ integrity sha512-vqP6pKH8RrhT/2MoaU+DY/OsYK9h7HmEBNCdoMj+4ZwujQtw/Mq2JifjwsJ7gits7Q+HWJwx8q6WmQoVZAWugg==
+ dependencies:
+ util "^0.12.5"
+ web3-core-helpers "1.10.4"
+ web3-providers-http "1.10.4"
+ web3-providers-ipc "1.10.4"
+ web3-providers-ws "1.10.4"
+
web3-core-requestmanager@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.7.4.tgz#2dc8a526dab8183dca3fef54658621801b1d0469"
@@ -8674,6 +9824,14 @@ web3-core-subscriptions@1.10.0:
eventemitter3 "4.0.4"
web3-core-helpers "1.10.0"
+web3-core-subscriptions@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.10.4.tgz#2f4dcb404237e92802a563265d11a33934dc38e6"
+ integrity sha512-o0lSQo/N/f7/L76C0HV63+S54loXiE9fUPfHFcTtpJRQNDBVsSDdWRdePbWwR206XlsBqD5VHApck1//jEafTw==
+ dependencies:
+ eventemitter3 "4.0.4"
+ web3-core-helpers "1.10.4"
+
web3-core-subscriptions@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.7.4.tgz#cfbd3fa71081a8c8c6f1a64577a1a80c5bd9826f"
@@ -8695,6 +9853,19 @@ web3-core@1.10.0:
web3-core-requestmanager "1.10.0"
web3-utils "1.10.0"
+web3-core@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.4.tgz#639de68b8b9871d2dc8892e0dd4e380cb1361a98"
+ integrity sha512-B6elffYm81MYZDTrat7aEhnhdtVE3lDBUZft16Z8awYMZYJDbnykEbJVS+l3mnA7AQTnSDr/1MjWofGDLBJPww==
+ dependencies:
+ "@types/bn.js" "^5.1.1"
+ "@types/node" "^12.12.6"
+ bignumber.js "^9.0.0"
+ web3-core-helpers "1.10.4"
+ web3-core-method "1.10.4"
+ web3-core-requestmanager "1.10.4"
+ web3-utils "1.10.4"
+
web3-core@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.7.4.tgz#943fff99134baedafa7c65b4a0bbd424748429ff"
@@ -8716,6 +9887,14 @@ web3-eth-abi@1.10.0:
"@ethersproject/abi" "^5.6.3"
web3-utils "1.10.0"
+web3-eth-abi@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.10.4.tgz#16c19d0bde0aaf8c1a56cb7743a83156d148d798"
+ integrity sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ==
+ dependencies:
+ "@ethersproject/abi" "^5.6.3"
+ web3-utils "1.10.4"
+
web3-eth-abi@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.7.4.tgz#3fee967bafd67f06b99ceaddc47ab0970f2a614a"
@@ -8740,6 +9919,22 @@ web3-eth-accounts@1.10.0:
web3-core-method "1.10.0"
web3-utils "1.10.0"
+web3-eth-accounts@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.4.tgz#df30e85a7cd70e475f8cf52361befba408829e34"
+ integrity sha512-ysy5sVTg9snYS7tJjxVoQAH6DTOTkRGR8emEVCWNGLGiB9txj+qDvSeT0izjurS/g7D5xlMAgrEHLK1Vi6I3yg==
+ dependencies:
+ "@ethereumjs/common" "2.6.5"
+ "@ethereumjs/tx" "3.5.2"
+ "@ethereumjs/util" "^8.1.0"
+ eth-lib "0.2.8"
+ scrypt-js "^3.0.1"
+ uuid "^9.0.0"
+ web3-core "1.10.4"
+ web3-core-helpers "1.10.4"
+ web3-core-method "1.10.4"
+ web3-utils "1.10.4"
+
web3-eth-accounts@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.7.4.tgz#7a24a4dfe947f7e9d1bae678529e591aa146167a"
@@ -8771,6 +9966,20 @@ web3-eth-contract@1.10.0:
web3-eth-abi "1.10.0"
web3-utils "1.10.0"
+web3-eth-contract@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.10.4.tgz#22d39f04e11d9ff4e726e8025a56d78e843a2c3d"
+ integrity sha512-Q8PfolOJ4eV9TvnTj1TGdZ4RarpSLmHnUnzVxZ/6/NiTfe4maJz99R0ISgwZkntLhLRtw0C7LRJuklzGYCNN3A==
+ dependencies:
+ "@types/bn.js" "^5.1.1"
+ web3-core "1.10.4"
+ web3-core-helpers "1.10.4"
+ web3-core-method "1.10.4"
+ web3-core-promievent "1.10.4"
+ web3-core-subscriptions "1.10.4"
+ web3-eth-abi "1.10.4"
+ web3-utils "1.10.4"
+
web3-eth-contract@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.7.4.tgz#e5761cfb43d453f57be4777b2e5e7e1082078ff7"
@@ -8799,6 +10008,20 @@ web3-eth-ens@1.10.0:
web3-eth-contract "1.10.0"
web3-utils "1.10.0"
+web3-eth-ens@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.10.4.tgz#3d991adac52bc8e598f1f1b8528337fa6291004c"
+ integrity sha512-LLrvxuFeVooRVZ9e5T6OWKVflHPFgrVjJ/jtisRWcmI7KN/b64+D/wJzXqgmp6CNsMQcE7rpmf4CQmJCrTdsgg==
+ dependencies:
+ content-hash "^2.5.2"
+ eth-ens-namehash "2.0.8"
+ web3-core "1.10.4"
+ web3-core-helpers "1.10.4"
+ web3-core-promievent "1.10.4"
+ web3-eth-abi "1.10.4"
+ web3-eth-contract "1.10.4"
+ web3-utils "1.10.4"
+
web3-eth-ens@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.7.4.tgz#346720305379c0a539e226141a9602f1da7bc0c8"
@@ -8821,6 +10044,14 @@ web3-eth-iban@1.10.0:
bn.js "^5.2.1"
web3-utils "1.10.0"
+web3-eth-iban@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.10.4.tgz#bc61b4a1930d19b1df8762c606d669902558e54d"
+ integrity sha512-0gE5iNmOkmtBmbKH2aTodeompnNE8jEyvwFJ6s/AF6jkw9ky9Op9cqfzS56AYAbrqEFuClsqB/AoRves7LDELw==
+ dependencies:
+ bn.js "^5.2.1"
+ web3-utils "1.10.4"
+
web3-eth-iban@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.7.4.tgz#711fb2547fdf0f988060027331b2b6c430505753"
@@ -8841,6 +10072,18 @@ web3-eth-personal@1.10.0:
web3-net "1.10.0"
web3-utils "1.10.0"
+web3-eth-personal@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.10.4.tgz#e2ee920f47e84848288e03442659cdbb2c4deea2"
+ integrity sha512-BRa/hs6jU1hKHz+AC/YkM71RP3f0Yci1dPk4paOic53R4ZZG4MgwKRkJhgt3/GPuPliwS46f/i5A7fEGBT4F9w==
+ dependencies:
+ "@types/node" "^12.12.6"
+ web3-core "1.10.4"
+ web3-core-helpers "1.10.4"
+ web3-core-method "1.10.4"
+ web3-net "1.10.4"
+ web3-utils "1.10.4"
+
web3-eth-personal@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.7.4.tgz#22c399794cb828a75703df8bb4b3c1331b471546"
@@ -8871,6 +10114,24 @@ web3-eth@1.10.0:
web3-net "1.10.0"
web3-utils "1.10.0"
+web3-eth@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.10.4.tgz#3a908c635cb5d935bd30473e452f3bd7f2ee66a5"
+ integrity sha512-Sql2kYKmgt+T/cgvg7b9ce24uLS7xbFrxE4kuuor1zSCGrjhTJ5rRNG8gTJUkAJGKJc7KgnWmgW+cOfMBPUDSA==
+ dependencies:
+ web3-core "1.10.4"
+ web3-core-helpers "1.10.4"
+ web3-core-method "1.10.4"
+ web3-core-subscriptions "1.10.4"
+ web3-eth-abi "1.10.4"
+ web3-eth-accounts "1.10.4"
+ web3-eth-contract "1.10.4"
+ web3-eth-ens "1.10.4"
+ web3-eth-iban "1.10.4"
+ web3-eth-personal "1.10.4"
+ web3-net "1.10.4"
+ web3-utils "1.10.4"
+
web3-eth@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.7.4.tgz#a7c1d3ccdbba4de4a82df7e3c4db716e4a944bf2"
@@ -8898,6 +10159,15 @@ web3-net@1.10.0:
web3-core-method "1.10.0"
web3-utils "1.10.0"
+web3-net@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.4.tgz#20e12c60e4477d4298979d8d5d66b9abf8e66a09"
+ integrity sha512-mKINnhOOnZ4koA+yV2OT5s5ztVjIx7IY9a03w6s+yao/BUn+Luuty0/keNemZxTr1E8Ehvtn28vbOtW7Ids+Ow==
+ dependencies:
+ web3-core "1.10.4"
+ web3-core-method "1.10.4"
+ web3-utils "1.10.4"
+
web3-net@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.7.4.tgz#3153dfd3423262dd6fbec7aae5467202c4cad431"
@@ -8917,6 +10187,16 @@ web3-providers-http@1.10.0:
es6-promise "^4.2.8"
web3-core-helpers "1.10.0"
+web3-providers-http@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.10.4.tgz#ca7aa58aeaf8123500c24ffe0595896319f830e8"
+ integrity sha512-m2P5Idc8hdiO0l60O6DSCPw0kw64Zgi0pMjbEFRmxKIck2Py57RQMu4bxvkxJwkF06SlGaEQF8rFZBmuX7aagQ==
+ dependencies:
+ abortcontroller-polyfill "^1.7.5"
+ cross-fetch "^4.0.0"
+ es6-promise "^4.2.8"
+ web3-core-helpers "1.10.4"
+
web3-providers-http@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.7.4.tgz#8209cdcb115db5ccae1f550d1c4e3005e7538d02"
@@ -8933,6 +10213,14 @@ web3-providers-ipc@1.10.0:
oboe "2.1.5"
web3-core-helpers "1.10.0"
+web3-providers-ipc@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.10.4.tgz#2e03437909e4e7771d646ff05518efae44b783c3"
+ integrity sha512-YRF/bpQk9z3WwjT+A6FI/GmWRCASgd+gC0si7f9zbBWLXjwzYAKG73bQBaFRAHex1hl4CVcM5WUMaQXf3Opeuw==
+ dependencies:
+ oboe "2.1.5"
+ web3-core-helpers "1.10.4"
+
web3-providers-ipc@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.7.4.tgz#02e85e99e48f432c9d34cee7d786c3685ec9fcfa"
@@ -8950,6 +10238,15 @@ web3-providers-ws@1.10.0:
web3-core-helpers "1.10.0"
websocket "^1.0.32"
+web3-providers-ws@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.10.4.tgz#55d0c3ba36c6a79d105f02e20a707eb3978e7f82"
+ integrity sha512-j3FBMifyuFFmUIPVQR4pj+t5ILhAexAui0opgcpu9R5LxQrLRUZxHSnU+YO25UycSOa/NAX8A+qkqZNpcFAlxA==
+ dependencies:
+ eventemitter3 "4.0.4"
+ web3-core-helpers "1.10.4"
+ websocket "^1.0.32"
+
web3-providers-ws@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.7.4.tgz#6e60bcefb456f569a3e766e386d7807a96f90595"
@@ -8969,6 +10266,16 @@ web3-shh@1.10.0:
web3-core-subscriptions "1.10.0"
web3-net "1.10.0"
+web3-shh@1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.10.4.tgz#9852d6f3d05678e31e49235a60fea10ca7a9e21d"
+ integrity sha512-cOH6iFFM71lCNwSQrC3niqDXagMqrdfFW85hC9PFUrAr3PUrIem8TNstTc3xna2bwZeWG6OBy99xSIhBvyIACw==
+ dependencies:
+ web3-core "1.10.4"
+ web3-core-method "1.10.4"
+ web3-core-subscriptions "1.10.4"
+ web3-net "1.10.4"
+
web3-shh@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.7.4.tgz#bee91cce2737c529fd347274010b548b6ea060f1"
@@ -8992,6 +10299,20 @@ web3-utils@1.10.0:
randombytes "^2.1.0"
utf8 "3.0.0"
+web3-utils@1.10.4, web3-utils@^1.0.0-beta.31, web3-utils@^1.2.5:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec"
+ integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==
+ dependencies:
+ "@ethereumjs/util" "^8.1.0"
+ bn.js "^5.2.1"
+ ethereum-bloom-filters "^1.0.6"
+ ethereum-cryptography "^2.1.2"
+ ethjs-unit "0.1.6"
+ number-to-bn "1.7.0"
+ randombytes "^2.1.0"
+ utf8 "3.0.0"
+
web3-utils@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.7.4.tgz#eb6fa3706b058602747228234453811bbee017f5"
@@ -9045,11 +10366,29 @@ web3@1.7.4:
web3-shh "1.7.4"
web3-utils "1.7.4"
+web3@^1.2.5:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/web3/-/web3-1.10.4.tgz#5d5e59b976eaf758b060fe1a296da5fe87bdc79c"
+ integrity sha512-kgJvQZjkmjOEKimx/tJQsqWfRDPTTcBfYPa9XletxuHLpHcXdx67w8EFn5AW3eVxCutE9dTVHgGa9VYe8vgsEA==
+ dependencies:
+ web3-bzz "1.10.4"
+ web3-core "1.10.4"
+ web3-eth "1.10.4"
+ web3-eth-personal "1.10.4"
+ web3-net "1.10.4"
+ web3-shh "1.10.4"
+ web3-utils "1.10.4"
+
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+webidl-conversions@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
+ integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
+
websocket@^1.0.32:
version "1.0.34"
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111"
@@ -9062,6 +10401,14 @@ websocket@^1.0.32:
utf-8-validate "^5.0.2"
yaeti "^0.0.6"
+whatwg-url@^11.0.0:
+ version "11.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
+ integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
+ dependencies:
+ tr46 "^3.0.0"
+ webidl-conversions "^7.0.0"
+
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
@@ -9081,6 +10428,11 @@ which-boxed-primitive@^1.0.2:
is-string "^1.0.5"
is-symbol "^1.0.3"
+which-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
+ integrity sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==
+
which-module@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409"
@@ -9118,6 +10470,11 @@ wide-align@1.1.3:
dependencies:
string-width "^1.0.2 || 2"
+window-size@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
+ integrity sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==
+
word-wrap@^1.0.3, word-wrap@~1.2.3:
version "1.2.5"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
@@ -9141,6 +10498,14 @@ workerpool@6.2.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
+wrap-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+ integrity sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+
wrap-ansi@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
@@ -9150,6 +10515,15 @@ wrap-ansi@^5.1.0:
string-width "^3.0.0"
strip-ansi "^5.0.0"
+wrap-ansi@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+ integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -9178,6 +10552,11 @@ ws@7.4.6:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
+ws@8.5.0:
+ version "8.5.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
+ integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
+
ws@^3.0.0:
version "3.3.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
@@ -9239,6 +10618,11 @@ xtend@^4.0.0:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+y18n@^3.2.1:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
+ integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
+
y18n@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
@@ -9282,6 +10666,22 @@ yargs-parser@20.2.4:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
+yargs-parser@^18.1.2:
+ version "18.1.3"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
+ integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
+ dependencies:
+ camelcase "^5.0.0"
+ decamelize "^1.2.0"
+
+yargs-parser@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4"
+ integrity sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==
+ dependencies:
+ camelcase "^3.0.0"
+ lodash.assign "^4.0.6"
+
yargs-parser@^20.2.2, yargs-parser@^20.2.3:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
@@ -9340,6 +10740,23 @@ yargs@16.2.0:
y18n "^5.0.5"
yargs-parser "^20.2.2"
+yargs@^15.3.1:
+ version "15.4.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
+ integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
+ dependencies:
+ cliui "^6.0.0"
+ decamelize "^1.2.0"
+ find-up "^4.1.0"
+ get-caller-file "^2.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^2.0.0"
+ set-blocking "^2.0.0"
+ string-width "^4.2.0"
+ which-module "^2.0.0"
+ y18n "^4.0.0"
+ yargs-parser "^18.1.2"
+
yargs@^17.0.0:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
@@ -9353,6 +10770,26 @@ yargs@^17.0.0:
y18n "^5.0.5"
yargs-parser "^21.1.1"
+yargs@^4.7.1:
+ version "4.8.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0"
+ integrity sha512-LqodLrnIDM3IFT+Hf/5sxBnEGECrfdC1uIbgZeJmESCSo4HoCAaKEus8MylXHAkdacGc0ye+Qa+dpkuom8uVYA==
+ dependencies:
+ cliui "^3.2.0"
+ decamelize "^1.1.1"
+ get-caller-file "^1.0.1"
+ lodash.assign "^4.0.3"
+ os-locale "^1.4.0"
+ read-pkg-up "^1.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^1.0.1"
+ set-blocking "^2.0.0"
+ string-width "^1.0.1"
+ which-module "^1.0.0"
+ window-size "^0.2.0"
+ y18n "^3.2.1"
+ yargs-parser "^2.4.1"
+
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"