Support for crowdloan refund (#4982)
* Support for crowdloan refund * Cleanup ending checks * Remove old comment * Withdrawal type only on non-refund * Additional events support * Additional comments * Update packages/page-parachains/src/Crowdloan/Fund.tsx
This commit is contained in:
parent
f1427d3ab0
commit
d4e61d4f3f
@ -43,14 +43,17 @@ function extractContributors (allAccounts: string[], keys: StorageKey[]): Contri
|
||||
};
|
||||
}
|
||||
|
||||
function Fund ({ bestNumber, className, isOngoing, leasePeriod, value: { childKey, info: { cap, depositor, end, firstSlot, lastSlot, raised, retiring }, isCapped, isEnded, isRetired, isWinner, paraId, retireEnd } }: Props): React.ReactElement<Props> {
|
||||
function Fund ({ bestNumber, className, isOngoing, leasePeriod, value: { childKey, info: { cap, depositor, end, firstSlot, lastSlot, raised }, isCapped, isEnded, isWinner, paraId } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { allAccounts, isAccount } = useAccounts();
|
||||
const [{ myAccounts, uniqueKeys }, setContributors] = useState<Contributions>(NO_CONTRIB);
|
||||
const trigger = useEventTrigger([api.events.crowdloan?.Contributed, api.events.crowdloan?.Withdrew], useCallback(
|
||||
({ event: { data: [, fundIndex] } }: EventRecord) =>
|
||||
(fundIndex as ParaId).eq(paraId),
|
||||
const trigger = useEventTrigger([api.events.crowdloan?.Contributed, api.events.crowdloan?.Withdrew, api.events.crowdloan?.AllRefunded, api.events.crowdloan?.PartiallyRefunded], useCallback(
|
||||
({ event: { data } }: EventRecord) =>
|
||||
((data.length === 1
|
||||
? data[0] // AllRefunded, PartiallyRefunded [ParaId]
|
||||
: data[1] // Contributed, Withdrew [AccountId, ParaId, Balance]
|
||||
) as ParaId).eq(paraId),
|
||||
[paraId]
|
||||
));
|
||||
|
||||
@ -69,22 +72,13 @@ function Fund ({ bestNumber, className, isOngoing, leasePeriod, value: { childKe
|
||||
[depositor, isAccount]
|
||||
);
|
||||
|
||||
const [blocksLeft, retiringLeft] = useMemo(
|
||||
() => bestNumber
|
||||
? [
|
||||
end.gt(bestNumber)
|
||||
? end.sub(bestNumber)
|
||||
: null,
|
||||
retireEnd?.gt(bestNumber)
|
||||
? retireEnd.sub(bestNumber)
|
||||
: null
|
||||
]
|
||||
: [null, null],
|
||||
[bestNumber, end, retireEnd]
|
||||
const blocksLeft = useMemo(
|
||||
() => bestNumber && end.gt(bestNumber)
|
||||
? end.sub(bestNumber)
|
||||
: null,
|
||||
[bestNumber, end]
|
||||
);
|
||||
|
||||
// TODO Dissolve should look at retirement and the actual period
|
||||
|
||||
const percentage = useMemo(
|
||||
() => cap.isZero()
|
||||
? '100.00%'
|
||||
@ -92,14 +86,14 @@ function Fund ({ bestNumber, className, isOngoing, leasePeriod, value: { childKe
|
||||
[cap, raised]
|
||||
);
|
||||
|
||||
const isLeaseOver = !blocksLeft && !!leasePeriod && (
|
||||
const hasEnded = !blocksLeft && !!leasePeriod && (
|
||||
isWinner
|
||||
? leasePeriod.currentPeriod.gt(lastSlot)
|
||||
: leasePeriod.currentPeriod.gt(firstSlot)
|
||||
);
|
||||
const canContribute = !!blocksLeft && isOngoing && !isCapped && !isWinner && retiring.isFalse;
|
||||
const canDissolve = raised.isZero() || (retiring.isTrue && isLeaseOver);
|
||||
const canWithdraw = !raised.isZero() && isLeaseOver;
|
||||
const canContribute = isOngoing && !isCapped && !isWinner && !!blocksLeft;
|
||||
const canDissolve = raised.isZero() || hasEnded;
|
||||
const canWithdraw = !raised.isZero() && hasEnded;
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
@ -108,35 +102,17 @@ function Fund ({ bestNumber, className, isOngoing, leasePeriod, value: { childKe
|
||||
<td className='media--800'>
|
||||
{isWinner
|
||||
? t<string>('Winner')
|
||||
: isRetired
|
||||
? retiring.isTrue
|
||||
? t<string>('Retired')
|
||||
: t<string>('Completed')
|
||||
: retiring.isTrue
|
||||
? t<string>('Retiring')
|
||||
: blocksLeft
|
||||
? isCapped
|
||||
? t<string>('Capped')
|
||||
: isOngoing
|
||||
? t<string>('Active')
|
||||
: t<string>('Past')
|
||||
: t<string>('Ended')
|
||||
: blocksLeft
|
||||
? isCapped
|
||||
? t<string>('Capped')
|
||||
: isOngoing
|
||||
? t<string>('Active')
|
||||
: t<string>('Past')
|
||||
: t<string>('Ended')
|
||||
}
|
||||
</td>
|
||||
<td className='address media--1400'><AddressMini value={depositor} /></td>
|
||||
{!isOngoing && (
|
||||
<td className='all number together media--1000'>
|
||||
{(isRetired || retiring.isTrue) && (
|
||||
<>
|
||||
{retiringLeft && (
|
||||
<BlockToTime value={retiringLeft} />
|
||||
)}
|
||||
#{formatNumber(retireEnd)}
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
<td className='all number together media--1000'>
|
||||
<td className='all number together media--1100'>
|
||||
{blocksLeft && (
|
||||
<BlockToTime value={blocksLeft} />
|
||||
)}
|
||||
|
||||
@ -76,7 +76,6 @@ function Funds ({ bestNumber, className, leasePeriod, value }: Props): React.Rea
|
||||
[t('completed'), 'start', 2],
|
||||
[undefined, 'media--800'],
|
||||
[undefined, 'media--1400'],
|
||||
[t('retired'), 'media--1000'],
|
||||
[t('ending'), 'media--1000'],
|
||||
[t('leases')],
|
||||
[t('raised')],
|
||||
|
||||
@ -79,24 +79,38 @@ function Refund ({ allAccounts, className, myAccounts, paraId }: Props): React.R
|
||||
value={accountId}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The type of withdrawal, all accounts or only own accounts (if available)')}>
|
||||
<Dropdown
|
||||
label={t<string>('withdrawal type')}
|
||||
onChange={setWithdrawType}
|
||||
options={typeOptions}
|
||||
value={withdrawType}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
{!api.tx.crowdloan.refund && (
|
||||
<Modal.Columns hint={t<string>('The type of withdrawal, all accounts or only own accounts (if available)')}>
|
||||
<Dropdown
|
||||
label={t<string>('withdrawal type')}
|
||||
onChange={setWithdrawType}
|
||||
options={typeOptions}
|
||||
value={withdrawType}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
)}
|
||||
</Modal.Content>
|
||||
<Modal.Actions onCancel={toggleOpen}>
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
extrinsic={extrinsics}
|
||||
icon='credit-card'
|
||||
isDisabled={!extrinsics || !extrinsics.length || !accountId}
|
||||
label={t<string>('Refund')}
|
||||
onStart={toggleOpen}
|
||||
/>
|
||||
{api.tx.crowdloan.refund
|
||||
? (
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
icon='credit-card'
|
||||
label={t<string>('Refund')}
|
||||
onStart={toggleOpen}
|
||||
tx={api.tx.crowdloan.refund}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
extrinsic={extrinsics}
|
||||
icon='credit-card'
|
||||
isDisabled={!extrinsics || !extrinsics.length || !accountId}
|
||||
label={t<string>('Refund')}
|
||||
onStart={toggleOpen}
|
||||
/>
|
||||
)}
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
@ -53,9 +53,7 @@ export interface Campaign extends WinnerData {
|
||||
info: FundInfo;
|
||||
isCapped?: boolean;
|
||||
isEnded?: boolean;
|
||||
isRetired?: boolean;
|
||||
isWinner?: boolean;
|
||||
retireEnd?: BN;
|
||||
}
|
||||
|
||||
export interface LeasePeriod {
|
||||
|
||||
@ -49,28 +49,25 @@ function createChildKey (trieIndex: TrieIndex): string {
|
||||
}
|
||||
|
||||
// map into a campaign
|
||||
function updateFund (bestNumber: BN, minContribution: BN, retirementPeriod: BN, data: Campaign, leased: ParaId[]): Campaign {
|
||||
function updateFund (bestNumber: BN, minContribution: BN, data: Campaign, leased: ParaId[]): Campaign {
|
||||
data.isCapped = data.info.cap.sub(data.info.raised).lt(minContribution);
|
||||
data.isEnded = bestNumber.gt(data.info.end);
|
||||
data.retireEnd = data.info.end.add(retirementPeriod);
|
||||
data.isRetired = bestNumber.gt(data.retireEnd);
|
||||
data.isWinner = hasLease(data.paraId, leased);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function isFundUpdated (bestNumber: BlockNumber, minContribution: BN, retirementPeriod: BN, { info: { cap, end, raised }, paraId }: Campaign, leased: ParaId[], allPrev: Campaigns): boolean {
|
||||
function isFundUpdated (bestNumber: BlockNumber, minContribution: BN, { info: { cap, end, raised }, paraId }: Campaign, leased: ParaId[], allPrev: Campaigns): boolean {
|
||||
const prev = allPrev.funds?.find((p) => p.paraId.eq(paraId));
|
||||
|
||||
return !prev ||
|
||||
(!prev.isEnded && bestNumber.gt(end)) ||
|
||||
(!prev.isCapped && cap.sub(raised).lt(minContribution)) ||
|
||||
(!prev.isRetired && bestNumber.gt(end.add(retirementPeriod))) ||
|
||||
(!prev.isWinner && hasLease(paraId, leased));
|
||||
}
|
||||
|
||||
// compare the current campaigns against the previous, manually adding ending and calculating the new totals
|
||||
function createResult (bestNumber: BlockNumber, minContribution: BN, retirementPeriod: BN, funds: Campaign[], leased: ParaId[], prev: Campaigns): Campaigns {
|
||||
function createResult (bestNumber: BlockNumber, minContribution: BN, funds: Campaign[], leased: ParaId[], prev: Campaigns): Campaigns {
|
||||
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),
|
||||
@ -84,7 +81,7 @@ function createResult (bestNumber: BlockNumber, minContribution: BN, retirementP
|
||||
const hasChanged =
|
||||
!prev.funds || prev.funds.length !== funds.length ||
|
||||
hasNewActiveCap || hasNewActiveRaised || hasNewTotalCap || hasNewTotalRaised ||
|
||||
funds.some((c) => isFundUpdated(bestNumber, minContribution, retirementPeriod, c, leased, prev));
|
||||
funds.some((c) => isFundUpdated(bestNumber, minContribution, c, leased, prev));
|
||||
|
||||
if (!hasChanged) {
|
||||
return prev;
|
||||
@ -98,7 +95,7 @@ function createResult (bestNumber: BlockNumber, minContribution: BN, retirementP
|
||||
? activeRaised
|
||||
: prev.activeRaised,
|
||||
funds: funds
|
||||
.map((c) => updateFund(bestNumber, minContribution, retirementPeriod, c, leased))
|
||||
.map((c) => updateFund(bestNumber, minContribution, c, leased))
|
||||
.sort((a, b) =>
|
||||
a.isWinner !== b.isWinner
|
||||
? a.isWinner
|
||||
@ -108,15 +105,11 @@ function createResult (bestNumber: BlockNumber, minContribution: BN, retirementP
|
||||
? a.isCapped
|
||||
? -1
|
||||
: 1
|
||||
: a.isRetired !== b.isRetired
|
||||
? a.isRetired
|
||||
: a.isEnded !== b.isEnded
|
||||
? a.isEnded
|
||||
? 1
|
||||
: -1
|
||||
: a.isEnded !== b.isEnded
|
||||
? a.isEnded
|
||||
? 1
|
||||
: -1
|
||||
: 0
|
||||
: 0
|
||||
),
|
||||
totalCap: hasNewTotalCap
|
||||
? totalCap
|
||||
@ -187,7 +180,7 @@ export default function useFunds (): Campaigns {
|
||||
// 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, api.consts.crowdloan.retirementPeriod as BlockNumber, campaigns, leases, prev)
|
||||
createResult(bestNumber, api.consts.crowdloan.minContribution as BlockNumber, campaigns, leases, prev)
|
||||
);
|
||||
}, [api, bestNumber, campaigns, leases]);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user