import { Web3Networks } from "./Web3Networks"; export class Web3Result { universalProvider: any; evmProvider: any; // chainType: Web3Networks; account: any; _signer: any; paymentProvider: any; public get signer(): any { if (this._signer == null) { if (this.evmProvider != null && this.evmProvider.isCon) { this._signer = this.evmProvider.getSigner(); } } return this._signer; } public set signer(signer: any) { this._signer = signer; } public get success(): boolean { if (this.account == null || this.account == "") { return false; } if (this.evmProvider == null) { return false; } return true; } public static async formatEther(balance: any): Promise { // @ts-ignore return Number(Number(window.ethers.utils.formatEther(balance)).toFixed(8)); } public async getBalance(): Promise { const wei = await this.evmProvider.getBalance(this.account); return await Web3Result.formatEther(wei); } } export enum ContractResult { WalletConnectError = "wallet connect error", SUCCESS = "all success", NOT_OWNER = "not owner", NOT_APPROVED = "not approved", GAS_ERROR = "gas error", EXECUTION_ERROR = "execution error", } export interface ContractError extends Error { code?: string; data?: any; } export class Contract { private static readonly ERROR_NOT_OWNER = "not owner"; private static readonly ERROR_NOT_APPROVED = "not approved"; private static readonly ERROR_GAS = "gas error"; private static readonly ERROR_EXECUTION = "execution error"; private readonly web3Result: Web3Result; private readonly abi: any; private readonly contractAddress: string; private contract: any; private constructor( web3Result: Web3Result, abi: any, contractAddress: string ) { this.web3Result = web3Result; this.abi = abi; this.contractAddress = contractAddress; } public static async build( web3Result: Web3Result, abi: any, contractAddress: string ): Promise { const contract = new Contract(web3Result, abi, contractAddress); await contract.initialize(); return contract; } private async initialize(): Promise { console.log("Contract initializing", this.contractAddress); // @ts-ignore this.contract = await new window.ethers.Contract( this.contractAddress, this.abi, this.web3Result.signer ); } public async getContractBalance(){ const balance = await this.contract.balanceOf(this.web3Result.account); console.log("balance", balance); let balanceNumber = await Web3Result.formatEther(balance); console.log("balanceNumber", balanceNumber); return balanceNumber; } private async isOwnerOf(tokenId: number): Promise { console.log( "Checking ownership for token", tokenId, this.web3Result.account ); const owner = await this.contract.ownerOf(tokenId); console.log("Ownership check result", owner, this.web3Result.account); return owner.toLowerCase() === this.web3Result.account.toLowerCase(); } public async getAllTokensOfOwner(account: string): Promise { try { // Get all Transfer events const filter = this.contract.filters.Transfer(null, account); const events = await this.contract.queryFilter(filter); // Also get transfers out to handle cases where tokens were transferred away const filterOut = this.contract.filters.Transfer(account, null); const eventsOut = await this.contract.queryFilter(filterOut); // Create a map to track current ownership const tokenMap = new Map(); // Process incoming transfers for (const event of events) { if (event.args) { const tokenId = event.args.tokenId.toNumber(); tokenMap.set(tokenId, true); } } // Process outgoing transfers for (const event of eventsOut) { if (event.args) { const tokenId = event.args.tokenId.toNumber(); tokenMap.delete(tokenId); } } // Convert map keys to array const tokenIds = Array.from(tokenMap.keys()); console.log("Found tokens:", tokenIds); return tokenIds; } catch (error) { console.error("Get all tokens of owner failed:", error); return []; } } private async isApproved( tokenId: number, toAddress: string ): Promise { console.log("Checking approval for token", tokenId, toAddress); const approvedAddress = await this.contract.getApproved(tokenId); console.log("Approval check result", approvedAddress, toAddress); return approvedAddress.toLowerCase() === toAddress.toLowerCase(); } private async approve(tokenId: number, toAddress: string): Promise { console.log("Approving token", tokenId, toAddress); try { const tx = await this.contract.approve(toAddress, tokenId); console.log("Approval transaction", tx); return true; } catch (error) { console.error("Approval failed:", error); return false; } } private async wallApproval( userAddress: string, receiveAddress: string, nftNo: number ) { console.log("Approval transaction", userAddress, receiveAddress, nftNo); const tx = this.contract.Approval(userAddress, receiveAddress, nftNo); console.log("Approval transaction", tx); return tx; } public async transferTo( userAddress: string, receiveAddress: string, nftNo: number ): Promise { try { const fromAddress = userAddress.toLowerCase(); const toAddress = receiveAddress.toLowerCase(); // Then execute the transfer console.log("Executing transfer..."); const transferTx = await this.contract.transferFrom( fromAddress, toAddress, nftNo ); console.log("Transfer transaction hash:", transferTx.hash); await transferTx.wait(); console.log("Transfer confirmed"); return ContractResult.SUCCESS; } catch (error: any) { console.error("Transfer failed:", error); if (error.code === "INSUFFICIENT_FUNDS") { return ContractResult.GAS_ERROR; } return ContractResult.EXECUTION_ERROR; } } public async ownerAndApprove( tokenId: number, toAddress: string ): Promise { console.log("Starting owner and approve check for token", tokenId); const isOwner = await this.isOwnerOf(tokenId); if (!isOwner) { return ContractResult.NOT_OWNER; } console.log("Ownership verified"); const isApproved = await this.isApproved(tokenId, toAddress); if (!isApproved) { const approvalSuccess = await this.approve(tokenId, toAddress); if (!approvalSuccess) { return ContractResult.NOT_APPROVED; } } console.log("Approval verified"); return ContractResult.SUCCESS; } public async burnSBTAndSoulNFT( sbtAddress: string, userId: number, sbtId: number, soulIds: number[] ): Promise { try { const tx = await this.contract.fusionBurn( userId, sbtAddress, sbtId, soulIds ); console.log("Burn SBT and Soul NFT transaction", tx); return true; } catch (error) { console.error("Burn SBT and Soul NFT failed:", error); return false; } } public async burnSBT( sbtAddress: string, userId: number, sbtId: number ): Promise { try { const tx = await this.contract.burnNFT(sbtAddress, sbtId, userId); console.log("Burn SBT transaction", tx); return true; } catch (error) { console.error("Burn SBT failed:", error); return false; } } public async getBalanceOf(owner: string): Promise { try { const balance = await this.contract.balanceOf(owner); return balance.toNumber(); } catch (error) { console.error("Get balance failed:", error); return 0; } } public async getTokenOfOwnerByIndex(owner: string, index: number): Promise { try { const tokenId = await this.contract.tokenOfOwnerByIndex(owner, index); return tokenId.toNumber(); } catch (error) { console.error("Get token of owner by index failed:", error); return 0; } } public async getTokenURI(tokenId: number): Promise { try { return await this.contract.tokenURI(tokenId); } catch (error) { console.error("Get token URI failed:", error); return ""; } } }