Skip to content

Commit

Permalink
feat(app): update claim missing delegator rewards page
Browse files Browse the repository at this point in the history
  • Loading branch information
kingsleydon committed Dec 7, 2023
1 parent 62243de commit 4a49b10
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 87 deletions.
1 change: 1 addition & 0 deletions apps/app/assets/vault_cheat_reimbursements.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion apps/app/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {theme} from '@/lib/theme'
import {chainAtom} from '@/store/common'
import {CacheProvider, css, type EmotionCache} from '@emotion/react'
import {
Box,
CssBaseline,
GlobalStyles,
ThemeProvider as MuiThemeProvider,
Expand Down Expand Up @@ -65,7 +66,11 @@ const App: FC<MyAppProps> = (props) => {
<Layout>
<Component {...pageProps} />
<ZendeskWidget />
{process.env.NODE_ENV === 'development' && <JotaiDevTools />}
{process.env.NODE_ENV === 'development' && (
<Box position="fixed" ml={1} mb={8} bottom={0} left={0}>
<JotaiDevTools />
</Box>
)}
<ReactQueryDevtools />
</Layout>
</MuiThemeProvider>
Expand Down
148 changes: 122 additions & 26 deletions apps/app/pages/claim-missing-delegator-rewards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Empty from '@/components/Empty'
import PageHeader from '@/components/PageHeader'
import usePolkadotApi from '@/hooks/usePolkadotApi'
import useSignAndSend from '@/hooks/useSignAndSend'
import {chainAtom} from '@/store/common'
import {walletDialogOpenAtom} from '@/store/ui'
import {LoadingButton} from '@mui/lab'
import {
Expand Down Expand Up @@ -36,15 +37,20 @@ import {useAtom} from 'jotai'
import {type NextPage} from 'next'
import {useEffect, useMemo, useState} from 'react'

type Kind = 'legacy' | 'reimbursement'

interface Row {
pid: string
amount: Decimal
claimed: boolean
kind: Kind
}

const ClaimMissingDelegatorRewards: NextPage = () => {
const [chain, setChain] = useAtom(chainAtom)
const signAndSend = useSignAndSend()
const [pid, setPid] = useState<string>()
const [kind, setKind] = useState<Kind>()
const [loading, setLoading] = useState(false)
const [dialogOpen, setDialogOpen] = useState(false)
const [targetAddress, setTargetAddress] = useState('')
Expand All @@ -53,21 +59,22 @@ const ClaimMissingDelegatorRewards: NextPage = () => {
}
const [account] = useAtom(polkadotAccountAtom)
const [, setWalletDialogOpen] = useAtom(walletDialogOpenAtom)
const [list, setList] = useState<Row[]>()
const [legacyRewardsList, setLegacyRewardsList] = useState<Row[]>()
const [reimbursementsList, setReimbursementsList] = useState<Row[]>()
const api = usePolkadotApi()
const {data} = useQuery(
['legacyRewards', account?.address, list],
const {data: legacyRewardsData} = useQuery(
['legacyRewards', account?.address, legacyRewardsList],
async () => {
if (
account?.address !== undefined &&
account?.address != null &&
api != null &&
list !== undefined &&
list.length > 0
legacyRewardsList != null &&
legacyRewardsList.length > 0
) {
const result = list
const result = legacyRewardsList
const legacyRewards =
await api.query.phalaStakePoolv2.legacyRewards.multi(
list.map((x) => [account.address, x.pid]),
legacyRewardsList.map((x) => [account.address, x.pid]),
)
for (let i = 0; i < legacyRewards.length; i++) {
if (legacyRewards[i].isNone) {
Expand All @@ -80,9 +87,43 @@ const ClaimMissingDelegatorRewards: NextPage = () => {
},
{
enabled:
list !== undefined &&
account?.address !== undefined &&
api !== undefined,
chain === 'khala' &&
legacyRewardsList != null &&
account?.address != null &&
api != null,
refetchInterval: 12000,
},
)

const {data: reimbursementsData} = useQuery(
['vaultCheatReimbursements', account?.address, reimbursementsList],
async () => {
if (
account?.address != null &&
api != null &&
reimbursementsList != null &&
reimbursementsList.length > 0
) {
const result = reimbursementsList
const reimbursements =
await api.query.phalaBasePool.reimbursements.multi(
reimbursementsList.map((x) => [account.address, x.pid]),
)
for (let i = 0; i < reimbursements.length; i++) {
if (reimbursements[i].isNone) {
result[i].claimed = true
}
}
return result
}
return []
},
{
enabled:
chain === 'khala' &&
reimbursementsList != null &&
account?.address != null &&
api != null,
refetchInterval: 12000,
},
)
Expand All @@ -93,50 +134,83 @@ const ClaimMissingDelegatorRewards: NextPage = () => {
const legacyRewardsMap = res.default.legacyRewards
if (
!unmounted &&
account?.address !== undefined &&
account?.address != null &&
account.address in legacyRewardsMap
) {
setList(
setLegacyRewardsList(
res.default.legacyRewards[
account.address as keyof typeof legacyRewardsMap
].map(([pid, amount]) => ({
pid,
amount: new Decimal(amount).div(1e12),
claimed: false,
kind: 'legacy',
})),
)
} else {
setList([])
setLegacyRewardsList([])
}
})

void import('@/assets/vault_cheat_reimbursements.json').then((res) => {
const reimbursementsMap = res.default
if (
!unmounted &&
account?.address != null &&
account.address in reimbursementsMap
) {
setReimbursementsList(
res.default[account.address as keyof typeof reimbursementsMap].map(
([pid, amount]) => ({
pid,
amount: new Decimal(amount).div(1e12),
claimed: false,
kind: 'reimbursement',
}),
),
)
} else {
setReimbursementsList([])
}
})
return () => {
unmounted = true
}
}, [account?.address])

const rowData = useMemo(() => {
if (legacyRewardsData == null || reimbursementsData == null) return null
return [...legacyRewardsData, ...reimbursementsData]
}, [legacyRewardsData, reimbursementsData])

const totalRewards = useMemo(() => {
return data
return rowData
?.filter((x) => !x.claimed)
.reduce((acc, cur) => acc.add(cur.amount), new Decimal(0))
}, [data])
}, [rowData])

const addressValid = useMemo(
() => validateAddress(targetAddress),
[targetAddress],
)

const claim = async (): Promise<void> => {
if (api == null || data == null) return
if (api == null || rowData == null) return
const getExtrinsic = (
pid: string,
kind: Kind,
): SubmittableExtrinsic<'promise', ISubmittableResult> =>
api.tx.phalaStakePoolv2.claimLegacyRewards(pid, targetAddress)
kind === 'legacy'
? api.tx.phalaStakePoolv2.claimLegacyRewards(pid, targetAddress)
: api.tx.phalaBasePool.claimReimbursement(pid, targetAddress)
const calls = []
if (pid !== undefined) {
calls.push(getExtrinsic(pid))
if (pid != null && kind != null) {
calls.push(getExtrinsic(pid, kind))
} else {
calls.push(
...data.filter((x) => !x.claimed).map(({pid}) => getExtrinsic(pid)),
...rowData
.filter((x) => !x.claimed)
.map(({pid, kind}) => getExtrinsic(pid, kind)),
)
}
setLoading(true)
Expand All @@ -149,6 +223,20 @@ const ClaimMissingDelegatorRewards: NextPage = () => {
})
}

if (chain !== 'khala') {
return (
<Stack
alignItems="center"
pt={4}
onClick={() => {
setChain('khala')
}}
>
<Button variant="contained">Switch to Khala</Button>
</Stack>
)
}

return (
<>
<PageHeader title="Claim Missing Delegator Rewards" />
Expand Down Expand Up @@ -210,16 +298,17 @@ const ClaimMissingDelegatorRewards: NextPage = () => {
setTargetAddress('')
setDialogOpen(true)
setPid(undefined)
setKind(undefined)
}}
>
Claim All
</Button>
</Stack>
)}

{data != null && data.length === 0 && <Empty sx={{mt: 6}} />}
{rowData != null && rowData.length === 0 && <Empty sx={{mt: 6}} />}

{account !== null && data == null && (
{account !== null && legacyRewardsData == null && (
<Box
display="flex"
alignItems="center"
Expand Down Expand Up @@ -247,7 +336,7 @@ const ClaimMissingDelegatorRewards: NextPage = () => {
</Box>
)}

{data != null && data.length > 0 && (
{rowData != null && rowData.length > 0 && (
<TableContainer
component={Paper}
variant="outlined"
Expand All @@ -256,17 +345,23 @@ const ClaimMissingDelegatorRewards: NextPage = () => {
<Table>
<TableHead>
<TableRow>
<TableCell width={200}>StakePool</TableCell>
<TableCell width={200}>Date</TableCell>
<TableCell width={200}>Vault/StakePool</TableCell>
<TableCell>Rewards</TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map((row) => (
{rowData.map((row) => (
<TableRow
key={row.pid}
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">
<Typography variant="body1">
{row.kind === 'legacy' ? '2023-01-13' : '2023-11-28'}
</Typography>
</TableCell>
<TableCell component="th" scope="row">
<Typography variant="body1">{`#${row.pid}`}</Typography>
</TableCell>
Expand All @@ -284,6 +379,7 @@ const ClaimMissingDelegatorRewards: NextPage = () => {
setTargetAddress('')
setPid(row.pid)
setDialogOpen(true)
setKind(row.kind)
}}
>
{row.claimed ? 'Claimed' : 'Claim'}
Expand Down
7 changes: 6 additions & 1 deletion packages/polkadot-types/interfaces/augment-api-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ declare module '@polkadot/api-base/types/errors' {
* Burn nft failed
**/
BurnNftFailed: AugmentedError<ApiType>;
DeprecatedTransferSharesAmountInvalid: AugmentedError<ApiType>;
/**
* Too long for pool description length
**/
Expand All @@ -813,6 +814,7 @@ declare module '@polkadot/api-base/types/errors' {
* Too many stakers in contribution whitelist that exceed the limit
**/
ExceedWhitelistMaxLen: AugmentedError<ApiType>;
InternalSubsidyPoolCannotWithdraw: AugmentedError<ApiType>;
/**
* Occurs when pool's shares is zero
**/
Expand All @@ -833,6 +835,10 @@ declare module '@polkadot/api-base/types/errors' {
* NftId does not match any nft
**/
NftIdNotFound: AugmentedError<ApiType>;
/**
* No reimbursement to claim
**/
NoReimbursementToClaim: AugmentedError<ApiType>;
/**
* Invalid staker to contribute because origin isn't in Pool's contribution whitelist.
**/
Expand Down Expand Up @@ -866,7 +872,6 @@ declare module '@polkadot/api-base/types/errors' {
* RMRK errors
**/
RmrkError: AugmentedError<ApiType>;
TransferSharesAmountInvalid: AugmentedError<ApiType>;
/**
* The caller is not the owner of the pool
**/
Expand Down
4 changes: 4 additions & 0 deletions packages/polkadot-types/interfaces/augment-api-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,10 @@ declare module '@polkadot/api-base/types/storage' {
* Mapping from pids to pools (including stake pools and vaults)
**/
pools: AugmentedQuery<ApiType, (arg: u64 | AnyNumber | Uint8Array) => Observable<Option<PhalaPalletsComputePoolProxy>>, [u64]> & QueryableStorageEntry<ApiType, [u64]>;
/**
* Claimable reimbursement due to previous on-chain issues.
**/
reimbursements: AugmentedQuery<ApiType, (arg: ITuple<[AccountId32, u64]> | [AccountId32 | string | Uint8Array, u64 | AnyNumber | Uint8Array]) => Observable<Option<u128>>, [ITuple<[AccountId32, u64]>]> & QueryableStorageEntry<ApiType, [ITuple<[AccountId32, u64]>]>;
/**
* Generic query
**/
Expand Down
Loading

0 comments on commit 4a49b10

Please sign in to comment.