diff --git a/lib/account.d.ts b/lib/account.d.ts index 869a5fb0c1..969cfc7289 100644 --- a/lib/account.d.ts +++ b/lib/account.d.ts @@ -4,10 +4,16 @@ import { FinalExecutionOutcome } from './providers'; import { Connection } from './connection'; import { PublicKey } from './utils/key_pair'; export interface AccountState { - account_id: string; amount: string; - staked: string; code_hash: string; + storage_usage: number; + locked: string; +} +export interface AccountBalance { + total: string; + stateStaked: string; + staked: string; + available: string; } /** * More information on [the Account spec](https://nomicon.io/DataStructures/Account.html) @@ -119,4 +125,9 @@ export declare class Account { * @returns {Promise} */ getAccountDetails(): Promise; + /** + * Returns calculated account balance + * @returns {Promise} + */ + getAccountBalance(): Promise; } diff --git a/lib/account.js b/lib/account.js index 71154b5d74..dc92b0ae8a 100644 --- a/lib/account.js +++ b/lib/account.js @@ -279,5 +279,24 @@ class Account { }); return result; } + /** + * Returns calculated account balance + * @returns {Promise} + */ + async getAccountBalance() { + const genesisConfig = await this.connection.provider.experimental_genesisConfig(); + const state = await this.state(); + const costPerByte = new bn_js_1.default(genesisConfig.runtime_config.storage_amount_per_byte); + const stateStaked = new bn_js_1.default(state.storage_usage).mul(costPerByte); + const staked = new bn_js_1.default(state.locked); + const totalBalance = new bn_js_1.default(state.amount).add(staked); + const availableBalance = totalBalance.sub(staked).sub(stateStaked); + return { + total: totalBalance.toString(), + stateStaked: stateStaked.toString(), + staked: staked.toString(), + available: availableBalance.toString() + }; + } } exports.Account = Account; diff --git a/lib/providers/json-rpc-provider.d.ts b/lib/providers/json-rpc-provider.d.ts index 8f938d4215..f363284f14 100644 --- a/lib/providers/json-rpc-provider.d.ts +++ b/lib/providers/json-rpc-provider.d.ts @@ -1,4 +1,4 @@ -import { Provider, FinalExecutionOutcome, NodeStatusResult, BlockId, BlockResult, ChunkId, ChunkResult, EpochValidatorInfo } from './provider'; +import { Provider, FinalExecutionOutcome, NodeStatusResult, BlockId, BlockResult, ChunkId, ChunkResult, EpochValidatorInfo, GenesisConfig } from './provider'; import { Network } from '../utils/network'; import { ConnectionInfo } from '../utils/web'; import { TypedError } from '../utils/errors'; @@ -59,6 +59,11 @@ export declare class JsonRpcProvider extends Provider { * @param blockId Block hash or height, or null for latest. */ validators(blockId: BlockId): Promise; + /** + * Gets EXPERIMENTAL_genesis_config from RPC + * @returns {Promise} + */ + experimental_genesisConfig(): Promise; /** * Directly call the RPC specifying the method and params * @param method RPC method diff --git a/lib/providers/json-rpc-provider.js b/lib/providers/json-rpc-provider.js index 2a291ead47..a398508905 100644 --- a/lib/providers/json-rpc-provider.js +++ b/lib/providers/json-rpc-provider.js @@ -89,6 +89,13 @@ class JsonRpcProvider extends provider_1.Provider { async validators(blockId) { return this.sendJsonRpc('validators', [blockId]); } + /** + * Gets EXPERIMENTAL_genesis_config from RPC + * @returns {Promise} + */ + async experimental_genesisConfig() { + return await this.sendJsonRpc('EXPERIMENTAL_genesis_config', []); + } /** * Directly call the RPC specifying the method and params * @param method RPC method diff --git a/lib/providers/provider.d.ts b/lib/providers/provider.d.ts index ddf5db0b74..f7d9238e51 100644 --- a/lib/providers/provider.d.ts +++ b/lib/providers/provider.d.ts @@ -133,6 +133,12 @@ export interface ValidatorStakeView { public_key: string; stake: string; } +export interface GenesisConfig { + runtime_config: RuntimeConfig; +} +export interface RuntimeConfig { + storage_amount_per_byte: string; +} export interface EpochValidatorInfo { next_validators: NextEpochValidatorInfo[]; current_validators: CurrentEpochValidatorInfo[]; @@ -151,6 +157,7 @@ export declare abstract class Provider { abstract block(blockId: BlockId): Promise; abstract chunk(chunkId: ChunkId): Promise; abstract validators(blockId: BlockId): Promise; + abstract experimental_genesisConfig(): Promise; } export declare function getTransactionLastResult(txResult: FinalExecutionOutcome): any; export declare function adaptTransactionResult(txResult: any): FinalExecutionOutcome; diff --git a/src/account.ts b/src/account.ts index 0bb8886080..d8c60dbf8d 100644 --- a/src/account.ts +++ b/src/account.ts @@ -32,10 +32,17 @@ function sleep(millis: number): Promise { } export interface AccountState { - account_id: string; amount: string; - staked: string; code_hash: string; + storage_usage: number; + locked: string; +} + +export interface AccountBalance { + total: string; + stateStaked: string; + staked: string; + available: string; } /** @@ -324,4 +331,26 @@ export class Account { }); return result; } + + /** + * Returns calculated account balance + * @returns {Promise} + */ + async getAccountBalance(): Promise { + const genesisConfig = await this.connection.provider.experimental_genesisConfig(); + const state = await this.state(); + + const costPerByte = new BN(genesisConfig.runtime_config.storage_amount_per_byte); + const stateStaked = new BN(state.storage_usage).mul(costPerByte); + const staked = new BN(state.locked); + const totalBalance = new BN(state.amount).add(staked); + const availableBalance = totalBalance.sub(staked).sub(stateStaked); + + return { + total: totalBalance.toString(), + stateStaked: stateStaked.toString(), + staked: staked.toString(), + available: availableBalance.toString() + }; + } } diff --git a/src/providers/json-rpc-provider.ts b/src/providers/json-rpc-provider.ts index a8e9fda43f..927f5b2cb8 100644 --- a/src/providers/json-rpc-provider.ts +++ b/src/providers/json-rpc-provider.ts @@ -1,6 +1,7 @@ import { Provider, FinalExecutionOutcome, NodeStatusResult, BlockId, - BlockResult, ChunkId, ChunkResult, adaptTransactionResult, EpochValidatorInfo + BlockResult, ChunkId, ChunkResult, adaptTransactionResult, EpochValidatorInfo, + GenesisConfig } from './provider'; import { Network } from '../utils/network'; import { ConnectionInfo, fetchJson } from '../utils/web'; @@ -106,6 +107,14 @@ export class JsonRpcProvider extends Provider { return this.sendJsonRpc('validators', [blockId]); } + /** + * Gets EXPERIMENTAL_genesis_config from RPC + * @returns {Promise} + */ + async experimental_genesisConfig(): Promise { + return await this.sendJsonRpc('EXPERIMENTAL_genesis_config', []); + } + /** * Directly call the RPC specifying the method and params * @param method RPC method diff --git a/src/providers/provider.ts b/src/providers/provider.ts index f2a0c7b371..c278e26d4e 100644 --- a/src/providers/provider.ts +++ b/src/providers/provider.ts @@ -156,6 +156,14 @@ export interface ValidatorStakeView { stake: string; } +export interface GenesisConfig { + runtime_config: RuntimeConfig; +} + +export interface RuntimeConfig { + storage_amount_per_byte: string; +} + export interface EpochValidatorInfo { // Validators for the current epoch. next_validators: NextEpochValidatorInfo[]; @@ -183,6 +191,7 @@ export abstract class Provider { abstract async block(blockId: BlockId): Promise; abstract async chunk(chunkId: ChunkId): Promise; abstract async validators(blockId: BlockId): Promise; + abstract async experimental_genesisConfig(): Promise; } export function getTransactionLastResult(txResult: FinalExecutionOutcome): any {