Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions contracts/nft/erc721m/ERC721CM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ contract ERC721CM is
address cosigner,
uint256 timestampExpirySeconds,
address mintCurrency,
address fundReceiver
) ERC721ACQueryable(collectionName, collectionSymbol) Ownable() {
address fundReceiver,
address initialOwner
) ERC721ACQueryable(collectionName, collectionSymbol) {
_transferOwnership(initialOwner);
if (globalWalletLimit > maxMintableSupply) {
revert GlobalWalletLimitOverflow();
}
Expand All @@ -76,7 +78,7 @@ contract ERC721CM is
/// @notice Returns the contract name and version
/// @return The contract name and version as strings
function contractNameAndVersion() public pure returns (string memory, string memory) {
return ("ERC721CM", "1.0.0");
return ("ERC721CM", "1.0.1");
}

/// @notice Gets the token URI for a specific token ID
Expand Down Expand Up @@ -315,6 +317,16 @@ contract ERC721CM is
emit SetMintable(mintable);
}

/// @notice Sets the default royalty for the contract
/// @param receiver The address to receive royalties
/// @param feeNumerator The royalty fee numerator
function setDefaultRoyalty(address receiver, uint96 feeNumerator) public onlyOwner {
super._setDefaultRoyalty(receiver, feeNumerator);
_royaltyBps = feeNumerator;
_royaltyRecipient = receiver;
emit DefaultRoyaltySet(receiver, feeNumerator);
}

/// @notice Sets the maximum mintable supply
/// @param maxMintableSupply The maximum mintable supply to set
function setMaxMintableSupply(uint256 maxMintableSupply) external virtual onlyOwner {
Expand Down
79 changes: 79 additions & 0 deletions scripts-foundry/common/100a-create2-magicdrop-impl.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/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=""
INITIAL_OWNER=""

# Function to display usage
usage() {
echo "Usage: $0 --impl <path to implementation> --name <name> --symbol <symbol> --fund-receiver <fund receiver address> --uri <token uri suffix> --maxMintableSupply <max mintable supply> --globalWalletLimit <global wallet limit> --cosigner <cosigner address> --timestamp-expiry <timestamp expiry seconds> --mint-currency <mint currency address> --initial-owner <initial owner address>"
exit 1
}

# Process arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--impl) IMPL_PATH=$2; shift ;;
--name) NAME=$2; shift ;;
--symbol) SYMBOL=$2; shift ;;
--uri) TOKEN_URI_SUFFIX=$2; shift ;;
--maxMintableSupply) MAX_MINTABLE_SUPPLY=$2; shift ;;
--globalWalletLimit) GLOBAL_WALLET_LIMIT=$2; shift ;;
--cosigner) COSIGNER=$2; shift ;;
--timestamp-expiry) TIMESTAMP_EXPIRY_SECONDS=$2; shift ;;
--mint-currency) MINT_CURRENCY=$2; shift ;;
--fund-receiver) FUND_RECEIVER=$2; shift ;;
--initial-owner) INITIAL_OWNER=$2; shift ;;
*) usage ;;
esac
shift
done

echo "IMPL_PATH: $IMPL_PATH"
echo "NAME: $NAME"
echo "SYMBOL: $SYMBOL"
echo "TOKEN_URI_SUFFIX: $TOKEN_URI_SUFFIX"
echo "MAX_MINTABLE_SUPPLY: $MAX_MINTABLE_SUPPLY"
echo "GLOBAL_WALLET_LIMIT: $GLOBAL_WALLET_LIMIT"
echo "COSIGNER: $COSIGNER"
echo "TIMESTAMP_EXPIRY_SECONDS: $TIMESTAMP_EXPIRY_SECONDS"
echo "MINT_CURRENCY: $MINT_CURRENCY"
echo "FUND_RECEIVER: $FUND_RECEIVER"
echo "INITIAL_OWNER: $INITIAL_OWNER"

