diff --git a/README.md b/README.md index a95a534..db21aba 100644 --- a/README.md +++ b/README.md @@ -42,50 +42,32 @@ The composability of the stages is generic enough to enable flexible and complic - Native TypeScript and Typechain-Types Support ## Contracts -| Contract | Description | -|-------------------------|---------------------------------------------------------------------------------------| -| 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. | -| ERC721MOperatorFilterer | ERC721M with OpenSea Operator Filterer | -| BucketAuction | Bucket 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. - -## Installation -Provide step by step series of examples and explanations about how to get a development env running. - - -```bash -npm add @magiceden-oss/erc721m -``` - -## Code Example - -```typescript -import { ERC721M, ERC721M__factory } from '@magiceden-oss/erc721m'; - -const contract = ERC721M__factory.connect( - contractAddress, - signerOrProvider, -); -``` - -## API Reference - -```bash -# Compile the contract -npm run build - -# Get the auto generated typechain-types -./typechain-types -``` - -## Tests - -```bash -npm run test -``` +| Contract | Description | +|-----------------------------|------------------------------------------------------------------------------------| +| ERC721M | The basic minting contract based on ERC721A. | +| ERC721CM | The basic minting contract based on ERC721C and ERC721M. | +| ERC1155M | The basic minting contract based on ERC1155. | +| MagicDropTokenImplRegistry | The implementation registry for MagicDrop contracts. | +| MagicDropCloneFactory | The factory contract for cloning MagicDrop contracts. | +| ERC721MInitializableV1_0_0 | The initializable implementation for ERC721M. | +| ERC721CMInitializableV1_0_0 | The initializable implementation for ERC721CM. | +| ERC1155MInitializableV1_0_0 | The initializable implementation for ERC1155M. | + +## Deployment Address & Salts +| Name | Address | Salt | +|-----------------------------|-------------------------------------------|-----------------------------------------| +| MagicDropTokenImplRegistry | 0x00000000caF1E3978e291c5Fb53FeedB957eC146 |0x78c643228c532b1aee1930fedd4a4b0e6d3d8723987c0809d76a222b0d59b461 | +| MagicDropCloneFactory | 0x000000009e44eBa131196847C685F20Cd4b68aC4 | 0xd8c5a3057ccf31c5fd5cee4e4a5ad9005d0a9a7f4983365010b8785805b44eb1 +| ERC721MInitializableV1_0_0 | 0x00000000b55a1126458841Cc756E565C50759484 | 0x4ca859ec4f4daad3d92dcc2959e01718def5eb520350e3e93bd31fc8d2b3beff +| ERC721CMInitializableV1_0_0 | 0x00000000760644De6b7b40362288e944f4154121 | 0x8ae63539ad30ece1889c0999c70b900ffaf0e10ee23b777924c310ad548b6266 +| ERC1155MInitializableV1_0_0 | 0x000000009B3dC659D26BD2f3D38136E2b270C28d | 0x8b72ee316ce281e983b3694fc794164ce2eac8c3b8d7751c42edfc89310c6665 + +### Supported Chains +- Polygon +- Base +- Sei +- Arbitrum +- Apechain ## Using Foundry @@ -119,10 +101,6 @@ This project includes a script to generate and view a test coverage report. The ./test/generate-coverage-report.sh ``` -We are targeting 100% lines coverage. - -![](https://bafkreic3dyzp5i2fi7co2fekkbgmyxgv342irjy5zfiuhvjqic6fuu53ju.ipfs.nftstorage.link/) - ## Security - [ERC721M Kudelski Security Audit](./docs/AUDIT-PUBLIC-RELEASE-MagicEden-ERC721M1.pdf) ### Bounty Program diff --git a/contracts/factory/MagicDropCloneFactory.sol b/contracts/factory/MagicDropCloneFactory.sol index 7bb656e..419a594 100644 --- a/contracts/factory/MagicDropCloneFactory.sol +++ b/contracts/factory/MagicDropCloneFactory.sol @@ -44,7 +44,6 @@ contract MagicDropCloneFactory is Ownable, UUPSUpgradeable { /// @param initialOwner The address of the initial owner /// @param registry The address of the registry contract - /// @dev This function can only be called once constructor(address initialOwner, address registry) public { if (registry == address(0)) { revert RegistryAddressCannotBeZero(); diff --git a/contracts/registry/MagicDropTokenImplRegistry.sol b/contracts/registry/MagicDropTokenImplRegistry.sol index 5fd2045..ca6fb37 100644 --- a/contracts/registry/MagicDropTokenImplRegistry.sol +++ b/contracts/registry/MagicDropTokenImplRegistry.sol @@ -56,8 +56,7 @@ contract MagicDropTokenImplRegistry is UUPSUpgradeable, Ownable, IMagicDropToken = CONSTRUCTOR = ==============================================================*/ - /// @dev Initializes the contract, setting up the owner and UUPS upgradeability. - /// This function replaces the constructor for upgradeable contracts. + /// @param initialOwner The address of the initial owner constructor(address initialOwner) public { _initializeOwner(initialOwner); diff --git a/foundry.toml b/foundry.toml index cc4b2c4..1c70be4 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,3 +8,7 @@ solc_version = "0.8.22" evm_version = "cancun" via_ir = true optimizer_runs = 777 + +[etherscan] +apechain = {key = "${VERIFICATION_API_KEY_APECHAIN}", chain = 33139, url = "https://api.apescan.io/api"} +sei = {key = "${VERIFICATION_API_KEY_SEI}", chain = 1329, url = "https://api.seiscan.io/api"} diff --git a/scripts-foundry/common/.env.example b/scripts-foundry/common/.env.example new file mode 100644 index 0000000..3d70756 --- /dev/null +++ b/scripts-foundry/common/.env.example @@ -0,0 +1,15 @@ +# Verification API Keys +VERIFICATION_API_KEY_ETHEREUM= +VERIFICATION_API_KEY_POLYGON= +VERIFICATION_API_KEY_BASE= +VERIFICATION_API_KEY_ARBITRUM= +VERIFICATION_API_KEY_SEI= +VERIFICATION_API_KEY_APECHAIN= +VERIFICATION_API_KEY_SEPOLIA= +VERIFICATION_API_KEY_BSC= +VERIFICATION_API_KEY_AVALANCHE= + + +# Deployment Configuration +DEPLOYER= +PRIVATE_KEY= diff --git a/scripts-foundry/common/0a-create2-magicdrop-impl-registry.sh b/scripts-foundry/common/0a-create2-magicdrop-impl-registry.sh index 7c34d19..f4ce467 100755 --- a/scripts-foundry/common/0a-create2-magicdrop-impl-registry.sh +++ b/scripts-foundry/common/0a-create2-magicdrop-impl-registry.sh @@ -8,11 +8,42 @@ else exit 1 fi + +INITIAL_OWNER="" + +# Function to display usage +usage() { + echo "Usage: $0 --initial-owner " + exit 1 +} + +# Process arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --initial-owner) INITIAL_OWNER=$2; shift ;; + *) usage ;; + esac + shift +done + +if [ -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 MagicDropImplRegistry START" -registryCode="$(forge inspect contracts/registry/MagicDropTokenImplRegistry.sol:MagicDropTokenImplRegistry bytecode --optimizer-runs 777 --via-ir)" -registryInitCode="$registryCode" +registryByteCode="$(forge inspect contracts/registry/MagicDropTokenImplRegistry.sol:MagicDropTokenImplRegistry bytecode --optimizer-runs 777 --via-ir)" + +# Encode the constructor arguments +constructorArgs=$(cast abi-encode "constructor(address)" $INITIAL_OWNER) +constructorArgsNoPrefix=${constructorArgs#0x} + +# Concatenate the bytecode and constructor arguments +registryInitCode=$(cast concat-hex $registryByteCode $constructorArgsNoPrefix) + +echo "registryInitCode: $registryInitCode" + cast create2 --starts-with 00000000 --case-sensitive --init-code $registryInitCode echo "create2 MagicDropImplRegistry END" echo "-------------------------------------" diff --git a/scripts-foundry/common/0b-create2-magicdrop-clone-factory.sh b/scripts-foundry/common/0b-create2-magicdrop-clone-factory.sh index 5bf8568..eaae871 100755 --- a/scripts-foundry/common/0b-create2-magicdrop-clone-factory.sh +++ b/scripts-foundry/common/0b-create2-magicdrop-clone-factory.sh @@ -8,11 +8,40 @@ else exit 1 fi +REGISTRY_ADDRESS="" +INITIAL_OWNER="" + +# Function to display usage +usage() { + echo "Usage: $0 --initial-owner --registry-address " + exit 1 +} + +# Process arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --initial-owner) INITIAL_OWNER=$2; shift ;; + --registry-address) REGISTRY_ADDRESS=$2; shift ;; + *) usage ;; + esac + shift +done + +if [ -z "$INITIAL_OWNER" ] || [ -z "$REGISTRY_ADDRESS" ]; 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 MagicDropCloneFactory START" factoryCode="$(forge inspect contracts/factory/MagicDropCloneFactory.sol:MagicDropCloneFactory bytecode --optimizer-runs 777 --via-ir)" -factoryInitCode="$factoryCode" + +# Encode the constructor arguments +constructorArgs=$(cast abi-encode "constructor(address,address)" $INITIAL_OWNER $REGISTRY_ADDRESS) +constructorArgsNoPrefix=${constructorArgs#0x} +factoryInitCode=$(cast concat-hex $factoryCode $constructorArgsNoPrefix) + cast create2 --starts-with 00000000 --case-sensitive --init-code $factoryInitCode echo "create2 MagicDropCloneFactory END" echo "-------------------------------------" diff --git a/scripts-foundry/common/1a-deploy-magicdrop-impl-registry.sh b/scripts-foundry/common/1a-deploy-magicdrop-impl-registry.sh index a476fde..1849dbb 100755 --- a/scripts-foundry/common/1a-deploy-magicdrop-impl-registry.sh +++ b/scripts-foundry/common/1a-deploy-magicdrop-impl-registry.sh @@ -67,6 +67,6 @@ CHAIN_ID=$CHAIN_ID RPC_URL=$RPC_URL REGISTRY_SALT=$REGISTRY_SALT REGISTRY_EXPECT --rpc-url $RPC_URL \ --broadcast \ --optimizer-runs 777 \ - --via-ir # \ - # --verify $RESUME \ - # -v + --via-ir \ + --verify \ + -v diff --git a/scripts-foundry/common/1b-deploy-magicdrop-clone-factory.sh b/scripts-foundry/common/1b-deploy-magicdrop-clone-factory.sh index 5bc796f..d6f5093 100755 --- a/scripts-foundry/common/1b-deploy-magicdrop-clone-factory.sh +++ b/scripts-foundry/common/1b-deploy-magicdrop-clone-factory.sh @@ -69,6 +69,5 @@ esac CHAIN_ID=$CHAIN_ID RPC_URL=$RPC_URL FACTORY_SALT=$FACTORY_SALT FACTORY_EXPECTED_ADDRESS=$FACTORY_EXPECTED_ADDRESS INITIAL_OWNER=$INITIAL_OWNER REGISTRY_ADDRESS=$REGISTRY_ADDRESS forge script ./DeployMagicDropCloneFactory.s.sol:DeployMagicDropCloneFactory \ --rpc-url $RPC_URL \ --broadcast \ - --via-ir # \ - # --verify $RESUME \ - # -v + --via-ir \ + --verify diff --git a/scripts-foundry/common/2a-deploy-magicdrop-impl.sh b/scripts-foundry/common/2a-deploy-magicdrop-impl.sh index 798a89c..d93cb72 100755 --- a/scripts-foundry/common/2a-deploy-magicdrop-impl.sh +++ b/scripts-foundry/common/2a-deploy-magicdrop-impl.sh @@ -8,6 +8,8 @@ else 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 @@ -17,13 +19,12 @@ set -e CHAIN_ID=${CHAIN_ID:-""} RPC_URL="" STANDARD="" -VERSION="" # e.g. 1_0_0, 1_0_1, 1_1_0 IMPL_EXPECTED_ADDRESS="" IMPL_SALT="" # Function to display usage usage() { - # Example Usage: ./2a-deploy-magicdrop-impl.sh --chain-id 137 --version 1_0_0 --token-standard ERC721 --expected-address 0x0000000000000000000000000000000000000000 --salt 0x0000000000000000000000000000000000000000000000000000000000000000 + # Example Usage: ./2a-deploy-magicdrop-impl.sh --chain-id 137 --token-standard ERC721 --expected-address 0x0000000000000000000000000000000000000000 --salt 0x0000000000000000000000000000000000000000000000000000000000000000 echo "Usage: $0 --chain-id --version --token-standard --expected-address --salt " exit 1 } @@ -32,7 +33,6 @@ usage() { while [[ "$#" -gt 0 ]]; do case $1 in --chain-id) CHAIN_ID=$2; shift ;; - --version) VERSION=$2; shift ;; --token-standard) STANDARD=$2; shift ;; --expected-address) IMPL_EXPECTED_ADDRESS=$2; shift ;; --salt) IMPL_SALT=$2; shift ;; @@ -42,7 +42,7 @@ while [[ "$#" -gt 0 ]]; do done # Check if all parameters are set -if [ -z "$CHAIN_ID" ] || [ -z "$VERSION" ] || [ -z "$STANDARD" ] || [ -z "$IMPL_EXPECTED_ADDRESS" ] || [ -z "$IMPL_SALT" ]; then +if [ -z "$CHAIN_ID" ] || [ -z "$STANDARD" ] || [ -z "$IMPL_EXPECTED_ADDRESS" ] || [ -z "$IMPL_SALT" ]; then usage fi @@ -52,9 +52,6 @@ set_rpc_url $CHAIN_ID # Set the ETHERSCAN API KEY based on chain ID set_etherscan_api_key $CHAIN_ID -# Create CONTRACT_VERSION by replacing underscores with dots -CONTRACT_VERSION=$(echo $VERSION | tr '_' '.') - # Convert STANDARD to lowercase for the path STANDARD_LOWERCASE=$(echo $STANDARD | tr '[:upper:]' '[:lower:]') @@ -62,7 +59,6 @@ echo "" echo "==================== DEPLOYMENT DETAILS ====================" echo "Chain ID: $CHAIN_ID" echo "RPC URL: $RPC_URL" -echo "Version: $CONTRACT_VERSION" echo "Token Standard: $STANDARD" echo "Expected Address: $IMPL_EXPECTED_ADDRESS" echo "Salt: $IMPL_SALT" @@ -82,7 +78,7 @@ echo "" echo "============= DEPLOYING MAGICDROP IMPLEMENTATION =============" echo "" -CHAIN_ID=$CHAIN_ID RPC_URL=$RPC_URL TOKEN_STANDARD=$STANDARD CONTRACT_VERSION=$CONTRACT_VERSION IMPL_EXPECTED_ADDRESS=$IMPL_EXPECTED_ADDRESS IMPL_SALT=$IMPL_SALT forge script ./DeployMagicDropImplementation.s.sol:DeployMagicDropImplementation \ +CHAIN_ID=$CHAIN_ID RPC_URL=$RPC_URL TOKEN_STANDARD=$STANDARD IMPL_EXPECTED_ADDRESS=$IMPL_EXPECTED_ADDRESS IMPL_SALT=$IMPL_SALT forge script ./DeployMagicDropImplementation.s.sol:DeployMagicDropImplementation \ --rpc-url $RPC_URL \ --broadcast \ --optimizer-runs 777 \ diff --git a/scripts-foundry/common/DeployMagicDropImplementation.s.sol b/scripts-foundry/common/DeployMagicDropImplementation.s.sol index e1dbe33..7756c13 100644 --- a/scripts-foundry/common/DeployMagicDropImplementation.s.sol +++ b/scripts-foundry/common/DeployMagicDropImplementation.s.sol @@ -2,20 +2,18 @@ pragma solidity ^0.8.22; import {Script, console} from "forge-std/Script.sol"; -import {ERC721CMInitializableV1_0_0} from "contracts/nft/erc721m/ERC721CMInitializableV1_0_0.sol"; +import {ERC721MInitializableV1_0_0} from "contracts/nft/erc721m/ERC721MInitializableV1_0_0.sol"; import {ERC1155MInitializableV1_0_0} from "contracts/nft/erc1155m/ERC1155MInitializableV1_0_0.sol"; import {TokenStandard} from "contracts/common/Structs.sol"; contract DeployMagicDropImplementation is Script { error AddressMismatch(address expected, address actual); - error InvalidVersion(string expected, string actual); error InvalidTokenStandard(string standard); - error FailedToGetContractVersion(); + 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")); - string memory version = vm.envString("CONTRACT_VERSION"); uint256 privateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(privateKey); @@ -23,24 +21,13 @@ contract DeployMagicDropImplementation is Script { address deployedAddress; if (standard == TokenStandard.ERC721) { - deployedAddress = address(new ERC721CMInitializableV1_0_0{salt: salt}()); + deployedAddress = address(new ERC721MInitializableV1_0_0{salt: salt}()); } else if (standard == TokenStandard.ERC1155) { deployedAddress = address(new ERC1155MInitializableV1_0_0{salt: salt}()); } if (address(deployedAddress) != expectedAddress) { - revert AddressMismatch(expectedAddress, address(deployedAddress)); - } - - bytes memory data = abi.encodeWithSignature("contractNameAndVersion()"); - (bool success, bytes memory result) = deployedAddress.call(data); - if (!success) { - revert FailedToGetContractVersion(); - } - - (, string memory deployedVersion) = abi.decode(result, (string, string)); - if (keccak256(abi.encodePacked(deployedVersion)) != keccak256(abi.encodePacked(version))) { - revert InvalidVersion(version, deployedVersion); + revert AddressMismatch(expectedAddress, deployedAddress); } vm.stopBroadcast(); diff --git a/scripts-foundry/common/DeployMagicDropTokenImplRegistry.s.sol b/scripts-foundry/common/DeployMagicDropTokenImplRegistry.s.sol index bf6656b..8af9267 100644 --- a/scripts-foundry/common/DeployMagicDropTokenImplRegistry.s.sol +++ b/scripts-foundry/common/DeployMagicDropTokenImplRegistry.s.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; import {MagicDropTokenImplRegistry} from "contracts/registry/MagicDropTokenImplRegistry.sol"; contract DeployMagicDropTokenImplRegistry is Script {