@roochnetwork/rooch-sdk
Version:
527 lines (526 loc) • 16 kB
JavaScript
import { Args } from "../bcs/index.js";
import { Session } from "../session/index.js";
import {
decodeToRoochAddressStr,
decodeToPackageAddressStr,
BitcoinAddress
} from "../address/index.js";
import { fromHEX, str } from "../utils/index.js";
import { RoochHTTPTransport } from "./httpTransport.js";
import {
CallFunction,
Transaction,
normalizeTypeArgsToStr
} from "../transactions/index.js";
import { fixedBalance } from "../utils/balance.js";
const DEFAULT_GAS = 5e7;
const ROOCH_CLIENT_BRAND = Symbol.for("@roochnetwork/RoochClient");
function isRoochClient(client) {
return typeof client === "object" && client !== null && client[ROOCH_CLIENT_BRAND] === true;
}
class RoochClient {
get [ROOCH_CLIENT_BRAND]() {
return true;
}
getTransport() {
return this.transport;
}
/**
* Establish a connection to a rooch RPC endpoint
*
* @param options configuration options for the API Client
*/
constructor(options) {
this.transport = options.transport ?? new RoochHTTPTransport({ url: options.url });
}
async getRpcApiVersion() {
const resp = await this.transport.request({
method: "rpc.discover",
params: []
});
return resp.info.version;
}
async getChainId() {
if (this.chainID) {
return this.chainID;
}
return this.transport.request({
method: "rooch_getChainID",
params: []
});
}
async executeViewFunction(input) {
const callFunction = new CallFunction(input);
return await this.transport.request({
method: "rooch_executeViewFunction",
params: [
{
function_id: callFunction.functionId(),
args: callFunction.encodeArgs(),
ty_args: callFunction.typeArgs
}
]
});
}
async dryrun(input) {
return await this.transport.request({
method: "rooch_dryRunRawTransaction",
params: [input.txBcsHex]
});
}
async signAndExecuteTransaction({
transaction,
signer,
option = { withOutput: true }
}) {
let transactionHex;
if (transaction instanceof Uint8Array) {
transactionHex = str("hex", transaction);
} else {
let sender = signer.getRoochAddress().toHexAddress();
transaction.setChainId(await this.getChainId());
transaction.setSeqNumber(await this.getSequenceNumber(sender));
transaction.setSender(sender);
if (!transaction.getMaxGas()) {
transaction.setMaxGas(DEFAULT_GAS);
}
const auth = await signer.signTransaction(transaction);
transaction.setAuth(auth);
transactionHex = `0x${transaction.encode().toHex()}`;
}
return await this.transport.request({
method: "rooch_executeRawTransaction",
params: [transactionHex, option]
});
}
async repairIndexer(input) {
await this.transport.request({
method: "rooch_repairIndexer",
params: [input.repairType, input.repairParams]
});
}
async syncStates(input) {
const opt = input.queryOption || {
decode: true,
showDisplay: true
};
return await this.transport.request({
method: "rooch_syncStates",
params: [input.filter, input.cursor, input.limit, opt]
});
}
// Get the states by access_path
async getStates(input) {
const opt = input.stateOption || {
decode: true,
showDisplay: true
};
const result = await this.transport.request({
method: "rooch_getStates",
params: [input.accessPath, opt]
});
const typedResult = result;
return typedResult[0] === null ? [] : typedResult;
}
async listStates(input) {
const opt = input.stateOption || {
decode: true,
showDisplay: true
};
return await this.transport.request({
method: "rooch_listStates",
params: [input.accessPath, input.cursor, input.limit, opt]
});
}
async getModuleAbi(input) {
return await this.transport.request({
method: "rooch_getModuleABI",
params: [input.moduleAddr, input.moduleName]
});
}
async getEvents(input) {
const opt = input.eventOptions || {
decode: true
};
return await this.transport.request({
method: "rooch_getEventsByEventHandle",
params: [input.eventHandle, input.cursor, input.limit, input.descendingOrder, opt]
});
}
async queryEvents(input) {
if (typeof input.filter === "object" && "sender" in input.filter) {
if (input.filter.sender === "") {
throw Error("Invalid Address");
}
}
if (typeof input.filter === "object" && "event_type_with_sender" in input.filter) {
if (input.filter.event_type_with_sender.sender === "") {
throw Error("Invalid Address");
}
}
const opt = input.queryOption || {
decode: true,
showDisplay: true
};
return await this.transport.request({
method: "rooch_queryEvents",
params: [input.filter, input.cursor, input.limit, opt]
});
}
async queryInscriptions(input) {
if (typeof input.filter !== "string" && "owner" in input.filter) {
if (input.filter.owner === "") {
throw Error("Invalid Address");
}
}
return await this.transport.request({
method: "btc_queryInscriptions",
params: [input.filter, input.cursor, input.limit, input.descendingOrder]
});
}
async queryUTXO(input) {
if (typeof input.filter !== "string" && "owner" in input.filter) {
if (input.filter.owner === "") {
throw Error("Invalid Address");
}
}
return this.transport.request({
method: "btc_queryUTXOs",
params: [input.filter, input.cursor, input.limit, input.descendingOrder]
});
}
async broadcastBitcoinTX(input) {
return this.transport.request({
method: "btc_broadcastTX",
params: [input.hex, input.maxfeerate, input.maxburnamount]
});
}
async getObjectStates(input) {
const idsStr = input.ids.join(",");
const opt = input.stateOption || {
decode: true,
showDisplay: true
};
return this.transport.request({
method: "rooch_getObjectStates",
params: [idsStr, opt]
});
}
async getFieldStates(input) {
const opt = input.stateOption || {
decode: true,
showDisplay: true
};
return this.transport.request({
method: "rooch_getFieldStates",
params: [input.objectId, input.fieldKey, opt]
});
}
async listFieldStates(input) {
const opt = input.stateOption || {
decode: true,
showDisplay: true
};
return this.transport.request({
method: "rooch_listFieldStates",
params: [input.objectId, input.cursor, input.limit, opt]
});
}
async queryObjectStates(input) {
if ("owner" in input.filter) {
if (input.filter.owner === "") {
throw Error("Invalid Address");
}
}
if ("object_type_with_owner" in input.filter) {
if (input.filter.object_type_with_owner.owner === "") {
throw Error("Invalid Address");
}
}
const opt = input.queryOption || {
decode: true,
showDisplay: true
};
return this.transport.request({
method: "rooch_queryObjectStates",
params: [input.filter, input.cursor, input.limit, opt]
});
}
async getTransactionsByHash(input) {
return this.transport.request({
method: "rooch_getTransactionsByHash",
params: [input.txHashes]
});
}
async getTransactionsByOrder(input) {
return this.transport.request({
method: "rooch_queryTransactions",
params: [input.cursor, input.limit, input.descendingOrder]
});
}
async queryTransactions(input) {
if (typeof input.filter === "object" && "sender" in input.filter) {
if (input.filter.sender === "") {
throw Error("Invalid Address");
}
}
const opt = input.queryOption || {
decode: true,
showDisplay: true
};
return this.transport.request({
method: "rooch_queryTransactions",
params: [input.filter, input.cursor, input.limit, opt]
});
}
// helper fn
async getSequenceNumber(address2) {
const resp = await this.executeViewFunction({
target: "0x2::account::sequence_number",
args: [Args.address(address2)]
});
if (resp && resp.return_values) {
return BigInt(resp.return_values?.[0]?.decoded_value);
}
return BigInt(0);
}
/**
* Get the total coin balance for one coin type, owned by the address owner.
*/
async getBalance(input) {
const owner = decodeToRoochAddressStr(input.owner);
let balanceInfoView = await this.transport.request({
method: "rooch_getBalance",
params: [owner, input.coinType]
});
balanceInfoView.fixedBalance = fixedBalance(balanceInfoView.balance, balanceInfoView.decimals);
return balanceInfoView;
}
async getBalances(input) {
const owner = decodeToRoochAddressStr(input.owner);
const result = await this.transport.request({
method: "rooch_getBalances",
params: [owner, input.cursor, input.limit]
});
result.data.forEach((item) => {
item.fixedBalance = fixedBalance(item.balance, item.decimals);
});
return result;
}
async transfer(input) {
const recipient = decodeToRoochAddressStr(input.recipient);
const tx = new Transaction();
tx.callFunction({
target: "0x3::transfer::transfer_coin",
args: [Args.address(recipient), Args.u256(BigInt(input.amount))],
typeArgs: [normalizeTypeArgsToStr(input.coinType)]
});
return await this.signAndExecuteTransaction({
transaction: tx,
signer: input.signer
});
}
async transferObject(input) {
const recipient = decodeToRoochAddressStr(input.recipient);
const tx = new Transaction();
tx.callFunction({
target: "0x3::transfer::transfer_object",
args: [Args.address(recipient), Args.objectId(input.objectId)],
typeArgs: [normalizeTypeArgsToStr(input.objectType)]
});
return await this.signAndExecuteTransaction({
transaction: tx,
signer: input.signer
});
}
async resolveBTCAddress(input) {
const address2 = decodeToRoochAddressStr(input.roochAddress);
const result = await this.executeViewFunction({
target: "0x3::address_mapping::resolve_bitcoin",
args: [Args.address(address2)]
});
if (result.vm_status === "Executed" && result.return_values) {
const value = (result.return_values?.[0]?.decoded_value).value;
const address3 = value && value.vec ? (
//compatible with old option version
value.vec.value[0][0]
) : value.bytes;
return new BitcoinAddress(address3, input.network);
}
return void 0;
}
async createSession({ sessionArgs, signer }) {
return Session.CREATE({
...sessionArgs,
client: this,
signer
});
}
async removeSession({ authKey, signer }) {
const tx = new Transaction();
tx.callFunction({
target: "0x3::session_key::remove_session_key_entry",
args: [Args.vec("u8", Array.from(fromHEX(authKey)))]
});
return (await this.signAndExecuteTransaction({
transaction: tx,
signer
})).execution_info.status.type === "executed";
}
async sessionIsExpired({
address: address2,
authKey
}) {
const _address = decodeToRoochAddressStr(address2);
const result = await this.executeViewFunction({
target: "0x3::session_key::is_expired_session_key",
args: [Args.address(_address), Args.vec("u8", Array.from(fromHEX(authKey)))]
});
if (result.vm_status !== "Executed") {
throw new Error("view 0x3::session_key::is_expired_session_key fail");
}
return result.return_values[0]?.decoded_value;
}
async getAllModules({
package_address,
limit,
cursor
}) {
const packageObjectID = `0x14481947570f6c2f50d190f9a13bf549ab2f0c9debc41296cd4d506002379659${decodeToPackageAddressStr(package_address)}`;
const result = await this.transport.request({
method: "rooch_listFieldStates",
params: [packageObjectID, cursor, limit, { decode: true }]
});
const moduleInfo = result;
const moduleMap = /* @__PURE__ */ new Map();
if (moduleInfo && typeof moduleInfo === "object" && "data" in moduleInfo) {
const { data } = moduleInfo;
if (Array.isArray(data)) {
for (const item of data) {
const decodedValue = item?.state?.decoded_value;
if (decodedValue) {
const name = decodedValue?.value?.name;
const byte_codes = decodedValue?.value?.value?.value?.byte_codes;
if (name && byte_codes) {
moduleMap.set(name, byte_codes);
}
}
}
}
}
return moduleMap;
}
async getSessionKeys({
address: address2,
limit,
cursor
}) {
const _address = decodeToRoochAddressStr(address2);
const accessPath = `/resource/${_address}/0x3::session_key::SessionKeys`;
const states = await this.getStates({
accessPath,
stateOption: {
decode: true,
showDisplay: true
}
});
if (states.length === 0) {
return {
data: [],
hasNextPage: false
};
}
const tableId = (states?.[0]?.decoded_value).value["value"].value["keys"].value["handle"].value["id"];
const tablePath = `/table/${tableId}`;
const statePage = await this.listStates({
accessPath: tablePath,
cursor,
limit: limit?.toString(),
stateOption: {
decode: true,
showDisplay: true
}
});
const parseScopes = (data) => {
const result = new Array();
for (const scope of data) {
const [pkg, mod, fn] = [scope[0], scope[1], scope[2]];
result.push(`${pkg}::${mod}::${fn}`);
}
return result;
};
const parseStateToSessionInfo = () => {
const result = new Array();
for (const state of statePage.data) {
const moveValue = state?.state?.decoded_value;
if (moveValue) {
const val = moveValue.value.value.value;
result.push({
appName: val.app_name,
appUrl: val.app_url,
authenticationKey: val.authentication_key,
scopes: parseScopes(val.scopes.value),
createTime: parseInt(val.create_time),
lastActiveTime: parseInt(val.last_active_time),
maxInactiveInterval: parseInt(val.max_inactive_interval)
});
}
}
return result.sort((a, b) => b.createTime - a.createTime);
};
return {
data: parseStateToSessionInfo(),
cursor: statePage.next_cursor,
hasNextPage: statePage.has_next_page
};
}
async subscribeEventWithSSE(input) {
const params = input.filter ? input.filter : "all";
return this.transport.subscribeWithSSE({
method: "/subscribe/sse/events",
params,
onMessage: input.onMessage,
onError: input.onError,
signal: input.signal
});
}
async subscribeTransactionWithSSE(input) {
const params = input.filter ? input.filter : "all";
return this.transport.subscribeWithSSE({
method: "/subscribe/sse/transactions",
params,
onMessage: input.onMessage,
onError: input.onError,
signal: input.signal
});
}
async subscribeEvent(input) {
const params = input.filter ? [input.filter] : ["all"];
return this.transport.subscribe({
method: "rooch_subscribeEvents",
params,
onMessage: input.onMessage,
signal: input.signal
});
}
async subscribeTransaction(input) {
const params = input.filter ? [input.filter] : ["all"];
return this.transport.subscribe({
method: "rooch_subscribeTransactions",
params,
onMessage: input.onMessage,
signal: input.signal
});
}
events() {
throw new Error("Method not implemented. Use getEvents() or queryEvents() instead.");
}
destroy() {
this.transport.destroy();
}
}
export {
RoochClient,
isRoochClient
};
//# sourceMappingURL=client.js.map