# Check if all parameters are set
if [ -z "$IMPL_PATH" ] || [ -z "$NAME" ] || [ -z "$SYMBOL" ] || [ -z "$TOKEN_URI_SUFFIX" ] || [ -z "$MAX_MINTABLE_SUPPLY" ] || [ -z "$GLOBAL_WALLET_LIMIT" ] || [ -z "$COSIGNER" ] || [ -z "$TIMESTAMP_EXPIRY_SECONDS" ] || [ -z "$MINT_CURRENCY" ] || [ -z "$FUND_RECEIVER" ] || [ -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,address)" "$NAME" "$SYMBOL" "$TOKEN_URI_SUFFIX" "$MAX_MINTABLE_SUPPLY" "$GLOBAL_WALLET_LIMIT" "$COSIGNER" "$TIMESTAMP_EXPIRY_SECONDS" "$MINT_CURRENCY" "$FUND_RECEIVER" "$INITIAL_OWNER")
constructorArgsNoPrefix=${constructorArgs#0x}
implInitCode=$(cast concat-hex $implByteCode $constructorArgsNoPrefix)

echo $implByteCode
echo $constructorArgs

cast create2 --starts-with 88888888 --case-sensitive --init-code $implInitCode
echo "create2 MagicDropImpl END"
echo "-------------------------------------"
echo ""
135 changes: 135 additions & 0 deletions scripts-foundry/common/101a-deploy-magicdrop-impl-direct.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/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=""
TOKEN_URI_SUFFIX=""
MAX_MINTABLE_SUPPLY="1000"
GLOBAL_WALLET_LIMIT="0"
COSIGNER="0x0000000000000000000000000000000000000000"
TIMESTAMP_EXPIRY_SECONDS="60"
MINT_CURRENCY="0x0000000000000000000000000000000000000000"
FUND_RECEIVER=""
ROYALTY_RECIPIENT="0x0000000000000000000000000000000000000000"
ROYALTY_BPS=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 <chain id> --token-standard <token standard> --is-erc721c <bool> --expected-address <expected address> --salt <salt> --name <name> --symbol <symbol> --fund-receiver <fund receiver address> --uri <token uri suffix> --maxMintableSupply <max mintable supply> --globalWalletLimit <global wallet limit> --cosigner <cosigner address> --timestamp-expiry <timestamp expiry seconds> --mint-currency <mint currency address> --initial-owner <initial owner address> --royalty-recipient <royalty recipient address> --royalty-bps <250>"
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 ;;
--uri) TOKEN_URI_SUFFIX=$2; shift ;;
--maxMintableSupply) MAX_MINTABLE_SUPPLY=$2; shift ;;
--globalWalletLimit) GLOBAL_WALLET_LIMIT=$2; shift ;;
--cosigner) COSIGNER=$2; shift ;;
--timestamp-expiry) TIMESTAMP_EXPIRY_SECONDS=$2; shift ;;
--mint-currency) MINT_CURRENCY=$2; shift ;;
--fund-receiver) FUND_RECEIVER=$2; shift ;;
--royalty-recipient) ROYALTY_RECIPIENT=$2; shift;;
--royalty-bps) ROYALTY_BPS=$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 "$TOKEN_URI_SUFFIX" ] || [ -z "$MAX_MINTABLE_SUPPLY" ] || [ -z "$GLOBAL_WALLET_LIMIT" ] || [ -z "$COSIGNER" ] || [ -z "$TIMESTAMP_EXPIRY_SECONDS" ] || [ -z "$MINT_CURRENCY" ] || [ -z "$FUND_RECEIVER" ] || [ -z "$ROYALTY_RECIPIENT" ] || [ -z "$ROYALTY_BPS" ] || [ -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 "Token URI Suffix: $TOKEN_URI_SUFFIX"
echo "Max Mintable Supply: $MAX_MINTABLE_SUPPLY"
echo "Global Wallet Limit: $GLOBAL_WALLET_LIMIT"
echo "Cosigner: $COSIGNER"
echo "Timestamp Expiry: $TIMESTAMP_EXPIRY_SECONDS"
echo "Mint Currency: $MINT_CURRENCY"
echo "Fund Reciever: $FUND_RECEIVER"
echo "Royalty Recipient: $ROYALTY_RECIPIENT"
echo "Royalty BPS: $ROYALTY_BPS"
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

constructorArgs=$(cast abi-encode "constructor(string,string,string,uint256,uint256,address,uint256,address,address,address)" "$NAME" "$SYMBOL" "$TOKEN_URI_SUFFIX" "$MAX_MINTABLE_SUPPLY" "$GLOBAL_WALLET_LIMIT" "$COSIGNER" "$TIMESTAMP_EXPIRY_SECONDS" "$MINT_CURRENCY" "$FUND_RECEIVER" "$INITIAL_OWNER")
echo "constructorArgs: $constructorArgs"

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 TOKEN_URI_SUFFIX=$TOKEN_URI_SUFFIX MAX_MINTABLE_SUPPLY=$MAX_MINTABLE_SUPPLY GLOBAL_WALLET_LIMIT=$GLOBAL_WALLET_LIMIT COSIGNER=$COSIGNER TIMESTAMP_EXPIRY_SECONDS=$TIMESTAMP_EXPIRY_SECONDS MINT_CURRENCY=$MINT_CURRENCY FUND_RECEIVER=$FUND_RECEIVER ROYALTY_RECIPIENT=$ROYALTY_RECIPIENT ROYALTY_BPS=$ROYALTY_BPS INITIAL_OWNER=$INITIAL_OWNER forge script ./DeployMagicDropImplementationDirect.s.sol:DeployMagicDropImplementationDirect \
--rpc-url $RPC_URL \
--broadcast \
--optimizer-runs 777 \
--via-ir \
--verify \
-v

echo ""
echo "============= DEPLOYED MAGICDROP IMPLEMENTATION ============="
echo ""
75 changes: 75 additions & 0 deletions scripts-foundry/common/DeployMagicDropImplementationDirect.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {Script, console} from "forge-std/Script.sol";
import {ERC721CM} from "contracts/nft/erc721m/ERC721CM.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 = vm.envString("TOKEN_URI_SUFFIX");
uint256 maxMintableSupply = vm.envUint("MAX_MINTABLE_SUPPLY");
uint256 globalWalletLimit = vm.envUint("GLOBAL_WALLET_LIMIT");
address cosigner = address(uint160(vm.envUint("COSIGNER")));
uint256 timestampExpirySeconds = vm.envUint("TIMESTAMP_EXPIRY_SECONDS");
address mintCurrency = address(uint160(vm.envUint("MINT_CURRENCY")));
address fundReceiver = address(uint160(vm.envUint("FUND_RECEIVER")));
address royaltyRecipient = address(uint160(vm.envUint("ROYALTY_RECIPIENT")));
uint96 royaltyBps = uint96(vm.envUint("ROYALTY_BPS"));
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,
initialOwner
));

ERC721CM(deployedAddress).setDefaultRoyalty(royaltyRecipient, royaltyBps);
} 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);
}
}
}