@0xobelisk/rooch-client
Version:
Tookit for interacting with rooch move framework
299 lines (281 loc) • 8.07 kB
text/typescript
import {
Args,
Bytes,
CallFunctionArgs,
ModuleABIView,
RoochClient,
Secp256k1Keypair,
Transaction,
TypeArgs,
TypeTag,
NetworkType,
GetStatesParams,
ObjectStateView,
ListStatesParams,
PaginatedStateKVViews,
ExecuteTransactionResponseView,
address,
GetEventsByEventHandleParams,
PaginatedEventViews,
} from '@roochnetwork/rooch-sdk';
import { delay } from './util';
/**
* `RoochTransaction` is used to send transaction with a given gas coin.
* It always uses the gas coin to pay for the gas,
* and update the gas coin after the transaction.
*/
export class RoochInteractor {
public readonly clients: RoochClient[];
public currentClient: RoochClient;
public readonly fullNodes: string[];
public currentFullNode: string;
public network?: NetworkType;
constructor(fullNodeUrls: string[], network?: NetworkType) {
if (fullNodeUrls.length === 0)
throw new Error('fullNodeUrls must not be empty');
this.clients = fullNodeUrls.map((url) => new RoochClient({ url }));
this.currentClient = this.clients[0];
this.fullNodes = fullNodeUrls;
this.currentFullNode = fullNodeUrls[0];
this.network = network;
}
switchToNextProvider() {
const currentClientIdx = this.clients.indexOf(this.currentClient);
this.currentClient =
this.clients[(currentClientIdx + 1) % this.clients.length];
this.currentFullNode =
this.fullNodes[(currentClientIdx + 1) % this.clients.length];
}
async createSession(
sessionArgs: {
appName: string;
appUrl: string;
scopes: string[];
},
signer: Secp256k1Keypair
) {
for (const client of this.clients) {
try {
const session = await client.createSession({
sessionArgs,
signer,
});
return session;
} catch (err) {
console.warn(`Failed to create session: ${err}`);
await delay(2000);
}
}
throw new Error('Failed to create session with all fullnodes');
}
async signAndExecuteTransaction(
transaction: Transaction | Bytes,
signer: Secp256k1Keypair,
option: {
withOutput: boolean;
}
): Promise<any> {
for (const client of this.clients) {
try {
const txnResponse = await client.signAndExecuteTransaction({
transaction,
signer,
option,
});
return txnResponse;
} catch (err) {
console.warn(`Failed to send transaction: ${err}`);
await delay(2000);
}
}
throw new Error('Failed to send transaction with all fullnodes');
}
async executeViewFunction(
contractAddress: string,
moduleName: string,
funcName: string,
typeArguments: TypeTag[] = [],
args: Args[] = []
) {
for (const client of this.clients) {
try {
const input: CallFunctionArgs = {
address: contractAddress,
module: moduleName,
function: funcName,
typeArgs: typeArguments,
args,
};
const result = await client.executeViewFunction(input);
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to execute view function: ${err}`);
}
}
throw new Error('Failed to execute view function with all fullnodes');
}
async getModuleAbi(
accountAddress: string,
moduleName: string
): Promise<ModuleABIView> {
for (const client of this.clients) {
try {
const result = await client.getModuleAbi({
moduleAddr: accountAddress,
moduleName,
});
return result;
} catch (err) {
await delay(2000);
console.warn(
`Failed to get ModuleAbi ${accountAddress} ${moduleName}: ${err}`
);
}
}
throw new Error(
`Failed to get ModuleAbi ${accountAddress} ${moduleName} with all fullnodes`
);
}
async getModuleAbis(
accountAddress: string,
modules: string[]
): Promise<ModuleABIView[]> {
for (const client of this.clients) {
try {
const result: ModuleABIView[] = [];
for (const moduleName of modules) {
const moduleAbi = await client.getModuleAbi({
moduleAddr: accountAddress,
moduleName,
});
result.push(moduleAbi);
}
return result;
} catch (err) {
await delay(2000);
console.warn(
`Failed to get ModuleAbi ${accountAddress} ${modules.join(
','
)}: ${err}`
);
}
}
throw new Error(
`Failed to get ModuleAbi ${accountAddress} ${modules.join(
','
)} with all fullnodes`
);
}
async getBalance(address: string, coinType: string) {
for (const client of this.clients) {
try {
const result = await client.getBalance({
owner: address,
coinType,
});
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to get balance: ${err}`);
}
}
throw new Error(`Failed to get balance with all fullnodes`);
}
async getRpcApiVersion(): Promise<string | undefined> {
for (const client of this.clients) {
try {
const result = await client.getRpcApiVersion();
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to get RPC API version: ${err}`);
}
}
throw new Error('Failed to get RPC API version with all fullnodes');
}
async getChainId(): Promise<bigint> {
for (const client of this.clients) {
try {
const result = await client.getChainId();
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to get chain ID: ${err}`);
}
}
throw new Error('Failed to get chain ID with all fullnodes');
}
async getStates(params: GetStatesParams): Promise<ObjectStateView[]> {
for (const client of this.clients) {
try {
const result = await client.getStates(params);
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to get states: ${err}`);
}
}
throw new Error('Failed to get states with all fullnodes');
}
async listStates(params: ListStatesParams): Promise<PaginatedStateKVViews> {
for (const client of this.clients) {
try {
const result = await client.listStates(params);
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to list states: ${err}`);
}
}
throw new Error('Failed to list states with all fullnodes');
}
async getEvents(
input: GetEventsByEventHandleParams
): Promise<PaginatedEventViews> {
for (const client of this.clients) {
try {
const result = await client.getEvents(input);
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to get events: ${err}`);
}
}
throw new Error('Failed to get events with all fullnodes');
}
async transfer(input: {
signer: Secp256k1Keypair;
recipient: address;
amount: number | bigint;
coinType: TypeArgs;
}): Promise<ExecuteTransactionResponseView> {
for (const client of this.clients) {
try {
const result = await client.transfer(input);
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to transfer: ${err}`);
}
}
throw new Error('Failed to transfer with all fullnodes');
}
async transferObject(input: {
signer: Secp256k1Keypair;
recipient: address;
objectId: string;
objectType: TypeArgs;
}): Promise<ExecuteTransactionResponseView> {
for (const client of this.clients) {
try {
const result = await client.transferObject(input);
return result;
} catch (err) {
await delay(2000);
console.warn(`Failed to transfer object: ${err}`);
}
}
throw new Error('Failed to transfer object with all fullnodes');
}
}