@paulstinchcombe/kami721c-sdk
Version:
SDK for interacting with KAMI721C NFT contracts
484 lines (483 loc) • 35.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ethers_1 = require("ethers");
const KAMI721C_1 = require("../contracts/KAMI721C");
const KAMI721CFactory_1 = require("../factories/KAMI721CFactory");
const console_colors_1 = require("../utils/console-colors");
const dotenv_1 = __importDefault(require("dotenv"));
// Load environment variables
dotenv_1.default.config();
async function main() {
try {
// Environment variables
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const RPC_URL = process.env.RPC_URL || 'https://testnet.skalenodes.com/v1/giant-half-dual-testnet';
const ROYALTY_RECEIVER = process.env.ROYALTY_RECEIVER;
const BUYER_ADDRESS = process.env.BUYER_ADDRESS;
const DEPLOY_NEW_CONTRACT = process.env.DEPLOY_NEW_CONTRACT === 'true';
const PLATFORM_ADDRESS = process.env.PLATFORM_ADDRESS;
const PLATFORM_COMMISSION = process.env.PLATFORM_COMMISSION ? parseInt(process.env.PLATFORM_COMMISSION) : 500; // Default 5%
if (!PRIVATE_KEY) {
throw new Error('PRIVATE_KEY environment variable is required');
}
console_colors_1.colorLog.info('Connecting to provider...');
const provider = new ethers_1.ethers.JsonRpcProvider(RPC_URL);
// Create a wallet instance from the private key
const wallet = new ethers_1.ethers.Wallet(PRIVATE_KEY, provider);
console_colors_1.colorLog.success(`Connected with wallet address: ${console_colors_1.logStyles.address(wallet.address)}`);
// Variable to hold our contract instance
let nftContract;
// DEPLOYMENT SECTION: Deploy a new contract or connect to existing one
if (DEPLOY_NEW_CONTRACT) {
console_colors_1.colorLog.section('DEPLOYING NEW CONTRACT');
// Create a factory instance for deploying KAMI721C contracts
const factory = new KAMI721CFactory_1.KAMI721CFactory(wallet);
// Deploy parameters
const contractName = process.env.CONTRACT_NAME || 'KAMI NFT Collection';
const contractSymbol = process.env.CONTRACT_SYMBOL || 'KNFT';
const baseURI = process.env.BASE_URI || 'ipfs://bafkreid4xrk5mxq4t3ilfhwmvpn5scy2kmsra7gm7a3kscnudsyb4dtsqq/';
const usdcAddress = process.env.USDC_ADDRESS;
const initialMintPrice = process.env.MINT_PRICE ? BigInt(process.env.MINT_PRICE) : 1000000n; // Default 1 USDC (6 decimals)
const platformAddress = PLATFORM_ADDRESS || wallet.address; // Default to deployer address
if (!usdcAddress) {
throw new Error('USDC_ADDRESS environment variable is required for deployment');
}
console_colors_1.colorLog.info(`Deploying new KAMI721C contract with the following parameters:`);
console.log((0, console_colors_1.formatKeyValue)('USDC Address', console_colors_1.logStyles.address(usdcAddress)));
console.log((0, console_colors_1.formatKeyValue)('Name', contractName));
console.log((0, console_colors_1.formatKeyValue)('Symbol', contractSymbol));
console.log((0, console_colors_1.formatKeyValue)('Base URI', baseURI));
console.log((0, console_colors_1.formatKeyValue)('Initial Mint Price', `${ethers_1.ethers.formatUnits(initialMintPrice, 6)} USDC`));
console.log((0, console_colors_1.formatKeyValue)('Platform Address', console_colors_1.logStyles.address(platformAddress)));
console.log((0, console_colors_1.formatKeyValue)('Platform Commission', `${PLATFORM_COMMISSION / 100}%`));
const startNonce = await wallet.getNonce();
console.log(`Starting nonce: ${startNonce}`);
// Deploy a new contract instance using the upgradeable proxy
console.log('Deploying upgradeable contract via proxy...');
try {
// Deploy the contract
nftContract = await factory.deployUpgradeable(usdcAddress, contractName, contractSymbol, baseURI, initialMintPrice, platformAddress, // This will also be the proxy admin
PLATFORM_COMMISSION, wallet.address // Initial owner address
);
const deployedAddress = nftContract.getAddress();
console_colors_1.colorLog.success(`Proxy contract deployed at: ${console_colors_1.logStyles.address(deployedAddress)}`);
// Wait a bit for the chain to sync
await new Promise((resolve) => setTimeout(resolve, 2000));
}
catch (error) {
console_colors_1.colorLog.error(`Deployment failed: ${error.message || error}`);
process.exit(1);
}
}
else {
// Connect to an existing contract
if (!CONTRACT_ADDRESS) {
throw new Error('CONTRACT_ADDRESS environment variable is required when not deploying a new contract');
}
console_colors_1.colorLog.info(`Connecting to existing KAMI721C contract at ${console_colors_1.logStyles.address(CONTRACT_ADDRESS)}...`);
nftContract = new KAMI721C_1.KAMI721C(wallet, CONTRACT_ADDRESS);
}
// Get basic contract information
const name = await nftContract.name();
const symbol = await nftContract.symbol();
const totalSupply = await nftContract.totalSupply();
const currentMintPrice = await nftContract.mintPrice();
const platformCommission = await nftContract.getPlatformCommission();
const royaltyPct = await nftContract.royaltyPercentage();
console_colors_1.colorLog.section('CONTRACT INFORMATION');
console.log((0, console_colors_1.formatKeyValue)('Name', name));
console.log((0, console_colors_1.formatKeyValue)('Symbol', symbol));
console.log((0, console_colors_1.formatKeyValue)('Total Supply', totalSupply.toString()));
console.log((0, console_colors_1.formatKeyValue)('Current Mint Price', `${ethers_1.ethers.formatUnits(currentMintPrice, 6)} USDC`));
console.log((0, console_colors_1.formatKeyValue)('Platform Commission', `${Number(platformCommission.percentage) / 100}% to ${console_colors_1.logStyles.address(platformCommission.address)}`));
console.log((0, console_colors_1.formatKeyValue)('Royalty Percentage', `${Number(royaltyPct) / 100}%`));
// Example: Update mint price (requires owner role)
const shouldUpdateMintPrice = process.env.UPDATE_MINT_PRICE === 'true';
if (shouldUpdateMintPrice) {
try {
const newMintPrice = process.env.NEW_MINT_PRICE ? BigInt(process.env.NEW_MINT_PRICE) : 2000000n; // Default 2 USDC
console_colors_1.colorLog.info(`Updating mint price to ${console_colors_1.logStyles.value(ethers_1.ethers.formatUnits(newMintPrice, 6))} USDC...`);
const tx = await nftContract.setMintPrice(newMintPrice);
console_colors_1.colorLog.transaction('Mint price update transaction submitted', tx.hash);
await tx.wait();
console_colors_1.colorLog.success('Mint price updated successfully');
const updatedPrice = await nftContract.mintPrice();
console.log((0, console_colors_1.formatKeyValue)('New mint price', `${ethers_1.ethers.formatUnits(updatedPrice, 6)} USDC`));
}
catch (error) {
console_colors_1.colorLog.error(`Error updating mint price: ${error.message}`);
console_colors_1.colorLog.warning('Note: Updating mint price requires the OWNER_ROLE');
}
}
// Example: Mint a new token (requires USDC approval)
const shouldMint = process.env.MINT_TOKEN === 'true';
let tokenId;
if (shouldMint) {
try {
console_colors_1.colorLog.section('MINTING NEW TOKEN');
const mintPrice = await nftContract.mintPrice();
console_colors_1.colorLog.info(`This will require ${console_colors_1.logStyles.value(ethers_1.ethers.formatUnits(mintPrice, 6))} USDC and USDC approval for the contract`);
// Create USDC contract instance
const usdcAddress = process.env.USDC_ADDRESS;
if (!usdcAddress) {
throw new Error('USDC_ADDRESS environment variable is required for minting');
}
// USDC contract ABI (only what we need)
const usdcAbi = [
'function approve(address spender, uint256 amount) external returns (bool)',
'function allowance(address owner, address spender) external view returns (uint256)',
'function balanceOf(address account) external view returns (uint256)',
'function decimals() external view returns (uint8)',
];
const usdcContract = new ethers_1.ethers.Contract(usdcAddress, usdcAbi, wallet);
// Check USDC balance
const balance = await usdcContract.balanceOf(wallet.address);
const decimals = await usdcContract.decimals();
console.log((0, console_colors_1.formatKeyValue)('USDC Balance', `${ethers_1.ethers.formatUnits(balance, decimals)} USDC`));
console.log((0, console_colors_1.formatKeyValue)('Required for mint', `${ethers_1.ethers.formatUnits(mintPrice, decimals)} USDC`));
if (balance < mintPrice) {
throw new Error(`Insufficient USDC balance. Need ${ethers_1.ethers.formatUnits(mintPrice, decimals)} USDC but have ${ethers_1.ethers.formatUnits(balance, decimals)} USDC`);
}
// Check and set USDC approval if needed
const contractAddress = nftContract.getAddress();
const currentAllowance = await usdcContract.allowance(wallet.address, contractAddress);
console.log((0, console_colors_1.formatKeyValue)('Current USDC allowance', `${ethers_1.ethers.formatUnits(currentAllowance, decimals)} USDC`));
if (currentAllowance < mintPrice) {
console_colors_1.colorLog.info('Approving USDC spend...');
const approveTx = await usdcContract.approve(contractAddress, mintPrice);
console_colors_1.colorLog.transaction('Approval transaction submitted', approveTx.hash);
await approveTx.wait();
console_colors_1.colorLog.success('USDC approved successfully');
// Verify approval
const newAllowance = await usdcContract.allowance(wallet.address, contractAddress);
console.log((0, console_colors_1.formatKeyValue)('New USDC allowance', `${ethers_1.ethers.formatUnits(newAllowance, decimals)} USDC`));
if (newAllowance < mintPrice) {
throw new Error('USDC approval failed - allowance not set correctly');
}
}
// Mint the token
console_colors_1.colorLog.info('Sending mint transaction...');
const mintTx = await nftContract.mint();
console_colors_1.colorLog.transaction('Mint transaction submitted', mintTx.hash);
// Wait for transaction to be confirmed
console_colors_1.colorLog.info('Waiting for mint transaction confirmation...');
const receipt = await mintTx.wait();
console_colors_1.colorLog.success(`Token minted successfully in block ${console_colors_1.logStyles.value(receipt?.blockNumber)}`);
// For demonstration, we'll assume the token ID is the current total supply
// In a real application, you would get the token ID from the mint event
const newTotalSupply = await nftContract.totalSupply();
tokenId = newTotalSupply > 0n ? newTotalSupply - 1n : 0n;
console_colors_1.colorLog.success(`Newly minted token ID: ${console_colors_1.logStyles.value(tokenId.toString())}`);
}
catch (error) {
console_colors_1.colorLog.error(`Error minting token: ${error.message}`);
if (error.data) {
console.error('Error data:', error.data);
}
if (error.transaction) {
console.error('Transaction:', error.transaction);
}
const mintPrice = await nftContract.mintPrice();
console_colors_1.colorLog.warning(`Note: Minting requires ${console_colors_1.logStyles.value(ethers_1.ethers.formatUnits(mintPrice, 6))} USDC and USDC approval for the contract`);
}
}
else {
console_colors_1.colorLog.warning('\nNo tokens available for demonstration. Set MINT_TOKEN=true to mint a new token.');
}
// If we have a token to work with, proceed with royalty and selling examples
if (tokenId !== undefined) {
// SECTION 1: Setting Royalties
console_colors_1.colorLog.section('ROYALTY SETUP EXAMPLE');
try {
// Get the royalty receiver address (default to the wallet address if not provided)
const royaltyReceiver = ROYALTY_RECEIVER || wallet.address;
console_colors_1.colorLog.info(`Setting royalties with receiver: ${console_colors_1.logStyles.address(royaltyReceiver)}`);
// Set default royalty percentage for all transfers (requires OWNER_ROLE)
const newRoyaltyPercentage = 1000; // 10% (out of 10000)
console_colors_1.colorLog.info(`Setting default royalty percentage to ${console_colors_1.logStyles.value(newRoyaltyPercentage / 100)}%...`);
const setRoyaltyPctTx = await nftContract.setRoyaltyPercentage(newRoyaltyPercentage);
console_colors_1.colorLog.transaction('Royalty percentage transaction submitted', setRoyaltyPctTx.hash);
await setRoyaltyPctTx.wait();
console_colors_1.colorLog.success('Royalty percentage set successfully');
// Create royalty data with the royalty receiver getting 100% of the royalties
const royalties = [
{
receiver: royaltyReceiver,
feeNumerator: 1000n, // 10% of the royalties (matches the royalty percentage set earlier)
},
];
// Add this before trying setMintRoyalties
const hasOwnerRole = await nftContract.hasRole(await nftContract.OWNER_ROLE(), wallet.address);
console_colors_1.colorLog.info(`Wallet has OWNER_ROLE: ${hasOwnerRole ? 'Yes' : 'No'}`);
// Set global mint royalties (for all newly minted tokens)
console_colors_1.colorLog.info('Setting global mint royalties...');
const setMintRoyaltiesTx = await nftContract.setMintRoyalties(royalties);
console_colors_1.colorLog.transaction('Mint royalties transaction submitted', setMintRoyaltiesTx.hash);
await setMintRoyaltiesTx.wait();
console_colors_1.colorLog.success('Global mint royalties set successfully');
// Set global transfer royalties with the same receiver but using 10000n as total share
console_colors_1.colorLog.info('Setting global transfer royalties...');
const transferRoyalties = [
{
receiver: royaltyReceiver,
feeNumerator: 10000n, // 100% of the royalties (must total 10000 for transfer royalties)
},
];
const setTransferRoyaltiesTx = await nftContract.setTransferRoyalties(transferRoyalties);
console_colors_1.colorLog.transaction('Transfer royalties transaction submitted', setTransferRoyaltiesTx.hash);
await setTransferRoyaltiesTx.wait();
console_colors_1.colorLog.success('Global transfer royalties set successfully');
// Set token-specific transfer royalties (optional)
console_colors_1.colorLog.info(`Setting transfer royalties for token #${console_colors_1.logStyles.value(tokenId)}...`);
const setTokenRoyaltiesTx = await nftContract.setTokenTransferRoyalties(tokenId, transferRoyalties);
console_colors_1.colorLog.transaction('Token royalties transaction submitted', setTokenRoyaltiesTx.hash);
await setTokenRoyaltiesTx.wait();
console_colors_1.colorLog.success(`Transfer royalties for token #${console_colors_1.logStyles.value(tokenId)} set successfully`);
// Verify royalty information
const salePrice = ethers_1.ethers.parseEther('1.0'); // 1 ETH as the example sale price
const royaltyInfo = await nftContract.royaltyInfo(tokenId, salePrice);
console_colors_1.colorLog.info('\nRoyalty Information:');
console.log((0, console_colors_1.formatKeyValue)('Receiver', console_colors_1.logStyles.address(royaltyInfo.receiver)));
console.log((0, console_colors_1.formatKeyValue)('Amount for 1 ETH sale', `${ethers_1.ethers.formatEther(royaltyInfo.royaltyAmount)} ETH (${((Number(royaltyInfo.royaltyAmount) * 100) /
Number(salePrice)).toFixed(2)}%)`));
// SECTION 2: Token Sale Example
console_colors_1.colorLog.section('TOKEN SALE EXAMPLE');
// Simulate a token sale
if (BUYER_ADDRESS) {
console_colors_1.colorLog.info(`Simulating sale to buyer: ${console_colors_1.logStyles.address(BUYER_ADDRESS)}`);
// Get USDC for the sale price calculation
const usdcAddress = process.env.USDC_ADDRESS;
if (!usdcAddress) {
throw new Error('USDC_ADDRESS environment variable is required for token sale');
}
// Sale price in USDC (with 6 decimals)
const salePrice = 5000000n; // 5 USDC
console_colors_1.colorLog.info(`Setting sale price to ${console_colors_1.logStyles.value(ethers_1.ethers.formatUnits(salePrice, 6))} USDC`);
// Check token ownership first
const currentOwner = await nftContract.ownerOf(tokenId);
console_colors_1.colorLog.info(`Current owner of token #${console_colors_1.logStyles.value(tokenId)}: ${console_colors_1.logStyles.address(currentOwner)}`);
if (currentOwner.toLowerCase() !== wallet.address.toLowerCase()) {
console_colors_1.colorLog.warning(`Wallet does not own token #${console_colors_1.logStyles.value(tokenId)} - sale may fail`);
}
// USDC contract ABI (only what we need)
const usdcAbi = [
'function approve(address spender, uint256 amount) external returns (bool)',
'function allowance(address owner, address spender) external view returns (uint256)',
'function balanceOf(address account) external view returns (uint256)',
'function decimals() external view returns (uint8)',
'function transfer(address to, uint256 amount) external returns (bool)',
];
// Need to create a signer for the buyer to approve USDC
const buyerContract = new ethers_1.ethers.Contract(usdcAddress, usdcAbi, wallet);
// Check and display USDC balances
const buyerBalance = await buyerContract.balanceOf(BUYER_ADDRESS);
const sellerBalance = await buyerContract.balanceOf(wallet.address);
const decimals = await buyerContract.decimals();
console_colors_1.colorLog.info('USDC Balances:');
console.log((0, console_colors_1.formatKeyValue)('Seller', `${ethers_1.ethers.formatUnits(sellerBalance, decimals)} USDC`));
console.log((0, console_colors_1.formatKeyValue)('Buyer', `${ethers_1.ethers.formatUnits(buyerBalance, decimals)} USDC`));
// For sellToken to work, the BUYER needs to approve the contract to spend their USDC
// But since we don't have the buyer's private key in this example, we'll simulate by:
// 1. First transfer USDC from seller to buyer (if needed)
// 2. Then use the seller's key to approve on behalf of the buyer (would need to be done by buyer in real scenario)
// 1. Transfer USDC to buyer if they don't have enough
if (buyerBalance < salePrice) {
console_colors_1.colorLog.info(`Buyer needs USDC. Transferring ${console_colors_1.logStyles.value(ethers_1.ethers.formatUnits(salePrice, decimals))} USDC to buyer for demo...`);
if (sellerBalance < salePrice) {
console_colors_1.colorLog.error(`Seller doesn't have enough USDC to transfer to buyer for this demo.`);
throw new Error('Insufficient USDC for demo');
}
const transferTx = await buyerContract.transfer(BUYER_ADDRESS, salePrice);
console_colors_1.colorLog.transaction('USDC transfer transaction submitted', transferTx.hash);
await transferTx.wait();
console_colors_1.colorLog.success('USDC transferred to buyer');
// Verify new balance
const newBuyerBalance = await buyerContract.balanceOf(BUYER_ADDRESS);
console.log((0, console_colors_1.formatKeyValue)("Buyer's new balance", `${ethers_1.ethers.formatUnits(newBuyerBalance, decimals)} USDC`));
}
// For the example, since we can't sign as the buyer, we'll mock the approval
console_colors_1.colorLog.warning(`NOTE: In a real application, the BUYER would need to call the approve function themselves.`);
console_colors_1.colorLog.info(`For this demo, we're showing the approval that the buyer would need to make.`);
// Approve the NFT transfer before selling
console_colors_1.colorLog.info(`Approving NFT contract to transfer token #${console_colors_1.logStyles.value(tokenId)}...`);
// We need to use ethers.js directly to approve the NFT since we can't access the private contract property
const ERC721_ABI = [
'function setApprovalForAll(address operator, bool approved) external',
'function isApprovedForAll(address owner, address operator) external view returns (bool)',
];
const nftERC721 = new ethers_1.ethers.Contract(nftContract.getAddress(), ERC721_ABI, wallet);
const approveTknTx = await nftERC721.setApprovalForAll(nftContract.getAddress(), true);
console_colors_1.colorLog.transaction('NFT approval transaction submitted', approveTknTx.hash);
await approveTknTx.wait();
// Verify approval
const isApproved = await nftERC721.isApprovedForAll(wallet.address, nftContract.getAddress());
console_colors_1.colorLog.info(`NFT approved for contract: ${isApproved ? 'Yes' : 'No'}`);
// Try the built-in sellToken function
console_colors_1.colorLog.section('TOKEN TRANSFER');
try {
// Try using the built-in sellToken function
console_colors_1.colorLog.info(`Selling token #${console_colors_1.logStyles.value(tokenId)} to ${console_colors_1.logStyles.address(BUYER_ADDRESS)} for ${console_colors_1.logStyles.value(ethers_1.ethers.formatUnits(salePrice, 6))} USDC...`);
// Create a buyer wallet and fund it with USDC
console_colors_1.colorLog.info('Creating buyer wallet and funding with USDC...');
const bw = ethers_1.ethers.Wallet.createRandom();
console_colors_1.colorLog.info(`Buyer wallet address: ${bw.address}`);
console_colors_1.colorLog.info(`Buyer wallet private key: ${bw.privateKey}`);
const buyerWallet = new ethers_1.ethers.Wallet(bw.privateKey, provider);
const usdcContract = new ethers_1.ethers.Contract(usdcAddress, usdcAbi, wallet);
const transferTx = await usdcContract.transfer(buyerWallet.address, salePrice);
console_colors_1.colorLog.transaction('USDC transfer transaction submitted', transferTx.hash);
await transferTx.wait();
const buyerBalance = await usdcContract.balanceOf(buyerWallet.address);
console_colors_1.colorLog.success(`Buyer wallet funded with ${ethers_1.ethers.formatUnits(buyerBalance, decimals)} USDC`);
// Log all relevant parameters for debugging
console_colors_1.colorLog.info('Parameters for sellToken:');
console.log((0, console_colors_1.formatKeyValue)('From address', console_colors_1.logStyles.address(wallet.address)));
console.log((0, console_colors_1.formatKeyValue)('To address', console_colors_1.logStyles.address(buyerWallet.address)));
console.log((0, console_colors_1.formatKeyValue)('Token ID', tokenId.toString()));
console.log((0, console_colors_1.formatKeyValue)('Sale Price', `${ethers_1.ethers.formatUnits(salePrice, 6)} USDC`));
// Approve the NFT contract to spend the USDC
const buyerUsdcContract = new ethers_1.ethers.Contract(usdcAddress, usdcAbi, buyerWallet);
const approveTx = await buyerUsdcContract.approve(nftContract.getAddress(), salePrice);
console_colors_1.colorLog.transaction('USDC approval transaction submitted', approveTx.hash);
await approveTx.wait();
console_colors_1.colorLog.success('USDC approved successfully');
// Send some native tokens for gas fees
const gasAmount = ethers_1.ethers.parseEther('0.05'); // Small amount for gas
const gasTx = await wallet.sendTransaction({
to: buyerWallet.address,
value: gasAmount,
});
console_colors_1.colorLog.transaction('Gas transaction submitted', gasTx.hash);
await gasTx.wait();
console_colors_1.colorLog.success('Gas sent successfully');
// Now attempt the sellToken call
const sellTokenTx = await nftContract.sellToken(buyerWallet.address, tokenId, salePrice);
console_colors_1.colorLog.transaction('Sale transaction submitted', sellTokenTx.hash);
await sellTokenTx.wait();
console_colors_1.colorLog.success(`Sale completed successfully!`);
// Verify new owner
const newOwner = await nftContract.ownerOf(tokenId);
console_colors_1.colorLog.success(`New owner of token #${console_colors_1.logStyles.value(tokenId)}: ${console_colors_1.logStyles.address(newOwner)}`);
}
catch (error) {
console_colors_1.colorLog.error(`Sale failed: ${error.message}`);
// Enhanced error logging
if (error.data) {
console_colors_1.colorLog.error('Error data:');
console.dir(error.data, { depth: null });
}
if (error.transaction) {
console_colors_1.colorLog.error('Transaction details:');
console.dir(error.transaction, { depth: null });
}
if (error.error) {
console_colors_1.colorLog.error('Error details:');
console.dir(error.error, { depth: null });
}
// Fallback to direct ERC721 transfer
console_colors_1.colorLog.info(`Trying direct ERC721 transfer as fallback...`);
try {
// Try direct ERC721 transfer
const transferABI = [
'function safeTransferFrom(address from, address to, uint256 tokenId) external',
'function transferFrom(address from, address to, uint256 tokenId) external',
];
const erc721 = new ethers_1.ethers.Contract(nftContract.getAddress(), transferABI, wallet);
const tx = await erc721.safeTransferFrom(wallet.address, BUYER_ADDRESS, tokenId);
console_colors_1.colorLog.transaction('Transfer transaction submitted', tx.hash);
await tx.wait();
console_colors_1.colorLog.success(`Transfer completed successfully!`);
// Verify new owner
const newOwner = await nftContract.ownerOf(tokenId);
console_colors_1.colorLog.success(`New owner of token #${console_colors_1.logStyles.value(tokenId)}: ${console_colors_1.logStyles.address(newOwner)}`);
}
catch (transferError) {
console_colors_1.colorLog.error(`Direct transfer failed: ${transferError.message}`);
console_colors_1.colorLog.warning(`\nToken remains with original owner.`);
}
}
}
else {
console_colors_1.colorLog.warning('\nSkipping token sale simulation (BUYER_ADDRESS not provided)');
console_colors_1.colorLog.info('To simulate a sale, set the BUYER_ADDRESS environment variable');
}
}
catch (error) {
console_colors_1.colorLog.error(`Error in royalty/sales operations: ${error.message}`);
console_colors_1.colorLog.warning('Note: Setting royalties and other operations require appropriate roles.');
}
}
// Simulate token rental if renter address is provided
if (process.env.RENT_TOKEN === 'true' && process.env.RENTER_ADDRESS) {
const renterAddress = process.env.RENTER_ADDRESS;
const tokenId = 0; // Assuming we're renting the first token
const rentalDuration = BigInt(process.env.RENTAL_DURATION || '86400'); // 1 day in seconds
const rentalPrice = BigInt(process.env.RENTAL_PRICE || '1000000'); // 1 USDC
// Create a new wallet for the renter
const renterWallet = ethers_1.ethers.Wallet.createRandom().connect(provider);
console_colors_1.colorLog.info(`Created renter wallet: ${renterWallet.address}`);
// Fund the renter wallet with some ETH for gas
const gasAmount = ethers_1.ethers.parseEther('0.01'); // Small amount for gas
await wallet.sendTransaction({
to: renterWallet.address,
value: gasAmount,
});
console_colors_1.colorLog.success(`Funded renter wallet with ${ethers_1.ethers.formatEther(gasAmount)} ETH for gas`);
// Get USDC contract
const usdcAddress = await nftContract.getAddress();
const usdcAbi = ['function approve(address spender, uint256 amount) returns (bool)'];
const renterUsdcContract = new ethers_1.ethers.Contract(usdcAddress, usdcAbi, renterWallet);
// Approve USDC spending
await renterUsdcContract.approve(await nftContract.getAddress(), rentalPrice);
console_colors_1.colorLog.success('Approved USDC spending for token rental');
// Attempt to rent the token
try {
await nftContract.rentToken(tokenId, rentalDuration, rentalPrice);
console_colors_1.colorLog.success(`Token #${tokenId} rented to ${renterWallet.address} for ${ethers_1.ethers.formatUnits(rentalPrice, 6)} USDC`);
// Get rental information
const rentalInfo = await nftContract.getRentalInfo(tokenId);
console_colors_1.colorLog.info('Rental Information:');
console.log((0, console_colors_1.formatKeyValue)('Renter', console_colors_1.logStyles.address(rentalInfo.renter)));
console.log((0, console_colors_1.formatKeyValue)('Start Time', new Date(Number(rentalInfo.startTime) * 1000).toISOString()));
console.log((0, console_colors_1.formatKeyValue)('End Time', new Date(Number(rentalInfo.endTime) * 1000).toISOString()));
console.log((0, console_colors_1.formatKeyValue)('Rental Price', ethers_1.ethers.formatUnits(rentalInfo.rentalPrice, 6) + ' USDC'));
console.log((0, console_colors_1.formatKeyValue)('Active', rentalInfo.active ? 'Yes' : 'No'));
// Check if token is rented
const isRented = await nftContract.isRented(tokenId);
console_colors_1.colorLog.info(`Token #${tokenId} is ${isRented ? 'rented' : 'not rented'}`);
// Check if renter has active rentals
const hasRentals = await nftContract.hasActiveRentals(renterWallet.address);
console_colors_1.colorLog.info(`Renter has active rentals: ${hasRentals}`);
// End the rental
await nftContract.endRental(tokenId);
console_colors_1.colorLog.success(`Rental ended for token #${tokenId}`);
}
catch (error) {
console_colors_1.colorLog.error(`Rental failed: ${error}`);
}
}
console_colors_1.colorLog.section('COMPLETE');
console_colors_1.colorLog.success('Basic usage example completed.');
}
catch (error) {
console_colors_1.colorLog.error(`Error in main: ${error.message}`);
if (error.data) {
console.error('Error data:', error.data);
}
if (error.transaction) {
console.error('Transaction:', error.transaction);
}
process.exit(1);
}
}
// Run the example
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});