UNPKG

@spheron/protocol-sdk

Version:
1,630 lines (1,388 loc) 63.7 kB
import { ethers } from 'ethers'; declare enum Tier { One, Two, Three, Four, Five, Six, Seven, } declare enum Mode { Fizz, Provider, } interface OrderDetails { maxPrice: bigint; numOfBlocks: bigint; token: string; spec: string; version: number; mode: Mode; tier: Tier[]; } declare enum OrderState { OPEN = 'open', PROVISIONED = 'provisioned', CLOSED = 'closed', MATCHED = 'matched', } interface OrderSpecs { specs: string; version: string; mode: string; tier: Tier[]; } interface InitialOrder { id: number; name: string; region: string; uptime: number; reputation: number; slashes: number; maxPrice: number; numOfBlocks: number; token?: { symbol?: string; decimal?: number; address: string; }; creator: string; state: OrderState; specs: OrderSpecs; } type NetworkType = 'testnet' | 'mainnet'; declare class OrderModule { private provider: ethers.Provider; private websocketProvider?: ethers.WebSocketProvider; private createTimeoutId: NodeJS.Timeout | null; private updateTimeoutId: NodeJS.Timeout | null; private wallet: ethers.Wallet | undefined; constructor( provider: ethers.Provider, websocketProvider?: ethers.WebSocketProvider, wallet?: ethers.Wallet ) { this.provider = provider; this.websocketProvider = websocketProvider; this.createTimeoutId = null; this.updateTimeoutId = null; this.wallet = wallet; } async createOrder(orderDetails: OrderDetails) { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contract = new ethers.Contract(OrderRequest, OrderRequestAbi, signer); const tx = await contract.createOrder(orderDetails); const receipt = await tx.wait(); console.log('Order created successfully'); return receipt; } catch (error) { console.error('Error creating order -> ', error); const errorMessage = handleContractError(error, OrderRequestAbi); throw errorMessage; } } async updateOrder(orderId: string, orderDetails: OrderDetails) { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contract = new ethers.Contract(OrderRequest, OrderRequestAbi, signer); const tx = await contract.updateInitialOrder(orderId, orderDetails); const receipt = await tx.wait(); console.log('Order Update Request Sent'); return receipt; } catch (error) { console.error('Error in updating order -> ', error); const errorMessage = handleContractError(error, OrderRequestAbi); throw errorMessage; } } async getOrderDetails(leaseId: string) { const contractAbi = OrderRequestAbi; const contractAddress = OrderRequest; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.getOrderById(leaseId); const specs = { specs: response.specs.specs, version: response.specs.version, mode: response.specs.mode, tier: response.specs.tier.map((t: bigint) => Number(t)) as Tier[], }; const tokenDetails = getTokenDetails(response.token, 'testnet'); const token = { symbol: tokenDetails?.symbol, decimal: tokenDetails?.decimal, address: tokenDetails?.address, }; return { id: response.id.toString(), maxPrice: Number(response.maxPrice), numOfBlocks: Number(response.numOfBlocks), token, creator: response.creator, state: getOrderStateAsString(response.state), specs, } as InitialOrder; } async listenToOrderCreated( timeoutTime = 60000, onSuccessCallback: ( orderId: string, providerAddress: string, providerId: string | number | bigint, acceptedPrice: string | number | bigint, creatorAddress: string ) => void, onFailureCallback: () => void ) { if (!this.websocketProvider) { console.log('Please pass websocket provider in constructor'); return; } const { signer } = await initializeSigner({ wallet: this.wallet }); const account = await signer.getAddress(); const contractAbi = BidAbi; const contractAddress = Bid; const contract = new ethers.Contract(contractAddress, contractAbi, this.websocketProvider); return new Promise((resolve, reject) => { this.createTimeoutId = setTimeout(() => { contract.off('OrderMatched'); onFailureCallback(); reject({ error: true, msg: 'Order creation Failed' }); }, timeoutTime); contract.on( 'OrderMatched', ( orderId: string, providerAddress: string, providerId: string | number | bigint, acceptedPrice: string | number | bigint, creatorAddress: string ) => { if (creatorAddress.toString().toLowerCase() === account.toString().toLowerCase()) { onSuccessCallback(orderId, providerAddress, providerId, acceptedPrice, creatorAddress); this.websocketProvider?.destroy(); contract.off('OrderMatched'); clearTimeout(this.createTimeoutId as NodeJS.Timeout); resolve({ orderId, providerAddress, providerId, acceptedPrice, creatorAddress }); } } ); }); } async listenToOrderUpdated( timeoutTime = 60000, onSuccessCallback: (orderId: string, providerAddress: string) => void, onFailureCallback: () => void ) { if (!this.websocketProvider) { console.log('Please pass websocket provider in constructor'); return; } const { signer } = await initializeSigner({ wallet: this.wallet }); const account = await signer.getAddress(); const contractAbi = BidAbi; const contractAddress = Bid; const contract = new ethers.Contract(contractAddress, contractAbi, this.websocketProvider); return new Promise((resolve, reject) => { this.updateTimeoutId = setTimeout(() => { contract.off('leaseUpdated'); onFailureCallback(); reject({ error: true, msg: 'Order updation Failed' }); }, timeoutTime); contract.on('leaseUpdated', (orderId, providerAddress, tenantAddress) => { if (tenantAddress.toString().toLowerCase() === account.toString().toLowerCase()) { onSuccessCallback(orderId, providerAddress); this.websocketProvider?.destroy(); contract.off('leaseUpdated'); clearTimeout(this.updateTimeoutId as NodeJS.Timeout); resolve({ orderId, providerAddress, tenantAddress }); } }); }); } async listenToOrderUpdateAccepted( timeoutTime = 60000, onSuccessCallback: (orderId: string, providerAddress: string) => void, onFailureCallback: () => void ) { if (!this.websocketProvider) { console.log('Please pass websocket provider in constructor'); return; } const { signer } = await initializeSigner({ wallet: this.wallet }); const account = await signer.getAddress(); const contractAbi = BidAbi; const contractAddress = Bid; const contract = new ethers.Contract(contractAddress, contractAbi, this.websocketProvider); return new Promise((resolve, reject) => { this.updateTimeoutId = setTimeout(() => { contract.off('UpdateRequestAccepted'); onFailureCallback(); reject({ error: true, msg: 'Order updation Failed' }); }, timeoutTime); contract.on('UpdateRequestAccepted', (orderId, providerAddress, tenantAddress) => { if (tenantAddress.toString().toLowerCase() === account.toString().toLowerCase()) { onSuccessCallback(orderId, providerAddress); this.websocketProvider?.destroy(); contract.off('UpdateRequestAccepted'); clearTimeout(this.updateTimeoutId as NodeJS.Timeout); resolve({ orderId, providerAddress }); } }); }); } } interface ResourceAttributes$1 { cpuUnits: number; cpuAttributes: string[]; ramUnits: number; ramAttributes: string[]; gpuUnits: number; gpuAttributes: string[]; endpointsKind: number; endpointsSequenceNumber: number; } declare enum LeaseState { ACTIVE = 'active', TERMINATED = 'terminated', } interface Lease { leaseId: string; fizzId: string; requestId: string; resourceAttribute: ResourceAttributes$1; acceptedPrice: number; providerAddress: string; tenantAddress: string; startBlock: string; startTime: number; endTime: number; state: LeaseState; } interface LeaseWithOrderDetails extends Lease { name: string; region: string; tier: Tier[]; token: { symbol?: string; decimal?: number; }; } interface FizzParams { providerId: bigint; spec: string; walletAddress: string; paymentsAccepted: string[]; rewardWallet: string; } interface FizzNode { fizzId: bigint; providerId: bigint; region: string; spec: string; walletAddress: string; paymentsAccepted: string[]; status: number; joinTimestamp: bigint; rewardWallet: string; } interface ResourceCategory { name: string; registry: string; baseReward: bigint; } interface Resource { name: string; tier: string; multiplier: bigint; } interface ResourceAttributes { cpuUnits: bigint; cpuAttributes: string[]; ramUnits: bigint; ramAttributes: string[]; gpuUnits: bigint; gpuAttributes: string[]; endpointsKind: number; endpointsSequenceNumber: number; } interface FizzLease { leaseId: bigint; fizzId: bigint; requestId: bigint; resourceAttribute: ResourceAttributes; acceptedPrice: bigint; providerAddress: string; tenantAddress: string; startBlock: bigint; startTime: bigint; endTime: bigint; state: string; } declare enum FizzProviderStatus { Unregistered, Registered, Active, Maintenance, Suspended, Deactivated, } declare enum FizzProviderTrustTier { One, Two, Three, Four, Five, Six, Seven, } interface FizzProvider { providerId: bigint; name: string; region: string; walletAddress: string; paymentsAccepted: string[]; spec: string; hostUri: string; certificate: string; status: FizzProviderStatus; tier: FizzProviderTrustTier; joinTimestamp: bigint; rewardWallet: string; } interface IProvider { spec: string; hostUri: string; certificate: string; paymentsAccepted: any; status: string; trust: number; timestamp: number; } interface Attribute { id: bigint; units: bigint; } declare enum ProviderStatus { Unregistered, Registered, Active, Maintenance, Suspended, Deactivated, } declare enum ProviderTrustTier { One, Two, Three, Four, Five, Six, Seven, } interface Provider { providerId: bigint; name: string; region: string; walletAddress: string; paymentsAccepted: string[]; attributes: string; hostUri: string; certificate: string; status: ProviderStatus; tier: ProviderTrustTier; joinTimestamp: bigint; rewardWallet: string; } type Category = 'CPU' | 'GPU'; declare class ProviderModule { private provider: ethers.Provider; constructor(provider: ethers.Provider) { this.provider = provider; } async getProviderDetails(providerAddress: string) { if (!providerAddress) { console.log('Pass Provider Address'); return; } if (!isValidEthereumAddress(providerAddress)) { console.log('Pass Valid Address'); return; } try { const contractAbi = ProviderRegistryAbi; const contractAddress = ProviderRegistry; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.getProviderByAddress(providerAddress); const providerDetailsData: IProvider = { spec: response[0], hostUri: response[1], certificate: response[2], paymentsAccepted: response[3], status: response[4].toString(), trust: Number(response[5].toString()) + 1, timestamp: Number(response[6].toString()), }; return providerDetailsData; } catch (error) { console.log('Error in get Provider Details ->', error); const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getProviderPendingAttributes(providerAddress: string, category: Category) { if (!providerAddress) { console.log('Pass Provider Address'); return; } if (!isValidEthereumAddress(providerAddress)) { console.log('Pass Valid Address'); return; } if (!category) { console.log('Please pass a category'); return; } try { const contractAbi = ProviderRegistryAbi; const contractAddress = ProviderRegistry; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.getProviderPendingAttributes(providerAddress, category); return response; } catch (error) { console.log('Error in get Provider Pending Attrs ->', error); const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getProviderAttributes(providerAddress: string, category: Category) { if (!providerAddress) { console.log('Pass Provider Address'); return; } if (!isValidEthereumAddress(providerAddress)) { console.log('Pass Valid Address'); return; } if (!category) { console.log('Please pass a category'); return; } try { const contractAbi = ProviderRegistryAbi; const contractAddress = ProviderRegistry; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.getProviderAttributes(providerAddress, category); return response; } catch (error) { console.log('Error in get Provider Attrs ->', error); const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getProvider(providerId: bigint): Promise<any> { try { const contractAddress = ProviderRegistryTestnet; const contractAbi = ProviderRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const providerData = await contract.getProvider(providerId); return { name: providerData[0], region: providerData[1], attributes: providerData[2], hostUri: providerData[3], certificate: providerData[4], paymentsAccepted: providerData[5], status: providerData[6], tier: providerData[7], joinTimestamp: providerData[8], walletAddress: providerData[9], rewardWallet: providerData[10], }; } catch (error) { console.error('Failed to retrieve provider details: ', error); const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getProviderByAddress(walletAddress: string): Promise<any> { try { const contractAddress = ProviderRegistryTestnet; const contractAbi = ProviderRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const providerData = await contract.getProviderByAddress(walletAddress); return { name: providerData[0], region: providerData[1], attributes: providerData[2], hostUri: providerData[3], certificate: providerData[4], paymentsAccepted: providerData[5], status: providerData[6], tier: providerData[7], joinTimestamp: providerData[8], rewardWallet: providerData[9], }; } catch (error) { console.error('Failed to retrieve provider details by address: ', error); const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getAllProviders(): Promise<Provider[]> { try { const contractAddress = ProviderRegistryTestnet; const contractAbi = ProviderRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const providersData = await contract.getAllProviders(); const providers: Provider[] = providersData.map((provider: any) => ({ providerId: provider.providerId.toString(), name: provider.name, region: provider.region, walletAddress: provider.walletAddress, paymentsAccepted: provider.paymentsAccepted, attributes: provider.attributes, hostUri: provider.hostUri, certificate: provider.certificate, status: ProviderStatus[provider.status], // tier: ProviderTrustTier[provider.tier], tier: Number(provider.tier.toString()), joinTimestamp: Number(provider.joinTimestamp.toString()), rewardWallet: provider.rewardWallet, })); return providers; } catch (error) { console.error('Failed to retrieve all providers: ', error); const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getAttributes(providerAddress: string, category: string): Promise<Attribute[]> { try { const contractAddress = FizzAttributeRegistryTestnet; const contractAbi = ProviderRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const attributes: Attribute[] = await contract.getAttributes(providerAddress, category); console.log('attributes raw -> ', attributes); const decoratedAttributes = attributes.map((attr: any) => ({ id: attr[0], units: attr[1], })); console.log( `Attributes for ${providerAddress} in category ${category} retrieved successfully:`, decoratedAttributes ); return decoratedAttributes; } catch (error) { console.error('Failed to retrieve attributes: ', error); const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getPendingAttributes(providerAddress: string, category: string): Promise<Attribute[]> { try { const contractAddress = FizzAttributeRegistryTestnet; const contractAbi = ProviderRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const attributes: Attribute[] = await contract.getPendingAttributes( providerAddress, category ); const decoratedAttributes = attributes.map((attr: any) => ({ id: attr[0], units: attr[1], })); console.log( `Pending Attributes for ${providerAddress} in category ${category} retrieved successfully:`, decoratedAttributes ); return decoratedAttributes; } catch (error) { console.error('Failed to retrieve pending attributes: ', error); const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } } /* eslint-disable @typescript-eslint/no-explicit-any */ declare class FizzModule { private provider: ethers.Provider; private webSocketProvider: ethers.WebSocketProvider | undefined; private timeoutId: NodeJS.Timeout | undefined; private wallet: ethers.Wallet | undefined; private providerModule: ProviderModule; constructor( provider: ethers.Provider, webSocketProvider?: ethers.WebSocketProvider, wallet?: ethers.Wallet ) { this.provider = provider; this.webSocketProvider = webSocketProvider; this.wallet = wallet; this.providerModule = new ProviderModule(provider); } async addFizzNode(fizzParams: FizzParams): Promise<unknown> { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contractAddress = FizzRegistryTestnet; const abi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, abi, signer); const tx = await contract.addFizzNode(fizzParams); const receipt = await tx.wait(); console.log('Fizz registration successful: ', receipt); return tx; } catch (error) { console.error('Fizz registration failed: ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async updateFizzName(newName: string): Promise<unknown> { try { const { signer } = await initializeSigner({ wallet: this.wallet }); // Contract address (hardcoded or retrieved from an environment variable) const contractAddress = FizzRegistryTestnet; const abi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, abi, signer); const tx = await contract.updateFizzName(newName); const receipt = await tx.wait(); console.log('Update Fizz Name successful: ', receipt); return tx; } catch (error) { console.error('Update Fizz Name failed: ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async getFizzById(fizzId: bigint): Promise<unknown> { try { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const fizzDetails = await contract.getFizz(fizzId); const { providerId, spec, paymentsAccepted, status, joinTimestamp, walletAddress, rewardWallet, } = fizzDetails; return { region: spec?.split(',')?.[7] ?? '', providerId, spec, paymentsAccepted, status, joinTimestamp, walletAddress, rewardWallet, }; } catch (error) { console.error('Failed to retrieve Fizz details: ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async getFizzNodeByAddress(walletAddress: string) { try { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const fizzId = await contract.addressToFizzId(walletAddress); const fizzNode: any = await this.getFizzById(fizzId); const result: FizzNode = { region: fizzNode.spec?.split(',')?.[7] ?? '', fizzId, providerId: fizzNode.providerId, spec: fizzNode.spec, walletAddress: fizzNode.walletAddress, paymentsAccepted: fizzNode.paymentsAccepted, status: Number(fizzNode.status.toString()), joinTimestamp: fizzNode.joinTimestamp, rewardWallet: fizzNode.rewardWallet, }; console.log('Fizz Node details: ', result); return result; } catch (error) { console.error('Failed to fetch Fizz Node details: ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async getAllFizzNodes(): Promise<FizzNode[]> { try { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const allFizzNodes = await contract.getAllFizzNodes(); const fizzNodes: FizzNode[] = allFizzNodes.map((fizzNode: any) => ({ fizzId: fizzNode[0], providerId: fizzNode[1], spec: fizzNode[2], walletAddress: fizzNode[3], paymentsAccepted: fizzNode[4], status: fizzNode[5], joinTimestamp: fizzNode[6], rewardWallet: fizzNode[7], })); console.log('All Fizz Nodes: ', fizzNodes); return fizzNodes; } catch (error) { console.error('Failed to fetch all Fizz Nodes: ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async getResource(resourceID: bigint, category: string): Promise<Resource> { try { const contractAbi = ResourceRegistryAbi; const contractAddress = category === 'CPU' ? ResourceRegistryCPUTestnet : ResourceRegistryGPUTestnet; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const [name, tier, multiplier]: [string, string, bigint] = await contract.getResource( resourceID ); const resource: Resource = { name, tier, multiplier }; console.log(`Resource with ID ${resourceID} retrieved successfully:`, resource); return resource; } catch (error) { console.error('Failed to retrieve resource: ', error); const errorMessage = handleContractError(error, ResourceRegistryAbi); throw errorMessage; } } async getFizzLeases( fizzId: bigint, providerId: bigint, state?: string ): Promise<FizzLease[] | unknown> { try { const providerData = await this.providerModule.getProvider(providerId); const walletAddress = providerData.walletAddress; const leaseContractAddress = ComputeLeaseTestnet; const leaseContractAbi = ComputeLeaseAbi; const leaseContract = new ethers.Contract( leaseContractAddress, leaseContractAbi, this.provider ); const [activeLeases, allLeases] = await leaseContract.getProviderLeases(walletAddress); const selectedLeases = state === 'ACTIVE' ? activeLeases : allLeases; const leases: FizzLease[] = []; for (const leaseId of selectedLeases) { const leaseData = await leaseContract.leases(leaseId); // Filter by fizzId if (leaseData.fizzId === fizzId) { const lease: FizzLease = { leaseId: leaseData.leaseId, fizzId: leaseData.fizzId, requestId: leaseData.requestId, resourceAttribute: leaseData.resourceAttributes, acceptedPrice: leaseData.acceptedPrice, providerAddress: leaseData.providerAddress, tenantAddress: leaseData.tenantAddress, startBlock: leaseData.startBlock, startTime: leaseData.startTime, endTime: leaseData.endTime, state: leaseData.state, }; leases.push(lease); } } return leases; } catch (error) { console.error('Failed to retrieve fizz leases: ', error); const errorMessage = handleContractError(error, ComputeLeaseAbi); throw errorMessage; } } async listenToFizzCreated( onSuccessCallback: (fizzId: bigint, walletAddress: string) => void, onFailureCallback: () => void, timeoutTime = 60000 ) { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; try { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const contract = new ethers.Contract(contractAddress, contractAbi, this.webSocketProvider); return new Promise((resolve, reject) => { this.timeoutId = setTimeout(() => { contract.off('FizzNodeAdded'); onFailureCallback(); reject({ error: true, msg: 'Fizz creation failed' }); }, timeoutTime); contract.on('FizzNodeAdded', (fizzId: bigint, walletAddress: string) => { if (walletAddress.toString().toLowerCase() === accounts[0].toString().toLowerCase()) { onSuccessCallback(fizzId, walletAddress); this.webSocketProvider?.destroy(); contract.off('FizzNodeAdded'); clearTimeout(this.timeoutId as NodeJS.Timeout); resolve({ fizzId, walletAddress }); } }); }); } catch (error) { console.log('Error in listenToFizzCreated -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async updateFizzSpecs(specs: string) { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, signer); const tx = await contract.updateFizzSpec(specs); const receipt = await tx.wait(); return receipt; } catch (error) { console.log('Error in updateFizzSpecs -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async listenSpecUpdated( onSuccessCallback: (fizzId: bigint, specs: string, walletAddress: string) => void, onFailureCallback: () => void, timeoutTime = 60000 ) { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; try { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const contract = new ethers.Contract(contractAddress, contractAbi, this.webSocketProvider); let timeoutId: NodeJS.Timeout | undefined; return new Promise((resolve, reject) => { timeoutId = setTimeout(() => { contract.off('FizzNodeSpecUpdated'); onFailureCallback(); reject({ error: true, msg: 'Fizz update failed' }); }, timeoutTime); contract.on('FizzNodeSpecUpdated', (fizzId: bigint, specs: string) => { const fizz: any = this.getFizzById(fizzId); if ( fizz.walletAddress.toString().toLowerCase() === accounts[0].toString().toLowerCase() ) { onSuccessCallback(fizzId, specs, fizz.walletAddress); this.webSocketProvider?.destroy(); contract.off('FizzNodeSpecUpdated'); clearTimeout(timeoutId as NodeJS.Timeout); resolve({ fizzId, specs, walletAddress: fizz.walletAddress }); } }); }); } catch (error) { console.log('Error in listenToFizzNodeUpdated -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async updateFizzRegion(region: string) { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, signer); const tx = await contract.updateFizzRegion(region); const receipt = await tx.wait(); return receipt; } catch (error) { console.log('Error in updateFizzRegion -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async listenRegionUpdated( onSuccessCallback: (fizzId: bigint, region: string, walletAddress: string) => void, onFailureCallback: () => void, timeoutTime = 60000 ) { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; try { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const contract = new ethers.Contract(contractAddress, contractAbi, this.webSocketProvider); let timeoutId: NodeJS.Timeout | undefined; return new Promise((resolve, reject) => { timeoutId = setTimeout(() => { contract.off('FizzNodeRegionUpdated'); onFailureCallback(); reject({ error: true, msg: 'Fizz update failed' }); }, timeoutTime); contract.on('FizzNodeRegionUpdated', (fizzId: bigint, region: string) => { const fizz: any = this.getFizzById(fizzId); if ( fizz.walletAddress.toString().toLowerCase() === accounts[0].toString().toLowerCase() ) { onSuccessCallback(fizzId, region, fizz.walletAddress); this.webSocketProvider?.destroy(); contract.off('FizzNodeRegionUpdated'); clearTimeout(timeoutId as NodeJS.Timeout); resolve({ fizzId, region, walletAddress: fizz.walletAddress }); } }); }); } catch (error) { console.log('Error in listenToFizzNodeUpdated -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async updateFizzProvider(providerId: bigint) { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, signer); const tx = await contract.updateFizzProviderId(providerId); const receipt = await tx.wait(); return receipt; } catch (error) { console.log('Error in updateFizzProvider -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async listenProviderUpdated( onSuccessCallback: (fizzId: bigint, providerId: bigint, walletAddress: string) => void, onFailureCallback: () => void, timeoutTime = 60000 ) { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; try { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const contract = new ethers.Contract(contractAddress, contractAbi, this.webSocketProvider); let timeoutId: NodeJS.Timeout | undefined; return new Promise((resolve, reject) => { timeoutId = setTimeout(() => { contract.off('FizzNodeProviderIdUpdated'); onFailureCallback(); reject({ error: true, msg: 'Fizz update failed' }); }, timeoutTime); contract.on('FizzNodeProviderIdUpdated', (fizzId: bigint, providerId: bigint) => { const fizz: any = this.getFizzById(fizzId); if ( fizz.walletAddress.toString().toLowerCase() === accounts[0].toString().toLowerCase() ) { onSuccessCallback(fizzId, providerId, fizz.walletAddress); this.webSocketProvider?.destroy(); contract.off('FizzNodeProviderIdUpdated'); clearTimeout(timeoutId as NodeJS.Timeout); resolve({ fizzId, providerId, walletAddress: fizz.walletAddress }); } }); }); } catch (error) { console.log('Error in listenToFizzNodeUpdated -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async addAcceptedPayment(tokenAddress: string) { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, signer); const tx = await contract.addAcceptedPayment(tokenAddress); const receipt = await tx.wait(); return receipt; } catch (error) { console.log('Error in addAcceptedPayment -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async listenToAddAcceptedPayment( onSuccessCallback: (fizzId: bigint, tokenAddress: string, walletAddress: string) => void, onFailureCallback: () => void, timeoutTime = 60000 ) { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; try { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const contract = new ethers.Contract(contractAddress, contractAbi, this.webSocketProvider); let timeoutId: NodeJS.Timeout | undefined; return new Promise((resolve, reject) => { timeoutId = setTimeout(() => { contract.off('PaymentAdded'); onFailureCallback(); reject({ error: true, msg: 'Fizz update failed' }); }, timeoutTime); contract.on('PaymentAdded', (fizzId: bigint, tokenAddress: string) => { const fizz: any = this.getFizzById(fizzId); if ( fizz.walletAddress.toString().toLowerCase() === accounts[0].toString().toLowerCase() ) { onSuccessCallback(fizzId, tokenAddress, fizz.walletAddress); this.webSocketProvider?.destroy(); contract.off('PaymentAdded'); clearTimeout(timeoutId as NodeJS.Timeout); resolve({ fizzId, tokenAddress, walletAddress: fizz.walletAddress }); } }); }); } catch (error) { console.log('Error in listenToAddAcceptedPayment -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async removeAcceptedPayment(tokenAddress: string) { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, signer); const tx = await contract.removeAcceptedPayment(tokenAddress); const receipt = await tx.wait(); return receipt; } catch (error) { console.log('Error in removeAcceptedPayment -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async listenToRemoveAcceptedPayment( onSuccessCallback: (fizzId: bigint, tokenAddress: string, walletAddress: string) => void, onFailureCallback: () => void, timeoutTime = 60000 ) { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; try { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const contract = new ethers.Contract(contractAddress, contractAbi, this.webSocketProvider); let timeoutId: NodeJS.Timeout | undefined; return new Promise((resolve, reject) => { timeoutId = setTimeout(() => { contract.off('PaymentRemoved'); onFailureCallback(); reject({ error: true, msg: 'Fizz update failed' }); }, timeoutTime); contract.on('PaymentRemoved', (fizzId: bigint, tokenAddress: string) => { const fizz: any = this.getFizzById(fizzId); if ( fizz.walletAddress.toString().toLowerCase() === accounts[0].toString().toLowerCase() ) { onSuccessCallback(fizzId, tokenAddress, fizz.walletAddress); this.webSocketProvider?.destroy(); contract.off('PaymentRemoved'); clearTimeout(timeoutId as NodeJS.Timeout); resolve({ fizzId, tokenAddress, walletAddress: fizz.walletAddress }); } }); }); } catch (error) { console.log('Error in listenToRemoveAcceptedPayment -> ', error); const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } } declare class LeaseModule { private provider: ethers.Provider; private orderModule: OrderModule; private fizzModule: FizzModule; private providerModule: ProviderModule; private websocketProvider?: ethers.WebSocketProvider; private leaseCloseTimeoutId: NodeJS.Timeout | null; private wallet: ethers.Wallet | undefined; constructor( provider: ethers.Provider, websocketProvider?: ethers.WebSocketProvider, wallet?: ethers.Wallet ) { this.provider = provider; this.websocketProvider = websocketProvider; this.getLeaseDetails = this.getLeaseDetails.bind(this); this.orderModule = new OrderModule(provider); this.fizzModule = new FizzModule(provider, websocketProvider); this.providerModule = new ProviderModule(provider); this.leaseCloseTimeoutId = null; this.wallet = wallet; } async getLeaseDetails(leaseId: string) { const contractAbi = ComputeLeaseAbi; const contractAddress = ComputeLease; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.leases(leaseId); const resourceAttribute = { cpuUnits: Number(response.resourceAttribute[0]), cpuAttributes: response.resourceAttribute[1], ramUnits: Number(response.resourceAttribute[2]), ramAttributes: response.resourceAttribute[3], gpuUnits: Number(response.resourceAttribute[4]), gpuAttributes: response.resourceAttribute[5], endpointsKind: Number(response.resourceAttribute[6]), endpointsSequenceNumber: Number(response.resourceAttribute[7]), }; const lease: Lease = { leaseId: response.leaseId.toString(), fizzId: response.fizzId.toString(), requestId: response.requestId.toString(), resourceAttribute, acceptedPrice: Number(response.acceptedPrice), providerAddress: response.providerAddress.toString(), tenantAddress: response.tenantAddress.toString(), startBlock: response.startBlock.toString(), startTime: Number(response.startTime), endTime: Number(response.endTime), state: getLeaseStateAsString(response.state.toString()) as LeaseState, }; return lease; } async getLeaseIds(address: string) { const contractAbi = ComputeLeaseAbi; const contractAddress = ComputeLease; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.getTenantLeases(address); const activeLeaseIds = response[0].map((id: bigint) => id.toString()) as string[]; const allLeaseIds = response[1].map((id: bigint) => id.toString()) as string[]; const terminatedLeaseIds = allLeaseIds.filter((lId) => { return !activeLeaseIds.includes(lId); }); return { activeLeaseIds, allLeaseIds, terminatedLeaseIds, }; } async getLeasesByState( address: string, options?: { state?: LeaseState; page?: number; pageSize?: number } ) { const { activeLeaseIds, terminatedLeaseIds, allLeaseIds } = await this.getLeaseIds(address); let leaseIds = allLeaseIds; const totalCount = allLeaseIds.length; const terminatedCount = terminatedLeaseIds.length; const activeCount = activeLeaseIds.length; if (options?.state) { switch (options.state) { case LeaseState.ACTIVE: leaseIds = activeLeaseIds; break; case LeaseState.TERMINATED: leaseIds = terminatedLeaseIds; break; } } leaseIds.sort((a, b) => Number(b) - Number(a)); if (options?.page) { const pageSize = options.pageSize || DEFAULT_PAGE_SIZE; leaseIds = leaseIds.slice((options.page - 1) * pageSize, options.page * pageSize); } const filteredLeases = await Promise.all(leaseIds.map((lId) => this.getLeaseDetails(lId))); const orderDetails = await Promise.all( leaseIds.map((lId) => this.orderModule.getOrderDetails(lId)) ); const leaseWithToken: LeaseWithOrderDetails[] = await Promise.all( filteredLeases.map(async (lease, index) => { const order = orderDetails[index]; let tokenDetails; if (order.token?.address) tokenDetails = getTokenDetails(order.token.address, 'testnet'); let region; if (lease.fizzId.toString() !== '0') { const fizz: any = await this.fizzModule.getFizzById(BigInt(lease.fizzId)); region = fizz?.region; } else { const provider: any = await this.providerModule.getProviderByAddress( lease.providerAddress ); region = provider?.region; } return { ...lease, name: order.name, tier: order.specs.tier, region: region, token: { symbol: tokenDetails?.symbol, decimal: tokenDetails?.decimal, }, }; }) ); console.log('leases -> ', leaseWithToken); return { leases: leaseWithToken, activeCount, terminatedCount, totalCount, }; } async closeLease(leaseId: string) { const contractAbi = ComputeLeaseAbi; const contractAddress = ComputeLease; try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contract = new ethers.Contract(contractAddress, contractAbi, signer); const tx = await contract.closeLease(leaseId); const receipt = await tx.wait(); return receipt; } catch (error) { console.log('Error in close lease ->', error); const errorMessage = handleContractError(error, contractAbi); throw errorMessage; } } async listenToLeaseClosedEvent( onSuccessCallback: ({ orderId, providerAddress, tenantAddress, }: { orderId: string; providerAddress: string; tenantAddress: string; }) => void, onFailureCallback: () => void, timeout = 60000 ) { if (!this.websocketProvider) { console.log('Please pass websocket provider in constructor'); return; } const { signer } = await initializeSigner({ wallet: this.wallet }); const account = await signer.getAddress(); const contractAbi = ComputeLeaseAbi; const contractAddress = ComputeLease; const contract = new ethers.Contract(contractAddress, contractAbi, this.websocketProvider); return new Promise((resolve, reject) => { this.leaseCloseTimeoutId = setTimeout(() => { contract.off('LeaseClosed'); onFailureCallback(); reject({ error: true, msg: 'Lease Close Failed' }); }, timeout); contract.on( 'LeaseClosed', (orderId: string, providerAddress: string, tenantAddress: string) => { if ( providerAddress.toString().toLowerCase() === account.toString().toLowerCase() || tenantAddress.toString().toLowerCase() === account.toString().toLowerCase() ) { onSuccessCallback({ orderId, providerAddress, tenantAddress }); this.websocketProvider?.destroy(); contract.off('LeaseClosed'); clearTimeout(this.leaseCloseTimeoutId as NodeJS.Timeout); resolve({ orderId, providerAddress, tenantAddress }); } } ); }); } } interface ProviderDetails { name: string; region: string; attributes: string; hostUri: string; certificate: string; paymentsAccepted: string[]; status: string; trust: number; timestamp: number; } declare enum TransactionStatus { SUCCESS = 'success', FAILURE = 'failure', } interface TransactionData { rewardWallet: string; tokenAddress: string; amount: number; decimals: number; onSuccessCallback?: (data: unknown) => void; onFailureCallback?: (data: unknown) => void; } interface DepositData { token: string; amount: number; onSuccessCallback?: (data: unknown) => void; onFailureCallback?: (data: unknown) => void; } declare class EscrowModule { private provider: ethers.Provider; private wallet: ethers.Wallet | undefined; constructor(provider: ethers.Provider, wallet?: ethers.Wallet) { this.provider = provider; this.wallet = wallet; } // read operations async getProviderEarnings(providerAddress: string, tokenAddress: string) { try { const contractAbi = EscrowAbi; const contractAddress = Escrow; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.getProviderEarnings(providerAddress, tokenAddress); const providerEarnings: { earned: string; withdrawn: string; balance: string } = { earned: response[0].toString(), withdrawn: response[1].toString(), balance: response[2].toString(), }; return providerEarnings; } catch (error) { console.error('Error in getProviderEarnings:', error); const errorMessage = handleContractError(error, EscrowAbi); throw errorMessage; } } async getUserBalance(token: string, walletAddress?: string) { try { const contractAbi = EscrowAbi; const contractAddress = Escrow; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const tokenDetails = tokenMap[networkType].find( (eachToken) => eachToken.symbol.toLowerCase() === token.toLowerCase() ); if (!tokenDetails) { throw new Error('Provided token symbol is invalid.'); } const tokenAddress: any = tokenDetails?.address || '0x0000000000000000000000000000000000000000'; let userWalletAddress; if (walletAddress) { userWalletAddress = walletAddress; } else { if (this.wallet) { userWalletAddress = await this.wallet.getAddress(); } else { throw new Error("No wallet address provided"); } } const response = await contract.getUserData(userWalletAddress, tokenAddress); const userData: { lockedBalance: string; unlockedBalance: string; token: any } = { lockedBalance: response[0].toString(), unlockedBalance: response[1].toString(), token: { name: tokenDetails?.name, symbol: tokenDetails?.symbol, decimal: tokenDetails?.decimal, }, }; return userData; } catch (error) { console.error('Error in getUserData:', error); const errorMessage = handleContractError(error, EscrowAbi); throw errorMessage; } } // write operations async depositBalance({ token, amount, onSuccessCallback, onFailureCallback }: DepositData) { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contractABI = EscrowAbi; const contractAddress = Escrow; const tokenABI = TokenAbi; const tokenDetails = tokenMap[networkType].find( (eachToken) => eachToken.symbol.toLowerCase() === token.toLowerCase() ); if (!tokenDetails) { throw new Error('Provided token symbol is invalid.'); } const decimals = t