Format stakingBalance as votingBalance + nominatingBalance (#204)

* Format stakingBalance as votingBalance + nominatingBalance

* Fix version number change (revert)

* Bump api 0.15.x: rxjs 6 & class-ified providers

* API bump

* Type cleanup

* Update version of packages

* RxJs 6 style Observable imports

* Remove ZERO const
This commit is contained in:
Jaco Greeff 2018-08-08 02:12:30 +02:00 committed by GitHub
parent ab26caa399
commit fb9c0f7f0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 202 additions and 83 deletions

View File

@ -11,6 +11,7 @@
],
"resolutions": {
"babel-core": "^7.0.0-bridge.0",
"rxjs": "^6.2.2",
"typescript": "^2.9.2"
},
"scripts": {

View File

@ -5,6 +5,7 @@
import { Header } from '@polkadot/primitives/header';
import { I18nProps } from '@polkadot/ui-app/types';
import { ApiProps } from '@polkadot/ui-react-rx/types';
import { Balance, BalanceMap } from './observables/types';
import BN from 'bn.js';
import React from 'react';
@ -14,7 +15,9 @@ import classes from '@polkadot/ui-app/util/classes';
import withApiCall from '@polkadot/ui-react-rx/with/apiCall';
import withMulti from '@polkadot/ui-react-rx/with/multi';
import withStorage from '@polkadot/ui-react-rx/with/storage';
import numberFormat from '@polkadot/ui-react-rx/util/numberFormat';
import validatingBalances from './observables/validatingBalances';
import translate from './translate';
type Props = ApiProps & I18nProps & {
@ -25,16 +28,11 @@ type Props = ApiProps & I18nProps & {
validators: Array<string>
};
type StateBalances = {
[index: string]: BN
};
type State = {
balances: StateBalances,
subscriptions: Array<any>
balances: BalanceMap,
subBalances: any
};
const DEFAULT_BALANCE = new BN(0);
const DEFAULT_BLOCKNUMBER = new BN(0);
const DEFAULT_SESSION_CHANGE = new BN(0);
const DEFAULT_SESSION_LENGTH = new BN(60);
@ -45,7 +43,7 @@ class Summary extends React.PureComponent<Props, State> {
this.state = {
balances: {},
subscriptions: []
subBalances: null
};
}
@ -57,46 +55,31 @@ class Summary extends React.PureComponent<Props, State> {
}
}
private subscribeBalances (accounts: string[]) {
private unsubscribeBalances () {
const { subBalances } = this.state;
if (subBalances) {
subBalances.unsubscribe();
}
}
private subscribeBalances (accounts: string[] = []) {
const { api } = this.props;
const { balances, subscriptions } = this.state;
const newBalances = { ...balances };
accounts.forEach((account) => {
if (newBalances[account]) {
return;
} else {
newBalances[account] = DEFAULT_BALANCE;
}
this.unsubscribeBalances();
subscriptions.push(
api.state
// Here we pass a parameter to the key generator, so it points to the correct storage entry
.getStorage(storage.staking.public.freeBalanceOf, account)
.subscribe((balance: BN) => {
this.setState(({ balances }: State) => {
const newBalances = { ...balances };
newBalances[account] = balance;
return {
balances: newBalances
} as State;
});
})
);
// FIXME: We want these as a HOC on the class
const subBalances = validatingBalances(api, accounts).subscribe((balances: BalanceMap) => {
this.setState({ balances });
});
this.setState({
balances: newBalances,
subscriptions
subBalances
} as State);
}
componentWillUnmount () {
const { subscriptions } = this.state;
subscriptions.forEach((sub) => sub.unsubscribe());
this.unsubscribeBalances();
}
render () {
@ -122,19 +105,23 @@ class Summary extends React.PureComponent<Props, State> {
<div>{t('summary.balance.validator', {
defaultValue: 'lowest validator balance is {{validatorLow}}',
replace: {
validatorLow: validatorLow ? validatorLow.toString() : 'unknown'
validatorLow: validatorLow && validatorLow.stakingBalance
? `${numberFormat(validatorLow.stakingBalance)} (+${numberFormat(validatorLow.nominatedBalance)})`
: 'unknown'
}
})}</div>
<div>{t('summary.balance.stake', {
defaultValue: ' highest balance intending to stake is {{intentionHigh}}',
replace: {
intentionHigh: intentionHigh ? intentionHigh.toString() : 'unknown'
intentionHigh: intentionHigh
? `${numberFormat(intentionHigh.stakingBalance)} (+${numberFormat(intentionHigh.nominatedBalance)})`
: 'unknown'
}
})}</div>
<div>{t('summary.countdown', {
defaultValue: 'session block {{remainder}} / {{length}} at #{{blockNumber}}',
replace: {
blockNumber: blockNumber.toString(),
blockNumber: numberFormat(blockNumber),
remainder: Math.max(1, blockNumber.sub(lastLengthChange).mod(sessionLength).addn(1).toNumber()).toString(),
length: sessionLength.toString()
}
@ -143,16 +130,16 @@ class Summary extends React.PureComponent<Props, State> {
);
}
private calcIntentionsHigh (): BN | null {
private calcIntentionsHigh (): Balance | null {
const { intentions, validators } = this.props;
const { balances } = this.state;
return intentions.reduce((high: BN | null, addr) => {
const balance = validators.includes(addr)
return intentions.reduce((high: Balance | null, addr) => {
const balance = validators.includes(addr) || !balances[addr]
? null
: balances[addr] || null;
: balances[addr];
if (high === null || (balance && high.lt(balance))) {
if (high === null || (balance && high.stakingBalance.lt(balance.stakingBalance))) {
return balance;
}
@ -160,14 +147,14 @@ class Summary extends React.PureComponent<Props, State> {
}, null);
}
private calcValidatorLow (): BN | null {
private calcValidatorLow (): Balance | null {
const { validators } = this.props;
const { balances } = this.state;
return validators.reduce((low: BN | null, addr) => {
return validators.reduce((low: Balance | null, addr) => {
const balance = balances[addr] || null;
if (low === null || (balance && low.gt(balance))) {
if (low === null || (balance && low.stakingBalance.gt(balance.stakingBalance))) {
return balance;
}

View File

@ -0,0 +1,19 @@
// Copyright 2017-2018 @polkadot/app-staking authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import BN from 'bn.js';
export type Balance = {
address: string,
freeBalance: BN,
nominatedBalance: BN,
reservedBalance: BN,
votingBalance: BN,
stakingBalance: BN,
nominators?: Array<Balance>
}
export type BalanceMap = {
[index: string]: Balance
}

View File

@ -0,0 +1,45 @@
// Copyright 2017-2018 @polkadot/app-staking authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { RxApiInterface } from '@polkadot/api-rx/types';
import { Balance } from './types';
import BN from 'bn.js';
import { Observable, combineLatest } from 'rxjs';
import { map, switchMap, toArray } from 'rxjs/operators';
import storage from '@polkadot/storage';
import encodeAddress from '@polkadot/util-keyring/address/encode';
import votingBalance from './votingBalance';
import votingBalances from './/votingBalances';
type Result = [Balance, [Array<Balance>]];
export default function validatingBalance (api: RxApiInterface, address: string): Observable<Balance> {
return combineLatest(
votingBalance(api, address),
api.state
.getStorage(storage.staking.public.nominatorsFor, address)
.pipe(
switchMap((nominators = []) =>
// FIXME: Will not be needed after https://github.com/polkadot-js/apps/issues/128
votingBalances(api, nominators.map(encodeAddress))
),
toArray()
)
).pipe(
map(([balance, [nominators = []]]: Result): Balance => {
const nominatedBalance = nominators.reduce((total, nominator: Balance) => {
return total.add(nominator.votingBalance);
}, new BN(0));
return {
...balance,
nominators,
nominatedBalance,
stakingBalance: nominatedBalance.add(balance.votingBalance)
};
})
);
}

View File

@ -0,0 +1,27 @@
// Copyright 2017-2018 @polkadot/app-staking authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { RxApiInterface } from '@polkadot/api-rx/types';
import { Balance, BalanceMap } from './types';
import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import validatingBalance from './validatingBalance';
export default function validatingBalances (api: RxApiInterface, addresses: Array<string>): Observable<BalanceMap> {
return combineLatest(
addresses.map((address) =>
validatingBalance(api, address)
)
).pipe(
map((result: Array<Balance>): BalanceMap => {
return result.reduce((balances, balance) => {
balances[balance.address] = balance;
return balances;
}, {} as BalanceMap);
})
);
}

View File

@ -0,0 +1,27 @@
// Copyright 2017-2018 @polkadot/app-staking authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { RxApiInterface } from '@polkadot/api-rx/types';
import { Balance } from './types';
import BN from 'bn.js';
import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import storage from '@polkadot/storage';
export default function votingBalance (api: RxApiInterface, address: string): Observable<Balance> {
return combineLatest(
api.state.getStorage(storage.staking.public.freeBalanceOf, address),
api.state.getStorage(storage.staking.public.reservedBalanceOf, address)
).pipe(
map(([freeBalance = new BN(0), reservedBalance = new BN(0)]): Balance => ({
address,
freeBalance,
nominatedBalance: new BN(0),
reservedBalance,
stakingBalance: new BN(0),
votingBalance: freeBalance.add(reservedBalance)
}))
);
}

View File

@ -0,0 +1,18 @@
// Copyright 2017-2018 @polkadot/app-staking authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { RxApiInterface } from '@polkadot/api-rx/types';
import { Balance } from './types';
import { Observable, combineLatest } from 'rxjs';
import votingBalance from './votingBalance';
export default function votingBalances (api: RxApiInterface, addresses: Array<string>): Observable<Balance[]> {
return combineLatest(
addresses.map((address) =>
votingBalance(api, address)
)
);
}

View File

@ -36,9 +36,10 @@
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.51",
"@polkadot/api-rx": "^0.14.29",
"@polkadot/api-rx": "^0.15.6",
"@polkadot/extrinsics": "^0.27.9",
"@polkadot/storage": "^0.27.9"
"@polkadot/storage": "^0.27.9",
"rxjs-compat": "^6.2.2"
},
"devDependencies": {
"babel-loader": "^8.0.0-beta.2",

View File

@ -10,7 +10,7 @@ import { ApiProps } from '../types';
import React from 'react';
import shouldUseLatestChain from '@polkadot/ui-react-rx/util/shouldUseLatestChain';
import createWsProvider from '@polkadot/api-provider/ws';
import WsProvider from '@polkadot/api-provider/ws';
import createApi from '@polkadot/api-rx';
import defaults from '@polkadot/api-rx/defaults';
@ -40,7 +40,7 @@ export default class Api extends React.PureComponent<Props, State> {
const { provider, url = '' } = props;
const api = props.api || createApi(
url && url.length
? createWsProvider(url)
? new WsProvider(url)
: provider
);
const setApi = (api: RxApiInterface): void => {
@ -51,7 +51,7 @@ export default class Api extends React.PureComponent<Props, State> {
const setApiProvider = (provider?: ProviderInterface): void =>
setApi(createApi(provider));
const setApiWsUrl = (url: string = defaults.WS_URL): void =>
setApiProvider(createWsProvider(url));
setApiProvider(new WsProvider(url));
this.state = {
api,

View File

@ -765,9 +765,9 @@
version "1.1.0"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz#50c1e2260ac0ed9439a181de3725a0168d59c48a"
"@polkadot/api-format@^0.14.29":
version "0.14.29"
resolved "https://registry.yarnpkg.com/@polkadot/api-format/-/api-format-0.14.29.tgz#02b1cc84d19219909d4dc82ab9ecd57f07aa888c"
"@polkadot/api-format@^0.15.6":
version "0.15.6"
resolved "https://registry.yarnpkg.com/@polkadot/api-format/-/api-format-0.15.6.tgz#9d50258df3fcd9e11ad591d43a3f25db3647ff7a"
dependencies:
"@babel/runtime" "^7.0.0-beta.51"
"@polkadot/jsonrpc" "^0.27.9"
@ -775,9 +775,9 @@
"@polkadot/util" "^0.27.9"
"@polkadot/util-keyring" "^0.27.9"
"@polkadot/api-provider@^0.14.29":
version "0.14.29"
resolved "https://registry.yarnpkg.com/@polkadot/api-provider/-/api-provider-0.14.29.tgz#d1bb7e8e879e62f868e0149e25a55995e600536e"
"@polkadot/api-provider@^0.15.6":
version "0.15.6"
resolved "https://registry.yarnpkg.com/@polkadot/api-provider/-/api-provider-0.15.6.tgz#e3c664687d9d82d59d16e9b5a8e3d10fd285a7a0"
dependencies:
"@babel/runtime" "^7.0.0-beta.51"
"@polkadot/storage" "^0.27.9"
@ -789,23 +789,23 @@
isomorphic-fetch "^2.2.1"
websocket "^1.0.25"
"@polkadot/api-rx@^0.14.29":
version "0.14.29"
resolved "https://registry.yarnpkg.com/@polkadot/api-rx/-/api-rx-0.14.29.tgz#e71c3b2dec8dd0e7cbc391024bd17b9616c8b453"
"@polkadot/api-rx@^0.15.6":
version "0.15.6"
resolved "https://registry.yarnpkg.com/@polkadot/api-rx/-/api-rx-0.15.6.tgz#849adc73d129d87eaf3c9463f9eb7201b8d6bfa7"
dependencies:
"@babel/runtime" "^7.0.0-beta.51"
"@polkadot/api" "^0.14.29"
"@polkadot/api-provider" "^0.14.29"
"@polkadot/api" "^0.15.6"
"@polkadot/api-provider" "^0.15.6"
"@types/rx" "^4.1.1"
rxjs "^5.5.10"
rxjs "^6.2.2"
"@polkadot/api@^0.14.29":
version "0.14.29"
resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-0.14.29.tgz#6a3f08ee1bd98e5ee96d85361f195496ebab65b7"
"@polkadot/api@^0.15.6":
version "0.15.6"
resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-0.15.6.tgz#6bf02708c5ef77d71ca0b00bdbbf6154abe80f6f"
dependencies:
"@babel/runtime" "^7.0.0-beta.51"
"@polkadot/api-format" "^0.14.29"
"@polkadot/api-provider" "^0.14.29"
"@polkadot/api-format" "^0.15.6"
"@polkadot/api-provider" "^0.15.6"
"@polkadot/jsonrpc" "^0.27.9"
"@polkadot/params" "^0.27.9"
"@polkadot/util" "^0.27.9"
@ -9865,15 +9865,13 @@ rx-lite@*, rx-lite@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
rxjs@^5.5.10, rxjs@^5.5.2:
version "5.5.11"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.11.tgz#f733027ca43e3bec6b994473be4ab98ad43ced87"
dependencies:
symbol-observable "1.0.1"
rxjs-compat@^6.2.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.2.2.tgz#3c0fcdb46130cc70aa55412c2b1147905ab4680a"
rxjs@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1"
rxjs@^5.5.2, rxjs@^6.1.0, rxjs@^6.2.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.2.tgz#eb75fa3c186ff5289907d06483a77884586e1cf9"
dependencies:
tslib "^1.9.0"
@ -10521,10 +10519,6 @@ svgo@^0.7.0:
sax "~1.2.1"
whet.extend "~0.9.9"
symbol-observable@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
symbol-observable@1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"