Skip to content

Commit

Permalink
t
Browse files Browse the repository at this point in the history
  • Loading branch information
adamkwolf committed Nov 27, 2024
1 parent 0a748ff commit abb28cd
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 86 deletions.
74 changes: 26 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion contracts/factory/MagicDropCloneFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 1 addition & 2 deletions contracts/registry/MagicDropTokenImplRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 4 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
15 changes: 15 additions & 0 deletions scripts-foundry/common/.env.example
Original file line number Diff line number Diff line change
@@ -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=
35 changes: 33 additions & 2 deletions scripts-foundry/common/0a-create2-magicdrop-impl-registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,42 @@ else
exit 1
fi


INITIAL_OWNER=""

# Function to display usage
usage() {
echo "Usage: $0 --initial-owner <initial owner address>"
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 "-------------------------------------"
Expand Down
31 changes: 30 additions & 1 deletion scripts-foundry/common/0b-create2-magicdrop-clone-factory.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,40 @@ else
exit 1
fi

REGISTRY_ADDRESS=""
INITIAL_OWNER=""

# Function to display usage
usage() {
echo "Usage: $0 --initial-owner <initial owner address> --registry-address <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 "-------------------------------------"
Expand Down
6 changes: 3 additions & 3 deletions scripts-foundry/common/1a-deploy-magicdrop-impl-registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 2 additions & 3 deletions scripts-foundry/common/1b-deploy-magicdrop-clone-factory.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 5 additions & 9 deletions scripts-foundry/common/2a-deploy-magicdrop-impl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 <chain id> --version <magic drop impl version> --token-standard <token standard> --expected-address <expected address> --salt <salt>"
exit 1
}
Expand All @@ -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 ;;
Expand All @@ -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

Expand All @@ -52,17 +52,13 @@ 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:]')

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"
Expand All @@ -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 \
Expand Down
21 changes: 4 additions & 17 deletions scripts-foundry/common/DeployMagicDropImplementation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,32 @@
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);

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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit abb28cd

Please sign in to comment.