@spheron/protocol-sdk
Version:
Spheron Protocol SDK
1,630 lines (1,388 loc) • 63.7 kB
TypeScript
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