import { Chain, ContractFunctionArgs, ContractFunctionName, ReadContractParameters, ReadContractReturnType, SimulateContractParameters, SimulateContractReturnType, TransactionReceipt, WriteContractParameters, } from 'viem'; import { WalletNotConnectedError } from '../errors/sdk.errors'; import { ContractNames, SdkSupportedChainIds, getChain, getMintClubContractAddress } from '../exports'; import { Client } from '../helpers/ClientHelper'; import { SupportedAbiType } from '../types/abi.types'; import { GenericWriteParams, TokenContractReadArgs } from '../types/transactions.types'; import { customWaitForTransaction } from '../utils/transaction'; type GenericLogicConstructorParams< A extends SupportedAbiType = SupportedAbiType, C extends ContractNames = ContractNames, > = { chainId: SdkSupportedChainIds; type: C; abi: A; }; export class GenericContractLogic< A extends SupportedAbiType = SupportedAbiType, C extends ContractNames = ContractNames, > { private abi: A; private contractType: C; private chainId: SdkSupportedChainIds; private clientHelper: Client; private chain: Chain; constructor(params: GenericLogicConstructorParams) { const { chainId, type, abi } = params; this.contractType = type; this.abi = abi; this.chainId = chainId; this.chain = getChain(chainId); this.clientHelper = new Client(); } public read< T extends ContractFunctionName, R extends ContractFunctionArgs, >(params: TokenContractReadArgs): Promise> { const { functionName, blockNumber } = params as TokenContractReadArgs; const args = 'args' in params ? params.args : undefined; let address: `0x${string}`; if ('tokenAddress' in params) { address = params.tokenAddress; } else { address = getMintClubContractAddress(this.contractType, this.chainId); } const publicClient = this.clientHelper._getPublicClient(this.chainId); return publicClient.readContract({ abi: this.abi, address, functionName, args, ...(blockNumber !== undefined ? { blockNumber } : {}), } as unknown as ReadContractParameters) as Promise>; } public async write< T extends ContractFunctionName, R extends ContractFunctionArgs, >(params: GenericWriteParams) { const { functionName, value, debug, onError, onSignatureRequest: onSignatureRequest, onSigned, onSuccess } = params; let args, simulationArgs; args = 'args' in params ? params.args : undefined; let address: `0x${string}`; if ('tokenAddress' in params) { address = params.tokenAddress; } else { address = getMintClubContractAddress(this.contractType, this.chainId); } try { const walletClient = this.clientHelper.getWalletClient(); const isPrivateKey = this.clientHelper.isPrivateKey(); if (isPrivateKey && !walletClient?.account) { throw new WalletNotConnectedError(); } else if (!walletClient || !walletClient.account) { await this.clientHelper.connect(); return; } else if (!isPrivateKey && walletClient.chain?.id !== this.chainId) { await walletClient.addChain?.({ chain: this.chain }); await walletClient.switchChain?.({ id: this.chainId }); } simulationArgs = { chain: this.chain, account: walletClient.account, abi: this.abi, address, functionName, args, ...(value !== undefined && { value }), } as unknown as SimulateContractParameters; debug?.(simulationArgs); let tx: `0x${string}` | undefined; // If wallet client is available, use it if (isPrivateKey) { const publicClient = this.clientHelper._getPublicClient(this.chainId); const { request } = (await publicClient.simulateContract(simulationArgs)) as SimulateContractReturnType< A, T, R >; onSignatureRequest?.(); tx = await walletClient.writeContract(request as WriteContractParameters); } else { const { request } = (await this.clientHelper ._getPublicClient(this.chainId) .simulateContract(simulationArgs)) as SimulateContractReturnType; onSignatureRequest?.(); tx = await walletClient.writeContract(request as WriteContractParameters); } onSigned?.(tx); // const receipt = await this.clientHelper._getPublicClient(this.chainId).waitForTransactionReceipt({ // hash: tx, // }); // use custom wait for transaction for better stability const receipt = await customWaitForTransaction(this.chainId, tx); onSuccess?.(receipt as TransactionReceipt); return receipt; } catch (e) { if (e) { Object.assign(e, { functionName, args, simulationArgs, value }); } onError?.(e); return; } } }