banani
Version:
JS/TS library for the Banano cryptocurrency in the style of bananopie
220 lines (219 loc) • 8.52 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RPCWithBackup = exports.RPC = void 0;
/** Sends RPC requests to the RPC node, also has wrappers for actions that only read the network (write actions are handled by the Wallet class) */
class RPC {
/**
* @param {boolean} [use_pending = false] If true, uses "pending" instead of "receivable" in RPC action names, for compatibility with older versions of the node
*/
constructor(rpc_url, use_pending = false) {
this.DECIMALS = undefined; //for nano, change to nano decimals
this.debug = false;
this.rpc_url = rpc_url;
this.use_pending = use_pending;
}
//Network information related
/** The function that sends the RPC POST request */
async call(payload) {
if (this.debug)
console.log(JSON.stringify(payload));
const resp = await fetch(this.rpc_url, {
method: "POST",
headers: this.headers ?? { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!resp.ok && this.debug)
console.log(await resp.text());
if (!resp.ok)
throw Error(`Request to RPC node failed with status code ${resp.status}`);
const resp_json = await resp.json();
if (resp_json.error)
throw Error(`RPC node response: ${resp_json.error}`);
return resp_json;
}
/** See https://docs.nano.org/commands/rpc-protocol/#block_count */
async get_block_count() {
return (await this.call({
action: "block_count",
}));
}
/** See https://docs.nano.org/commands/rpc-protocol/#block_info */
async get_block_info(block_hash) {
return (await this.call({
action: "block_info",
hash: block_hash,
json_block: true,
}));
}
/** See https://docs.nano.org/commands/rpc-protocol/#blocks */
async get_blocks(block_hashes) {
return (await this.call({
action: "blocks",
hashes: block_hashes,
json_block: true,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#blocks_info */
async get_blocks_info(block_hashes) {
return (await this.call({
action: "blocks_info",
hashes: block_hashes,
json_block: true,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#representatives */
async get_representatives() {
return (await this.call({
action: "representatives",
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#representatives_online */
async get_representatives_online(weight) {
return (await this.call({
action: "representatives_online",
weight: weight ? true : undefined, //better not to include "weight" if false, rather than sending "weight": false
}));
}
//Account information related
/** https://docs.nano.org/commands/rpc-protocol/#account_history */
async get_account_history(account, count, raw, head, offset, reverse, account_filter) {
return (await this.call({
action: "account_history",
account,
count: `${count}`,
raw: raw ? true : undefined,
head,
offset: offset ? `${offset}` : undefined,
reverse: reverse ? true : undefined,
account_filter,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#account_info */
async get_account_info(account, include_confirmed, representative, weight, pending) {
return (await this.call({
action: "account_info",
account,
include_confirmed: include_confirmed ? true : undefined,
representative: representative ? true : undefined,
weight: weight ? true : undefined,
pending: pending ? true : undefined,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#account_balance */
async get_account_balance(account) {
return (await this.call({
action: "account_balance",
account,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#accounts_balances */
async get_accounts_balances(accounts) {
return (await this.call({
action: "accounts_balances",
accounts,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#account_representative */
async get_account_representative(account) {
return (await this.call({
action: "account_representative",
account,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#accounts_representatives */
async get_accounts_representatives(account) {
return (await this.call({
action: "accounts_representatives",
account,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#account_weight */
async get_account_weight(account) {
return (await this.call({
action: "account_weight",
account,
}));
}
//I hate nano node rpc here. I don't want to do the conditional type thing here, so have a union
/** Keep in mind "threshold" parameter is in raw. https://docs.nano.org/commands/rpc-protocol/#receivable */
async get_account_receivable(account, count, threshold, source) {
return (await this.call({
action: this.use_pending ? "pending" : "receivable",
account,
count: count ? `${count}` : undefined,
threshold,
source: source ? true : undefined,
}));
}
/** Keep in mind "threshold" parameter is in raw. https://docs.nano.org/commands/rpc-protocol/#delegators */
async get_delegators(account, threshold, count, start) {
return (await this.call({
action: "delegators",
account,
threshold,
count: count ? `${count}` : undefined,
start,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#account_weight */
async get_delegators_count(account) {
return (await this.call({
action: "account_weight",
account,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#telemetry */
async get_telemetry(raw, address, port) {
return (await this.call({
action: "telemetry",
raw: raw ? raw : undefined,
address: address ? address : undefined,
port: port ? `${port}` : undefined,
}));
}
/** https://docs.nano.org/commands/rpc-protocol/#version */
async get_version() {
return (await this.call({
action: "version",
}));
}
}
exports.RPC = RPC;
class RPCWithBackup extends RPC {
/**
* @param {number} [timeout] Request to RPC timeout, in milliseconds. If RPC request fails or timeouts, tries the next RPC
*/
constructor(rpc_urls, timeout, use_pending = false) {
if (rpc_urls.length < 2)
throw Error("Must provide at least two RPC URLs");
super(rpc_urls[0], use_pending);
this.rpc_urls = rpc_urls;
this.timeout = timeout;
}
async call(payload) {
let i = 0;
while (true) {
try {
const resp = await fetch(this.rpc_urls[i], {
method: "POST",
headers: this.headers ?? { "Content-Type": "application/json" },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(this.timeout), //for old typescript versions
});
if (!resp.ok)
throw Error(`Request to RPC node failed with status code ${resp.status}`);
const resp_json = await resp.json();
if (resp_json.error)
throw Error(`RPC node response: ${resp_json.error}`);
return resp_json;
}
catch (e) {
//increment (so try next RPC in provided list), if all RPCs exhausted (all failed), throw error
//typescript says e might not inherit from Error which is technically true, but in this case it always will be
if (!this.rpc_urls[++i])
throw Error(e instanceof Error ? e.toString() : "RPC call error");
}
}
}
}
exports.RPCWithBackup = RPCWithBackup;