From 2eaedbae72ef060b5710587dc6388fb1e7b377c1 Mon Sep 17 00:00:00 2001 From: Channing <95395274+channing-magiceden@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:17:22 -0700 Subject: [PATCH 01/15] Improve hardhat deploy script (#124) --- hardhat.config.ts | 9 ++++++++- package.json | 4 ++-- scripts/utils/helper.ts | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 0fb5b36b..ef3c99c4 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -194,7 +194,14 @@ task('deploy', 'Deploy ERC721M') ) .addOptionalParam('gaspricegwei', 'Set gas price in Gwei') .addOptionalParam('gaslimit', 'Set maximum gas units to spend on transaction') - .setAction(deploy); + .setAction(async (tasksArgs, hre) => { + console.log('Cleaning...'); + await hre.run('clean'); + console.log('Compiling...'); + await hre.run('compile'); + console.log('Deploying...'); + await deploy(tasksArgs, hre); + }); task('setBaseURI', 'Set the base uri') .addParam('uri', 'uri') diff --git a/package.json b/package.json index 574958b9..e68018ca 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "scripts": { "node": "hardhat node", "build": "npm run build:contracts && npm run build:typescript", - "build:contracts": "hardhat compile", + "build:contracts": "hardhat clean && hardhat compile", "build:typescript": "npx rollup -c --bundleConfigAsCjs && tsc -d -p tsconfig.build.json", - "clean": "rm -rf dist node_modules/.cache typechain-types dist artifacts cache", + "clean": "rm -rf dist node_modules typechain-types dist artifacts cache coverage coverage.json", "test": "hardhat test", "test:gas": "REPORT_GAS=true hardhat test", "coverage": "REPORT_COVERAGE=true hardhat coverage", diff --git a/scripts/utils/helper.ts b/scripts/utils/helper.ts index 27bb9104..3d3a3596 100644 --- a/scripts/utils/helper.ts +++ b/scripts/utils/helper.ts @@ -23,7 +23,7 @@ export const checkCodeVersion = async () => { if (localLatestCommit !== remoteLatestCommit) { console.log( - '🟡 Warning: you are NOT using the latest version of the code. Please run `git pull` on main branch to update the code.', + '🟡 Warning: you are NOT using the latest version of the code. Please run `git pull` on main branch to update the code. Then run `npm run clean && npm run install && npm run build`', ); if (!(await confirm({ message: 'Proceed anyway?', default: false }))) { return false; From 196f4851bdbaf0b92ecb54304cec5775e4c03cb3 Mon Sep 17 00:00:00 2001 From: Channing <95395274+channing-magiceden@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:11:36 -0700 Subject: [PATCH 02/15] Clean up whitelist in setStages (#125) * Improve hardhat deploy script * Clean up whitelist --- hardhat.config.ts | 6 +++ scripts/cleanWhitelist.ts | 20 +++++++++ scripts/index.ts | 1 + scripts/setStages.ts | 67 ++++++----------------------- scripts/utils/helper.ts | 88 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 126 insertions(+), 56 deletions(-) create mode 100644 scripts/cleanWhitelist.ts diff --git a/hardhat.config.ts b/hardhat.config.ts index ef3c99c4..6cc7d5a3 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -42,6 +42,7 @@ import { send721Batch, freezeTrading, thawTrading, + cleanWhitelist, } from './scripts'; const config: HardhatUserConfig = { @@ -433,4 +434,9 @@ task('thawTrading', 'Thaw trading for 721Cv2') .addParam('contract', 'contract address') .setAction(thawTrading); +task('cleanWhitelist', 'Clean up whitelist') + .addOptionalParam('whitelistpath', 'plain whitelist path') + .addOptionalParam('variablewalletlimitpath', 'variable wallet limit whitelist path') + .setAction(cleanWhitelist) + export default config; diff --git a/scripts/cleanWhitelist.ts b/scripts/cleanWhitelist.ts new file mode 100644 index 00000000..22add6d5 --- /dev/null +++ b/scripts/cleanWhitelist.ts @@ -0,0 +1,20 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { cleanWhitelist as cleanWL, cleanVariableWalletLimit } from './utils/helper'; + +export interface ICleanWhitelistParams { + whitelistpath: string; + variablewalletlimitpath: string; +} + +export const cleanWhitelist = async ( + args: ICleanWhitelistParams, + hre: HardhatRuntimeEnvironment, +) => { + if (args.variablewalletlimitpath) { + cleanVariableWalletLimit(args.variablewalletlimitpath, true); + } + + if (args.whitelistpath) { + cleanWL(args.whitelistpath, true); + } +}; diff --git a/scripts/index.ts b/scripts/index.ts index 622474e8..0b81d769 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -31,3 +31,4 @@ export * from './dev/deploy721BatchTransfer'; export * from './send721Batch'; export * from './freezeTrading'; export * from './thawTrading'; +export * from './cleanWhitelist'; diff --git a/scripts/setStages.ts b/scripts/setStages.ts index 511131dc..42417443 100644 --- a/scripts/setStages.ts +++ b/scripts/setStages.ts @@ -3,7 +3,7 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { MerkleTree } from 'merkletreejs'; import fs from 'fs'; import { ContractDetails } from './common/constants'; -import { estimateGas } from './utils/helper'; +import { cleanVariableWalletLimit, cleanWhitelist, estimateGas } from './utils/helper'; import { Overrides } from 'ethers'; export interface ISetStagesParams { @@ -49,33 +49,10 @@ export const setStages = async ( * - for `variable wallet limit list`, leaves are `solidityKeccak256(['address', 'uint32'], [address, limit])` */ const merkleRoots = await Promise.all( - stagesConfig.map((stage) => { + stagesConfig.map(async (stage) => { if (stage.whitelistPath) { - const whitelist = JSON.parse( - fs.readFileSync(stage.whitelistPath, 'utf-8'), - ); - - // Clean up whitelist - const filteredWhitelist = whitelist.filter((address: string) => - ethers.utils.isAddress(address), - ); - console.log( - `Filtered whitelist: ${filteredWhitelist.length} addresses. ${whitelist.length - filteredWhitelist.length} invalid addresses removed.`, - ); - const invalidWhitelist = whitelist.filter( - (address: string) => !ethers.utils.isAddress(address), - ); - console.log( - `❌ Invalid whitelist: ${invalidWhitelist.length} addresses.\r\n${invalidWhitelist.join(', \r\n')}`, - ); - - if (invalidWhitelist.length > 0) { - console.log(`🔄 🚨 updating whitelist file: ${stage.whitelistPath}`); - fs.writeFileSync( - stage.whitelistPath, - JSON.stringify(filteredWhitelist, null, 2), - ); - } + const filteredSet = await cleanWhitelist(stage.whitelistPath, true); + const filteredWhitelist = Array.from(filteredSet.values()); const mt = new MerkleTree( filteredWhitelist.map((address: string) => @@ -92,35 +69,15 @@ export const setStages = async ( ); return mt.getHexRoot(); } else if (stage.variableWalletLimitPath) { + const filteredMap = await cleanVariableWalletLimit(stage.variableWalletLimitPath, true); const leaves: any[] = []; - const file = fs.readFileSync(stage.variableWalletLimitPath, 'utf-8'); - file - .split('\n') - .filter((line) => line) - .forEach((line) => { - const [addressStr, limitStr] = line.split(','); - - if (!ethers.utils.isAddress(addressStr.trim().toLowerCase())) { - console.log(`Ignored invalid address: ${addressStr}`); - return; - } - - const address = ethers.utils.getAddress( - addressStr.trim().toLowerCase(), - ); - const limit = parseInt(limitStr, 10); - - if (!Number.isInteger(limit)) { - console.log(`Ignored invalid limit for address: ${addressStr}`); - return; - } - - const digest = ethers.utils.solidityKeccak256( - ['address', 'uint32'], - [address, limit], - ); - leaves.push(digest); - }); + for (const [address, limit] of filteredMap.entries()) { + const digest = ethers.utils.solidityKeccak256( + ['address', 'uint32'], + [address, limit], + ); + leaves.push(digest); + } const mt = new MerkleTree(leaves, ethers.utils.keccak256, { sortPairs: true, diff --git a/scripts/utils/helper.ts b/scripts/utils/helper.ts index 3d3a3596..e97e2897 100644 --- a/scripts/utils/helper.ts +++ b/scripts/utils/helper.ts @@ -1,5 +1,7 @@ +import fs from 'fs'; +import path from 'path'; import { confirm } from '@inquirer/prompts'; -import { Deferrable } from 'ethers/lib/utils'; +import { Deferrable, getAddress, isAddress } from 'ethers/lib/utils'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { TransactionRequest } from '@ethersproject/abstract-provider'; import * as child from 'child_process'; @@ -119,3 +121,87 @@ const getTokenName = (hre: HardhatRuntimeEnvironment) => { return 'ETH'; } }; + +export const cleanVariableWalletLimit = async (variableWalletLimitPath: string, writeToFile: boolean) => { + console.log(`=========================================`); + console.log(`Cleaning variable wallet limit file: ${variableWalletLimitPath}`); + const file = fs.readFileSync(variableWalletLimitPath, 'utf-8'); + const walletsWithLimit = new Map(); + let invalidNum = 0; + + file + .split('\n') + .filter((line) => line) + .forEach((line) => { + const [addressStr, limitStr] = line.split(','); + + if (!isAddress(addressStr.trim().toLowerCase())) { + console.log(`Ignored invalid address: ${addressStr}`); + invalidNum++; + return; + } + const address = getAddress( + addressStr.trim().toLowerCase(), + ); + const limit = parseInt(limitStr, 10); + + if (!Number.isInteger(limit)) { + console.log(`Ignored invalid limit for address: ${addressStr}`); + invalidNum++; + return; + } + walletsWithLimit.set(address, (walletsWithLimit.get(address) ?? 0) + limit) + }); + + console.log(`Cleaned whitelist:\t${walletsWithLimit.size}`); + console.log(`Invalid entries:\t${invalidNum}`); + + if (writeToFile) { + const fileName = path.basename(variableWalletLimitPath); + const cleanedFileName = `cleaned-${fileName}`; + fs.closeSync(fs.openSync(cleanedFileName, 'w')); + + for (const [address, limit] of walletsWithLimit.entries()) { + fs.appendFileSync(cleanedFileName, `${address},${limit}\n`); + } + console.log(`Cleaned file: ${cleanedFileName}`); + } + console.log(`=========================================`); + return walletsWithLimit; +} + +export const cleanWhitelist = async (whitelistPath: string, writeToFile: boolean) => { + console.log(`=========================================`); + console.log(`Cleaning whitelist file: ${whitelistPath}`); + + let invalidNum = 0; + const whitelist = JSON.parse( + fs.readFileSync(whitelistPath, 'utf-8'), + ); + const wallets = new Set(); + + whitelist.forEach((address: string) => { + if (!isAddress(address)) { + console.log(`Ignored invalid address: ${address}`); + invalidNum++; + return; + } + wallets.add(getAddress(address)) + }); + + console.log(`Cleaned whitelist:\t${wallets.size}`); + console.log(`Invalid addresses:\t${invalidNum}`); + + if (writeToFile) { + const fileName = path.basename(whitelistPath); + const cleanedFileName = `cleaned-${fileName}`; + + fs.writeFileSync( + cleanedFileName, + JSON.stringify(Array.from(wallets.values()), null, 2), + ); + console.log(`Cleaned file: ${cleanedFileName}`); + } + console.log(`=========================================`); + return wallets; +} \ No newline at end of file From a1d65e4c06b5c951708797f0ebc40c6237f54589 Mon Sep 17 00:00:00 2001 From: Channing <95395274+channing-magiceden@users.noreply.github.com> Date: Mon, 1 Apr 2024 10:14:09 -0700 Subject: [PATCH 03/15] Fix test (#127) --- contracts/ERC721CM.sol | 68 +---------- contracts/ERC721M.sol | 53 --------- contracts/IERC721M.sol | 2 - test/ERC721CM.test.ts | 251 ----------------------------------------- test/erc721m.test.ts | 251 ----------------------------------------- 5 files changed, 6 insertions(+), 619 deletions(-) diff --git a/contracts/ERC721CM.sol b/contracts/ERC721CM.sol index f551a474..e3b797b2 100644 --- a/contracts/ERC721CM.sol +++ b/contracts/ERC721CM.sol @@ -29,9 +29,6 @@ contract ERC721CM is IERC721M, ERC721ACQueryable, Ownable, ReentrancyGuard { // Whether this contract is mintable. bool private _mintable; - // Whether base URI is permanent. Once set, base URI is immutable. - bool private _baseURIPermanent; - // Specify how long a signature from cosigner is valid for, recommend 300 seconds. uint64 private _timestampExpirySeconds; @@ -161,12 +158,9 @@ contract ERC721CM is IERC721M, ERC721ACQueryable, Ownable, ReentrancyGuard { * ] */ function setStages(MintStageInfo[] calldata newStages) external onlyOwner { - uint256 originalSize = _mintStages.length; - for (uint256 i = 0; i < originalSize; i++) { - _mintStages.pop(); - } + delete _mintStages; - for (uint256 i = 0; i < newStages.length; i++) { + for (uint256 i = 0; i < newStages.length;) { if (i >= 1) { if ( newStages[i].startTimeUnixSeconds < @@ -199,6 +193,8 @@ contract ERC721CM is IERC721M, ERC721ACQueryable, Ownable, ReentrancyGuard { newStages[i].startTimeUnixSeconds, newStages[i].endTimeUnixSeconds ); + + unchecked { ++i; } } } @@ -288,50 +284,6 @@ contract ERC721CM is IERC721M, ERC721ACQueryable, Ownable, ReentrancyGuard { return (_mintStages[index], walletMinted, stageMinted); } - /** - * @dev Updates info for one stage specified by index (starting from 0). - */ - function updateStage( - uint256 index, - uint80 price, - uint32 walletLimit, - bytes32 merkleRoot, - uint24 maxStageSupply, - uint64 startTimeUnixSeconds, - uint64 endTimeUnixSeconds - ) external onlyOwner { - if (index >= _mintStages.length) revert InvalidStage(); - if (index >= 1) { - if ( - startTimeUnixSeconds < - _mintStages[index - 1].endTimeUnixSeconds + - _timestampExpirySeconds - ) { - revert InsufficientStageTimeGap(); - } - } - _assertValidStartAndEndTimestamp( - startTimeUnixSeconds, - endTimeUnixSeconds - ); - _mintStages[index].price = price; - _mintStages[index].walletLimit = walletLimit; - _mintStages[index].merkleRoot = merkleRoot; - _mintStages[index].maxStageSupply = maxStageSupply; - _mintStages[index].startTimeUnixSeconds = startTimeUnixSeconds; - _mintStages[index].endTimeUnixSeconds = endTimeUnixSeconds; - - emit UpdateStage( - index, - price, - walletLimit, - merkleRoot, - maxStageSupply, - startTimeUnixSeconds, - endTimeUnixSeconds - ); - } - /** * @dev Returns mint currency address. */ @@ -512,19 +464,10 @@ contract ERC721CM is IERC721M, ERC721ACQueryable, Ownable, ReentrancyGuard { * @dev Sets token base URI. */ function setBaseURI(string calldata baseURI) external onlyOwner { - if (_baseURIPermanent) revert CannotUpdatePermanentBaseURI(); _currentBaseURI = baseURI; emit SetBaseURI(baseURI); } - /** - * @dev Sets token base URI permanent. Cannot revert. - */ - function setBaseURIPermanent() external onlyOwner { - _baseURIPermanent = true; - emit PermanentBaseURI(_currentBaseURI); - } - /** * @dev Sets token URI suffix. e.g. ".json". */ @@ -614,13 +557,14 @@ contract ERC721CM is IERC721M, ERC721ACQueryable, Ownable, ReentrancyGuard { function getActiveStageFromTimestamp( uint64 timestamp ) public view returns (uint256) { - for (uint256 i = 0; i < _mintStages.length; i++) { + for (uint256 i = 0; i < _mintStages.length;) { if ( timestamp >= _mintStages[i].startTimeUnixSeconds && timestamp < _mintStages[i].endTimeUnixSeconds ) { return i; } + unchecked { ++i; } } revert InvalidStage(); } diff --git a/contracts/ERC721M.sol b/contracts/ERC721M.sol index 75e45d79..722edf5a 100644 --- a/contracts/ERC721M.sol +++ b/contracts/ERC721M.sol @@ -284,50 +284,6 @@ contract ERC721M is IERC721M, ERC721AQueryable, Ownable, ReentrancyGuard { return (_mintStages[index], walletMinted, stageMinted); } - /** - * @dev Updates info for one stage specified by index (starting from 0). - */ - function updateStage( - uint256 index, - uint80 price, - uint32 walletLimit, - bytes32 merkleRoot, - uint24 maxStageSupply, - uint64 startTimeUnixSeconds, - uint64 endTimeUnixSeconds - ) external onlyOwner { - if (index >= _mintStages.length) revert InvalidStage(); - if (index >= 1) { - if ( - startTimeUnixSeconds < - _mintStages[index - 1].endTimeUnixSeconds + - _timestampExpirySeconds - ) { - revert InsufficientStageTimeGap(); - } - } - _assertValidStartAndEndTimestamp( - startTimeUnixSeconds, - endTimeUnixSeconds - ); - _mintStages[index].price = price; - _mintStages[index].walletLimit = walletLimit; - _mintStages[index].merkleRoot = merkleRoot; - _mintStages[index].maxStageSupply = maxStageSupply; - _mintStages[index].startTimeUnixSeconds = startTimeUnixSeconds; - _mintStages[index].endTimeUnixSeconds = endTimeUnixSeconds; - - emit UpdateStage( - index, - price, - walletLimit, - merkleRoot, - maxStageSupply, - startTimeUnixSeconds, - endTimeUnixSeconds - ); - } - /** * @dev Returns mint currency address. */ @@ -508,19 +464,10 @@ contract ERC721M is IERC721M, ERC721AQueryable, Ownable, ReentrancyGuard { * @dev Sets token base URI. */ function setBaseURI(string calldata baseURI) external onlyOwner { - if (_baseURIPermanent) revert CannotUpdatePermanentBaseURI(); _currentBaseURI = baseURI; emit SetBaseURI(baseURI); } - /** - * @dev Sets token base URI permanent. Cannot revert. - */ - function setBaseURIPermanent() external onlyOwner { - _baseURIPermanent = true; - emit PermanentBaseURI(_currentBaseURI); - } - /** * @dev Sets token URI suffix. e.g. ".json". */ diff --git a/contracts/IERC721M.sol b/contracts/IERC721M.sol index 2ee94e66..78f89908 100644 --- a/contracts/IERC721M.sol +++ b/contracts/IERC721M.sol @@ -5,7 +5,6 @@ import "erc721a/contracts/extensions/IERC721AQueryable.sol"; interface IERC721M is IERC721AQueryable { error CannotIncreaseMaxMintableSupply(); - error CannotUpdatePermanentBaseURI(); error CosignerNotSet(); error CrossmintAddressNotSet(); error CrossmintOnly(); @@ -56,7 +55,6 @@ interface IERC721M is IERC721AQueryable { event SetBaseURI(string baseURI); event SetTimestampExpirySeconds(uint64 expiry); event SetMintCurrency(address mintCurrency); - event PermanentBaseURI(string baseURI); event Withdraw(uint256 value); event WithdrawERC20(address mintCurrency, uint256 value); diff --git a/test/ERC721CM.test.ts b/test/ERC721CM.test.ts index 2b848eda..2f9abc10 100644 --- a/test/ERC721CM.test.ts +++ b/test/ERC721CM.test.ts @@ -281,202 +281,6 @@ describe('ERC721CM', function () { expect(walletMintedCount).to.equal(0); }); - it('can update stage', async () => { - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 3, - merkleRoot: ethers.utils.hexZeroPad('0x1', 32), - maxStageSupply: 5, - startTimeUnixSeconds: 0, - endTimeUnixSeconds: 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 4, - merkleRoot: ethers.utils.hexZeroPad('0x2', 32), - maxStageSupply: 10, - startTimeUnixSeconds: 61, - endTimeUnixSeconds: 62, - }, - ]); - - expect(await contract.getNumberStages()).to.equal(2); - - let [stageInfo, walletMintedCount] = await contract.getStageInfo(0); - expect(stageInfo.price).to.equal(ethers.utils.parseEther('0.5')); - expect(stageInfo.walletLimit).to.equal(3); - expect(stageInfo.maxStageSupply).to.equal(5); - expect(stageInfo.merkleRoot).to.equal(ethers.utils.hexZeroPad('0x1', 32)); - expect(walletMintedCount).to.equal(0); - - [stageInfo, walletMintedCount] = await contract.getStageInfo(1); - expect(stageInfo.price).to.equal(ethers.utils.parseEther('0.6')); - expect(stageInfo.walletLimit).to.equal(4); - expect(stageInfo.maxStageSupply).to.equal(10); - expect(stageInfo.merkleRoot).to.equal(ethers.utils.hexZeroPad('0x2', 32)); - expect(walletMintedCount).to.equal(0); - - // Update first stage - await expect( - contract.updateStage( - /* _index= */ 0, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 0, - /* endTimeUnixSeconds= */ 1, - ), - ) - .to.emit(contract, 'UpdateStage') - .withArgs( - 0, - ethers.utils.parseEther('0.1'), - 13, - ethers.utils.hexZeroPad('0x9', 32), - 15, - 0, - 1, - ); - - expect(await contract.getNumberStages()).to.equal(2); - - [stageInfo, walletMintedCount] = await contract.getStageInfo(0); - expect(stageInfo.price).to.equal(ethers.utils.parseEther('0.1')); - expect(stageInfo.walletLimit).to.equal(13); - expect(stageInfo.maxStageSupply).to.equal(15); - expect(stageInfo.merkleRoot).to.equal(ethers.utils.hexZeroPad('0x9', 32)); - expect(walletMintedCount).to.equal(0); - - // Stage 2 is unchanged. - [stageInfo, walletMintedCount] = await contract.getStageInfo(1); - expect(stageInfo.price).to.equal(ethers.utils.parseEther('0.6')); - expect(stageInfo.walletLimit).to.equal(4); - expect(stageInfo.maxStageSupply).to.equal(10); - expect(stageInfo.merkleRoot).to.equal(ethers.utils.hexZeroPad('0x2', 32)); - expect(walletMintedCount).to.equal(0); - }); - - it('updates stage reverts for non-existent stage', async () => { - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 3, - merkleRoot: ethers.utils.hexZeroPad('0x1', 32), - maxStageSupply: 5, - startTimeUnixSeconds: 0, - endTimeUnixSeconds: 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 4, - merkleRoot: ethers.utils.hexZeroPad('0x2', 32), - maxStageSupply: 10, - startTimeUnixSeconds: 61, - endTimeUnixSeconds: 62, - }, - ]); - - // Update a stage which doesn't exist. - const updateStage = contract.updateStage( - /* _index= */ 2, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 0, - /* endTimeUnixSeconds= */ 0, - ); - - await expect(updateStage).to.be.revertedWith('InvalidStage'); - }); - - it('cannot update stage to insufficient stage gap', async () => { - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 3, - merkleRoot: ethers.utils.hexZeroPad('0x1', 32), - maxStageSupply: 5, - startTimeUnixSeconds: 0, - endTimeUnixSeconds: 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 4, - merkleRoot: ethers.utils.hexZeroPad('0x2', 32), - maxStageSupply: 10, - startTimeUnixSeconds: 61, - endTimeUnixSeconds: 62, - }, - ]); - - // Set stage 1 to only be 59 seconds from stage 2 - const updateStage = contract.updateStage( - /* _index= */ 1, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 0, - /* endTimeUnixSeconds= */ 2, - ); - - await expect(updateStage).to.be.revertedWith('InsufficientStageTimeGap'); - }); - - it('cannot update stage due to the startTimeUnixSeconds is not smaller than the endTimeUnixSeconds', async () => { - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 3, - merkleRoot: ethers.utils.hexZeroPad('0x1', 32), - maxStageSupply: 5, - startTimeUnixSeconds: 0, - endTimeUnixSeconds: 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 4, - merkleRoot: ethers.utils.hexZeroPad('0x2', 32), - maxStageSupply: 10, - startTimeUnixSeconds: 61, - endTimeUnixSeconds: 62, - }, - ]); - - // Update stage 1 and set startTimeUnixSeconds and endTimeUnixSeconds to identical values - const updateStageWithIdenticalStartAndEndTime = contract.updateStage( - /* _index= */ 1, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 61, - /* endTimeUnixSeconds= */ 61, - ); - - await expect(updateStageWithIdenticalStartAndEndTime).to.be.revertedWith( - 'InvalidStartAndEndTimestamp', - ); - - // Update stage 1 and set startTimeUnixSeconds to a value which is not smaller than the endTimeUnixSeconds - const updateStageWithStartTimeAfterEndTime = contract.updateStage( - /* _index= */ 1, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 61, - /* endTimeUnixSeconds= */ 60, - ); - - await expect(updateStageWithStartTimeAfterEndTime).to.be.revertedWith( - 'InvalidStartAndEndTimestamp', - ); - }); - it('gets stage info', async () => { await contract.setStages([ { @@ -1759,61 +1563,6 @@ describe('ERC721CM', function () { 'URIQueryForNonexistentToken', ); }); - - it('Returns should not be able to set baseURI once frozen', async () => { - const block = await ethers.provider.getBlock( - await ethers.provider.getBlockNumber(), - ); - // +10 is a number bigger than the count of transactions up to mint - const stageStart = block.timestamp + 10; - // Set stages - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 10, - merkleRoot: ethers.utils.hexZeroPad('0x0', 32), - maxStageSupply: 5, - startTimeUnixSeconds: stageStart, - endTimeUnixSeconds: stageStart + 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 10, - merkleRoot: ethers.utils.hexZeroPad('0x0', 32), - maxStageSupply: 10, - startTimeUnixSeconds: stageStart + 61, - endTimeUnixSeconds: stageStart + 62, - }, - ]); - - await contract.setBaseURI('base_uri_'); - await contract.setMintable(true); - - // Setup the test context: Update block.timestamp to comply to the stage being active - await ethers.provider.send('evm_mine', [stageStart - 1]); - await contract.mint(2, [ethers.utils.hexZeroPad('0x', 32)], 0, '0x00', { - value: ethers.utils.parseEther('2.5'), - }); - - expect(await contract.tokenURI(0)).to.equal('base_uri_0'); - expect(await contract.tokenURI(1)).to.equal('base_uri_1'); - - await contract.setBaseURI('base_uri_again_'); - expect(await contract.tokenURI(0)).to.equal('base_uri_again_0'); - - // readonlyContract should not be able to set baseURI - await expect( - readonlyContract.setBaseURI('something_else_'), - ).to.be.revertedWith('Ownable'); - - await expect(contract.setBaseURIPermanent()).to.emit( - contract, - 'PermanentBaseURI', - ); - await expect( - contract.setBaseURI('base_uri_again_again_'), - ).to.be.revertedWith('CannotUpdatePermanentBaseURI'); - }); }); describe('Global wallet limit', function () { diff --git a/test/erc721m.test.ts b/test/erc721m.test.ts index b46f0e02..418353f0 100644 --- a/test/erc721m.test.ts +++ b/test/erc721m.test.ts @@ -285,202 +285,6 @@ describe('ERC721M', function () { expect(walletMintedCount).to.equal(0); }); - it('can update stage', async () => { - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 3, - merkleRoot: ethers.utils.hexZeroPad('0x1', 32), - maxStageSupply: 5, - startTimeUnixSeconds: 0, - endTimeUnixSeconds: 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 4, - merkleRoot: ethers.utils.hexZeroPad('0x2', 32), - maxStageSupply: 10, - startTimeUnixSeconds: 61, - endTimeUnixSeconds: 62, - }, - ]); - - expect(await contract.getNumberStages()).to.equal(2); - - let [stageInfo, walletMintedCount] = await contract.getStageInfo(0); - expect(stageInfo.price).to.equal(ethers.utils.parseEther('0.5')); - expect(stageInfo.walletLimit).to.equal(3); - expect(stageInfo.maxStageSupply).to.equal(5); - expect(stageInfo.merkleRoot).to.equal(ethers.utils.hexZeroPad('0x1', 32)); - expect(walletMintedCount).to.equal(0); - - [stageInfo, walletMintedCount] = await contract.getStageInfo(1); - expect(stageInfo.price).to.equal(ethers.utils.parseEther('0.6')); - expect(stageInfo.walletLimit).to.equal(4); - expect(stageInfo.maxStageSupply).to.equal(10); - expect(stageInfo.merkleRoot).to.equal(ethers.utils.hexZeroPad('0x2', 32)); - expect(walletMintedCount).to.equal(0); - - // Update first stage - await expect( - contract.updateStage( - /* _index= */ 0, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 0, - /* endTimeUnixSeconds= */ 1, - ), - ) - .to.emit(contract, 'UpdateStage') - .withArgs( - 0, - ethers.utils.parseEther('0.1'), - 13, - ethers.utils.hexZeroPad('0x9', 32), - 15, - 0, - 1, - ); - - expect(await contract.getNumberStages()).to.equal(2); - - [stageInfo, walletMintedCount] = await contract.getStageInfo(0); - expect(stageInfo.price).to.equal(ethers.utils.parseEther('0.1')); - expect(stageInfo.walletLimit).to.equal(13); - expect(stageInfo.maxStageSupply).to.equal(15); - expect(stageInfo.merkleRoot).to.equal(ethers.utils.hexZeroPad('0x9', 32)); - expect(walletMintedCount).to.equal(0); - - // Stage 2 is unchanged. - [stageInfo, walletMintedCount] = await contract.getStageInfo(1); - expect(stageInfo.price).to.equal(ethers.utils.parseEther('0.6')); - expect(stageInfo.walletLimit).to.equal(4); - expect(stageInfo.maxStageSupply).to.equal(10); - expect(stageInfo.merkleRoot).to.equal(ethers.utils.hexZeroPad('0x2', 32)); - expect(walletMintedCount).to.equal(0); - }); - - it('updates stage reverts for non-existent stage', async () => { - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 3, - merkleRoot: ethers.utils.hexZeroPad('0x1', 32), - maxStageSupply: 5, - startTimeUnixSeconds: 0, - endTimeUnixSeconds: 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 4, - merkleRoot: ethers.utils.hexZeroPad('0x2', 32), - maxStageSupply: 10, - startTimeUnixSeconds: 61, - endTimeUnixSeconds: 62, - }, - ]); - - // Update a stage which doesn't exist. - const updateStage = contract.updateStage( - /* _index= */ 2, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 0, - /* endTimeUnixSeconds= */ 0, - ); - - await expect(updateStage).to.be.revertedWith('InvalidStage'); - }); - - it('cannot update stage to insufficient stage gap', async () => { - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 3, - merkleRoot: ethers.utils.hexZeroPad('0x1', 32), - maxStageSupply: 5, - startTimeUnixSeconds: 0, - endTimeUnixSeconds: 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 4, - merkleRoot: ethers.utils.hexZeroPad('0x2', 32), - maxStageSupply: 10, - startTimeUnixSeconds: 61, - endTimeUnixSeconds: 62, - }, - ]); - - // Set stage 1 to only be 59 seconds from stage 2 - const updateStage = contract.updateStage( - /* _index= */ 1, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 0, - /* endTimeUnixSeconds= */ 2, - ); - - await expect(updateStage).to.be.revertedWith('InsufficientStageTimeGap'); - }); - - it('cannot update stage due to the startTimeUnixSeconds is not smaller than the endTimeUnixSeconds', async () => { - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 3, - merkleRoot: ethers.utils.hexZeroPad('0x1', 32), - maxStageSupply: 5, - startTimeUnixSeconds: 0, - endTimeUnixSeconds: 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 4, - merkleRoot: ethers.utils.hexZeroPad('0x2', 32), - maxStageSupply: 10, - startTimeUnixSeconds: 61, - endTimeUnixSeconds: 62, - }, - ]); - - // Update stage 1 and set startTimeUnixSeconds and endTimeUnixSeconds to identical values - const updateStageWithIdenticalStartAndEndTime = contract.updateStage( - /* _index= */ 1, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 61, - /* endTimeUnixSeconds= */ 61, - ); - - await expect(updateStageWithIdenticalStartAndEndTime).to.be.revertedWith( - 'InvalidStartAndEndTimestamp', - ); - - // Update stage 1 and set startTimeUnixSeconds to a value which is not smaller than the endTimeUnixSeconds - const updateStageWithStartTimeAfterEndTime = contract.updateStage( - /* _index= */ 1, - /* price= */ ethers.utils.parseEther('0.1'), - /* walletLimit= */ 13, - /* merkleRoot= */ ethers.utils.hexZeroPad('0x9', 32), - /* maxStageSupply= */ 15, - /* startTimeUnixSeconds= */ 61, - /* endTimeUnixSeconds= */ 60, - ); - - await expect(updateStageWithStartTimeAfterEndTime).to.be.revertedWith( - 'InvalidStartAndEndTimestamp', - ); - }); - it('gets stage info', async () => { await contract.setStages([ { @@ -1757,61 +1561,6 @@ describe('ERC721M', function () { 'URIQueryForNonexistentToken', ); }); - - it('Returns should not be able to set baseURI once frozen', async () => { - const block = await ethers.provider.getBlock( - await ethers.provider.getBlockNumber(), - ); - // +10 is a number bigger than the count of transactions up to mint - const stageStart = block.timestamp + 10; - // Set stages - await contract.setStages([ - { - price: ethers.utils.parseEther('0.5'), - walletLimit: 10, - merkleRoot: ethers.utils.hexZeroPad('0x0', 32), - maxStageSupply: 5, - startTimeUnixSeconds: stageStart, - endTimeUnixSeconds: stageStart + 1, - }, - { - price: ethers.utils.parseEther('0.6'), - walletLimit: 10, - merkleRoot: ethers.utils.hexZeroPad('0x0', 32), - maxStageSupply: 10, - startTimeUnixSeconds: stageStart + 61, - endTimeUnixSeconds: stageStart + 62, - }, - ]); - - await contract.setBaseURI('base_uri_'); - await contract.setMintable(true); - - // Setup the test context: Update block.timestamp to comply to the stage being active - await ethers.provider.send('evm_mine', [stageStart - 1]); - await contract.mint(2, [ethers.utils.hexZeroPad('0x', 32)], 0, '0x00', { - value: ethers.utils.parseEther('2.5'), - }); - - expect(await contract.tokenURI(0)).to.equal('base_uri_0'); - expect(await contract.tokenURI(1)).to.equal('base_uri_1'); - - await contract.setBaseURI('base_uri_again_'); - expect(await contract.tokenURI(0)).to.equal('base_uri_again_0'); - - // readonlyContract should not be able to set baseURI - await expect( - readonlyContract.setBaseURI('something_else_'), - ).to.be.revertedWith('Ownable'); - - await expect(contract.setBaseURIPermanent()).to.emit( - contract, - 'PermanentBaseURI', - ); - await expect( - contract.setBaseURI('base_uri_again_again_'), - ).to.be.revertedWith('CannotUpdatePermanentBaseURI'); - }); }); describe('Global wallet limit', function () { From c23f7fb6cb84e26a20e298ed0810b2db2bc23c36 Mon Sep 17 00:00:00 2001 From: Channing <95395274+channing-magiceden@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:20:26 -0700 Subject: [PATCH 04/15] Contracts cleanup (#128) * Remove deprecated contracts * Clean up scripts * Clean up more * rename erc721m test file --- .solcover.js | 1 - README.md | 2 - contracts/ERC721MAutoApprover.sol | 65 --- .../ERC721MIncreasableOperatorFilterer.sol | 74 --- contracts/ERC721MIncreasableSupply.sol | 69 --- .../ERC721MOperatorFiltererAutoApprover.sol | 65 --- contracts/ERC721MPausable.sol | 63 --- contracts/ERC721MPausableOperatorFilterer.sol | 73 --- .../BucketAuctionOperatorFilterer.sol | 75 --- contracts/auctions/DutchAuction.sol | 176 ------ contracts/auctions/IDutchAuction.sol | 27 - contracts/mocks/MockLayerZeroEndpoint.sol | 137 ----- contracts/onft/ERC721MLite.sol | 502 ------------------ contracts/onft/ERC721MOnft.sol | 106 ---- contracts/onft/LzAppLite.sol | 184 ------- contracts/onft/NonblockingLzAppLite.sol | 111 ---- contracts/onft/ONFT721CoreLite.sol | 306 ----------- contracts/onft/ONFT721Lite.sol | 146 ----- hardhat.config.ts | 71 +-- scripts/common/constants.ts | 52 -- scripts/deploy.ts | 25 +- scripts/deployBA.ts | 10 +- scripts/deployOnft.ts | 78 --- scripts/index.ts | 4 - scripts/sendOnft.ts | 76 --- scripts/setOnftMinDstGas.ts | 46 -- scripts/setTrustedRemote.ts | 49 -- test/DutchAuction.test.ts | 465 ---------------- test/ERC721CM.test.ts | 18 +- test/{erc721m.test.ts => ERC721M.test.ts} | 43 +- test/ERC721MAutoApprover.test.ts | 108 ---- test/ERC721MIncreasableSupply.test.ts | 41 -- test/ERC721MOnft.test.ts | 204 ------- ...RC721MOperatorFiltererAutoApprover.test.ts | 110 ---- test/ERC721MPausable.test.ts | 148 ------ test/ERC721MPausableOperatorFilterer.test.ts | 176 ------ 36 files changed, 40 insertions(+), 3866 deletions(-) delete mode 100644 contracts/ERC721MAutoApprover.sol delete mode 100644 contracts/ERC721MIncreasableOperatorFilterer.sol delete mode 100644 contracts/ERC721MIncreasableSupply.sol delete mode 100644 contracts/ERC721MOperatorFiltererAutoApprover.sol delete mode 100644 contracts/ERC721MPausable.sol delete mode 100644 contracts/ERC721MPausableOperatorFilterer.sol delete mode 100644 contracts/auctions/BucketAuctionOperatorFilterer.sol delete mode 100644 contracts/auctions/DutchAuction.sol delete mode 100644 contracts/auctions/IDutchAuction.sol delete mode 100644 contracts/mocks/MockLayerZeroEndpoint.sol delete mode 100644 contracts/onft/ERC721MLite.sol delete mode 100644 contracts/onft/ERC721MOnft.sol delete mode 100644 contracts/onft/LzAppLite.sol delete mode 100644 contracts/onft/NonblockingLzAppLite.sol delete mode 100644 contracts/onft/ONFT721CoreLite.sol delete mode 100644 contracts/onft/ONFT721Lite.sol delete mode 100644 scripts/deployOnft.ts delete mode 100644 scripts/sendOnft.ts delete mode 100644 scripts/setOnftMinDstGas.ts delete mode 100644 scripts/setTrustedRemote.ts delete mode 100644 test/DutchAuction.test.ts rename test/{erc721m.test.ts => ERC721M.test.ts} (98%) delete mode 100644 test/ERC721MAutoApprover.test.ts delete mode 100644 test/ERC721MIncreasableSupply.test.ts delete mode 100644 test/ERC721MOnft.test.ts delete mode 100644 test/ERC721MOperatorFiltererAutoApprover.test.ts delete mode 100644 test/ERC721MPausable.test.ts delete mode 100644 test/ERC721MPausableOperatorFilterer.test.ts diff --git a/.solcover.js b/.solcover.js index 2402709b..9d7fb2f1 100644 --- a/.solcover.js +++ b/.solcover.js @@ -3,6 +3,5 @@ module.exports = { "auctions/", "creator-token-standards/", "mocks/", - "onft/", ], }; \ No newline at end of file diff --git a/README.md b/README.md index 68d8ca58..323f9e50 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,8 @@ The composability of the stages is generic enough to enable flexible and complic | ERC721M | The basic minting contract based on ERC721A. | | ERC721CM | The basic minting contract based on ERC721C and ERC721M. | | ERC721CMRoyalties | Based on ERC721CM, implementing ERC2981 for on-chain royalty. | -| ERC721MLite | The lite version of ERC721M | | ERC721MOperatorFilterer | ERC721M with OpenSea Operator Filterer | | BucketAuction | Bucket auction style minting contract. The contract is on beta. Use at your own risk. | -| DutchAuction | Dutch auction style minting contract. The contract is on beta. Use at your own risk. | Please read [ERC721M Contract Usage Guide](./docs/ContractUsageGuide.md) for more details. diff --git a/contracts/ERC721MAutoApprover.sol b/contracts/ERC721MAutoApprover.sol deleted file mode 100644 index 4af23a80..00000000 --- a/contracts/ERC721MAutoApprover.sol +++ /dev/null @@ -1,65 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "./ERC721M.sol"; - -contract ERC721MAutoApprover is ERC721M { - address private _autoApproveAddress; - - event SetAutoApproveAddress(address autoApproveAddress); - - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint64 timestampExpirySeconds, - address mintCurrency, - address autoApproveAddress - ) - ERC721M( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - timestampExpirySeconds, - mintCurrency - ) - { - _autoApproveAddress = autoApproveAddress; - } - - function mint( - uint32 qty, - bytes32[] calldata proof, - uint64 timestamp, - bytes calldata signature - ) external payable override nonReentrant { - _mintInternal(qty, msg.sender, 0, proof, timestamp, signature); - - // if auto approve address is not all zero, check if the address is already approved - if ( - _autoApproveAddress != address(0) && - !super.isApprovedForAll(msg.sender, _autoApproveAddress) - ) { - // approve the address if not already approved - super.setApprovalForAll(_autoApproveAddress, true); - } - } - - function getAutoApproveAddress() external view returns (address) { - return _autoApproveAddress; - } - - function setAutoApproveAddress( - address autoApproveAddress - ) external onlyOwner { - _autoApproveAddress = autoApproveAddress; - emit SetAutoApproveAddress(autoApproveAddress); - } -} diff --git a/contracts/ERC721MIncreasableOperatorFilterer.sol b/contracts/ERC721MIncreasableOperatorFilterer.sol deleted file mode 100644 index 6f29f89e..00000000 --- a/contracts/ERC721MIncreasableOperatorFilterer.sol +++ /dev/null @@ -1,74 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "./ERC721M.sol"; -import "./ERC721MIncreasableSupply.sol"; -import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; -import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "./utils/Constants.sol"; - -contract ERC721MIncreasableOperatorFilterer is - ERC721MIncreasableSupply, - UpdatableOperatorFilterer -{ - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint64 timestampExpirySeconds, - address mintCurrency - ) - UpdatableOperatorFilterer( - CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, - ME_SUBSCRIPTION, - true - ) - ERC721MIncreasableSupply( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - timestampExpirySeconds, - mintCurrency - ) - {} - - function owner() - public - view - override(Ownable, UpdatableOperatorFilterer) - returns (address) - { - return Ownable.owner(); - } - - function transferFrom( - address from, - address to, - uint256 tokenId - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.transferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.safeTransferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory data - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.safeTransferFrom(from, to, tokenId, data); - } -} diff --git a/contracts/ERC721MIncreasableSupply.sol b/contracts/ERC721MIncreasableSupply.sol deleted file mode 100644 index 0aecb54a..00000000 --- a/contracts/ERC721MIncreasableSupply.sol +++ /dev/null @@ -1,69 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "./ERC721M.sol"; - -contract ERC721MIncreasableSupply is ERC721M { - // Whether mintable supply can increase. Once set to false, _maxMintableSupply can never increase. - bool private _canIncreaseMaxMintableSupply; - - event DisableIncreaseMaxMintableSupply(); - - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint64 timestampExpirySeconds, - address mintCurrency - ) - ERC721M( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - timestampExpirySeconds, - mintCurrency - ) - { - _canIncreaseMaxMintableSupply = true; - } - - /** - * @dev Return true if max mintable supply can be increased. - */ - function getCanIncreaseMaxMintableSupply() external view returns (bool) { - return _canIncreaseMaxMintableSupply; - } - - /** - * @dev Makes _canIncreaseMaxMintableSupply false permanently. - */ - function disableIncreaseMaxMintableSupply() external onlyOwner { - _canIncreaseMaxMintableSupply = false; - emit DisableIncreaseMaxMintableSupply(); - } - - /** - * @dev Sets maximum mintable supply. - * - * New supply cannot be larger than the old, unless _canIncreaseMaxMintableSupply is true. - */ - function setMaxMintableSupply( - uint256 maxMintableSupply - ) external override onlyOwner { - if ( - !_canIncreaseMaxMintableSupply && - maxMintableSupply > _maxMintableSupply - ) { - revert CannotIncreaseMaxMintableSupply(); - } - _maxMintableSupply = maxMintableSupply; - emit SetMaxMintableSupply(maxMintableSupply); - } -} diff --git a/contracts/ERC721MOperatorFiltererAutoApprover.sol b/contracts/ERC721MOperatorFiltererAutoApprover.sol deleted file mode 100644 index 5d7d3e1f..00000000 --- a/contracts/ERC721MOperatorFiltererAutoApprover.sol +++ /dev/null @@ -1,65 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "./ERC721MOperatorFilterer.sol"; - -contract ERC721MOperatorFiltererAutoApprover is ERC721MOperatorFilterer { - address private _autoApproveAddress; - - event SetAutoApproveAddress(address autoApproveAddress); - - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint64 timestampExpirySeconds, - address mintCurrency, - address autoApproveAddress - ) - ERC721MOperatorFilterer( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - timestampExpirySeconds, - mintCurrency - ) - { - _autoApproveAddress = autoApproveAddress; - } - - function mint( - uint32 qty, - bytes32[] calldata proof, - uint64 timestamp, - bytes calldata signature - ) external payable override nonReentrant { - _mintInternal(qty, msg.sender, 0, proof, timestamp, signature); - - // if auto approve address is not all zero, check if the address is already approved - if ( - _autoApproveAddress != address(0) && - !super.isApprovedForAll(msg.sender, _autoApproveAddress) - ) { - // approve the address if not already approved - super.setApprovalForAll(_autoApproveAddress, true); - } - } - - function getAutoApproveAddress() external view returns (address) { - return _autoApproveAddress; - } - - function setAutoApproveAddress( - address autoApproveAddress - ) external onlyOwner { - _autoApproveAddress = autoApproveAddress; - emit SetAutoApproveAddress(autoApproveAddress); - } -} diff --git a/contracts/ERC721MPausable.sol b/contracts/ERC721MPausable.sol deleted file mode 100644 index 6321c300..00000000 --- a/contracts/ERC721MPausable.sol +++ /dev/null @@ -1,63 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts/security/Pausable.sol"; -import "./ERC721M.sol"; - -contract ERC721MPausable is ERC721M, Pausable { - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint64 timestampExpirySeconds, - address mintCurrency - ) - ERC721M( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - timestampExpirySeconds, - mintCurrency - ) - {} - - function pause() external onlyOwner { - _pause(); - } - - function unpause() external onlyOwner { - _unpause(); - } - - function transferFrom( - address from, - address to, - uint256 tokenId - ) public payable virtual override(ERC721A, IERC721A) whenNotPaused { - super.transferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public payable virtual override(ERC721A, IERC721A) whenNotPaused { - super.safeTransferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory data - ) public payable virtual override(ERC721A, IERC721A) whenNotPaused { - super.safeTransferFrom(from, to, tokenId, data); - } -} diff --git a/contracts/ERC721MPausableOperatorFilterer.sol b/contracts/ERC721MPausableOperatorFilterer.sol deleted file mode 100644 index f865ecff..00000000 --- a/contracts/ERC721MPausableOperatorFilterer.sol +++ /dev/null @@ -1,73 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "./ERC721MPausable.sol"; -import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; -import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "./utils/Constants.sol"; - -contract ERC721MPausableOperatorFilterer is - ERC721MPausable, - UpdatableOperatorFilterer -{ - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint64 timestampExpirySeconds, - address mintCurrency - ) - UpdatableOperatorFilterer( - CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, - ME_SUBSCRIPTION, - true - ) - ERC721MPausable( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - timestampExpirySeconds, - mintCurrency - ) - {} - - function owner() - public - view - override(Ownable, UpdatableOperatorFilterer) - returns (address) - { - return Ownable.owner(); - } - - function transferFrom( - address from, - address to, - uint256 tokenId - ) public payable override(ERC721MPausable) onlyAllowedOperator(from) { - super.transferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public payable override(ERC721MPausable) onlyAllowedOperator(from) { - super.safeTransferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory data - ) public payable override(ERC721MPausable) onlyAllowedOperator(from) { - super.safeTransferFrom(from, to, tokenId, data); - } -} diff --git a/contracts/auctions/BucketAuctionOperatorFilterer.sol b/contracts/auctions/BucketAuctionOperatorFilterer.sol deleted file mode 100644 index ea82aa48..00000000 --- a/contracts/auctions/BucketAuctionOperatorFilterer.sol +++ /dev/null @@ -1,75 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "./BucketAuction.sol"; -import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; -import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "../utils/Constants.sol"; - -contract BucketAuctionOperatorFilterer is - BucketAuction, - UpdatableOperatorFilterer -{ - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint256 minimumContributionInWei, - uint64 startTimeUnixSeconds, - uint64 endTimeUnixSeconds - ) - UpdatableOperatorFilterer( - CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, - ME_SUBSCRIPTION, - true - ) - BucketAuction( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - minimumContributionInWei, - startTimeUnixSeconds, - endTimeUnixSeconds - ) - {} - - function owner() - public - view - override(Ownable, UpdatableOperatorFilterer) - returns (address) - { - return Ownable.owner(); - } - - function transferFrom( - address from, - address to, - uint256 tokenId - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.transferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.safeTransferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory data - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.safeTransferFrom(from, to, tokenId, data); - } -} diff --git a/contracts/auctions/DutchAuction.sol b/contracts/auctions/DutchAuction.sol deleted file mode 100644 index f37d1243..00000000 --- a/contracts/auctions/DutchAuction.sol +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/access/AccessControl.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/token/common/ERC2981.sol"; -import "erc721a/contracts/ERC721A.sol"; - -import "./IDutchAuction.sol"; -import "../ERC721M.sol"; - -contract DutchAuction is IDutchAuction, ERC721M { - bool private immutable _refundable; - uint256 private _settledPriceInWei; - Config private _config; - mapping(address => User) private _userData; - - constructor( - // ERC721M - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - bool refundable - ) - ERC721M( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - /* timestampExpirySeconds= */ - 300, - /* mintCurrency= */ - address(0) - ) - { - _refundable = refundable; - } - - modifier validTime() { - Config memory config = _config; - if ( - block.timestamp > config.endTime || - block.timestamp < config.startTime - ) revert InvalidStartEndTime(config.startTime, config.endTime); - _; - } - - function setConfig( - uint256 startAmountInWei, - uint256 endAmountInWei, - uint64 startTime, - uint64 endTime - ) external onlyOwner { - if (startTime == 0 || startTime >= endTime) - revert InvalidStartEndTime(startTime, endTime); - if (startAmountInWei == 0) revert InvalidAmountInWei(); - - _config = Config({ - startAmountInWei: startAmountInWei, - endAmountInWei: endAmountInWei, - startTime: startTime, - endTime: endTime - }); - } - - function getConfig() external view returns (Config memory) { - return _config; - } - - function getSettledPriceInWei() external view returns (uint256) { - return _settledPriceInWei; - } - - function getCurrentPriceInWei() public view returns (uint256) { - Config memory config = _config; // storage to memory - // Return startAmountInWei if auction not started - if (block.timestamp <= config.startTime) return config.startAmountInWei; - // Return endAmountInWei if auction ended - if (block.timestamp >= config.endTime) return config.endAmountInWei; - - if (config.startAmountInWei != config.endAmountInWei) { - uint256 amount; - bool roundUp = true; // we always round up the calculation - - // Declare variables to derive in the subsequent unchecked scope. - uint256 duration; - uint256 elapsed; - uint256 remaining; - - // Skip underflow checks as startTime <= block.timestamp < endTime. - unchecked { - // Derive the duration for the order and place it on the stack. - duration = config.endTime - config.startTime; - - // Derive time elapsed since the order started & place on stack. - elapsed = block.timestamp - config.startTime; - - // Derive time remaining until order expires and place on stack. - remaining = duration - elapsed; - } - - // Aggregate new amounts weighted by time with rounding factor. - // TODO: check the math boundary here - uint256 totalBeforeDivision = ((config.startAmountInWei * - remaining) + (config.endAmountInWei * elapsed)); - - // Use assembly to combine operations and skip divide-by-zero check. - assembly { - // Multiply by iszero(iszero(totalBeforeDivision)) to ensure - // amount is set to zero if totalBeforeDivision is zero, - // as intermediate overflow can occur if it is zero. - amount := mul( - iszero(iszero(totalBeforeDivision)), - // Subtract 1 from the numerator and add 1 to the result if - // roundUp is true to get the proper rounding direction. - // Division is performed with no zero check as duration - // cannot be zero as long as startTime < endTime. - add( - div(sub(totalBeforeDivision, roundUp), duration), - roundUp - ) - ) - } - - // Return the current amount. - return amount; - } - - // Return the original amount as startAmount == endAmount. - return config.endAmountInWei; - } - - function bid( - uint32 qty - ) external payable nonReentrant hasSupply(qty) validTime { - uint256 price = getCurrentPriceInWei(); - if (msg.value < qty * price) revert NotEnoughValue(); - - if (_refundable) { - User storage bidder = _userData[msg.sender]; // get user's current bid total - bidder.contribution = bidder.contribution + uint216(msg.value); - bidder.tokensBidded = bidder.tokensBidded + qty; - - // _settledPriceInWei is always the minimum price of all the bids' unit price - if (price < _settledPriceInWei || _settledPriceInWei == 0) { - _settledPriceInWei = price; - } - } - _safeMint(msg.sender, qty); - emit Bid(msg.sender, qty, price); - } - - function claimRefund() external nonReentrant { - Config memory config = _config; - if (!_refundable) revert NotRefundable(); - if (config.endTime > block.timestamp) revert NotEnded(); - - User storage user = _userData[msg.sender]; - if (user.refundClaimed) revert UserAlreadyClaimed(); - user.refundClaimed = true; - uint256 refundInWei = user.contribution - - (_settledPriceInWei * user.tokensBidded); - if (refundInWei > 0) { - (bool success, ) = msg.sender.call{value: refundInWei}(""); - if (!success) revert TransferFailed(); - emit ClaimRefund(msg.sender, refundInWei); - } - } -} diff --git a/contracts/auctions/IDutchAuction.sol b/contracts/auctions/IDutchAuction.sol deleted file mode 100644 index 1e1b7f1d..00000000 --- a/contracts/auctions/IDutchAuction.sol +++ /dev/null @@ -1,27 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -interface IDutchAuction { - error InvalidStartEndTime(uint64 startTime, uint64 endTime); - error NotStarted(); - error NotEnded(); - error InvalidAmountInWei(); - error NotRefundable(); - error TransferFailed(); - error UserAlreadyClaimed(); - - struct User { - uint216 contribution; // cumulative sum of Wei bids - uint32 tokensBidded; // cumulative sum of bidded tokens - bool refundClaimed; // has user been refunded yet - } - - struct Config { - uint256 startAmountInWei; - uint256 endAmountInWei; - uint64 startTime; - uint64 endTime; - } - event ClaimRefund(address user, uint256 refundInWei); - event Bid(address user, uint32 qty, uint256 price); -} diff --git a/contracts/mocks/MockLayerZeroEndpoint.sol b/contracts/mocks/MockLayerZeroEndpoint.sol deleted file mode 100644 index a74e451f..00000000 --- a/contracts/mocks/MockLayerZeroEndpoint.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {ILayerZeroEndpoint} from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroEndpoint.sol"; - -contract MockLayerZeroEndpoint is ILayerZeroEndpoint { - function send( - uint16 _dstChainId, - bytes calldata _destination, - bytes calldata _payload, - address payable _refundAddress, - address _zroPaymentAddress, - bytes calldata _adapterParams - ) external payable { - // do nothing - } - - function receivePayload( - uint16 _srcChainId, - bytes calldata _srcAddress, - address _dstAddress, - uint64 _nonce, - uint256 _gasLimit, - bytes calldata _payload - ) external { - // do nothing - } - - function getInboundNonce( - uint16 _srcChainId, - bytes calldata _srcAddress - ) external view returns (uint64) { - return 0; - } - - function getOutboundNonce( - uint16 _dstChainId, - address _srcAddress - ) external view returns (uint64) { - return 0; - } - - function estimateFees( - uint16 _dstChainId, - address _userApplication, - bytes calldata _payload, - bool _payInZRO, - bytes calldata _adapterParam - ) external view returns (uint256 nativeFee, uint256 zroFee) { - return (0, 0); - } - - function getChainId() external view returns (uint16) { - return 0; - } - - function retryPayload( - uint16 _srcChainId, - bytes calldata _srcAddress, - bytes calldata _payload - ) external { - // do nothing - } - - function hasStoredPayload( - uint16 _srcChainId, - bytes calldata _srcAddress - ) external view returns (bool) { - return false; - } - - function getSendLibraryAddress( - address _userApplication - ) external view returns (address) { - return address(0); - } - - function getReceiveLibraryAddress( - address _userApplication - ) external view returns (address) { - return address(0); - } - - function isSendingPayload() external view returns (bool) { - return false; - } - - function isReceivingPayload() external view returns (bool) { - return false; - } - - function getConfig( - uint16 _version, - uint16 _chainId, - address _userApplication, - uint256 _configType - ) external view returns (bytes memory) { - return ""; - } - - function getSendVersion( - address _userApplication - ) external view returns (uint16) { - return 0; - } - - function getReceiveVersion( - address _userApplication - ) external view returns (uint16) { - return 0; - } - - function setConfig( - uint16 _version, - uint16 _chainId, - uint256 _configType, - bytes calldata _config - ) external { - // do nothing - } - - function setSendVersion(uint16 _version) external { - // do nothing - } - - function setReceiveVersion(uint16 _version) external { - // do nothing - } - - function forceResumeReceive( - uint16 _srcChainId, - bytes calldata _srcAddress - ) external { - // do nothing - } -} diff --git a/contracts/onft/ERC721MLite.sol b/contracts/onft/ERC721MLite.sol deleted file mode 100644 index 0eb746ba..00000000 --- a/contracts/onft/ERC721MLite.sol +++ /dev/null @@ -1,502 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; -import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; -import {ERC721A, ERC721AQueryable, ERC721A__IERC721Receiver} from "erc721a/contracts/extensions/ERC721AQueryable.sol"; -import {IERC721A, IERC721M} from "../IERC721M.sol"; -import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; -import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "../utils/Constants.sol"; - -/** - * @title ERC721MLite - * - * @dev Lite version of ERC721M, without the following features: - * - crossminting - * - update a speficic stage - */ -contract ERC721MLite is - IERC721M, - ERC721AQueryable, - UpdatableOperatorFilterer, - Ownable, - ReentrancyGuard -{ - using ECDSA for bytes32; - - // Whether this contract is mintable. - bool private _mintable; - - // Specify how long a signature from cosigner is valid for, recommend 300 seconds. - uint64 private _timestampExpirySeconds; - - // The address of the cosigner server. - address private _cosigner; - - // The total mintable supply. - uint256 internal _maxMintableSupply; - - // Global wallet limit, across all stages. - uint256 private _globalWalletLimit; - - // Current base URI. - string private _currentBaseURI; - - // The suffix for the token URL, e.g. ".json". - string private _tokenURISuffix; - - // Mint stage infomation. See MintStageInfo for details. - MintStageInfo[] private _mintStages; - - // Minted count per stage per wallet. - mapping(uint256 => mapping(address => uint32)) - private _stageMintedCountsPerWallet; - - // Minted count per stage. - mapping(uint256 => uint256) private _stageMintedCounts; - - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint64 timestampExpirySeconds - ) - UpdatableOperatorFilterer( - CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, - ME_SUBSCRIPTION, - true - ) - ERC721A(collectionName, collectionSymbol) - { - if (globalWalletLimit > maxMintableSupply) - revert GlobalWalletLimitOverflow(); - _mintable = false; - _maxMintableSupply = maxMintableSupply; - _globalWalletLimit = globalWalletLimit; - _tokenURISuffix = tokenURISuffix; - _cosigner = cosigner; // ethers.constants.AddressZero for no cosigning - _timestampExpirySeconds = timestampExpirySeconds; - } - - /** - * @dev Returns whether mintable. - */ - modifier canMint() { - if (!_mintable) revert NotMintable(); - _; - } - - /** - * @dev Returns whether it has enough supply for the given qty. - */ - modifier hasSupply(uint256 qty) { - if (totalSupply() + qty > _maxMintableSupply) revert NoSupplyLeft(); - _; - } - - /** - * @dev Returns cosign nonce. - */ - function getCosignNonce(address minter) public view returns (uint256) { - return _numberMinted(minter); - } - - /** - * @dev Sets stages in the format of an array of `MintStageInfo`. - * - * Following is an example of launch with two stages. The first stage is exclusive for whitelisted wallets - * specified by merkle root. - * [{ - * price: 10000000000000000000, - * maxStageSupply: 2000, - * walletLimit: 1, - * merkleRoot: 0x559fadeb887449800b7b320bf1e92d309f329b9641ac238bebdb74e15c0a5218, - * startTimeUnixSeconds: 1667768000, - * endTimeUnixSeconds: 1667771600, - * }, - * { - * price: 20000000000000000000, - * maxStageSupply: 3000, - * walletLimit: 2, - * merkleRoot: 0, - * startTimeUnixSeconds: 1667771600, - * endTimeUnixSeconds: 1667775200, - * } - * ] - */ - function setStages(MintStageInfo[] calldata newStages) external onlyOwner { - uint256 originalSize = _mintStages.length; - for (uint256 i = 0; i < originalSize; i++) { - _mintStages.pop(); - } - - for (uint256 i = 0; i < newStages.length; i++) { - if (i >= 1) { - if ( - newStages[i].startTimeUnixSeconds < - newStages[i - 1].endTimeUnixSeconds + - _timestampExpirySeconds - ) { - revert InsufficientStageTimeGap(); - } - } - _assertValidStartAndEndTimestamp( - newStages[i].startTimeUnixSeconds, - newStages[i].endTimeUnixSeconds - ); - _mintStages.push( - MintStageInfo({ - price: newStages[i].price, - walletLimit: newStages[i].walletLimit, - merkleRoot: newStages[i].merkleRoot, - maxStageSupply: newStages[i].maxStageSupply, - startTimeUnixSeconds: newStages[i].startTimeUnixSeconds, - endTimeUnixSeconds: newStages[i].endTimeUnixSeconds - }) - ); - emit UpdateStage( - i, - newStages[i].price, - newStages[i].walletLimit, - newStages[i].merkleRoot, - newStages[i].maxStageSupply, - newStages[i].startTimeUnixSeconds, - newStages[i].endTimeUnixSeconds - ); - } - } - - /** - * @dev Sets mintable. - */ - function setMintable(bool mintable) external onlyOwner { - _mintable = mintable; - emit SetMintable(mintable); - } - - /** - * @dev Returns number of stages. - */ - function getNumberStages() external view override returns (uint256) { - return _mintStages.length; - } - - /** - * @dev Returns maximum mintable supply. - */ - function getMaxMintableSupply() external view override returns (uint256) { - return _maxMintableSupply; - } - - /** - * @dev Sets maximum mintable supply. - */ - function setMaxMintableSupply( - uint256 maxMintableSupply - ) external virtual onlyOwner { - if (maxMintableSupply > _maxMintableSupply) { - revert CannotIncreaseMaxMintableSupply(); - } - - _maxMintableSupply = maxMintableSupply; - emit SetMaxMintableSupply(maxMintableSupply); - } - - /** - * @dev Returns global wallet limit. This is the max number of tokens can be minted by one wallet. - */ - function getGlobalWalletLimit() external view override returns (uint256) { - return _globalWalletLimit; - } - - /** - * @dev Returns number of minted token for a given address. - */ - function totalMintedByAddress( - address a - ) external view virtual override returns (uint256) { - return _numberMinted(a); - } - - /** - * @dev Returns info for one stage specified by index (starting from 0). - */ - function getStageInfo( - uint256 index - ) external view override returns (MintStageInfo memory, uint32, uint256) { - if (index >= _mintStages.length) { - revert("InvalidStage"); - } - uint32 walletMinted = _stageMintedCountsPerWallet[index][msg.sender]; - uint256 stageMinted = _stageMintedCounts[index]; - return (_mintStages[index], walletMinted, stageMinted); - } - - /** - * @dev Mints token(s). - * - * qty - number of tokens to mint - * proof - the merkle proof generated on client side. This applies if using whitelist. - * timestamp - the current timestamp - * signature - the signature from cosigner if using cosigner. - */ - function mint( - uint32 qty, - bytes32[] calldata proof, - uint64 timestamp, - bytes calldata signature - ) external payable virtual canMint hasSupply(qty) nonReentrant { - uint64 stageTimestamp = uint64(block.timestamp); - - MintStageInfo memory stage; - if (_cosigner != address(0)) { - assertValidCosign(msg.sender, qty, timestamp, signature); - _assertValidTimestamp(timestamp); - stageTimestamp = timestamp; - } - - uint256 activeStage = getActiveStageFromTimestamp(stageTimestamp); - - stage = _mintStages[activeStage]; - - // Check value - if (msg.value < stage.price * qty) revert NotEnoughValue(); - - // Check stage supply if applicable - if (stage.maxStageSupply > 0) { - if (_stageMintedCounts[activeStage] + qty > stage.maxStageSupply) - revert StageSupplyExceeded(); - } - - // Check global wallet limit if applicable - if (_globalWalletLimit > 0) { - if (_numberMinted(msg.sender) + qty > _globalWalletLimit) - revert WalletGlobalLimitExceeded(); - } - - // Check wallet limit for stage if applicable, limit == 0 means no limit enforced - if (stage.walletLimit > 0) { - if ( - _stageMintedCountsPerWallet[activeStage][msg.sender] + qty > - stage.walletLimit - ) revert WalletStageLimitExceeded(); - } - - // Check merkle proof if applicable, merkleRoot == 0x00...00 means no proof required - if (stage.merkleRoot != 0) { - if ( - MerkleProof.processProof( - proof, - keccak256(abi.encodePacked(msg.sender, uint32(0))) // limit = 0 for consistency - ) != stage.merkleRoot - ) revert InvalidProof(); - } - - _stageMintedCountsPerWallet[activeStage][msg.sender] += qty; - _stageMintedCounts[activeStage] += qty; - _safeMint(msg.sender, qty); - } - - /** NOT SUPPORTED */ - function mintWithLimit( - uint32 qty, - uint32 limit, - bytes32[] calldata proof, - uint64 timestamp, - bytes calldata signature - ) external payable virtual nonReentrant { - revert NotSupported(); - } - - /** NOT SUPPORTED */ - function crossmint( - uint32 qty, - address to, - bytes32[] calldata proof, - uint64 timestamp, - bytes calldata signature - ) external payable nonReentrant { - revert NotSupported(); - } - - /** - * @dev Mints token(s) by owner. - * - * NOTE: This function bypasses validations thus only available for owner. - * This is typically used for owner to pre-mint or mint the remaining of the supply. - */ - function ownerMint( - uint32 qty, - address to - ) external onlyOwner hasSupply(qty) { - _safeMint(to, qty); - } - - /** - * @dev Withdraws funds by owner. - */ - function withdraw() external onlyOwner { - uint256 value = address(this).balance; - (bool success, ) = msg.sender.call{value: value}(""); - if (!success) revert WithdrawFailed(); - emit Withdraw(value); - } - - /** - * @dev Sets token base URI. - */ - function setBaseURI(string calldata baseURI) external onlyOwner { - _currentBaseURI = baseURI; - emit SetBaseURI(baseURI); - } - - /** - * @dev Returns token URI for a given token id. - */ - function tokenURI( - uint256 tokenId - ) public view override(ERC721A, IERC721A) returns (string memory) { - if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); - - string memory baseURI = _currentBaseURI; - return - bytes(baseURI).length != 0 - ? string( - abi.encodePacked( - baseURI, - _toString(tokenId), - _tokenURISuffix - ) - ) - : ""; - } - - /** - * @dev Returns data hash for the given minter, qty and timestamp. - */ - function _getCosignDigest( - address minter, - uint32 qty, - uint64 timestamp - ) public view returns (bytes32) { - if (_cosigner == address(0)) revert CosignerNotSet(); - return - keccak256( - abi.encodePacked( - address(this), - minter, - qty, - _cosigner, - timestamp, - _chainID(), - getCosignNonce(minter) - ) - ).toEthSignedMessageHash(); - } - - /** - * @dev Validates the the given signature. - */ - function assertValidCosign( - address minter, - uint32 qty, - uint64 timestamp, - bytes memory signature - ) public view { - if ( - !SignatureChecker.isValidSignatureNow( - _cosigner, - _getCosignDigest(minter, qty, timestamp), - signature - ) - ) revert InvalidCosignSignature(); - } - - /** - * @dev Returns the current active stage based on timestamp. - */ - function getActiveStageFromTimestamp( - uint64 timestamp - ) public view returns (uint256) { - for (uint256 i = 0; i < _mintStages.length; i++) { - if ( - timestamp >= _mintStages[i].startTimeUnixSeconds && - timestamp < _mintStages[i].endTimeUnixSeconds - ) { - return i; - } - } - revert InvalidStage(); - } - - /** - * @dev Validates the timestamp is not expired. - */ - function _assertValidTimestamp(uint64 timestamp) internal view { - if (timestamp < block.timestamp - _timestampExpirySeconds) - revert TimestampExpired(); - } - - /** - * @dev Validates the start timestamp is before end timestamp. Used when updating stages. - */ - function _assertValidStartAndEndTimestamp( - uint64 start, - uint64 end - ) internal pure { - if (start >= end) revert InvalidStartAndEndTimestamp(); - } - - /** - * @dev Returns chain id. - */ - function _chainID() private view returns (uint256) { - uint256 chainID; - assembly { - chainID := chainid() - } - return chainID; - } - - function owner() - public - view - virtual - override(Ownable, UpdatableOperatorFilterer) - returns (address) - { - return Ownable.owner(); - } - - function transferFrom( - address from, - address to, - uint256 tokenId - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.transferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.safeTransferFrom(from, to, tokenId); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory data - ) public payable override(ERC721A, IERC721A) onlyAllowedOperator(from) { - super.safeTransferFrom(from, to, tokenId, data); - } -} diff --git a/contracts/onft/ERC721MOnft.sol b/contracts/onft/ERC721MOnft.sol deleted file mode 100644 index 2304dfce..00000000 --- a/contracts/onft/ERC721MOnft.sol +++ /dev/null @@ -1,106 +0,0 @@ -//SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import {IONFT721Core, ONFT721CoreLite} from "./ONFT721CoreLite.sol"; -import {ERC721MLite, ERC721A, ERC721A__IERC721Receiver, IERC721A, Ownable} from "./ERC721MLite.sol"; -import {IONFT721} from "@layerzerolabs/solidity-examples/contracts/token/onft/IONFT721.sol"; - -/** - * @title ERC721MOnft - * - * @dev ERC721MOnft is an ERC721M contract with LayerZero integration. - */ -contract ERC721MOnft is ERC721MLite, ONFT721CoreLite, ERC721A__IERC721Receiver { - error CallerNotOwnerOrApproved(); - error FromAddressNotOwner(); - error NotExistOrNotOwnedByContract(); - - constructor( - string memory collectionName, - string memory collectionSymbol, - string memory tokenURISuffix, - uint256 maxMintableSupply, - uint256 globalWalletLimit, - address cosigner, - uint64 timestampExpirySeconds, - uint256 minGasToTransferAndStore, - address lzEndpoint - ) - ERC721MLite( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - timestampExpirySeconds - ) - ONFT721CoreLite(minGasToTransferAndStore, lzEndpoint) - {} - - function owner() - public - view - override(Ownable, ERC721MLite) - returns (address) - { - return Ownable.owner(); - } - - function supportsInterface( - bytes4 interfaceId - ) - public - view - virtual - override(ONFT721CoreLite, ERC721A, IERC721A) - returns (bool) - { - return - interfaceId == type(IONFT721Core).interfaceId || - super.supportsInterface(interfaceId); - } - - function _debitFrom( - address _from, - uint16, - bytes memory, - uint256 _tokenId - ) internal virtual override { - if (!_isApprovedOrOwner(_msgSender(), _tokenId)) - revert CallerNotOwnerOrApproved(); - if (ERC721A.ownerOf(_tokenId) != _from) revert FromAddressNotOwner(); - - safeTransferFrom(_from, address(this), _tokenId); - } - - function _creditTo( - uint16, - address _toAddress, - uint256 _tokenId - ) internal virtual override { - if (!_exists(_tokenId) || ERC721A.ownerOf(_tokenId) != address(this)) - revert NotExistOrNotOwnedByContract(); - safeTransferFrom(address(this), _toAddress, _tokenId); - } - - function onERC721Received( - address, - address, - uint256, - bytes memory - ) public virtual override returns (bytes4) { - return ERC721A__IERC721Receiver.onERC721Received.selector; - } - - function _isApprovedOrOwner( - address spender, - uint256 tokenId - ) internal view virtual returns (bool) { - address owner = ownerOf(tokenId); - return (spender == owner || - isApprovedForAll(owner, spender) || - getApproved(tokenId) == spender); - } -} diff --git a/contracts/onft/LzAppLite.sol b/contracts/onft/LzAppLite.sol deleted file mode 100644 index 13cf3067..00000000 --- a/contracts/onft/LzAppLite.sol +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/access/Ownable.sol"; -import {ILayerZeroReceiver} from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroReceiver.sol"; -import {ILayerZeroUserApplicationConfig} from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroUserApplicationConfig.sol"; -import {ILayerZeroEndpoint} from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroEndpoint.sol"; -import {BytesLib} from "@layerzerolabs/solidity-examples/contracts/util/BytesLib.sol"; - -/* - * A lite version of generic LzReceiver implementation (LzApp) - * - shorten error messages - * - remove unncessary functions including getConfig, setTrustedRemoteAddress, getTrustedRemoteAddress and isTrustedRemote - */ -abstract contract LzAppLite is - Ownable, - ILayerZeroReceiver, - ILayerZeroUserApplicationConfig -{ - using BytesLib for bytes; - - // ua can not send payload larger than this by default, but it can be changed by the ua owner - uint256 public constant DEFAULT_PAYLOAD_SIZE_LIMIT = 10000; - - ILayerZeroEndpoint public immutable lzEndpoint; - mapping(uint16 => bytes) public trustedRemoteLookup; - mapping(uint16 => mapping(uint16 => uint256)) public minDstGasLookup; - mapping(uint16 => uint256) public payloadSizeLimitLookup; - address public precrime; - - event SetPrecrime(address precrime); - event SetTrustedRemote(uint16 _remoteChainId, bytes _path); - event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress); - event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint256 _minDstGas); - - constructor(address _endpoint) { - lzEndpoint = ILayerZeroEndpoint(_endpoint); - } - - function lzReceive( - uint16 _srcChainId, - bytes calldata _srcAddress, - uint64 _nonce, - bytes calldata _payload - ) public virtual override { - // lzReceive must be called by the endpoint for security - require(_msgSender() == address(lzEndpoint), "invalid caller"); - - bytes memory trustedRemote = trustedRemoteLookup[_srcChainId]; - // if will still block the message pathway from (srcChainId, srcAddress). should not receive message from untrusted remote. - require( - _srcAddress.length == trustedRemote.length && - trustedRemote.length > 0 && - keccak256(_srcAddress) == keccak256(trustedRemote), - "invalid source contract" - ); - - _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); - } - - // abstract function - the default behaviour of LayerZero is blocking. See: NonblockingLzApp if you dont need to enforce ordered messaging - function _blockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload - ) internal virtual; - - function _lzSend( - uint16 _dstChainId, - bytes memory _payload, - address payable _refundAddress, - address _zroPaymentAddress, - bytes memory _adapterParams, - uint256 _nativeFee - ) internal virtual { - bytes memory trustedRemote = trustedRemoteLookup[_dstChainId]; - require( - trustedRemote.length != 0, - "destination chain not a trusted source" - ); - _checkPayloadSize(_dstChainId, _payload.length); - lzEndpoint.send{value: _nativeFee}( - _dstChainId, - trustedRemote, - _payload, - _refundAddress, - _zroPaymentAddress, - _adapterParams - ); - } - - function _checkGasLimit( - uint16 _dstChainId, - uint16 _type, - bytes memory _adapterParams, - uint256 _extraGas - ) internal view virtual { - uint256 providedGasLimit = _getGasLimit(_adapterParams); - uint256 minGasLimit = minDstGasLookup[_dstChainId][_type] + _extraGas; - require(minGasLimit > 0, "minGasLimit not set"); - require(providedGasLimit >= minGasLimit, "gas limit too low"); - } - - function _getGasLimit( - bytes memory _adapterParams - ) internal pure virtual returns (uint256 gasLimit) { - require(_adapterParams.length >= 34, "invalid adapterParams"); - assembly { - gasLimit := mload(add(_adapterParams, 34)) - } - } - - function _checkPayloadSize( - uint16 _dstChainId, - uint256 _payloadSize - ) internal view virtual { - uint256 payloadSizeLimit = payloadSizeLimitLookup[_dstChainId]; - if (payloadSizeLimit == 0) { - // use default if not set - payloadSizeLimit = DEFAULT_PAYLOAD_SIZE_LIMIT; - } - require(_payloadSize <= payloadSizeLimit, "payload size too large"); - } - - // generic config for LayerZero user Application - function setConfig( - uint16 _version, - uint16 _chainId, - uint256 _configType, - bytes calldata _config - ) external override onlyOwner { - lzEndpoint.setConfig(_version, _chainId, _configType, _config); - } - - function setSendVersion(uint16 _version) external override onlyOwner { - lzEndpoint.setSendVersion(_version); - } - - function setReceiveVersion(uint16 _version) external override onlyOwner { - lzEndpoint.setReceiveVersion(_version); - } - - function forceResumeReceive( - uint16 _srcChainId, - bytes calldata _srcAddress - ) external override onlyOwner { - lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress); - } - - // _path = abi.encodePacked(remoteAddress, localAddress) - // this function set the trusted path for the cross-chain communication - function setTrustedRemote( - uint16 _remoteChainId, - bytes calldata _path - ) external onlyOwner { - trustedRemoteLookup[_remoteChainId] = _path; - emit SetTrustedRemote(_remoteChainId, _path); - } - - function setPrecrime(address _precrime) external onlyOwner { - precrime = _precrime; - emit SetPrecrime(_precrime); - } - - function setMinDstGas( - uint16 _dstChainId, - uint16 _packetType, - uint256 _minGas - ) external onlyOwner { - require(_minGas > 0, "invalid minGas"); - minDstGasLookup[_dstChainId][_packetType] = _minGas; - emit SetMinDstGas(_dstChainId, _packetType, _minGas); - } - - // if the size is 0, it means default size limit - function setPayloadSizeLimit( - uint16 _dstChainId, - uint256 _size - ) external onlyOwner { - payloadSizeLimitLookup[_dstChainId] = _size; - } -} diff --git a/contracts/onft/NonblockingLzAppLite.sol b/contracts/onft/NonblockingLzAppLite.sol deleted file mode 100644 index de5ee78c..00000000 --- a/contracts/onft/NonblockingLzAppLite.sol +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./LzAppLite.sol"; -import {ExcessivelySafeCall} from "@layerzerolabs/solidity-examples/contracts/util/ExcessivelySafeCall.sol"; - -/* - * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel - * this abstract class try-catch all fail messages and store locally for future retry. hence, non-blocking - * NOTE: if the srcAddress is not configured properly, it will still block the message pathway from (srcChainId, srcAddress) - */ -abstract contract NonblockingLzAppLite is LzAppLite { - using ExcessivelySafeCall for address; - - constructor(address _endpoint) LzAppLite(_endpoint) {} - - mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) - public failedMessages; - - event MessageFailed( - uint16 _srcChainId, - bytes _srcAddress, - uint64 _nonce, - bytes _payload, - bytes _reason - ); - event RetryMessageSuccess( - uint16 _srcChainId, - bytes _srcAddress, - uint64 _nonce, - bytes32 _payloadHash - ); - - // overriding the virtual function in LzReceiver - function _blockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload - ) internal virtual override { - (bool success, bytes memory reason) = address(this).excessivelySafeCall( - gasleft(), - 150, - abi.encodeWithSelector( - this.nonblockingLzReceive.selector, - _srcChainId, - _srcAddress, - _nonce, - _payload - ) - ); - // try-catch all errors/exceptions - if (!success) { - _storeFailedMessage( - _srcChainId, - _srcAddress, - _nonce, - _payload, - reason - ); - } - } - - function _storeFailedMessage( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload, - bytes memory _reason - ) internal virtual { - failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload); - emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, _reason); - } - - function nonblockingLzReceive( - uint16 _srcChainId, - bytes calldata _srcAddress, - uint64 _nonce, - bytes calldata _payload - ) public virtual { - // only internal transaction - require(_msgSender() == address(this), "caller not LzApp"); - _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); - } - - //@notice override this function - function _nonblockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload - ) internal virtual; - - function retryMessage( - uint16 _srcChainId, - bytes calldata _srcAddress, - uint64 _nonce, - bytes calldata _payload - ) public payable virtual { - // assert there is message to retry - bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce]; - require(payloadHash != bytes32(0), "no stored message"); - require(keccak256(_payload) == payloadHash, "invalid payload"); - // clear the stored message - failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0); - // execute the message. revert if it fails again - _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); - emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash); - } -} diff --git a/contracts/onft/ONFT721CoreLite.sol b/contracts/onft/ONFT721CoreLite.sol deleted file mode 100644 index 35bb93f3..00000000 --- a/contracts/onft/ONFT721CoreLite.sol +++ /dev/null @@ -1,306 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IONFT721Core} from "@layerzerolabs/solidity-examples/contracts/token/onft/IONFT721Core.sol"; -import "./NonblockingLzAppLite.sol"; -import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; - -abstract contract ONFT721CoreLite is - NonblockingLzAppLite, - ERC165, - ReentrancyGuard, - IONFT721Core -{ - uint16 public constant FUNCTION_TYPE_SEND = 1; - - struct StoredCredit { - uint16 srcChainId; - address toAddress; - uint256 index; // which index of the tokenIds remain - bool creditsRemain; - } - - uint256 public minGasToTransferAndStore; // min amount of gas required to transfer, and also store the payload - mapping(uint16 => uint256) public dstChainIdToBatchLimit; - mapping(uint16 => uint256) public dstChainIdToTransferGas; // per transfer amount of gas required to mint/transfer on the dst - mapping(bytes32 => StoredCredit) public storedCredits; - - constructor( - uint256 _minGasToTransferAndStore, - address _lzEndpoint - ) NonblockingLzAppLite(_lzEndpoint) { - require( - _minGasToTransferAndStore > 0, - "minGasToTransferAndStore must be > 0" - ); - minGasToTransferAndStore = _minGasToTransferAndStore; - } - - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(ERC165, IERC165) returns (bool) { - return - interfaceId == type(IONFT721Core).interfaceId || - super.supportsInterface(interfaceId); - } - - function estimateSendFee( - uint16 _dstChainId, - bytes memory _toAddress, - uint256 _tokenId, - bool _useZro, - bytes memory _adapterParams - ) public view virtual override returns (uint256 nativeFee, uint256 zroFee) { - return - estimateSendBatchFee( - _dstChainId, - _toAddress, - _toSingletonArray(_tokenId), - _useZro, - _adapterParams - ); - } - - function estimateSendBatchFee( - uint16 _dstChainId, - bytes memory _toAddress, - uint256[] memory _tokenIds, - bool _useZro, - bytes memory _adapterParams - ) public view virtual override returns (uint256 nativeFee, uint256 zroFee) { - bytes memory payload = abi.encode(_toAddress, _tokenIds); - return - lzEndpoint.estimateFees( - _dstChainId, - address(this), - payload, - _useZro, - _adapterParams - ); - } - - function sendFrom( - address _from, - uint16 _dstChainId, - bytes memory _toAddress, - uint256 _tokenId, - address payable _refundAddress, - address _zroPaymentAddress, - bytes memory _adapterParams - ) public payable virtual override { - _send( - _from, - _dstChainId, - _toAddress, - _toSingletonArray(_tokenId), - _refundAddress, - _zroPaymentAddress, - _adapterParams - ); - } - - function sendBatchFrom( - address _from, - uint16 _dstChainId, - bytes memory _toAddress, - uint256[] memory _tokenIds, - address payable _refundAddress, - address _zroPaymentAddress, - bytes memory _adapterParams - ) public payable virtual override { - _send( - _from, - _dstChainId, - _toAddress, - _tokenIds, - _refundAddress, - _zroPaymentAddress, - _adapterParams - ); - } - - function _send( - address _from, - uint16 _dstChainId, - bytes memory _toAddress, - uint256[] memory _tokenIds, - address payable _refundAddress, - address _zroPaymentAddress, - bytes memory _adapterParams - ) internal virtual { - // allow 1 by default - require(_tokenIds.length > 0, "tokenIds[] is empty"); - require( - _tokenIds.length == 1 || - _tokenIds.length <= dstChainIdToBatchLimit[_dstChainId], - "batch size > limit" - ); - - for (uint256 i = 0; i < _tokenIds.length; i++) { - _debitFrom(_from, _dstChainId, _toAddress, _tokenIds[i]); - } - - bytes memory payload = abi.encode(_toAddress, _tokenIds); - - _checkGasLimit( - _dstChainId, - FUNCTION_TYPE_SEND, - _adapterParams, - dstChainIdToTransferGas[_dstChainId] * _tokenIds.length - ); - _lzSend( - _dstChainId, - payload, - _refundAddress, - _zroPaymentAddress, - _adapterParams, - msg.value - ); - emit SendToChain(_dstChainId, _from, _toAddress, _tokenIds); - } - - function _nonblockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 /*_nonce*/, - bytes memory _payload - ) internal virtual override { - // decode and load the toAddress - (bytes memory toAddressBytes, uint256[] memory tokenIds) = abi.decode( - _payload, - (bytes, uint256[]) - ); - - address toAddress; - assembly { - toAddress := mload(add(toAddressBytes, 20)) - } - - uint256 nextIndex = _creditTill(_srcChainId, toAddress, 0, tokenIds); - if (nextIndex < tokenIds.length) { - // not enough gas to complete transfers, store to be cleared in another tx - bytes32 hashedPayload = keccak256(_payload); - storedCredits[hashedPayload] = StoredCredit( - _srcChainId, - toAddress, - nextIndex, - true - ); - emit CreditStored(hashedPayload, _payload); - } - - emit ReceiveFromChain(_srcChainId, _srcAddress, toAddress, tokenIds); - } - - // Public function for anyone to clear and deliver the remaining batch sent tokenIds - function clearCredits(bytes memory _payload) external virtual nonReentrant { - bytes32 hashedPayload = keccak256(_payload); - require( - storedCredits[hashedPayload].creditsRemain, - "no credits stored" - ); - - (, uint256[] memory tokenIds) = abi.decode( - _payload, - (bytes, uint256[]) - ); - - uint256 nextIndex = _creditTill( - storedCredits[hashedPayload].srcChainId, - storedCredits[hashedPayload].toAddress, - storedCredits[hashedPayload].index, - tokenIds - ); - require( - nextIndex > storedCredits[hashedPayload].index, - "not enough gas" - ); - - if (nextIndex == tokenIds.length) { - // cleared the credits, delete the element - delete storedCredits[hashedPayload]; - emit CreditCleared(hashedPayload); - } else { - // store the next index to mint - storedCredits[hashedPayload] = StoredCredit( - storedCredits[hashedPayload].srcChainId, - storedCredits[hashedPayload].toAddress, - nextIndex, - true - ); - } - } - - // When a srcChain has the ability to transfer more chainIds in a single tx than the dst can do. - // Needs the ability to iterate and stop if the minGasToTransferAndStore is not met - function _creditTill( - uint16 _srcChainId, - address _toAddress, - uint256 _startIndex, - uint256[] memory _tokenIds - ) internal returns (uint256) { - uint256 i = _startIndex; - while (i < _tokenIds.length) { - // if not enough gas to process, store this index for next loop - if (gasleft() < minGasToTransferAndStore) break; - - _creditTo(_srcChainId, _toAddress, _tokenIds[i]); - i++; - } - - // indicates the next index to send of tokenIds, - // if i == tokenIds.length, we are finished - return i; - } - - function setMinGasToTransferAndStore( - uint256 _minGasToTransferAndStore - ) external onlyOwner { - require(_minGasToTransferAndStore > 0, "must be > 0"); - minGasToTransferAndStore = _minGasToTransferAndStore; - emit SetMinGasToTransferAndStore(_minGasToTransferAndStore); - } - - // ensures enough gas in adapter params to handle batch transfer gas amounts on the dst - function setDstChainIdToTransferGas( - uint16 _dstChainId, - uint256 _dstChainIdToTransferGas - ) external onlyOwner { - require(_dstChainIdToTransferGas > 0, "must be > 0"); - dstChainIdToTransferGas[_dstChainId] = _dstChainIdToTransferGas; - emit SetDstChainIdToTransferGas(_dstChainId, _dstChainIdToTransferGas); - } - - // limit on src the amount of tokens to batch send - function setDstChainIdToBatchLimit( - uint16 _dstChainId, - uint256 _dstChainIdToBatchLimit - ) external onlyOwner { - require(_dstChainIdToBatchLimit > 0, "must be > 0"); - dstChainIdToBatchLimit[_dstChainId] = _dstChainIdToBatchLimit; - emit SetDstChainIdToBatchLimit(_dstChainId, _dstChainIdToBatchLimit); - } - - function _debitFrom( - address _from, - uint16 _dstChainId, - bytes memory _toAddress, - uint256 _tokenId - ) internal virtual; - - function _creditTo( - uint16 _srcChainId, - address _toAddress, - uint256 _tokenId - ) internal virtual; - - function _toSingletonArray( - uint256 element - ) internal pure returns (uint256[] memory) { - uint256[] memory array = new uint256[](1); - array[0] = element; - return array; - } -} diff --git a/contracts/onft/ONFT721Lite.sol b/contracts/onft/ONFT721Lite.sol deleted file mode 100644 index f504a680..00000000 --- a/contracts/onft/ONFT721Lite.sol +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IONFT721} from "@layerzerolabs/solidity-examples/contracts/token/onft/IONFT721.sol"; -import "./ONFT721CoreLite.sol"; -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - -// NOTE: this ONFT contract has no public minting logic. -// must implement your own minting logic in child classes -contract ONFT721Lite is ONFT721CoreLite, ERC721, IONFT721 { - // Current base URI. - string private _currentBaseURI; - // The suffix for the token URL, e.g. ".json". - string private _tokenURISuffix; - - constructor( - string memory _name, - string memory _symbol, - uint256 _minGasToTransfer, - address _lzEndpoint - ) ERC721(_name, _symbol) ONFT721CoreLite(_minGasToTransfer, _lzEndpoint) {} - - function supportsInterface( - bytes4 interfaceId - ) - public - view - virtual - override(ONFT721CoreLite, ERC721, IERC165) - returns (bool) - { - return - interfaceId == type(IONFT721).interfaceId || - super.supportsInterface(interfaceId); - } - - function _debitFrom( - address _from, - uint16, - bytes memory, - uint256 _tokenId - ) internal virtual override { - require( - _isApprovedOrOwner(_msgSender(), _tokenId), - "caller not owner nor approved" - ); - require(ERC721.ownerOf(_tokenId) == _from, "incorrect owner"); - _transfer(_from, address(this), _tokenId); - } - - function _creditTo( - uint16, - address _toAddress, - uint256 _tokenId - ) internal virtual override { - require( - !_exists(_tokenId) || - (_exists(_tokenId) && ERC721.ownerOf(_tokenId) == address(this)) - ); - if (!_exists(_tokenId)) { - _safeMint(_toAddress, _tokenId); - } else { - _transfer(address(this), _toAddress, _tokenId); - } - } - - /** - * @dev Sets token base URI. - */ - function setBaseURI(string calldata baseURI) external onlyOwner { - _currentBaseURI = baseURI; - } - - /** - * @dev Sets token URI suffix. e.g. ".json". - */ - function setTokenURISuffix(string calldata suffix) external onlyOwner { - _tokenURISuffix = suffix; - } - - /** - * @dev Returns token URI for a given token id. - */ - function tokenURI( - uint256 tokenId - ) public view override returns (string memory) { - require(_exists(tokenId), "token not exist"); - - string memory baseURI = _currentBaseURI; - return - bytes(baseURI).length != 0 - ? string( - abi.encodePacked( - baseURI, - _toString(tokenId), - _tokenURISuffix - ) - ) - : ""; - } - - /** - * @dev Converts a uint256 to its ASCII string decimal representation. - */ - function _toString( - uint256 value - ) internal pure virtual returns (string memory str) { - assembly { - // The maximum value of a uint256 contains 78 digits (1 byte per digit), but - // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. - // We will need 1 word for the trailing zeros padding, 1 word for the length, - // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0. - let m := add(mload(0x40), 0xa0) - // Update the free memory pointer to allocate. - mstore(0x40, m) - // Assign the `str` to the end. - str := sub(m, 0x20) - // Zeroize the slot after the string. - mstore(str, 0) - - // Cache the end of the memory to calculate the length later. - let end := str - - // We write the string from rightmost digit to leftmost digit. - // The following is essentially a do-while loop that also handles the zero case. - // prettier-ignore - for { let temp := value } 1 {} { - str := sub(str, 1) - // Write the character to the pointer. - // The ASCII index of the '0' character is 48. - mstore8(str, add(48, mod(temp, 10))) - // Keep dividing `temp` until zero. - temp := div(temp, 10) - // prettier-ignore - if iszero(temp) { break } - } - - let length := sub(end, str) - // Move the pointer 32 bytes leftwards to make room for the length. - str := sub(str, 0x20) - // Store the length. - mstore(str, length) - } - } -} diff --git a/hardhat.config.ts b/hardhat.config.ts index 6cc7d5a3..b0055c44 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -32,10 +32,6 @@ import { getEndTimeBA, getPrice, setPrice, - deployOnft, - setTrustedRemote, - setOnftMinDstGas, - sendOnft, deployOwnedRegistrant, getContractCodehash, deploy721BatchTransfer, @@ -148,22 +144,9 @@ task('deploy', 'Deploy ERC721M') 'ERC-20 contract address (if minting with ERC-20)', '0x0000000000000000000000000000000000000000', ) - .addOptionalParam('autoapproveaddress', 'auto approve address') - .addParam( - 'increasesupply', - 'whether or not to enable increasing supply behavior', - false, - types.boolean, - ) - .addParam( - 'pausable', - 'whether to allow transfers to be paused', - false, - types.boolean, - ) .addParam( 'useoperatorfilterer', - 'whether or not to use operator filterer', + 'whether or not to use operator filterer, used with legacy 721M contract', false, types.boolean, ) @@ -262,7 +245,6 @@ task('deployBA', 'Deploy BucketAuction') ) .addParam('auctionstarttime', 'The start time of the bucket auction') .addParam('auctionendtime', 'The end time of the bucket auction') - .addFlag('useoperatorfilterer', 'whether or not to use operator filterer') .setAction(deployBA); task('setTimestampExpirySeconds', 'Set the timestamp expiry seconds') @@ -342,57 +324,6 @@ task('setPrice', 'set the price set for BA') .addParam('priceinwei', 'price in wei') .setAction(setPrice); -task('deployOnft', 'Deploy ERC721MOnft') - .addFlag('ismintingcontract', 'whether or not this is a minting contract') - .addParam('name', 'name') - .addParam('symbol', 'symbol') - .addParam('maxsupply', 'max supply') - .addParam('tokenurisuffix', 'token uri suffix', '.json') - .addParam('timestampexpiryseconds', 'timestamp expiry in seconds', '300') - .addParam('globalwalletlimit', 'global wallet limit', '0') - .addOptionalParam( - 'cosigner', - 'cosigner address (0x00...000 if not using cosign)', - '0x0000000000000000000000000000000000000000', - ) - .addOptionalParam( - 'mingastostore', - 'minimum gas to store default 15000', - '15000', - ) - .setAction(deployOnft); - -task('setTrustedRemote', 'Set trusted remote for ERC721MOnft') - .addParam( - 'sourceaddress', - 'the contract address you are setting the remote on', - ) - .addParam('targetnetwork', 'the network you are setting the remote to') - .addParam( - 'targetaddress', - 'the address of the contract on the target network', - ) - .setAction(setTrustedRemote); - -task('setOnftMinDstGas', 'Set min destination gas for ERC721MOnft') - .addParam('contract', 'the contract address') - .addParam('targetnetwork', 'the network you plan to send the tokens to') - .addOptionalParam('packettype', 'package type. default to 1', '1') - .addOptionalParam('mingas', 'min gas. default to 200000', '200000') - .setAction(setOnftMinDstGas); - -task('sendOnft', 'Send tokens to target network') - .addParam('contract', 'the contract address you are sending tokens from') - .addParam('targetnetwork', 'the network you are sending the tokens to') - .addParam('tokenid', 'the token id you are sending') - .addOptionalParam('tokenowner', 'the owner of the tokens') - .addOptionalParam('refundaddress', 'the address you want to refund to') - .addOptionalParam( - 'zeropaymentaddress', - 'the address you want to send a zero payment to', - ) - .setAction(sendOnft); - task('deployOwnedRegistrant', 'Deploy OwnedRegistrant') .addParam( 'newowner', diff --git a/scripts/common/constants.ts b/scripts/common/constants.ts index 376259c7..64c6a2f9 100644 --- a/scripts/common/constants.ts +++ b/scripts/common/constants.ts @@ -5,62 +5,10 @@ export const ContractDetails = { ERC721CMBasicRoyalties: { name: 'ERC721CMBasicRoyalties' }, // ERC721M on ERC721C v2 with basic royalties ERC721CMRoyalties: { name: 'ERC721CMRoyalties' }, // ERC721M on ERC721C v2 with updatable royalties ERC721M: { name: 'ERC721M' }, // The contract of direct sales - ERC721MIncreasableSupply: { name: 'ERC721MIncreasableSupply' }, // ERC721M with increasable supply ERC721MOperatorFilterer: { name: 'ERC721MOperatorFilterer' }, // ERC721M with operator filterer - ERC721MIncreasableOperatorFilterer: { - name: 'ERC721MIncreasableOperatorFilterer', - }, // ERC721M with increasable supply and operator filterer - ERC721MAutoApprover: { name: 'ERC721MAutoApprover' }, // ERC721M with auto approver - ERC721MOperatorFiltererAutoApprover: { - name: 'ERC721MOperatorFiltererAutoApprover', - }, // ERC721M with operator filterer and auto approver - ERC721MPausable: { name: 'ERC721MPausable' }, // ERC721M with Pausable - ERC721MPausableOperatorFilterer: { name: 'ERC721MPausableOperatorFilterer' }, // ERC721M with Pausable and operator filterer - ERC721MOnft: { name: 'ERC721MOnft' }, // ERC721M with LayerZero integration - ONFT721Lite: { name: 'ONFT721Lite' }, // ERC721 non-minting with LayerZero integration BucketAuction: { name: 'BucketAuction' }, // The contract of bucket auctions - BucketAuctionOperatorFilterer: { name: 'BucketAuctionOperatorFilterer' }, // Bucket auction with operator filterer } as const; -export const LayerZeroEndpoints: Record = { - ethereum: '0x66A71Dcef29A0fFBDBE3c6a460a3B5BC225Cd675', - bsc: '0x3c2269811836af69497E5F486A85D7316753cf62', - avalanche: '0x3c2269811836af69497E5F486A85D7316753cf62', - polygon: '0x3c2269811836af69497E5F486A85D7316753cf62', - arbitrum: '0x3c2269811836af69497E5F486A85D7316753cf62', - optimism: '0x3c2269811836af69497E5F486A85D7316753cf62', - fantom: '0xb6319cC6c8c27A8F5dAF0dD3DF91EA35C4720dd7', - goerli: '0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23', - 'bsc-testnet': '0x6Fcb97553D41516Cb228ac03FdC8B9a0a9df04A1', - fuji: '0x93f54D755A063cE7bB9e6Ac47Eccc8e33411d706', - mumbai: '0xf69186dfBa60DdB133E91E9A4B5673624293d8F8', - 'arbitrum-goerli': '0x6aB5Ae6822647046626e83ee6dB8187151E1d5ab', - 'optimism-goerli': '0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1', - 'fantom-testnet': '0x7dcAD72640F835B0FA36EFD3D6d3ec902C7E5acf', - 'meter-testnet': '0x3De2f3D1Ac59F18159ebCB422322Cb209BA96aAD', - 'zksync-testnet': '0x093D2CF57f764f09C3c2Ac58a42A2601B8C79281', -} as const; - -export const ChainIds: Record = { - ethereum: 101, - bsc: 102, - avalanche: 106, - polygon: 109, - arbitrum: 110, - optimism: 111, - fantom: 112, - - goerli: 10121, - 'bsc-testnet': 10102, - fuji: 10106, - mumbai: 10109, - 'arbitrum-goerli': 10143, - 'optimism-goerli': 10132, - 'fantom-testnet': 10112, - 'meter-testnet': 10156, - 'zksync-testnet': 10165, -}; - export const ERC721BatchTransferContract = '0x38F7ba911f7efc434D29D6E39c814E9d4De3FEef'; diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 2e690c4a..3bacf591 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -18,11 +18,8 @@ interface IDeployParams { globalwalletlimit: string; cosigner?: string; timestampexpiryseconds?: number; - increasesupply?: boolean; useoperatorfilterer?: boolean; openedition?: boolean; - autoapproveaddress?: string; - pausable?: boolean; mintcurrency?: string; useerc721c?: boolean; useerc2198?: boolean; @@ -49,23 +46,7 @@ export const deploy = async ( } else if (args.useerc721c) { contractName = ContractDetails.ERC721CM.name; } else if (args.useoperatorfilterer) { - if (args.increasesupply) { - contractName = ContractDetails.ERC721MIncreasableOperatorFilterer.name; - } else if (args.autoapproveaddress) { - contractName = ContractDetails.ERC721MOperatorFiltererAutoApprover.name; - } else if (args.pausable) { - contractName = ContractDetails.ERC721MPausableOperatorFilterer.name; - } else { - contractName = ContractDetails.ERC721MOperatorFilterer.name; - } - } else { - if (args.increasesupply) { - contractName = ContractDetails.ERC721MIncreasableSupply.name; - } else if (args.autoapproveaddress) { - contractName = ContractDetails.ERC721MAutoApprover.name; - } else if (args.pausable) { - contractName = ContractDetails.ERC721MPausable.name; - } + contractName = ContractDetails.ERC721MOperatorFilterer.name; } let maxsupply = hre.ethers.BigNumber.from(args.maxsupply); @@ -95,10 +76,6 @@ export const deploy = async ( args.mintcurrency ?? hre.ethers.constants.AddressZero, ] as any[]; - if (args.autoapproveaddress) { - params.push(args.autoapproveaddress); - } - if (args.useerc2198) { params.push( args.erc2198royaltyreceiver ?? hre.ethers.constants.AddressZero, diff --git a/scripts/deployBA.ts b/scripts/deployBA.ts index a2b4ece0..3c8abedf 100644 --- a/scripts/deployBA.ts +++ b/scripts/deployBA.ts @@ -19,21 +19,13 @@ interface IDeployParams { mincontributioninwei: number; auctionstarttime: string; auctionendtime: string; - useoperatorfilterer?: boolean; } export const deployBA = async ( args: IDeployParams, hre: HardhatRuntimeEnvironment, ) => { - // Set the contract name - let contractName: string; - - if (args.useoperatorfilterer) { - contractName = ContractDetails.BucketAuctionOperatorFilterer.name; - } else { - contractName = ContractDetails.BucketAuction.name; - } + const contractName = ContractDetails.BucketAuction.name; console.log( `Going to deploy ${contractName} with params`, diff --git a/scripts/deployOnft.ts b/scripts/deployOnft.ts deleted file mode 100644 index 695f425b..00000000 --- a/scripts/deployOnft.ts +++ /dev/null @@ -1,78 +0,0 @@ -// We require the Hardhat Runtime Environment explicitly here. This is optional -// but useful for running the script in a standalone fashion through `node