UNPKG

@paulstinchcombe/kami721c-sdk

Version:

SDK for interacting with KAMI721C NFT contracts

213 lines (177 loc) 9.2 kB
import { ethers, Wallet, Contract, Signer, ContractRunner } from 'ethers'; import { KAMI721CFactory } from '../factories/KAMI721CFactory'; import { KAMI721C, RoyaltyData } from '../contracts/KAMI721C'; import { colorLog, logStyles, formatKeyValue } from '../utils/console-colors'; import dotenv from 'dotenv'; dotenv.config(); // === Configuration === const RPC_URL = process.env.RPC_URL; const PRIVATE_KEY = process.env.PRIVATE_KEY; const USDC_ADDRESS = process.env.USDC_ADDRESS; const RECEIVER_1 = process.env.ROYALTY_RECEIVER_1; const RECEIVER_2 = process.env.ROYALTY_RECEIVER_2; const RECEIVER_3 = process.env.ROYALTY_RECEIVER_3; const BASE_RENTAL_PRICE = process.env.BASE_RENTAL_PRICE_PER_DAY ? BigInt(process.env.BASE_RENTAL_PRICE_PER_DAY) : 2000000n; // Default 2 USDC // Minimal ABI for USDC interactions const USDC_ABI = [ 'function approve(address spender, uint256 amount) external returns (bool)', 'function allowance(address owner, address spender) external view returns (uint256)', 'function decimals() external view returns (uint8)', ]; // --- 1. Deploy Contract Function --- async function deployContract(wallet: Wallet): Promise<KAMI721C> { colorLog.section('1. Deploying Contract'); if (!USDC_ADDRESS) throw new Error('USDC_ADDRESS environment variable is required.'); const factory = new KAMI721CFactory(wallet); const platformAddress = process.env.PLATFORM_ADDRESS || wallet.address; const platformCommission = process.env.PLATFORM_COMMISSION_PERCENTAGE ? parseInt(process.env.PLATFORM_COMMISSION_PERCENTAGE) : 500; // 5% const initialMintPrice = process.env.MINT_PRICE ? BigInt(process.env.MINT_PRICE) : 1000000n; // 1 USDC const name = process.env.CONTRACT_NAME || 'KAMI Setup Example'; const symbol = process.env.CONTRACT_SYMBOL || 'KSE'; const baseURI = process.env.BASE_URI || 'https://api.example.com/setup/'; console.log('Deploying upgradeable contract via proxy with parameters:'); console.log(formatKeyValue('USDC Address', USDC_ADDRESS)); console.log(formatKeyValue('Name', name)); console.log(formatKeyValue('Symbol', symbol)); console.log(formatKeyValue('Base URI', baseURI)); console.log(formatKeyValue('Initial Mint Price', `${ethers.formatUnits(initialMintPrice, 6)} USDC`)); console.log(formatKeyValue('Platform Address', platformAddress)); console.log(formatKeyValue('Platform Commission', `${platformCommission / 100}%`)); console.log(formatKeyValue('Initial Owner', wallet.address)); const nftContract = await factory.deployUpgradeable( USDC_ADDRESS, name, symbol, baseURI, initialMintPrice, platformAddress, platformCommission, wallet.address ); const proxyAddress = nftContract.getAddress(); colorLog.success(`Contract deployed successfully at proxy address: ${logStyles.address(proxyAddress)}`); return nftContract; } // --- 2. Set Royalties Function --- async function setRoyalties(nftContract: KAMI721C, receivers: string[]): Promise<void> { colorLog.section('2. Setting Royalties'); if (receivers.length !== 3) { throw new Error('This function expects exactly 3 receiver addresses.'); } // Assuming equal split (3333 basis points each, summing to 9999) const royaltyData: RoyaltyData[] = [ { receiver: receivers[0], feeNumerator: 3333n }, { receiver: receivers[1], feeNumerator: 3333n }, { receiver: receivers[2], feeNumerator: 3333n }, ]; const transferRoyaltyPercentage = 1000; // 10% for transfers console.log(`Setting mint royalties for 3 receivers: ${receivers.join(', ')}`); const mintTx = await nftContract.setMintRoyalties(royaltyData); console.log('Mint royalties transaction sent:', mintTx.hash); await mintTx.wait(); colorLog.success('Mint royalties set successfully.'); console.log(`Setting transfer royalties for 3 receivers: ${receivers.join(', ')}`); const transferTx = await nftContract.setTransferRoyalties(royaltyData); console.log('Transfer royalties transaction sent:', transferTx.hash); await transferTx.wait(); colorLog.success('Transfer royalties set successfully.'); console.log(`Setting transfer royalty percentage to ${transferRoyaltyPercentage / 100}%...`); const percentageTx = await nftContract.setRoyaltyPercentage(transferRoyaltyPercentage); console.log('Royalty percentage transaction sent:', percentageTx.hash); await percentageTx.wait(); colorLog.success('Transfer royalty percentage set successfully.'); } // --- 3. Mint Token Function --- async function mintToken(nftContract: KAMI721C, wallet: Wallet): Promise<bigint> { colorLog.section('3. Minting Token'); if (!USDC_ADDRESS) throw new Error('USDC_ADDRESS needed for minting.'); const mintPrice = await nftContract.mintPrice(); console.log(`Mint price: ${ethers.formatUnits(mintPrice, 6)} USDC`); const usdcContract = new Contract(USDC_ADDRESS, USDC_ABI, wallet); const decimals = await usdcContract.decimals(); // Check allowance const allowance = await usdcContract.allowance(wallet.address, nftContract.getAddress()); console.log(`Current USDC allowance: ${ethers.formatUnits(allowance, decimals)} USDC`); if (allowance < mintPrice) { console.log('Approving USDC spend...'); const approveTx = await usdcContract.approve(nftContract.getAddress(), mintPrice); console.log('Approval transaction sent:', approveTx.hash); await approveTx.wait(); colorLog.success('USDC Approved'); } else { console.log('Sufficient USDC allowance already set.'); } // Mint console.log('Sending mint transaction...'); const mintTx = await nftContract.mint(); console.log('Mint transaction sent:', mintTx.hash); const receipt = await mintTx.wait(); colorLog.success(`Token minted successfully in block ${receipt?.blockNumber}`); // Determine token ID (simplistic approach) const totalSupply = await nftContract.totalSupply(); const tokenId = totalSupply > 0n ? totalSupply - 1n : 0n; console.log(`Minted token ID (estimated): ${tokenId}`); return tokenId; } // --- 4. Prepare for Sale Function --- async function prepareForSale(nftContract: KAMI721C, tokenId: bigint): Promise<void> { colorLog.section('4. Preparing Token for Sale'); console.log(`Approving contract ${nftContract.getAddress()} to manage token ID ${tokenId}...`); // The owner calls approve, allowing the contract to transfer the token during sellToken const approveTx = await nftContract.approve(nftContract.getAddress(), tokenId); console.log('Approval transaction sent:', approveTx.hash); await approveTx.wait(); colorLog.success(`Token ${tokenId} approved for sale via contract.`); console.log('(Note: Actual sale price is set when calling sellToken)'); } // --- 5. Log Rental Parameters Function --- async function logRentalParameters(nftContract: KAMI721C, basePricePerDay: bigint): Promise<void> { colorLog.section('5. Logging Rental Parameters'); const platformCommPercentage = await nftContract.getPlatformCommissionPercentage(); const commissionAmount = (basePricePerDay * platformCommPercentage) / 10000n; const totalRentalPricePerDay = basePricePerDay + commissionAmount; console.log('Example Rental Parameters (for 1 day):'); console.log(formatKeyValue('Base Rental Price', `${ethers.formatUnits(basePricePerDay, 6)} USDC`)); console.log(formatKeyValue('Platform Commission', `${Number(platformCommPercentage) / 100}%`)); console.log(formatKeyValue('Commission Amount', `${ethers.formatUnits(commissionAmount, 6)} USDC`)); console.log(formatKeyValue('Total Price (Renter Pays)', `${ethers.formatUnits(totalRentalPricePerDay, 6)} USDC`)); console.log('(Note: Actual rental is initiated by calling rentToken with duration and total price)'); } // --- Main Orchestration Function --- async function main() { console.log('Starting KAMI721C Deployment and Setup Script...'); if (!RPC_URL || !PRIVATE_KEY) { throw new Error('RPC_URL and PRIVATE_KEY environment variables are required.'); } if (!RECEIVER_1 || !RECEIVER_2 || !RECEIVER_3) { throw new Error('ROYALTY_RECEIVER_1, ROYALTY_RECEIVER_2, and ROYALTY_RECEIVER_3 environment variables are required.'); } const provider = new ethers.JsonRpcProvider(RPC_URL); const wallet = new ethers.Wallet(PRIVATE_KEY, provider); colorLog.info(`Using wallet: ${logStyles.address(wallet.address)} on network ${(await provider.getNetwork()).name}`); try { // 1. Deploy const nftContract = await deployContract(wallet); // Wait a moment for chain/indexer await new Promise((resolve) => setTimeout(resolve, 3000)); // 2. Set Royalties const royaltyReceivers = [RECEIVER_1, RECEIVER_2, RECEIVER_3]; await setRoyalties(nftContract, royaltyReceivers); // 3. Mint Token const mintedTokenId = await mintToken(nftContract, wallet); // 4. Prepare for Sale await prepareForSale(nftContract, mintedTokenId); // 5. Log Rental Parameters await logRentalParameters(nftContract, BASE_RENTAL_PRICE); colorLog.section('Setup Complete!'); console.log(`Contract Address: ${nftContract.getAddress()}`); console.log(`Minted Token ID: ${mintedTokenId}`); } catch (error: any) { colorLog.error('Script failed:'); console.error(error.message); if (error.reason) console.error('Reason:', error.reason); if (error.data) console.error('Data:', error.data); process.exit(1); } } main();