Skip to content
This repository has been archived by the owner on May 10, 2023. It is now read-only.

Commit

Permalink
Merge pull request #99 from darwinia-network/opaquecall
Browse files Browse the repository at this point in the history
Opaquecall
  • Loading branch information
WoeOm authored Sep 17, 2020
2 parents 8fb1df0 + ff739ec commit a386a30
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 47 deletions.
67 changes: 20 additions & 47 deletions packages/api-derive/src/staking/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.

import { ApiInterfaceRx } from '@polkadot/api/types';
import { Balance } from '@polkadot/types/interfaces';
import { Balance, BlockNumber } from '@polkadot/types/interfaces';
import { StakingLedgerT as StakingLedger, Unbonding } from '@darwinia/typegen/interfaces';
import { DeriveSessionInfo, DeriveStakingAccount, DeriveStakingQuery, DeriveUnlocking } from '../types';

Expand All @@ -16,20 +16,6 @@ import { isUndefined } from '@polkadot/util';

import { memo } from '../util';

// groups the supplied chunks by era, i.e. { [era]: BN(total of values) }
function groupByEra (list: Unbonding[], sessionInfo: DeriveSessionInfo): Record<string, BN> {
return list.reduce((map: Record<string, BN>, { amount, moment }): Record<string, BN> => {
const era = moment.div(sessionInfo.eraLength);
const key = era.toString();

map[key] = !map[key]
? amount
: map[key].add(amount);

return map;
}, {});
}

// calculate the remaining blocks in a specific unlock era
function remainingBlocks (api: ApiInterfaceRx, blocks: BN, sessionInfo: DeriveSessionInfo): BlockNumber {
const era = blocks.div(sessionInfo.eraLength);
Expand All @@ -45,67 +31,54 @@ function remainingBlocks (api: ApiInterfaceRx, blocks: BN, sessionInfo: DeriveSe
);
}

function calculateUnlocking (api: ApiInterfaceRx, stakingLedger: StakingLedger | undefined, sessionInfo: DeriveSessionInfo, currencyType: currencyType): [DeriveUnlocking[] | undefined, Balance] {
function calculateUnlocking (api: ApiInterfaceRx, stakingLedger: StakingLedger | undefined, best: BlockNumber, currencyType: currencyType): [DeriveUnlocking[] | undefined, Balance] {
if (isUndefined(stakingLedger)) {
return [undefined, api.registry.createType('Balance', 0)];
}

const unlockingChunks = stakingLedger[`${currencyType}StakingLock`].unbondings.filter(({ moment }): boolean => {
const era = moment.div(sessionInfo.eraLength);

return era.sub(sessionInfo.activeEra).gtn(0);
}
);
return moment.gtn(best);
});

if (!unlockingChunks.length) {
return [undefined, api.registry.createType('Balance', 0)];
}

// group the unlock chunks that have the same era and sum their values
const groupedResult = groupByEra(unlockingChunks, sessionInfo);
const results = Object.entries(groupedResult).map(([eraString, amount]): DeriveUnlocking => ({
remainingEras: new BN(eraString).sub(sessionInfo.activeEra),
// remainingBlocks: remainingBlocks(api, new BN(eraString), sessionInfo),
value: api.registry.createType('Balance', amount)
}));
const total = Object.entries(unlockingChunks).reduce((all, [, unbond]) => (all.add(unbond.amount)), new BN(0));

const total = Object.entries(groupedResult).reduce((all, [, amount]) => (all.add(amount)), new BN(0));

return [results.length ? results : undefined, api.registry.createType('Balance', total)];
return [undefined, api.registry.createType('Balance', total)];
}

