Submit multiple txs via Payout all (#3756)

* Submit multiple txs via Payout all

* Single onstart event
This commit is contained in:
Jaco Greeff 2020-09-24 14:33:31 +02:00 committed by GitHub
parent eab4a7452d
commit 42e06de3c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 47 deletions

View File

@ -25,37 +25,44 @@ interface SinglePayout {
validatorId: string;
}
function createExtrinsic (api: ApiPromise, payout: PayoutValidator | PayoutValidator[], maxPayouts: number): SubmittableExtrinsic<'promise'> | null {
function createBatches (api: ApiPromise, maxPayouts: number, payouts: SinglePayout[]): SubmittableExtrinsic<'promise'>[] {
return payouts
.sort((a, b) => a.era.cmp(b.era))
.reduce((batches: SubmittableExtrinsic<'promise'>[][], { era, validatorId }): SubmittableExtrinsic<'promise'>[][] => {
const tx = api.tx.staking.payoutStakers(validatorId, era);
const batch = batches[batches.length - 1];
if (batch.length >= maxPayouts) {
batches.push([tx]);
} else {
batch.push(tx);
}
return batches;
}, [[]])
.map((batch) => api.tx.utility.batch(batch));
}
function createExtrinsics (api: ApiPromise, payout: PayoutValidator | PayoutValidator[], maxPayouts: number): SubmittableExtrinsic<'promise'>[] | null {
if (Array.isArray(payout)) {
if (payout.length === 1) {
return createExtrinsic(api, payout[0], maxPayouts);
return createExtrinsics(api, payout[0], maxPayouts);
}
return api.tx.utility.batch(
payout
.reduce((payouts: SinglePayout[], { eras, validatorId }): SinglePayout[] => {
eras.forEach(({ era }): void => {
payouts.push({ era, validatorId });
});
return createBatches(api, maxPayouts, payout.reduce((payouts: SinglePayout[], { eras, validatorId }): SinglePayout[] => {
eras.forEach(({ era }): void => {
payouts.push({ era, validatorId });
});
return payouts;
}, [])
.sort((a, b) => a.era.cmp(b.era))
.filter((_, index) => index < maxPayouts)
.map(({ era, validatorId }) => api.tx.staking.payoutStakers(validatorId, era))
);
return payouts;
}, []));
}
const { eras, validatorId } = payout;
return eras.length === 1
? api.tx.staking.payoutStakers(validatorId, eras[0].era)
: api.tx.utility.batch(
eras
.sort((a, b) => a.era.cmp(b.era))
.filter((_, index) => index < maxPayouts)
.map(({ era }) => api.tx.staking.payoutStakers(validatorId, era))
);
? [api.tx.staking.payoutStakers(validatorId, eras[0].era)]
: createBatches(api, maxPayouts, eras.map((era): SinglePayout => ({ era: era.era, validatorId })));
}
function PayButton ({ className, isAll, isDisabled, payout }: Props): React.ReactElement<Props> {
@ -64,7 +71,7 @@ function PayButton ({ className, isAll, isDisabled, payout }: Props): React.Reac
const { allAccounts } = useAccounts();
const [isVisible, togglePayout] = useToggle();
const [accountId, setAccount] = useState<string | null>(null);
const [extrinsic, setExtrinsic] = useState<SubmittableExtrinsic<'promise'> | null>(null);
const [extrinsics, setExtrinsics] = useState<SubmittableExtrinsic<'promise'>[] | null>(null);
const [maxPayouts, setMaxPayouts] = useState(0);
useEffect((): void => {
@ -90,8 +97,8 @@ function PayButton ({ className, isAll, isDisabled, payout }: Props): React.Reac
}, [allAccounts, api, payout]);
useEffect((): void => {
api.tx.utility && payout && setExtrinsic(
() => createExtrinsic(api, payout, maxPayouts)
api.tx.utility && payout && setExtrinsics(
() => createExtrinsics(api, payout, maxPayouts)
);
}, [api, maxPayouts, payout]);
@ -154,9 +161,9 @@ function PayButton ({ className, isAll, isDisabled, payout }: Props): React.Reac
<Modal.Actions onCancel={togglePayout}>
<TxButton
accountId={accountId}
extrinsic={extrinsic}
extrinsic={extrinsics}
icon='credit-card'
isDisabled={!extrinsic || !accountId}
isDisabled={!extrinsics || !extrinsics.length || !accountId}
label={t<string>('Payout')}
onStart={togglePayout}
/>

View File

@ -4,9 +4,9 @@
import { SubmittableExtrinsic } from '@polkadot/api/types';
import { TxButtonProps as Props } from './types';
import React, { useCallback, useContext } from 'react';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { SubmittableResult } from '@polkadot/api';
import { useApi, useToggle } from '@polkadot/react-hooks';
import { useApi } from '@polkadot/react-hooks';
import { assert, isFunction } from '@polkadot/util';
import Button from './Button';
@ -17,9 +17,14 @@ function TxButton ({ accountId, className = '', extrinsic: propsExtrinsic, icon,
const { t } = useTranslation();
const { api } = useApi();
const { queueExtrinsic } = useContext(StatusContext);
const [isSending, , setIsSending] = useToggle(false);
const [isSending, setIsSending] = useState(false);
const [isStarted, setIsStarted] = useState(false);
const needsAccount = !isUnsigned && !accountId;
useEffect((): void => {
(isStarted && onStart) && onStart();
}, [isStarted, onStart]);
const _onFailed = useCallback(
(result: SubmittableResult | null): void => {
setIsSending(false);
@ -38,43 +43,54 @@ function TxButton ({ accountId, className = '', extrinsic: propsExtrinsic, icon,
[onSuccess, setIsSending]
);
const _onStart = useCallback(
() => setIsStarted(true),
[setIsStarted]
);
const _onSend = useCallback(
(): void => {
let extrinsic: SubmittableExtrinsic<'promise'>;
let extrinsics: SubmittableExtrinsic<'promise'>[];
if (propsExtrinsic) {
extrinsic = propsExtrinsic;
extrinsics = Array.isArray(propsExtrinsic)
? propsExtrinsic
: [propsExtrinsic];
} else {
const [section, method] = (tx || '').split('.');
assert(api.tx[section] && api.tx[section][method], `Unable to find api.tx.${section}.${method}`);
extrinsic = api.tx[section][method](...(
isFunction(params)
? params()
: (params || [])
));
extrinsics = [
api.tx[section][method](...(
isFunction(params)
? params()
: (params || [])
))
];
}
assert(extrinsic, 'Expected generated extrinsic passed to TxButton');
assert(extrinsics?.length, 'Expected generated extrinsic passed to TxButton');
if (withSpinner) {
setIsSending(true);
}
queueExtrinsic({
accountId: accountId && accountId.toString(),
extrinsic,
isUnsigned,
txFailedCb: withSpinner ? _onFailed : onFailed,
txStartCb: onStart,
txSuccessCb: withSpinner ? _onSuccess : onSuccess,
txUpdateCb: onUpdate
extrinsics.forEach((extrinsic): void => {
queueExtrinsic({
accountId: accountId && accountId.toString(),
extrinsic,
isUnsigned,
txFailedCb: withSpinner ? _onFailed : onFailed,
txStartCb: _onStart,
txSuccessCb: withSpinner ? _onSuccess : onSuccess,
txUpdateCb: onUpdate
});
});
onClick && onClick();
},
[_onFailed, _onSuccess, accountId, api.tx, isUnsigned, onClick, onFailed, onStart, onSuccess, onUpdate, params, propsExtrinsic, queueExtrinsic, setIsSending, tx, withSpinner]
[_onFailed, _onStart, _onSuccess, accountId, api.tx, isUnsigned, onClick, onFailed, onSuccess, onUpdate, params, propsExtrinsic, queueExtrinsic, setIsSending, tx, withSpinner]
);
if (onSendRef) {

View File

@ -39,7 +39,7 @@ export interface TxTriggerProps {
export type TxTrigger = React.ComponentType<TxTriggerProps>;
export interface TxProps {
extrinsic?: SubmittableExtrinsic | null;
extrinsic?: SubmittableExtrinsic | SubmittableExtrinsic[] | null;
tx?: string;
params?: any[] | ConstructTxFn;
}