Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "forge-std/Script.sol";
import "../../../../src/v2/token/ERC721/sovereign/LazySovereignBatchMintFactory.sol";
import "../../../../src/v2/token/ERC721/sovereign/LazySovereignBatchMint.sol";

/// @title LazySovereignBatchMintFactoryDeploy
/// @notice Deployment script for LazySovereignBatchMintFactory contract
contract LazySovereignBatchMintFactoryDeploy is Script {
function run() external {
// 1. Load private key and start broadcast
uint256 privateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(privateKey);
address deployer = vm.addr(privateKey);

// 2. Load environment variables
address factoryOwner = vm.envOr("FACTORY_OWNER", deployer);
address lazySovereignBatchMintImplementation = vm.envOr("LAZY_SOVEREIGN_BATCH_MINT_IMPLEMENTATION", address(0));

// 3. Deploy LazySovereignBatchMint implementation if not provided
if (lazySovereignBatchMintImplementation == address(0)) {
LazySovereignBatchMint implementation = new LazySovereignBatchMint();
lazySovereignBatchMintImplementation = address(implementation);
console.log("LazySovereignBatchMint implementation deployed at:", lazySovereignBatchMintImplementation);
} else {
console.log("Using existing LazySovereignBatchMint implementation at:", lazySovereignBatchMintImplementation);
}

// 4. Deploy LazySovereignBatchMintFactory with the implementation address
LazySovereignBatchMintFactory factory = new LazySovereignBatchMintFactory(lazySovereignBatchMintImplementation);

// 5. Transfer ownership if a different owner was specified
if (factoryOwner != deployer) {
factory.transferOwnership(factoryOwner);
}

// 6. Log deployed addresses
console.log("LazySovereignBatchMintFactory deployed at:", address(factory));
console.log("LazySovereignBatchMint implementation at:", factory.lazySovereignNFT());
console.log("Factory owner:", factory.owner());
console.log("Deployer:", deployer);

vm.stopBroadcast();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "forge-std/Script.sol";
import {IERC721} from "openzeppelin-contracts/token/ERC721/IERC721.sol";
import "../../../../src/v2/marketplace/RareBatchListingMarketplace.sol";
import "../../../../src/v2/marketplace/IRareBatchListingMarketplace.sol";

/// @title ConfigureBatchListingScript
/// @notice Script to configure a batch listing on RareBatchListingMarketplace
contract ConfigureBatchListingScript is Script {
function run() external {
// 1. Load private key and start broadcast
uint256 privateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(privateKey);

// 2. Load environment variables
address marketplaceAddress = vm.envAddress("MARKETPLACE_ADDRESS");
bytes32 merkleRoot = vm.envBytes32("MERKLE_ROOT");

// Currency: address(0) for ETH, or specific ERC20 address
address currency = vm.envOr("CURRENCY", address(0));

// Sale price amount (in wei for ETH, or smallest unit for ERC20)
uint256 amount = vm.envUint("SALE_PRICE_AMOUNT");

// Optional: NFT contract address for marketplace approval
address nftContract = vm.envOr("NFT_CONTRACT", address(0));

// Optional: ERC721ApprovalManager address (required if NFT_CONTRACT is set)
address erc721ApprovalManager = vm.envOr("ERC721_APPROVAL_MANAGER", address(0));

// 3. Get marketplace instance
IRareBatchListingMarketplace marketplace = IRareBatchListingMarketplace(marketplaceAddress);

// 4. Prepare split configuration (optional)
// Use Foundry's built-in array parsing with comma delimiter
address[] memory splitAddressesArray = vm.envOr("SPLIT_ADDRESSES", ",", new address[](0));
uint256[] memory splitRatiosArray = vm.envOr("SPLIT_RATIOS", ",", new uint256[](0));

require(
splitAddressesArray.length == splitRatiosArray.length,
"Split addresses and ratios length mismatch"
);

// Convert to payable addresses and uint8 ratios
address payable[] memory splitAddresses = new address payable[](splitAddressesArray.length);
uint8[] memory splitRatios = new uint8[](splitRatiosArray.length);

for (uint256 i = 0; i < splitAddressesArray.length; i++) {
splitAddresses[i] = payable(splitAddressesArray[i]);
require(splitRatiosArray[i] <= type(uint8).max, "Split ratio exceeds uint8 max");
splitRatios[i] = uint8(splitRatiosArray[i]);
}

// 5. Check if root is already registered and warn about nonce increment
bytes32[] memory existingRoots = marketplace.getUserSalePriceMerkleRoots(msg.sender);
bool rootExists = false;
uint256 currentNonce = 0;
for (uint256 i = 0; i < existingRoots.length; i++) {
if (existingRoots[i] == merkleRoot) {
rootExists = true;
currentNonce = marketplace.getCreatorSalePriceMerkleRootNonce(msg.sender, merkleRoot);
break;
}
}

if (rootExists) {
console.log("\nWARNING: This Merkle root is already registered!");
console.log("Current nonce:", currentNonce);
console.log("Re-registering will:");
console.log(" - Increment nonce to", currentNonce + 1);
console.log(" - Update currency, amount, and splits");
console.log(" - Allow previously sold tokens to be sold again");
}

// Register the sale price Merkle root
console.log("\nRegistering sale price Merkle root...");
console.log("Marketplace:", marketplaceAddress);
console.log("Merkle Root:", vm.toString(merkleRoot));
console.log("Currency:", currency);
console.log("Amount:", amount);

marketplace.registerSalePriceMerkleRoot(
merkleRoot,
currency,
amount,
splitAddresses,
splitRatios
);

uint256 newNonce = marketplace.getCreatorSalePriceMerkleRootNonce(msg.sender, merkleRoot);
console.log("Sale price Merkle root registered successfully!");
console.log("New nonce:", newNonce);

// 6. Approve marketplace for NFT transfers (if NFT contract is provided)
if (nftContract != address(0)) {
if (erc721ApprovalManager == address(0)) {
revert("ERC721_APPROVAL_MANAGER is required when NFT_CONTRACT is set");
}

console.log("\nApproving marketplace for NFT transfers...");
console.log("NFT Contract:", nftContract);
console.log("ERC721 Approval Manager:", erc721ApprovalManager);

IERC721 nft = IERC721(nftContract);

// Check current approval status
bool alreadyApproved = nft.isApprovedForAll(msg.sender, erc721ApprovalManager);

if (alreadyApproved) {
console.log("Marketplace already approved for this NFT contract");
} else {
// Approve the ERC721ApprovalManager to transfer NFTs on behalf of the creator
nft.setApprovalForAll(erc721ApprovalManager, true);
console.log("Marketplace approval granted successfully!");
}
}

// 7. Optionally configure allowlist
bytes32 allowListRoot = vm.envOr("ALLOWLIST_ROOT", bytes32(0));
if (allowListRoot != bytes32(0)) {
uint256 allowListEndTimestamp = vm.envUint("ALLOWLIST_END_TIMESTAMP");

console.log("Setting allowlist configuration...");
console.log("Allowlist Root:", vm.toString(allowListRoot));
console.log("End Timestamp:", allowListEndTimestamp);

marketplace.setAllowListConfig(merkleRoot, allowListRoot, allowListEndTimestamp);

console.log("Allowlist configuration set successfully!");
}

// 8. Display configuration
IRareBatchListingMarketplace.MerkleSalePriceConfig memory config =
marketplace.getMerkleSalePriceConfig(msg.sender, merkleRoot);

console.log("\n=== Configuration Summary ===");
console.log("Creator:", msg.sender);
console.log("Merkle Root:", vm.toString(merkleRoot));
console.log("Currency:", config.currency);
console.log("Amount:", config.amount);
console.log("Nonce:", config.nonce);
console.log("Split Recipients Count:", config.splitRecipients.length);

if (allowListRoot != bytes32(0)) {
IRareBatchListingMarketplace.AllowListConfig memory allowListConfig =
marketplace.getAllowListConfig(msg.sender, merkleRoot);
console.log("Allowlist Root:", vm.toString(allowListConfig.root));
console.log("Allowlist End Timestamp:", allowListConfig.endTimestamp);
}

vm.stopBroadcast();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/bin/bash

# Default broadcast to false
BROADCAST=false

# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--broadcast)
BROADCAST=true
shift
;;
*)
echo "Unknown parameter: $1"
echo "Usage: $0 [--broadcast]"
echo " --broadcast : Execute transactions on-chain (default: simulation only)"
exit 1
;;
esac
done

# Load .env file
if [ -f .env ]; then
echo "Loading environment from .env file"
set -o allexport
source .env
set +o allexport
fi

# Set default RPC URL if not set
if [ -z "$RPC_URL" ]; then
echo "RPC_URL not set. Using default localhost:8545"
export RPC_URL="http://localhost:8545"
fi

# Validate required environment variables
if [ -z "$PRIVATE_KEY" ]; then
echo "Error: PRIVATE_KEY environment variable is required"
exit 1
fi

if [ -z "$MARKETPLACE_ADDRESS" ]; then
echo "Error: MARKETPLACE_ADDRESS environment variable is required"
echo "Set MARKETPLACE_ADDRESS to your deployed RareBatchListingMarketplace address"
exit 1
fi

if [ -z "$MERKLE_ROOT" ]; then
echo "Error: MERKLE_ROOT environment variable is required"
echo "Set MERKLE_ROOT to the Merkle root of your token set"
echo "The Merkle root should be computed from leaves: keccak256(abi.encodePacked(contractAddress, tokenId))"
exit 1
fi

if [ -z "$SALE_PRICE_AMOUNT" ]; then
echo "Error: SALE_PRICE_AMOUNT environment variable is required"
echo "Set SALE_PRICE_AMOUNT to the sale price amount (in wei for ETH, or smallest unit for ERC20)"
exit 1
fi

# Validate optional split configuration
# SPLIT_ADDRESSES and SPLIT_RATIOS should be comma-separated values
# Example: SPLIT_ADDRESSES="0x123...,0x456..." SPLIT_RATIOS="50,50"
if [ -n "$SPLIT_ADDRESSES" ] && [ -z "$SPLIT_RATIOS" ]; then
echo "Error: SPLIT_RATIOS is required when SPLIT_ADDRESSES is provided"
exit 1
fi

if [ -z "$SPLIT_ADDRESSES" ] && [ -n "$SPLIT_RATIOS" ]; then
echo "Error: SPLIT_ADDRESSES is required when SPLIT_RATIOS is provided"
exit 1
fi

# Validate split arrays have same length
if [ -n "$SPLIT_ADDRESSES" ] && [ -n "$SPLIT_RATIOS" ]; then
ADDR_COUNT=$(echo "$SPLIT_ADDRESSES" | tr ',' '\n' | wc -l | tr -d ' ')
RATIO_COUNT=$(echo "$SPLIT_RATIOS" | tr ',' '\n' | wc -l | tr -d ' ')
if [ "$ADDR_COUNT" -ne "$RATIO_COUNT" ]; then
echo "Error: SPLIT_ADDRESSES and SPLIT_RATIOS must have the same number of values"
echo " Addresses: $ADDR_COUNT, Ratios: $RATIO_COUNT"
exit 1
fi
fi

# Validate allowlist configuration if provided
if [ -n "$ALLOWLIST_ROOT" ] && [ -z "$ALLOWLIST_END_TIMESTAMP" ]; then
echo "Error: ALLOWLIST_END_TIMESTAMP is required when ALLOWLIST_ROOT is provided"
exit 1
fi

# Validate marketplace approval configuration if provided
if [ -n "$NFT_CONTRACT" ] && [ -z "$ERC721_APPROVAL_MANAGER" ]; then
echo "Error: ERC721_APPROVAL_MANAGER is required when NFT_CONTRACT is provided"
echo "Set ERC721_APPROVAL_MANAGER to the ERC721ApprovalManager contract address"
exit 1
fi

# Warn if NFT_CONTRACT is not set (approval won't happen)
if [ -z "$NFT_CONTRACT" ]; then
echo "Warning: NFT_CONTRACT not set. Marketplace approval will be skipped."
echo "You may need to manually approve the marketplace before tokens can be purchased."
fi

if [ "$BROADCAST" = true ]; then
if [ -z "$ETHERSCAN_API_KEY" ]; then
echo "Warning: ETHERSCAN_API_KEY not set. Contract verification will be skipped."
fi
if [ -z "$CHAIN_ID" ]; then
echo "Error: CHAIN_ID environment variable is required for broadcasting"
exit 1
fi
fi

# Prepare forge command
FORGE_CMD="forge script script/token/sovereign/lazy-sovereign-batch-mint-factory-deploy/configure-batch-listing.s.sol:ConfigureBatchListingScript --rpc-url ${RPC_URL} --via-ir -vv"

# Add broadcast flag if specified
if [ "$BROADCAST" = true ]; then
echo "Broadcasting transactions to chain ID: ${CHAIN_ID}..."
FORGE_CMD="${FORGE_CMD} --broadcast --chain-id ${CHAIN_ID}"

# Add verification if API key is provided
if [ -n "$ETHERSCAN_API_KEY" ]; then
FORGE_CMD="${FORGE_CMD} --verify --etherscan-api-key ${ETHERSCAN_API_KEY}"
fi
else
echo "Running in simulation mode (no broadcasting)..."
fi

# Execute the command
echo "Executing: ${FORGE_CMD}"
eval "${FORGE_CMD}"
Loading