Add dissolve for retired funds (#4948)

* Add dissolve for retired funds

* Move countdown

* Column for retiring countdown

* sorting
This commit is contained in:
Jaco Greeff 2021-03-26 12:53:18 +01:00 committed by GitHub
parent 30da81ca50
commit 2e8ce48c9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 55 deletions

View File

@ -21,7 +21,7 @@ interface Props {
value: Campaign;
}
function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, depositor, end, firstSlot, lastSlot, raised, retiring }, isCapped, isEnded, isWinner, paraId } }: Props): React.ReactElement<Props> {
function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, depositor, end, firstSlot, lastSlot, raised, retiring }, isCapped, isEnded, isRetired, isWinner, paraId, retireEnd } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { allAccounts } = useAccounts();
@ -35,9 +35,18 @@ function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, deposit
[allAccounts, depositor]
);
const blocksLeft = useMemo(
() => bestNumber && end.gt(bestNumber) && end.sub(bestNumber),
[bestNumber, end]
const [blocksLeft, retiringLeft] = useMemo(
() => bestNumber
? [
end.gt(bestNumber)
? end.sub(bestNumber)
: null,
retireEnd?.gt(bestNumber)
? retireEnd.sub(bestNumber)
: null
]
: [null, null],
[bestNumber, end, retireEnd]
);
// TODO Dissolve should look at retirement and the actual period
@ -50,25 +59,35 @@ function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, deposit
);
const canContribute = blocksLeft && !isCapped && !isWinner && retiring.isFalse;
const canDissolve = raised.isZero();
const canDissolve = raised.isZero() || isRetired;
return (
<tr className={className}>
<td className='number'><h1>{formatNumber(paraId)}</h1></td>
<td className='badge'><ParaLink id={paraId} /></td>
<td>
{retiring.isTrue
? t<string>('Retiring')
: isWinner
? t<string>('Winner')
: blocksLeft
? isCapped
? t<string>('Capped')
: t<string>('Active')
: t<string>('Ended')
{isWinner
? t<string>('Winner')
: isRetired
? t<string>('Retired')
: retiring.isTrue
? t<string>('Retiring')
: blocksLeft
? isCapped
? t<string>('Capped')
: t<string>('Active')
: t<string>('Ended')
}
</td>
<td className='address'><AddressMini value={depositor} /></td>
{!isOngoing && (
<td className='all number together'>
{retiringLeft && retiring.isTrue && (
<BlockToTime value={retiringLeft} />
)}
{!blocksLeft && <>#{formatNumber(retireEnd)}</>}
</td>
)}
<td className='all number together'>
{blocksLeft && (
<BlockToTime value={blocksLeft} />
@ -85,31 +104,29 @@ function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, deposit
/>
<div>{percentage}</div>
</td>
{isOngoing && (
<td className='button'>
{canDissolve && (
<TxButton
accountId={depositor}
icon='times'
isDisabled={!isDepositor}
label={
isEnded
? t<string>('Dissolve')
: t<string>('Cancel')
}
params={[paraId]}
tx={api.tx.crowdloan.dissolve}
/>
)}
{canContribute && (
<FundContribute
cap={cap}
paraId={paraId}
raised={raised}
/>
)}
</td>
)}
<td className='button'>
{canDissolve && (
<TxButton
accountId={depositor}
icon='times'
isDisabled={!isDepositor}
label={
isEnded
? t<string>('Dissolve')
: t<string>('Cancel')
}
params={[paraId]}
tx={api.tx.crowdloan.dissolve}
/>
)}
{isOngoing && canContribute && (
<FundContribute
cap={cap}
paraId={paraId}
raised={raised}
/>
)}
</td>
</tr>
);
}

View File

@ -19,7 +19,10 @@ interface Props {
function extractLists (value: Campaign[] | null): [Campaign[] | null, Campaign[] | null] {
return value
? [value.filter(({ isEnded }) => !isEnded), value.filter(({ isEnded }) => isEnded)]
? [
value.filter(({ isCapped, isEnded, isWinner }) => !(isCapped || isEnded || isWinner)),
value.filter(({ isCapped, isEnded, isWinner }) => (isCapped || isEnded || isWinner))
]
: [null, null];
}
@ -36,9 +39,11 @@ function Funds ({ bestNumber, className, value }: Props): React.ReactElement<Pro
const headedEndedRef = useRef([
[t('completed'), 'start', 4],
[t('ended')],
[t('retired')],
[t('ending')],
[t('periods')],
[t('raised')]
[t('raised')],
[]
]);
const [active, ended] = useMemo(

View File

@ -1,15 +1,18 @@
// Copyright 2017-2021 @polkadot/app-parachains authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type BN from 'bn.js';
import type { AccountId, BalanceOf, FundInfo, ParaId } from '@polkadot/types/interfaces';
export interface Campaign {
info: FundInfo;
isCapped?: boolean;
isEnded?: boolean;
isRetired?: boolean;
isWinner?: boolean;
key: string;
paraId: ParaId;
retireEnd?: BN;
}
export interface LeaseLease {

View File

@ -45,7 +45,7 @@ function hasLease (paraId: ParaId, leases: Lease[]): boolean {
}
// map into a campaign
function updateCampaign (bestNumber: BN, minContribution: BN, data: Campaign, leases: Lease[]): Campaign {
function updateCampaign (bestNumber: BN, minContribution: BN, retirementPeriod: BN, data: Campaign, leases: Lease[]): Campaign {
if (!data.isEnded && bestNumber.gt(data.info.end)) {
data.isEnded = true;
}
@ -54,6 +54,14 @@ function updateCampaign (bestNumber: BN, minContribution: BN, data: Campaign, le
data.isCapped = true;
}
if (!data.retireEnd) {
data.retireEnd = data.info.end.add(retirementPeriod);
}
if (!data.isRetired && bestNumber.gt(data.retireEnd)) {
data.isRetired = true;
}
if (!data.isWinner && hasLease(data.paraId, leases)) {
data.isWinner = true;
}
@ -61,14 +69,15 @@ function updateCampaign (bestNumber: BN, minContribution: BN, data: Campaign, le
return data;
}
function isFundUpdated (bestNumber: BlockNumber, minContribution: BN, { info: { cap, end, raised }, isCapped, isEnded, isWinner, paraId }: Campaign, leases: Lease[]): boolean {
function isFundUpdated (bestNumber: BlockNumber, minContribution: BN, retirementPeriod: BN, { info: { cap, end, raised }, isCapped, isEnded, isRetired, isWinner, paraId }: Campaign, leases: Lease[]): boolean {
return (!isEnded && bestNumber.gt(end)) ||
(!isCapped && cap.sub(raised).lt(minContribution)) ||
(!isRetired && bestNumber.gt(end.add(retirementPeriod))) ||
(!isWinner && hasLease(paraId, leases));
}
// compare the current campaigns against the previous, manually adding ending and calculating the new totals
function createResult (bestNumber: BlockNumber, minContribution: BN, funds: Campaign[], leases: Lease[], prev: Result): Result {
function createResult (bestNumber: BlockNumber, minContribution: BN, retirementPeriod: BN, funds: Campaign[], leases: Lease[], prev: Result): Result {
const [activeRaised, activeCap, totalRaised, totalCap] = funds.reduce(([ar, ac, tr, tc], { info: { cap, end, raised } }) => [
bestNumber.gt(end) ? ar : ar.iadd(raised),
bestNumber.gt(end) ? ac : ac.iadd(cap),
@ -83,7 +92,7 @@ function createResult (bestNumber: BlockNumber, minContribution: BN, funds: Camp
!prev.funds || prev.funds.length !== funds.length ||
hasNewActiveCap || hasNewActiveRaised ||
hasNewTotalCap || hasNewTotalRaised ||
funds.some((c) => isFundUpdated(bestNumber, minContribution, c, leases)) ||
funds.some((c) => isFundUpdated(bestNumber, minContribution, retirementPeriod, c, leases)) ||
true;
if (!hasChanged) {
@ -98,21 +107,25 @@ function createResult (bestNumber: BlockNumber, minContribution: BN, funds: Camp
? activeRaised
: prev.activeRaised,
funds: funds
.map((c) => updateCampaign(bestNumber, minContribution, c, leases))
.map((c) => updateCampaign(bestNumber, minContribution, retirementPeriod, c, leases))
.sort((a, b) =>
a.isWinner !== b.isWinner
? a.isWinner
? 1
: -1
? -1
: 1
: a.isCapped !== b.isCapped
? a.isCapped
? 1
: -1
: a.isEnded !== b.isEnded
? a.isEnded
? -1
: 1
: a.isRetired !== b.isRetired
? a.isRetired
? 1
: -1
: 0
: a.isEnded !== b.isEnded
? a.isEnded
? 1
: -1
: 0
),
totalCap: hasNewTotalCap
? totalCap
@ -168,7 +181,7 @@ export default function useFunds (): Result {
// here we manually add the actual ending status and calculate the totals
useEffect((): void => {
bestNumber && campaigns && leases && setResult((prev) =>
createResult(bestNumber, api.consts.crowdloan.minContribution as BlockNumber, campaigns, leases, prev)
createResult(bestNumber, api.consts.crowdloan.minContribution as BlockNumber, api.consts.crowdloan.retirementPeriod as BlockNumber, campaigns, leases, prev)
);
}, [api, bestNumber, campaigns, leases]);