//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-export const accounts_balances = Object.freeze({
- fields: [
- 'balances',
- ],
- balances: {
- address: {
- fields: [
- 'balance',
- 'receivable',
- ],
- },
+import { Account } from '../account'
+
+type AccountsBalances = {
+ balances?: {
+ [address: string]: {
+ balance: string,
+ receivable: string
+ }
},
-})
+ errors?: {
+ [address: string]: string
+ }
+}
+
+export function accounts_balances (body: unknown): AccountsBalances {
+ const response: AccountsBalances = {}
+ if (body == null || typeof body !== 'object') {
+ return response
+ }
+
+ const balances = 'balances' in body ? body.balances : null
+ const errors = 'errors' in body ? body.errors : null
+ if (balances == null && errors == null) {
+ return response
+ }
+
+ if (balances != null) {
+ if (typeof balances === 'object') {
+ for (const [address, data] of Object.entries(balances) as [string, unknown][]) {
+ try {
+ Account.validate(address)
+ if (data == null || typeof data !== 'object') {
+ throw new Error('Invalid account balance data')
+ }
+ if ('balance' in data && typeof data.balance === 'string') {
+ response.balances ??= {}
+ response.balances[address].balance = data.balance
+ }
+ if ('receivable' in data && typeof data.receivable === 'string') {
+ response.balances ??= {}
+ response.balances[address].receivable = data.receivable
+ }
+ } catch (err: any) {
+ response.errors ??= {}
+ response.errors[address] = err?.message
+ }
+ }
+ }
+ }
+
+ if (errors != null) {
+ if (typeof errors === 'object') {
+ for (const [address, error] of Object.entries(errors) as [string, unknown][]) {
+ try {
+ Account.validate(address)
+ if (typeof error === 'string') {
+ throw new Error(error)
+ }
+ } catch (err: any) {
+ response.errors ??= {}
+ response.errors[address] = err?.message
+ }
+ }
+ }
+ }
+
+ return response
+}
* @param {object} [data] - JSON to send to the node as defined by the action
* @returns {Promise<any>} JSON-formatted RPC results from the node
*/
- async post<T extends keyof typeof Schema> (action: T, data?: Record<string, unknown>): Promise<typeof Schema[T]>
+ async post<T extends keyof typeof Schema> (action: T, data?: Record<string, unknown>): Promise<ReturnType<typeof Schema[T]>>
async post (action: unknown, data: unknown): Promise<unknown> {
this.#validate(action)
const aborter = new AbortController()
if (error) {
throw new Error(`${code}${error}${message}`, { cause: JSON.stringify(body) })
}
- return body
+ return Schema[action](body)
} catch (err) {
console.error(err)
throw new Error(`RPC ${action} request failed`, { cause: err })
})
}
- #validate (action: unknown): asserts action is string {
+ #validate (action: unknown): asserts action is keyof typeof Schema {
if (!action) {
throw new ReferenceError('Action is required for RPCs')
}
import { process } from './process'
export const Schema = Object.freeze({
- account_info,
- account_representative,
+ // account_info,
+ // account_representative,
accounts_balances,
- accounts_frontiers,
- accounts_representatives,
- block_info,
- blocks_info,
- process,
+ // accounts_frontiers,
+ // accounts_representatives,
+ // block_info,
+ // blocks_info,
+ // process,
})
json_block: true,
accounts: addresses
}
- const { balances } = await rpc.post('accounts_balances', data) as { balances?: { [address: string]: { balance: string, receivable: string } } }
+ const { balances } = await rpc.post('accounts_balances', data)
const { frontiers } = await rpc.post('accounts_frontiers', data) as { frontiers: { [address: string]: string } }
const { blocks } = await rpc.post('blocks_info', { json_block: true, hashes: Object.values(frontiers) }) as BlockInfo
for (const account of accounts.values()) {