Skip to main content

Design

Promise-based client

To prevent a "callback hell" structure where code gets nested and messy; making it harder to understand. A Promise<?> based design is used as async/await implementation are very mature today, it brings better quality of life to modern development.

import {JsonRpcClient} from '@defichain/jellyfish-api-jsonrpc'

const client = new JsonRpcClient('http://foo:bar@localhost:8554')
const {blocks} = await client.mining.getMiningInfo()

IEEE-754 arbitrary precision

Due to the dynamic nature of the JavaScript language, it forces all number to be interpolated as IEEE-754 which can cause precision to be lost. BirthdayResearch/jellyfishsdk/issues/18

it('lost precision converting DFI 😥', () => {
const n = 1200000000.00000001
const a = JSON.parse(JSON.stringify(n)) * 1.0e8
expect(a.toString()).toStrictEqual("120000000000000001")
});

JellyfishJSON

api-core implements JellyfishJSON that allows parsing of JSON with 'lossless', 'bignumber' and 'number' numeric precision.

  • 'lossless' uses LosslessJSON that parses numeric values as LosslessNumber. With LosslessNumber, one can perform regular numeric operations, and it will throw an error when this would result in losing information.
  • 'bignumber' parse all numeric values as 'BigNumber' using bignumber.js library.
  • 'number' parse all numeric values as 'Number' and precision will be loss if it exceeds IEEE-754 standard.
  • 'PrecisionPath' provides path based precision mapping, specifying 'bignumber' will automatically map all Number in that path as 'bignumber'. Otherwise, it will default to number, This applies deeply.

As not all numbers parsed are significant in all context, (e.g. mining.getMiningInfo()), this allows jellyfish library users to use the number for non precision sensitive operation (e.g. networkhashps) and BigNumber for precision sensitive operations.

ApiClient

As jellyfish is written in TypeScript, all RPC exchanges with a node are typed. BigNumber precision is used for all wallet or transaction related operations. While IEEE-754 number is used for all other arbitrary operations.

export class Wallet {
async getBalance (minimumConfirmation: number = 0, includeWatchOnly: boolean = false): Promise<BigNumber> {
return await this.client.call('getbalance', ['*', minimumConfirmation, includeWatchOnly], 'bignumber')
}
}

export interface MiningInfo {
blocks: number
currentblockweight?: number
//...
}

export class Mining {
async getNetworkHashPerSecond (nblocks: number = 120, height: number = -1): Promise<number> {
return await this.client.call('getnetworkhashps', [nblocks, height], 'number')
}

async getMiningInfo (): Promise<MiningInfo> {
return await this.client.call('getmininginfo', [], 'number')
}
}

Protocol agnostic core

ApiClient in api-core is a protocol agnostic DeFiChain client implementation with APIs separated into their category. The protocol-agnostic core enables independent communication protocols, allowing vendor-agnostic middleware adaptable to any needs.

export abstract class ApiClient {
/**
* A promise based procedure call handling
*
* @param method Name of the RPC method
* @param params Array of params as RPC payload
* @param precision
* Numeric precision to parse RPC payload as 'lossless', 'bignumber' or 'number'.
*
* 'lossless' uses LosslessJSON that parses numeric values as LosslessNumber. With LosslessNumber, one can perform
* regular numeric operations, and it will throw an error when this would result in losing information.
*
* 'bignumber' parse all numeric values as 'BigNumber' using bignumber.js library.
*
* 'number' parse all numeric values as 'Number' and precision will be loss if it exceeds IEEE-754 standard.
*
* @throws ApiError
* @throws RpcApiError
* @throws ClientApiError
*/
abstract call<T> (method: string, params: any[], precision: Precision): Promise<T>
}