diff --git a/contracts/nft/erc721m/ERC721CM.sol b/contracts/nft/erc721m/ERC721CM.sol index 4a7a1da2..ee40b8c2 100644 --- a/contracts/nft/erc721m/ERC721CM.sol +++ b/contracts/nft/erc721m/ERC721CM.sol @@ -54,8 +54,11 @@ contract ERC721CM is uint256 timestampExpirySeconds, address mintCurrency, address fundReceiver, - uint256 mintFee - ) ERC721ACQueryable(collectionName, collectionSymbol) Ownable() { + uint256 mintFee, + address initialOwner + ) ERC721ACQueryable(collectionName, collectionSymbol) { + _transferOwnership(initialOwner); + if (globalWalletLimit > maxMintableSupply) { revert GlobalWalletLimitOverflow(); } diff --git a/scripts-foundry/common/100a-create2-magicdrop-impl.sh b/scripts-foundry/common/100a-create2-magicdrop-impl.sh new file mode 100755 index 00000000..b840e51f --- /dev/null +++ b/scripts-foundry/common/100a-create2-magicdrop-impl.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +if [ -f .env ] +then + export $(grep -v '^#' .env | xargs) +else + echo "Please set your .env file" + exit 1 +fi + +NAME="" +SYMBOL="" +TOKEN_URI_SUFFIX="" +MAX_MINTABLE_SUPPLY="1000" +GLOBAL_WALLET_LIMIT="0" +COSIGNER="0x0000000000000000000000000000000000000000" +TIMESTAMP_EXPIRY_SECONDS="60" +MINT_CURRENCY="0x0000000000000000000000000000000000000000" +FUND_RECEIVER="" +MINT_FEE="0" +INITIAL_OWNER="" + +# Function to display usage +usage() { + echo "Usage: $0 --impl --name --symbol --fund-receiver --mint-fee --initial-owner " + exit 1 +} + +# Process arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --impl) IMPL_PATH=$2; shift ;; + --name) NAME=$2; shift ;; + --symbol) SYMBOL=$2; shift ;; + --fund-receiver) FUND_RECEIVER=$2; shift ;; + --mint-fee) MINT_FEE=$2; shift ;; + --initial-owner) INITIAL_OWNER=$2; shift ;; + *) usage ;; + esac + shift +done + +echo "IMPL_PATH: $IMPL_PATH" +echo "NAME: $NAME" +echo "SYMBOL: $SYMBOL" +echo "FUND_RECEIVER: $FUND_RECEIVER" +echo "MINT_FEE: $MINT_FEE" +echo "INITIAL_OWNER: $INITIAL_OWNER" + +# Check if all parameters are set +if [ -z "$IMPL_PATH" ] || [ -z "$NAME" ] || [ -z "$SYMBOL" ] || [ -z "$FUND_RECEIVER" ] || [ -z "$MINT_FEE" ] || [ -z "$INITIAL_OWNER" ]; then + usage +fi + +# NOTE: If you change the number of optimizer runs, you must also change the number in the deploy script, otherwise the CREATE2 address will be different + +echo "create2 MagicDropImpl START" + +implByteCode="$(forge inspect contracts/nft/erc721m/ERC721CM.sol:ERC721CM bytecode --optimizer-runs 777 --via-ir)" +constructorArgs=$(cast abi-encode "constructor(string,string,string,uint256,uint256,address,uint256,address,address,uint256,address)" "$NAME" "$SYMBOL" "$TOKEN_URI_SUFFIX" "$MAX_MINTABLE_SUPPLY" "$GLOBAL_WALLET_LIMIT" "$COSIGNER" "$TIMESTAMP_EXPIRY_SECONDS" "$MINT_CURRENCY" "$FUND_RECEIVER" "$MINT_FEE" "$INITIAL_OWNER") +constructorArgsNoPrefix=${constructorArgs#0x} +implInitCode=$(cast concat-hex $implByteCode $constructorArgsNoPrefix) + +echo $constructorArgs + +cast create2 --starts-with 88888888 --case-sensitive --init-code $implInitCode +echo "create2 MagicDropImpl END" +echo "-------------------------------------" +echo "" diff --git a/scripts-foundry/common/101a-deploy-magicdrop-impl-direct.sh b/scripts-foundry/common/101a-deploy-magicdrop-impl-direct.sh new file mode 100755 index 00000000..94cd8b80 --- /dev/null +++ b/scripts-foundry/common/101a-deploy-magicdrop-impl-direct.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +if [ -f .env ] +then + export $(grep -v '^#' .env | xargs) +else + echo "Please set your .env file" + exit 1 +fi + +# Note: Update the contract in the deploy script if you want to deploy a different version. Default is 1_0_0 + +source ./utils + +# Exit on error +set -e + +# Initialize variables with environment values +CHAIN_ID=${CHAIN_ID:-""} +RPC_URL="" +STANDARD="" +IS_ERC721C=false #optional +IMPL_EXPECTED_ADDRESS="" +IMPL_SALT="" +NAME="" +SYMBOL="" +FUND_RECEIVER="" +MINT_FEE=0 +INITIAL_OWNER="" + +# Function to display usage +usage() { + # Example Usage: ./2a-deploy-magicdrop-impl.sh --chain-id 137 --token-standard ERC721 --is-erc721c true --expected-address 0x0000000000000000000000000000000000000000 --salt 0x0000000000000000000000000000000000000000000000000000000000000000 + echo "Usage: $0 --chain-id --token-standard --is-erc721c --expected-address --salt --name --symbol --fund-receiver --mint-fee --initial-owner " + exit 1 +} + +# Process arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --chain-id) CHAIN_ID=$2; shift ;; + --token-standard) STANDARD=$2; shift ;; + --is-erc721c) IS_ERC721C=$2; shift ;; + --expected-address) IMPL_EXPECTED_ADDRESS=$2; shift ;; + --salt) IMPL_SALT=$2; shift ;; + --name) NAME=$2; shift ;; + --symbol) SYMBOL=$2; shift ;; + --fund-receiver) FUND_RECEIVER=$2; shift ;; + --mint-fee) MINT_FEE=$2; shift ;; + --initial-owner) INITIAL_OWNER=$2; shift ;; + *) usage ;; + esac + shift +done + +# Check if all parameters are set +if [ -z "$CHAIN_ID" ] || [ -z "$STANDARD" ] || [ -z "$IMPL_EXPECTED_ADDRESS" ] || [ -z "$IMPL_SALT" ] || [ -z "$NAME" ] || [ -z "$SYMBOL" ] || [ -z "$FUND_RECEIVER" ] || [ -z "$MINT_FEE" ] || [ -z "$INITIAL_OWNER" ]; then + usage +fi + +# Set the RPC URL based on chain ID +set_rpc_url $CHAIN_ID + +# Set the ETHERSCAN API KEY based on chain ID +set_etherscan_api_key $CHAIN_ID + +# Convert STANDARD to lowercase for the path +STANDARD_LOWERCASE=$(echo $STANDARD | tr '[:upper:]' '[:lower:]') + +echo "" +echo "==================== DEPLOYMENT DETAILS ====================" +echo "Chain ID: $CHAIN_ID" +echo "RPC URL: $RPC_URL" +echo "Token Standard: $STANDARD" +echo "Is ERC721C: $IS_ERC721C" +echo "Expected Address: $IMPL_EXPECTED_ADDRESS" +echo "Salt: $IMPL_SALT" +echo "Name: $NAME" +echo "Symbol: $SYMBOL" +echo "Fund Reciever: $FUND_RECEIVER" +echo "Mint Fee: $MINT_FEE" +echo "Initial Owner: $INITIAL_OWNER" +echo "============================================================" +echo "" +read -p "Do you want to proceed? (yes/no) " yn + +case $yn in + yes ) echo ok, we will proceed;; + no ) echo exiting...; + exit;; + * ) echo invalid response; + exit 1;; +esac + +echo "" +echo "============= DEPLOYING MAGICDROP IMPLEMENTATION =============" +echo "" + +# remove --verify when deploying on Sei Chain. You will need to verify manually. +CHAIN_ID=$CHAIN_ID RPC_URL=$RPC_URL TOKEN_STANDARD=$STANDARD IS_ERC721C=$IS_ERC721C IMPL_EXPECTED_ADDRESS=$IMPL_EXPECTED_ADDRESS IMPL_SALT=$IMPL_SALT NAME=$NAME SYMBOL=$SYMBOL FUND_RECEIVER=$FUND_RECEIVER MINT_FEE=$MINT_FEE INITIAL_OWNER=$INITIAL_OWNER forge script ./DeployMagicDropImplementationDirect.s.sol:DeployMagicDropImplementationDirect \ + --rpc-url $RPC_URL \ + --broadcast \ + --optimizer-runs 777 \ + --via-ir \ + --verify \ + --verifier custom \ + -v + +echo "" +echo "============= DEPLOYED MAGICDROP IMPLEMENTATION =============" +echo "" diff --git a/scripts-foundry/common/DeployMagicDropImplementationDirect.s.sol b/scripts-foundry/common/DeployMagicDropImplementationDirect.s.sol new file mode 100644 index 00000000..bea7840c --- /dev/null +++ b/scripts-foundry/common/DeployMagicDropImplementationDirect.s.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {Script, console} from "forge-std/Script.sol"; +import {ERC721M} from "contracts/nft/erc721m/ERC721M.sol"; +import {ERC721CM} from "contracts/nft/erc721m/ERC721CM.sol"; +import {ERC1155M} from "contracts/nft/erc1155m/ERC1155M.sol"; +import {TokenStandard} from "contracts/common/Structs.sol"; + +contract DeployMagicDropImplementationDirect is Script { + error AddressMismatch(address expected, address actual); + error InvalidTokenStandard(string standard); + error NotImplementedYet(); + + function run() external { + bytes32 salt = vm.envBytes32("IMPL_SALT"); + address expectedAddress = address(uint160(vm.envUint("IMPL_EXPECTED_ADDRESS"))); + TokenStandard standard = parseTokenStandard(vm.envString("TOKEN_STANDARD")); + bool isERC721C = vm.envBool("IS_ERC721C"); + uint256 privateKey = vm.envUint("PRIVATE_KEY"); + string memory name = vm.envString("NAME"); + string memory symbol = vm.envString("SYMBOL"); + string memory tokenUriSuffix = ""; + uint256 maxMintableSupply = 1000; + uint256 globalWalletLimit = 0; + address cosigner = address(0); + uint256 timestampExpirySeconds = 60; + address mintCurrency = address(0); + address fundReceiver = address(uint160(vm.envUint("FUND_RECEIVER"))); + uint256 mintFee = vm.envUint("MINT_FEE"); + address initialOwner = address(uint160(vm.envUint("INITIAL_OWNER"))); + + vm.startBroadcast(privateKey); + + address deployedAddress; + + if (standard == TokenStandard.ERC721) { + if (isERC721C) { + deployedAddress = address(new ERC721CM{salt: salt}( + name, + symbol, + tokenUriSuffix, + maxMintableSupply, + globalWalletLimit, + cosigner, + timestampExpirySeconds, + mintCurrency, + fundReceiver, + mintFee, + initialOwner + )); + } else { + revert NotImplementedYet(); + } + } else if (standard == TokenStandard.ERC1155) { + revert NotImplementedYet(); + } + + if (address(deployedAddress) != expectedAddress) { + revert AddressMismatch(expectedAddress, deployedAddress); + } + + vm.stopBroadcast(); + } + + function parseTokenStandard(string memory standardString) internal pure returns (TokenStandard) { + if (keccak256(abi.encodePacked(standardString)) == keccak256(abi.encodePacked("ERC721"))) { + return TokenStandard.ERC721; + } else if (keccak256(abi.encodePacked(standardString)) == keccak256(abi.encodePacked("ERC1155"))) { + return TokenStandard.ERC1155; + } else { + revert InvalidTokenStandard(standardString); + } + } +} diff --git a/test/erc721m/ERC721CM.test.ts b/test/erc721m/ERC721CM.test.ts index c57acbe4..f9555393 100644 --- a/test/erc721m/ERC721CM.test.ts +++ b/test/erc721m/ERC721CM.test.ts @@ -69,6 +69,7 @@ describe('ERC721CM', function () { ethers.constants.AddressZero, fundReceiver.address, MINT_FEE, + owner.address, ); await erc721cm.deployed(); @@ -1670,6 +1671,7 @@ describe('ERC721CM', function () { ethers.constants.AddressZero, fundReceiver.address, MINT_FEE, + owner.address, ), ).to.be.revertedWith('GlobalWalletLimitOverflow'); }); @@ -1786,6 +1788,7 @@ describe('ERC721CM', function () { ethers.constants.AddressZero, fundReceiver.address, MINT_FEE, + owner.address, ); await erc721cm.deployed(); const ownerConn = erc721cm.connect(owner); @@ -1816,6 +1819,7 @@ describe('ERC721CM', function () { ethers.constants.AddressZero, fundReceiver.address, MINT_FEE, + owner.address, ); await erc721cm.deployed(); diff --git a/test/erc721m/mintCurrency.test.ts b/test/erc721m/mintCurrency.test.ts index dfd9de11..ef5b22cf 100644 --- a/test/erc721m/mintCurrency.test.ts +++ b/test/erc721m/mintCurrency.test.ts @@ -1,15 +1,16 @@ import { ERC721CM, ERC721M } from '../../typechain-types'; -import { Contract, Signer } from 'ethers'; +import { Contract } from 'ethers'; import { ethers } from 'hardhat'; import { expect } from 'chai'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; describe('ERC721M: Mint Currency', () => { let erc721M: ERC721M; let contract: ERC721M; let erc20: Contract; - let owner: Signer; - let fundReceiver: Signer; - let minter: Signer; + let owner: SignerWithAddress; + let fundReceiver: SignerWithAddress; + let minter: SignerWithAddress; const mintPrice = 50; const mintFee = 10; const mintQty = 3; @@ -216,9 +217,9 @@ describe('ERC721CM: Mint Currency', () => { let erc721CM: ERC721CM; let contract: ERC721CM; let erc20: Contract; - let owner: Signer; - let fundReceiver: Signer; - let minter: Signer; + let owner: SignerWithAddress; + let fundReceiver: SignerWithAddress; + let minter: SignerWithAddress; const mintPrice = 50; const mintFee = 10; const mintQty = 3; @@ -246,6 +247,7 @@ describe('ERC721CM: Mint Currency', () => { erc20.address, fundReceiver.getAddress(), mintFee, + owner.address, ); await erc721CM.deployed(); @@ -395,6 +397,7 @@ describe('ERC721CM: Mint Currency', () => { ethers.constants.AddressZero, fundReceiver.getAddress(), mintFee, + owner.address, ); await erc721CM.deployed();