UNPKG

@spheron/protocol-sdk

Version:
1,682 lines (1,435 loc) 67.6 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 | bigint; 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; maxPrice: number; numOfBlocks: number; token?: { symbol?: string; decimal?: number; address: string; }; creator: string; state: OrderState; specs: OrderSpecs; } interface OrderMatchedEvent { orderId: string; providerAddress: string; providerId: string | number | bigint; acceptedPrice: string | number | bigint; creatorAddress: string; } interface OrderUpdatedEvent { orderId: string; providerAddress: string; tenantAddress: string; acceptedPrice: string | number | bigint; } interface OrderUpdateAcceptedEvent { orderId: string; providerAddress: string; } interface RpcProvider { HTTP_URL: string; WSS_URL: string; } 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): Promise<ethers.ContractTransactionReceipt | null> { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contract = new ethers.Contract(OrderRequest, OrderRequestAbi, signer); const tx: ethers.ContractTransactionResponse = await contract.createOrder(orderDetails); const receipt: ethers.ContractTransactionReceipt | null = await tx.wait(); return receipt; } catch (error) { const errorMessage = handleContractError(error, OrderRequestAbi); throw errorMessage; } } async updateOrder( orderId: string, orderDetails: OrderDetails ): Promise<ethers.ContractTransactionReceipt | null> { try { const { signer } = await initializeSigner({ wallet: this.wallet }); const contract = new ethers.Contract(OrderRequest, OrderRequestAbi, signer); const tx: ethers.ContractTransactionResponse = await contract.updateInitialOrder( orderId, orderDetails ); const receipt: ethers.ContractTransactionReceipt | null = await tx.wait(); return receipt; } catch (error) { const errorMessage = handleContractError(error, OrderRequestAbi); throw errorMessage; } } async getOrderDetails(leaseId: string): Promise<InitialOrder> { 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 ): Promise<OrderMatchedEvent> { if (!this.websocketProvider) { throw new Error('Please pass websocket provider in constructor'); } 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, tenantAddress?: string, acceptedPrice?: string ) => void, onFailureCallback: () => void ): Promise<OrderUpdatedEvent> { if (!this.websocketProvider) { throw new Error('Please pass websocket provider in constructor'); } 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, acceptedPrice) => { if (tenantAddress.toString().toLowerCase() === account.toString().toLowerCase()) { onSuccessCallback(orderId, providerAddress, tenantAddress, acceptedPrice?.toString()); this.websocketProvider?.destroy(); contract.off('LeaseUpdated'); clearTimeout(this.updateTimeoutId as NodeJS.Timeout); resolve({ orderId, providerAddress, tenantAddress, acceptedPrice }); } }); }); } async listenToOrderUpdateAccepted( timeoutTime = 60000, onSuccessCallback: (orderId: string, providerAddress: string) => void, onFailureCallback: () => void ): Promise<OrderUpdateAcceptedEvent> { if (!this.websocketProvider) { throw new Error('Please pass websocket provider in constructor'); } 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 }); } }); }); } } declare enum LeaseState { ACTIVE = 'active', TERMINATED = 'terminated', } interface Lease { leaseId: string; fizzId: string; requestId: string; 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; } type RawFizzNode = [ bigint, // fizzId bigint, // providerId string, // spec string, // walletAddress string[], // paymentsAccepted bigint, // status bigint, // joinTimestamp string // rewardWallet ]; interface FizzNode { fizzId: bigint; providerId: bigint; region: string; spec: string; walletAddress: string; paymentsAccepted: string[]; status: number; joinTimestamp: bigint; rewardWallet: string; } interface FizzDetails { region: string; providerId: bigint; spec: string; walletAddress: string; paymentsAccepted: string[]; status: bigint; 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 FizzAttribute { id: bigint | string; units: bigint | string; } type RawFizzAttribute = [id: string, units: string]; interface IProvider { spec: string; hostUri: string; certificate: string; paymentsAccepted: string[]; status: string; trust: number; timestamp: number; } interface Attribute { id: bigint | string; units: bigint | string; } 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; spec?: string; walletAddress: string; paymentsAccepted: string[]; hostUri: string; certificate: string; status: ProviderStatus; tier: ProviderTrustTier; joinTimestamp: bigint; rewardWallet: string; } type Category = 'CPU' | 'GPU'; type RawProviderAttribute = [id: string, units: string]; declare class ProviderModule { private provider: ethers.Provider; constructor(provider: ethers.Provider) { this.provider = provider; } async getProviderDetails(providerAddress: string): Promise<IProvider> { if (!providerAddress) { throw new Error('Pass Provider Address'); } if (!isValidEthereumAddress(providerAddress)) { throw new Error('Pass Valid Address'); } 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) { const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getProviderPendingAttributes(providerAddress: string, category: Category) { if (!providerAddress) { throw new Error('Pass Provider Address'); } if (!isValidEthereumAddress(providerAddress)) { throw new Error('Pass Valid Address'); } if (!category) { throw new Error('Please pass a category'); } try { const contractAbi = ProviderAttributeRegistryAbi; const contractAddress = ProviderAttributeRegistry; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.getProviderPendingAttributes(providerAddress, category); return response; } catch (error) { const errorMessage = handleContractError(error, ProviderAttributeRegistryAbi); throw errorMessage; } } async getProviderAttributes(providerAddress: string, category: Category) { if (!providerAddress) { throw new Error('Pass Provider Address'); } if (!isValidEthereumAddress(providerAddress)) { throw new Error('Pass Valid Address'); } if (!category) { throw new Error('Please pass a category'); } try { const contractAbi = ProviderAttributeRegistryAbi; const contractAddress = ProviderAttributeRegistry; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const response = await contract.getProviderAttributes(providerAddress, category); return response; } catch (error) { const errorMessage = handleContractError(error, ProviderAttributeRegistryAbi); throw errorMessage; } } async getProvider(providerId: bigint): Promise<Provider> { try { const contractAddress = ProviderRegistry; const contractAbi = ProviderRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const providerData = await contract.getProvider(providerId); let name, region; try { const { Name, Region } = JSON.parse(providerData.spec); name = Name; region = Region; } catch { try { const { Name, Region } = decompressProviderSpec(providerData.spec) as { Name: string; Region: string; }; name = Name; region = Region; } catch { name = ''; region = ''; } } return { name, region, spec: providerData.spec, hostUri: providerData.hostUri, certificate: providerData.certificate, paymentsAccepted: providerData.paymentsAccepted, status: providerData.status, tier: providerData.tier, joinTimestamp: providerData.joinTimestamp, walletAddress: providerData.walletAddress, rewardWallet: providerData.rewardWallet, }; } catch (error) { const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getProviderByAddress(walletAddress: string): Promise<Provider> { try { const contractAddress = ProviderRegistry; const contractAbi = ProviderRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const providerData = await contract.getProviderByAddress(walletAddress); let name, region; try { const { Name, Region } = JSON.parse(providerData.spec); name = Name; region = Region; } catch { try { const { Name, Region } = decompressProviderSpec(providerData.spec) as { Name: string; Region: string; }; name = Name; region = Region; } catch { name = ''; region = ''; } } return { name, region, spec: providerData.spec, hostUri: providerData.hostUri, certificate: providerData.certificate, paymentsAccepted: providerData.paymentsAccepted, status: providerData.status, tier: providerData.tier, joinTimestamp: providerData.joinTimestamp, walletAddress, rewardWallet: providerData.rewardWallet, }; } catch (error) { const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getAllProviders(): Promise<Provider[]> { try { const contractAddress = ProviderRegistry; const contractAbi = ProviderRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const providersData = await contract.getAllProviders(); const providers: Provider[] = providersData.map((provider: Provider) => { let name, region; try { const { Name, Region } = JSON.parse(provider.spec as string); name = Name; region = Region; } catch { try { const { Name, Region } = decompressProviderSpec(provider.spec as string) as { Name: string; Region: string; }; name = Name; region = Region; } catch { name = ''; region = ''; } } return { name, region, providerId: (provider.providerId as bigint).toString(), walletAddress: provider.walletAddress, paymentsAccepted: provider.paymentsAccepted, spec: provider.spec, hostUri: provider.hostUri, certificate: provider.certificate, status: ProviderStatus[provider.status], tier: Number(provider.tier.toString()), // tier: ProviderTrustTier[provider.tier], joinTimestamp: Number(provider.joinTimestamp.toString()), rewardWallet: provider.rewardWallet, }; }); return providers; } catch (error) { const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getAttributes(providerAddress: string, category: string): Promise<Attribute[]> { try { const contractAddress = ProviderAttributeRegistry; const contractAbi = ProviderAttributeRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const attributes: RawProviderAttribute[] = await contract.getAttributes( providerAddress, category ); const decoratedAttributes = attributes.map((attr: RawProviderAttribute) => ({ id: attr[0], units: attr[1], })); return decoratedAttributes; } catch (error) { const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } async getPendingAttributes(providerAddress: string, category: string): Promise<Attribute[]> { try { const contractAddress = ProviderAttributeRegistry; const contractAbi = ProviderAttributeRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const attributes: RawProviderAttribute[] = await contract.getPendingAttributes( providerAddress, category ); const decoratedAttributes = attributes.map((attr: RawProviderAttribute) => ({ id: attr[0], units: attr[1], })); return decoratedAttributes; } catch (error) { const errorMessage = handleContractError(error, ProviderRegistryAbi); throw errorMessage; } } } 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(); return { tx, receipt }; } catch (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(); return { tx, receipt }; } catch (error) { const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async getFizzById(fizzId: bigint): Promise<FizzDetails> { 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) { 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: FizzDetails = 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, }; return result; } catch (error) { const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async getTotalFizzNodes(): Promise<bigint> { try { const contractAddress = FizzRegistryTestnet; const contractAbi = FizzRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const fizzCounter = await contract.nextFizzId(); return fizzCounter; } catch (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: RawFizzNode) => ({ fizzId: fizzNode[0], providerId: fizzNode[1], spec: fizzNode[2], walletAddress: fizzNode[3], paymentsAccepted: fizzNode[4], status: fizzNode[5], joinTimestamp: fizzNode[6], rewardWallet: fizzNode[7], })); return fizzNodes; } catch (error) { const errorMessage = handleContractError(error, FizzRegistryAbi); throw errorMessage; } } async getAttributes(providerAddress: string, category: string): Promise<FizzAttribute[]> { try { const contractAddress = FizzAttributeRegistryTestnet; const contractAbi = FizzAttributeRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const attributes: RawFizzAttribute[] = await contract.getAttributes( providerAddress, category ); const decoratedAttributes = attributes.map((attr: RawFizzAttribute) => ({ id: attr[0], units: attr[1], })); return decoratedAttributes; } catch (error) { const errorMessage = handleContractError(error, FizzAttributeRegistryAbi); throw errorMessage; } } async getPendingAttributes(providerAddress: string, category: string): Promise<FizzAttribute[]> { try { const contractAddress = FizzAttributeRegistryTestnet; const contractAbi = FizzAttributeRegistryAbi; const contract = new ethers.Contract(contractAddress, contractAbi, this.provider); const attributes: RawFizzAttribute[] = await contract.getPendingAttributes( providerAddress, category ); const decoratedAttributes = attributes.map((attr: RawFizzAttribute) => ({ id: attr[0], units: attr[1], })); return decoratedAttributes; } catch (error) { const errorMessage = handleContractError(error, FizzAttributeRegistryAbi); 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 }; return resource; } catch (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) { 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) { 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) { 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', async (fizzId: bigint, specs: string) => { const fizz: FizzDetails = await 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) { 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) { 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', async (fizzId: bigint, region: string) => { const fizz: FizzDetails = await 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) { 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) { 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', async (fizzId: bigint, providerId: bigint) => { const fizz: FizzDetails = await 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) { 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) { 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', async (fizzId: bigint, tokenAddress: string) => { const fizz: FizzDetails = await 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) { 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) { 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', async (fizzId: bigint, tokenAddress: string) => { const fizz: FizzDetails = await 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) { 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 lease: Lease = { leaseId: response.leaseId.toString(), fizzId: response.fizzId.toString(), requestId: response.requestId.toString(), 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: FizzDetails = await this.fizzModule.getFizzById(BigInt(lease.fizzId)); region = fizz?.region; } else { const provider: Provider = 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, }, }; }) ); 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) { 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) { throw new Error('Please pass websocket provider in constructor'); } 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; } interface TokenDetails { name: string; symbol: string; decimal: number; } interface UserBalance { lockedBalance: string; unlockedBalance: string; token: TokenDetails; } 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; } async getUserBalance(token: string, walletAddress?: string, isOperator: boolean = false) { 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: string = 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 respon