@paulstinchcombe/kami721c-sdk
Version:
SDK for interacting with KAMI721C NFT contracts
240 lines (219 loc) • 9.17 kB
text/typescript
import { ContractFactory, ContractRunner, ethers, Interface, Signer, BaseContract } from 'ethers';
// import KAMI721CABI from '../abis/KAMI721C.json';
import { KAMI721C } from '../contracts/KAMI721C';
// Import ABIs for implementation and proxy
import implementationCompiledContract from '../abis/KAMI721CUpgradeable/KAMI721CUpgradeable.json';
import proxyCompiledContract from '../abis/TransparentUpgradeableProxy/KAMITransparentUpgradeableProxy.json';
/**
* Factory class for deploying KAMI721C contracts (Standard and Upgradeable via Proxy)
*/
export class KAMI721CFactory {
private implementationFactory: ContractFactory;
private proxyFactory: ContractFactory;
private runner: ContractRunner;
/**
* Creates a new instance of the KAMI721C factory
* @param runner A signer or provider with deployment permissions
*/
constructor(runner: ContractRunner) {
this.runner = runner;
// Create factory for the implementation contract
this.implementationFactory = new ContractFactory(
implementationCompiledContract.abi,
implementationCompiledContract.bytecode,
runner
);
// Create factory for the proxy contract
this.proxyFactory = new ContractFactory(proxyCompiledContract.abi, proxyCompiledContract.bytecode, runner);
}
/**
* Deploy a new KAMI721C contract (Upgradeable via Transparent Proxy)
* @param usdcAddress The address of the USDC token contract
* @param name The name of the NFT collection
* @param symbol The symbol of the NFT collection
* @param baseURI The base URI for token metadata
* @param initialMintPrice The initial mint price in USDC (with 6 decimals)
* @param platformAddress The address that will receive platform commissions (also becomes proxy admin)
* @param platformCommissionPercentage The platform commission percentage in basis points (e.g., 500 = 5%)
* @param ownerAddress The address to grant the OWNER_ROLE to initially
* @returns The deployed contract instance, interacting with the proxy
*/
async deployUpgradeable(
usdcAddress: string,
name: string,
symbol: string,
baseURI: string,
initialMintPrice: bigint | string = 1000000n, // Default 1 USDC (6 decimals)
platformAddress?: string,
platformCommissionPercentage: number = 500, // Default 5%
ownerAddress?: string
): Promise<KAMI721C> {
let finalPlatformAddress = platformAddress;
let finalOwnerAddress = ownerAddress;
// Determine deployer address if needed
let deployerAddress: string | undefined;
if (this.runner && 'getAddress' in this.runner) {
deployerAddress = await (this.runner as Signer).getAddress();
} else {
// If runner is not a signer, addresses must be provided
if (!finalPlatformAddress || !finalOwnerAddress) {
throw new Error('platformAddress and ownerAddress are required when using a provider');
}
}
// If addresses not provided, use the deployer's address
if (!finalPlatformAddress && deployerAddress) {
finalPlatformAddress = deployerAddress;
console.log(`Using deployer address ${deployerAddress} as platform address and proxy admin.`);
}
if (!finalOwnerAddress && deployerAddress) {
finalOwnerAddress = deployerAddress;
console.log(`Using deployer address ${deployerAddress} as initial owner.`);
}
// Ensure addresses are set
if (!finalPlatformAddress || !finalOwnerAddress) {
throw new Error('Could not determine platformAddress or ownerAddress.');
}
console.log('Deploying KAMI721CUpgradeable implementation...');
try {
const implementationContract = await this.implementationFactory.deploy({ gasLimit: 5000000 });
await implementationContract.waitForDeployment();
const implementationAddress = await implementationContract.getAddress();
console.log('Implementation contract deployed at:', implementationAddress);
// Verify deployment
const provider = this.runner.provider;
if (!provider) {
throw new Error('Runner does not have a provider.');
}
const code = await provider.getCode(implementationAddress);
if (!code || code === '0x') {
throw new Error('Implementation deployment failed - no code at address');
}
// Prepare initialization calldata
const implementationInterface = new Interface(implementationCompiledContract.abi);
const initializeData = implementationInterface.encodeFunctionData('initialize', [
usdcAddress,
name,
symbol,
baseURI,
initialMintPrice,
finalPlatformAddress,
platformCommissionPercentage,
finalOwnerAddress,
]);
console.log('Prepared initialization calldata.');
// Deploy the proxy contract
console.log('Deploying KAMITransparentUpgradeableProxy...');
const proxyContract = await this.proxyFactory.deploy(
implementationAddress,
finalPlatformAddress, // Use platformAddress as proxy admin
initializeData,
{ gasLimit: 2000000 } // Gas limit for proxy deployment
);
await proxyContract.waitForDeployment();
const proxyAddress = await proxyContract.getAddress();
console.log('Proxy contract deployed at:', proxyAddress);
// Verify proxy deployment
const proxyCode = await provider.getCode(proxyAddress);
if (!proxyCode || proxyCode === '0x') {
throw new Error('Proxy deployment failed - no code at address');
}
// Return a contract instance attached to the proxy address, using the implementation ABI
console.log(`Returning contract instance attached to proxy address ${proxyAddress}`);
return new KAMI721C(this.runner, proxyAddress, implementationCompiledContract.abi);
} catch (error: any) {
console.error('Deployment failed:', {
error: error.message,
reason: error.reason,
code: error.code,
data: error.data,
transaction: error.transaction,
});
throw error;
}
}
// Keep the old deploy method for standard (non-upgradeable) deployment if needed,
// or remove/deprecate it.
/**
* Deploy a new KAMI721C contract (Standard Non-Upgradeable Version)
* @deprecated Use deployUpgradeable instead for the proxy-based version.
* @param usdcAddress The address of the USDC token contract
* @param name The name of the NFT collection
* @param symbol The symbol of the NFT collection
* @param baseURI The base URI for token metadata
* @param initialMintPrice The initial mint price in USDC (with 6 decimals)
* @param platformAddress The address that will receive platform commissions
* @param platformCommissionPercentage The platform commission percentage in basis points (e.g., 500 = 5%)
* @returns The deployed contract instance
*/
async deployStandard(
usdcAddress: string,
name: string,
symbol: string,
baseURI: string,
initialMintPrice: bigint | string = 1000000n, // Default 1 USDC (6 decimals)
platformAddress?: string,
platformCommissionPercentage: number = 500 // Default 5%
): Promise<KAMI721C> {
console.warn('Deploying standard non-upgradeable KAMI721C. Use deployUpgradeable for proxy deployment.');
let finalPlatformAddress = platformAddress;
// If platform address not provided, use the deployer's address
if (!finalPlatformAddress) {
if (this.runner && 'getAddress' in this.runner) {
finalPlatformAddress = await (this.runner as Signer).getAddress();
} else {
throw new Error('Platform address is required when deployer address cannot be determined');
}
}
console.log('Deploying standard contract with parameters:', {
usdcAddress,
name,
symbol,
baseURI,
initialMintPrice,
finalPlatformAddress,
platformCommissionPercentage,
});
// Use the original KAMI721C ABI/Bytecode for standard deployment
// Assuming the old ABI is still needed and available
const standardCompiledContract = await import('../abis/KAMI721C/KAMI721C.json');
const standardFactory = new ContractFactory(standardCompiledContract.abi, standardCompiledContract.bytecode, this.runner);
try {
console.log('Creating standard deployment transaction...');
const contract = (await standardFactory.deploy(
usdcAddress,
name,
symbol,
baseURI,
initialMintPrice,
finalPlatformAddress,
platformCommissionPercentage,
{ gasLimit: 5000000 }
)) as BaseContract;
console.log('Waiting for standard deployment transaction to be mined...');
await contract.waitForDeployment();
const address = await contract.getAddress();
console.log('Standard contract deployed at address:', address);
// Verify the contract was deployed successfully
console.log('Verifying standard contract deployment...');
const provider = this.runner.provider;
if (!provider) {
throw new Error('Runner does not have a provider.');
}
const code = await provider.getCode(address);
if (!code || code === '0x') {
throw new Error('Standard contract deployment failed - no code at contract address');
}
console.log('Standard contract code verified successfully');
return new KAMI721C(this.runner, address, standardCompiledContract.abi);
} catch (error: any) {
console.error('Standard deployment failed:', {
error: error.message,
reason: error.reason,
code: error.code,
data: error.data,
transaction: error.transaction,
});
throw error;
}
}
}