function redeemableSum (api: ApiInterfaceRx, stakingLedger: StakingLedger | undefined, sessionInfo: DeriveSessionInfo): Balance {
function redeemableSum (api: ApiInterfaceRx, stakingLedger: StakingLedger | undefined, best: BlockNumber): Balance {
if (isUndefined(stakingLedger)) {
return api.registry.createType('Balance');
}

return api.registry.createType('Balance', stakingLedger.ringStakingLock.unbondings.reduce((total, { amount, moment }): BN => {
const era = moment.div(sessionInfo.eraLength);

return era.sub(sessionInfo.activeEra).eqn(0)
return moment.gte(best)
? total.add(amount)
: total;
}, new BN(0)));
}

function parseResult (api: ApiInterfaceRx, sessionInfo: DeriveSessionInfo, query: DeriveStakingQuery): DeriveStakingAccount {
const calcUnlocking = calculateUnlocking(api, query.stakingLedger, sessionInfo, 'ring');
const calcUnlockingKton = calculateUnlocking(api, query.stakingLedger, sessionInfo, 'kton');
function parseResult (api: ApiInterfaceRx, best: BlockNumber, query: DeriveStakingQuery): DeriveStakingAccount {
const calcUnlocking = calculateUnlocking(api, query.stakingLedger, best, 'ring');
const calcUnlockingKton = calculateUnlocking(api, query.stakingLedger, best, 'kton');

return {
...query,
redeemable: redeemableSum(api, query.stakingLedger, sessionInfo),
redeemable: redeemableSum(api, query.stakingLedger, best),
unlocking: calcUnlocking[0],
unlockingTotalValue: calcUnlocking[1],
unlockingKton: calcUnlockingKton[0],
unlockingKtonTotalValue: calcUnlockingKton[1]
};
}

export function _account (api: ApiInterfaceRx): (sessionInfo: DeriveSessionInfo, accountId: Uint8Array | string) => Observable<DeriveStakingAccount> {
return memo((sessionInfo: DeriveSessionInfo, accountId: Uint8Array | string): Observable<DeriveStakingAccount> =>
export function _account (api: ApiInterfaceRx): (best: BlockNumber, accountId: Uint8Array | string) => Observable<DeriveStakingAccount> {
return memo((best: BlockNumber, accountId: Uint8Array | string): Observable<DeriveStakingAccount> =>
api.derive.staking.query(accountId).pipe(
map((query) => parseResult(api, sessionInfo, query))
map((query) => parseResult(api, best, query))
));
}

Expand All @@ -114,8 +87,8 @@ export function _account (api: ApiInterfaceRx): (sessionInfo: DeriveSessionInfo,
*/
export function account (api: ApiInterfaceRx): (accountId: Uint8Array | string) => Observable<DeriveStakingAccount> {
return memo((accountId: Uint8Array | string): Observable<DeriveStakingAccount> =>
api.derive.session.info().pipe(
switchMap((sessionInfo) => api.derive.staking._account(sessionInfo, accountId))
api.derive.chain.bestNumber().pipe(
switchMap((best) => api.derive.staking._account(best, accountId))
));
}

Expand All @@ -124,9 +97,9 @@ export function account (api: ApiInterfaceRx): (accountId: Uint8Array | string)
*/
export function accounts (api: ApiInterfaceRx): (accountIds: (Uint8Array | string)[]) => Observable<DeriveStakingAccount[]> {
return memo((accountIds: (Uint8Array | string)[]): Observable<DeriveStakingAccount[]> =>
api.derive.session.info().pipe(
switchMap((sessionInfo) =>
combineLatest(accountIds.map((accountId) => api.derive.staking._account(sessionInfo, accountId)))
api.derive.chain.bestNumber().pipe(
switchMap((best) =>
combineLatest(accountIds.map((accountId) => api.derive.staking._account(best, accountId)))
)
));
}
48 changes: 48 additions & 0 deletions packages/react-components-darwinia/src/Params/OpaqueCall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2017-2020 @polkadot/app-extrinsics authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { SubmittableExtrinsic } from '@polkadot/api/types';
import { Props, RawParam } from '@polkadot/react-params/types';

import React, { useCallback } from 'react';
import { useApi } from '@polkadot/react-hooks';

import ExtrinsicDisplay from './Extrinsic';

function OpaqueCall ({ className = '', isDisabled, isError, label, onChange, onEnter, onEscape, withLabel }: Props): React.ReactElement<Props> {
const { apiDefaultTxSudo } = useApi();

const _onChange = useCallback(
({ isValid, value }: RawParam): void => {
let callData = null;

if (isValid && value) {
callData = (value as SubmittableExtrinsic<'promise'>).method.toHex();
}

onChange && onChange({
isValid,
value: callData
});
},
[onChange]
);

return (
<ExtrinsicDisplay
className={className}
defaultValue={apiDefaultTxSudo}
isDisabled={isDisabled}
isError={isError}
isPrivate
label={label}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
withLabel={withLabel}
/>
);
}

export default React.memo(OpaqueCall);
2 changes: 2 additions & 0 deletions packages/react-components-darwinia/src/Params/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import { ComponentMap } from '@polkadot/react-params/types';

import Call from './Call';
import OpaqueCall from './OpaqueCall';
import Proposal from './Proposal';

const components: ComponentMap = {
Call,
OpaqueCall,
Proposal
};

Expand Down
48 changes: 48 additions & 0 deletions packages/react-components/src/Params/OpaqueCall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2017-2020 @polkadot/app-extrinsics authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { SubmittableExtrinsic } from '@polkadot/api/types';
import { Props, RawParam } from '@polkadot/react-params/types';

import React, { useCallback } from 'react';
import { useApi } from '@polkadot/react-hooks';

import ExtrinsicDisplay from './Extrinsic';

function OpaqueCall ({ className = '', isDisabled, isError, label, onChange, onEnter, onEscape, withLabel }: Props): React.ReactElement<Props> {
const { apiDefaultTxSudo } = useApi();

const _onChange = useCallback(
({ isValid, value }: RawParam): void => {
let callData = null;

if (isValid && value) {
callData = (value as SubmittableExtrinsic<'promise'>).method.toHex();
}

onChange && onChange({
isValid,
value: callData
});
},
[onChange]
);

return (
<ExtrinsicDisplay
className={className}
defaultValue={apiDefaultTxSudo}
isDisabled={isDisabled}
isError={isError}
isPrivate
label={label}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
withLabel={withLabel}
/>
);
}

export default React.memo(OpaqueCall);
2 changes: 2 additions & 0 deletions packages/react-components/src/Params/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import { ComponentMap } from '@polkadot/react-params/types';

import Call from './Call';
import OpaqueCall from './OpaqueCall';
import Proposal from './Proposal';

const components: ComponentMap = {
Call,
OpaqueCall,
Proposal
};

Expand Down
31 changes: 31 additions & 0 deletions packages/react-params/src/Param/OpaqueCall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2017-2020 @polkadot/react-params authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { Props } from '../types';

import React from 'react';
import { registry } from '@polkadot/react-api';
import { Bytes } from '@polkadot/types';

import CallDisplay from './Call';
import Unknown from './Unknown';

function OpaqueCall (props: Props): React.ReactElement<Props> {
if (!props.isDisabled) {
return (
<Unknown {...props} />
);
}

const value = registry.createType('Call', (props.defaultValue.value as Bytes).toHex());

return (
<CallDisplay
{...props}
defaultValue={{ isValid: true, value }}
/>
);
}

export default React.memo(OpaqueCall);
2 changes: 2 additions & 0 deletions packages/react-params/src/Param/findComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import KeyValue from './KeyValue';
import KeyValueArray from './KeyValueArray';
import Moment from './Moment';
import Null from './Null';
import OpaqueCall from './OpaqueCall';
import Option from './Option';
import Raw from './Raw';
import Struct from './Struct';
Expand Down Expand Up @@ -56,6 +57,7 @@ const componentDef: TypeToComponent[] = [
{ c: KeyValueArray, t: ['Vec<KeyValue>'] },
{ c: Moment, t: ['Moment', 'MomentOf'] },
{ c: Null, t: ['Null'] },
{ c: OpaqueCall, t: ['OpaqueCall'] },
{ c: Option, t: ['Option'] },
{ c: Text, t: ['String', 'Text'] },
{ c: Struct, t: ['Struct'] },
Expand Down

0 comments on commit a386a30

Please sign in to comment.