@paulstinchcombe/kami721c-sdk
Version:
SDK for interacting with KAMI721C NFT contracts
228 lines (227 loc) • 12.3 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 KAMI721CFactory_1 = require("../factories/KAMI721CFactory");
const dotenv_1 = __importDefault(require("dotenv"));
dotenv_1.default.config();
const IERC20_ABI = [
'function approve(address spender, uint256 amount) returns (bool)',
'function balanceOf(address account) view returns (uint256)',
'function decimals() view returns (uint8)',
'function transfer(address to, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)',
];
async function main() {
try {
// Get provider and wallet
const provider = new ethers_1.ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers_1.ethers.Wallet(process.env.PRIVATE_KEY, provider);
console.log(`Connected with wallet: ${wallet.address}`);
// Get starting nonce
const startNonce = await wallet.getNonce();
console.log(`Starting nonce: ${startNonce}`);
// Deploy a new contract instance using the upgradeable proxy
const factory = new KAMI721CFactory_1.KAMI721CFactory(wallet);
console.log('Deploying upgradeable contract via proxy...');
const nftContract = await factory.deployUpgradeable(process.env.USDC_ADDRESS, 'KAMI Special NFT Collection (Upgradeable)', // Updated name for clarity
'KAMIU', // Updated symbol for clarity
'https://api.kami.com/nft/upgradeable/', // Updated URI for clarity
1000000n, // 1 USDC mint price
wallet.address, // Platform address (will also be proxy admin)
500, // 5% platform commission
wallet.address // Initial owner address
);
console.log(`Proxy contract deployed at: ${nftContract.getAddress()}`);
console.log(`Contract Name: ${await nftContract.name()}`);
console.log(`Total supply: ${await nftContract.totalSupply()}`);
// Create renter wallet
const renterWallet = ethers_1.ethers.Wallet.createRandom().connect(provider);
console.log(`Created renter wallet: ${renterWallet.address}`);
// Fund renter wallet with ETH for gas
const fundingTx = await wallet.sendTransaction({
to: renterWallet.address,
value: ethers_1.ethers.parseEther('0.01'),
});
await fundingTx.wait();
console.log('Funded renter wallet with 0.01 ETH for gas');
// Get USDC contract
const usdcAddress = process.env.USDC_ADDRESS;
const usdcContract = new ethers_1.ethers.Contract(usdcAddress, IERC20_ABI, wallet);
const renterUsdcContract = new ethers_1.ethers.Contract(usdcAddress, IERC20_ABI, renterWallet);
// Get mint price
const mintPrice = await nftContract.mintPrice();
const decimals = await usdcContract.decimals();
console.log(`Mint price: ${ethers_1.ethers.formatUnits(mintPrice, Number(decimals))} USDC`);
// Approve USDC spending for minting
console.log('Approving USDC spending for minting...');
const approvalTx = await usdcContract.approve(nftContract.getAddress(), mintPrice);
await approvalTx.wait();
console.log('USDC spending approved for minting');
// Mint a token
console.log('Minting a token...');
const mintTx = await nftContract.mint();
await mintTx.wait();
console.log('Token minted successfully');
// Find the minted token
const tokenId = 0; // First token minted
console.log(`Using token #${tokenId}`);
// Get owner's USDC balance
const ownerBalance = await usdcContract.balanceOf(wallet.address);
console.log(`Owner USDC balance: ${ethers_1.ethers.formatUnits(ownerBalance, decimals)} USDC`);
// Set up rental parameters
const rentalDuration = 86400; // 1 day in seconds
const baseRentalPrice = ethers_1.ethers.parseUnits('1.0', decimals); // 1.0 USDC
// Calculate total rental price including platform commission
const platformCommission = await nftContract.getPlatformCommissionPercentage();
const platformCommissionAmount = (baseRentalPrice * platformCommission) / 10000n;
const totalRentalPrice = baseRentalPrice + platformCommissionAmount;
console.log(`Base rental price: ${ethers_1.ethers.formatUnits(baseRentalPrice, decimals)} USDC`);
console.log(`Platform commission (${Number(platformCommission) / 100}%): ${ethers_1.ethers.formatUnits(platformCommissionAmount, decimals)} USDC`);
console.log(`Total rental price: ${ethers_1.ethers.formatUnits(totalRentalPrice, decimals)} USDC`);
// Transfer enough USDC to cover rental price and platform commission
const transferAmount = totalRentalPrice;
console.log(`Transferring ${ethers_1.ethers.formatUnits(transferAmount, decimals)} USDC to renter...`);
const transferTx = await usdcContract.transfer(renterWallet.address, transferAmount);
await transferTx.wait();
console.log('USDC transferred to renter');
// Get renter's USDC balance
const renterInitialBalance = await usdcContract.balanceOf(renterWallet.address);
console.log(`Renter USDC balance: ${ethers_1.ethers.formatUnits(renterInitialBalance, decimals)} USDC`);
// Approve USDC spending for rental
console.log('Approving USDC spending for rental...');
const rentalApprovalTx = await renterUsdcContract.approve(nftContract.getAddress(), totalRentalPrice);
await rentalApprovalTx.wait();
console.log('USDC spending approved for rental');
console.log(`Token #${tokenId} exists and is owned by: ${await nftContract.ownerOf(tokenId)}`);
console.log(`Renter starting nonce: ${await renterWallet.getNonce()}`);
// Check and grant necessary roles
const ownerRole = await nftContract.OWNER_ROLE();
const platformRole = await nftContract.PLATFORM_ROLE();
const hasOwnerRole = await nftContract.hasRole(ownerRole, wallet.address);
const hasPlatformRole = await nftContract.hasRole(platformRole, wallet.address);
console.log(`Wallet has OWNER_ROLE: ${hasOwnerRole}`);
console.log(`Wallet has PLATFORM_ROLE: ${hasPlatformRole}`);
// Grant roles if needed
if (!hasOwnerRole) {
console.log('Granting OWNER_ROLE to wallet...');
const grantOwnerTx = await nftContract.grantRole(ownerRole, wallet.address);
await grantOwnerTx.wait();
console.log('OWNER_ROLE granted successfully');
}
if (!hasPlatformRole) {
console.log('Granting PLATFORM_ROLE to wallet...');
const grantPlatformTx = await nftContract.grantRole(platformRole, wallet.address);
await grantPlatformTx.wait();
console.log('PLATFORM_ROLE granted successfully');
}
// Attempt to rent the token directly
console.log(`Attempting to rent token #${tokenId}...`);
try {
// Check rental conditions
console.log('Checking rental conditions...');
// 1. Verify token exists and get owner
console.log(`Checking if token #${tokenId} exists...`);
try {
const tokenOwner = await nftContract.ownerOf(tokenId);
console.log(`Token owner: ${tokenOwner}`);
// 2. Verify renter is not the token owner
if (renterWallet.address === tokenOwner) {
throw new Error('Renter cannot be the token owner');
}
console.log('Renter is not the token owner ✓');
// 3. Check USDC allowance
const allowance = await usdcContract.allowance(renterWallet.address, nftContract.getAddress());
console.log(`Current USDC allowance: ${ethers_1.ethers.formatUnits(allowance, decimals)} USDC`);
if (allowance < totalRentalPrice) {
throw new Error('Insufficient USDC allowance');
}
console.log('USDC allowance is sufficient ✓');
// 4. Check USDC balance
const renterBalance = await usdcContract.balanceOf(renterWallet.address);
console.log(`Renter USDC balance: ${ethers_1.ethers.formatUnits(renterBalance, decimals)} USDC`);
if (renterBalance < totalRentalPrice) {
throw new Error('Insufficient USDC balance');
}
console.log('USDC balance is sufficient ✓');
// 5. Try to check if token is already rented
try {
const isRented = await nftContract.isRented(tokenId);
console.log(`Token rental status: ${isRented ? 'Rented' : 'Not rented'}`);
if (isRented) {
throw new Error('Token is already rented');
}
console.log('Token is available for rent ✓');
// 6. Attempt the rental
console.log('All conditions met, attempting rental...');
try {
// Connect renter wallet to the contract instance
const renterNftContract = nftContract.connect(renterWallet);
const rentalTx = await renterNftContract.rentToken(tokenId, rentalDuration, totalRentalPrice);
console.log('Rental transaction sent:', rentalTx.hash);
const receipt = await rentalTx.wait();
console.log('Rental transaction confirmed:', receipt.hash);
// Verify the rental was successful
const newRentalStatus = await nftContract.isRented(tokenId);
console.log(`Token is now rented: ${newRentalStatus}`);
}
catch (error) {
console.error('Failed to perform rental operation:', error);
if (error.data) {
console.error('Error data:', error.data);
}
if (error.transaction) {
console.error('Transaction data:', error.transaction);
}
if (error.receipt) {
console.error('Transaction receipt:', error.receipt);
}
throw error;
}
}
catch (error) {
console.error('Failed to check rental status:', error);
if (error.data) {
console.error('Rental status error data:', error.data);
}
if (error.transaction) {
console.error('Rental status transaction data:', error.transaction);
}
throw error;
}
}
catch (error) {
console.error('Failed to verify token existence:', error);
if (error.data) {
console.error('Token existence error data:', error.data);
}
if (error.transaction) {
console.error('Token existence transaction data:', error.transaction);
}
throw error;
}
}
catch (error) {
console.error('Failed to perform rental operation:', error);
if (error.data) {
console.error('Error data:', error.data);
}
if (error.transaction) {
console.error('Transaction data:', error.transaction);
}
throw error;
}
}
catch (error) {
console.error('Error:', error);
if (error.data) {
console.error('Error data:', error.data);
}
if (error.transaction) {
console.error('Transaction:', error.transaction);
}
}
}
main();