Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: nomination pool fees #1178

Merged
merged 11 commits into from
Sep 18, 2024
4 changes: 3 additions & 1 deletion apps/portal/src/components/recipes/StakeForm/StakeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const PoolInfo = (props: PoolInfoProps) => {
>
<div css={{ display: 'flex', alignItems: 'center', gap: '0.8rem' }}>
<StakeStatusIndicator status={props.status} />
<Text.Body css={{ fontSize: '1.4rem' }} alpha={expanded ? 'high' : 'medium'}>
<Text.Body alpha={expanded ? 'high' : 'medium'} className="md:max-w-[31rem] md:truncate text-[1.4rem]">
{props.noPoolsAvailable ? 'No pools available' : props.name}
</Text.Body>
</div>
Expand Down Expand Up @@ -333,6 +333,7 @@ export type StakeFormProps = {
poolInfo: ReactNode
estimatedYield: ReactNode
claimPermission: ReactNode
commissionFee: ReactNode
stakeButton: ReactNode
existingPool: ReactNode
}
Expand Down Expand Up @@ -361,6 +362,7 @@ const StakeForm = Object.assign(
{props.amountInput}
{props.poolInfo}
{props.estimatedYield}
{props.commissionFee}
{props.claimPermission}
{props.stakeButton}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const StakeTargetSelectorDialog = Object.assign(
: []),
])}
</motion.div>
<div css={{ display: 'flex', justifyContent: 'end', marginTop: '0.6rem' }}>
<div css={{ display: 'flex', justifyContent: 'end', marginTop: '0.6rem', gap: '1rem' }}>
<Button variant="noop" onClick={() => setPage(page => page - 1)} hidden={!hasPreviousPage}>
<div css={{ display: 'flex', alignItems: 'center', userSelect: 'none' }}>
<ChevronLeft />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export type StakeTargetSelectorItemProps = {
balance: string
balancePlanck?: bigint
balanceDescription: string
commissionFeeDescription?: string
commissionFee?: string
estimatedReturn?: number | bigint
estimatedApr?: string
estimatedAprDescription?: string
Expand Down Expand Up @@ -106,17 +108,27 @@ const StakeTargetSelectorItem = (props: StakeTargetSelectorItemProps) => {
</div>
) : null}
</Tooltip>
<Tooltip content={props.estimatedAprDescription}>
{props.estimatedApr ? <div>{props.estimatedApr}</div> : null}
</Tooltip>
</div>
</div>
<Tooltip content={props.estimatedAprDescription}>
{props.estimatedApr ? <div>{props.estimatedApr}</div> : null}
</Tooltip>
{props.talismanRecommended && (
<Tooltip content={props.talismanRecommendedDescription}>
<TalismanHand size="1.4rem" />
</Tooltip>
)}
</Text.Body>
<Tooltip content={<div className="max-w-[276px]">{props.commissionFeeDescription}</div>}>
{props.commissionFee ? (
<div className="text-[14px] flex justify-between">
<div className="flex gap-2 items-center">
<Text.Body alpha={alpha}>Commission fee</Text.Body>
</div>
<Text.Body alpha={alpha}>{props.commissionFee}</Text.Body>
</div>
) : null}
</Tooltip>
</article>
)
}
Expand Down
67 changes: 51 additions & 16 deletions apps/portal/src/components/widgets/staking/substrate/StakeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ import PoolClaimPermissionDialog, {
toUiPermission,
} from './PoolClaimPermissionDialog'
import UnstakeDialog from './UnstakeDialog'
import { usePoolCommission } from '@/domains/staking/substrate/nominationPools/hooks/usePoolCommission'
import type { ApiPromise } from '@polkadot/api'
import { type Decimal } from '@talismn/math'
import { CircularProgressIndicator, Select } from '@talismn/ui'
import { Tooltip } from '@talismn/ui'
import { Info } from '@talismn/web-icons'
import BN from 'bn.js'
import {
Suspense,
Expand Down Expand Up @@ -164,6 +167,7 @@ const PoolSelector = (props: {
const [chain, recommendedPools, nativeTokenDecimal] = useRecoilValue(
waitForAll([useChainRecoilState(), useRecommendedPoolsState(), useNativeTokenDecimalState()])
)
const { getCurrentCommission } = usePoolCommission()

return (
<PoolSelectorDialog
Expand All @@ -180,22 +184,26 @@ const PoolSelector = (props: {
props.onDismiss()
}, [newPoolId, props])}
>
{recommendedPools.map((pool, index) => (
<PoolSelectorDialog.Item
key={pool.poolId}
selected={props.selectedPoolId !== undefined && pool.poolId === props.selectedPoolId}
highlighted={newPoolId !== undefined && pool.poolId === newPoolId}
talismanRecommended={index === 0}
name={pool.name ?? ''}
detailUrl={
chain?.subscanUrl ? new URL(`nomination_pool/${pool.poolId}`, chain.subscanUrl).toString() : undefined
}
balance={`${nativeTokenDecimal.fromPlanck(pool.bondedPool.points.toBigInt()).toLocaleString()} staked`}
rating={3}
count={pool.bondedPool.memberCounter.toString()}
onClick={() => setNewPoolId(pool.poolId)}
/>
))}
{recommendedPools.map((pool, index) => {
return (
<PoolSelectorDialog.Item
key={pool.poolId}
selected={props.selectedPoolId !== undefined && pool.poolId === props.selectedPoolId}
highlighted={newPoolId !== undefined && pool.poolId === newPoolId}
talismanRecommended={index === 0}
name={pool.name ?? ''}
detailUrl={
chain?.subscanUrl ? new URL(`nomination_pool/${pool.poolId}`, chain.subscanUrl).toString() : undefined
}
balance={`${nativeTokenDecimal.fromPlanck(pool.bondedPool.points.toBigInt()).toLocaleString()} staked`}
rating={3}
count={pool.bondedPool.memberCounter.toString()}
onClick={() => setNewPoolId(pool.poolId)}
commissionFeeDescription="Commission shown is only for the nomination pool, but actual earnings will reflect fees charged by both validators and nomination pools. The total amount of fees can change regularly and can't be determined by Talisman."
commissionFee={getCurrentCommission(pool.poolId).toString() + '%'}
/>
)
})}
</PoolSelectorDialog>
)
}
Expand Down Expand Up @@ -267,6 +275,32 @@ const DeferredEstimatedYield = (props: { amount: Decimal }) => (
<EstimatedYield amount={useDeferredValue(props.amount)} />
)

const CommissionFee = ({ poolId }: { poolId: number }) => {
const { getCurrentCommission } = usePoolCommission()

const poolCommission = getCurrentCommission(poolId)

return (
<div className="text-[14px] flex justify-between">
<div className="flex gap-2 items-center">
<div>Commission fee</div>
<Tooltip
content={
<div className="max-w-[276px] text-[12px]">
chidg marked this conversation as resolved.
Show resolved Hide resolved
Commission shown is only for the nomination pool, but actual earnings will reflect fees charged by both
validators and nomination pools. The total amount of fees can change regularly and can't be determined by
Talisman.
</div>
}
>
<Info size="1.4rem" />
</Tooltip>
</div>
<div>{`${poolCommission}%`}</div>
</div>
)
}

export const ControlledStakeForm = (props: { assetSelector: ReactNode; account?: string }) => {
const location = useLocation()

Expand Down Expand Up @@ -476,6 +510,7 @@ export const ControlledStakeForm = (props: { assetSelector: ReactNode; account?:
</Suspense>
)
}
commissionFee={<CommissionFee poolId={selectedPoolId || 0} />}
claimPermission={
<StakeFormComponent.ClaimPermission
permission={toUiPermission(claimPermission)}
Expand Down
4 changes: 2 additions & 2 deletions apps/portal/src/domains/chains/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const chainConfigs: ChainConfig[] = [
{
genesisHash: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
hasNominationPools: true,
priorityPool: 16,
talismanPools: [12, 16],
priorityPool: 282,
talismanPools: [12, 16, 282],
novaIndexerUrl: 'https://api.subquery.network/sq/nova-wallet/nova-wallet-polkadot',
},
// Kusama
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useRecommendedPoolsState } from '../recoils'
import type { AnyJson } from '@w3ux/types'
import { useRecoilValue } from 'recoil'

export type BondedPool = {
commission?: {
current?: AnyJson | null
max?: AnyJson | null
changeRate: {
maxIncrease: AnyJson
minDelay: AnyJson
} | null
throttleFrom?: AnyJson | null
}
}

export const usePoolCommission = () => {
const pools = useRecoilValue(useRecommendedPoolsState())

const getCurrentCommission = (poolId: number) => {
const pool: BondedPool | undefined = pools.find(pool => pool.poolId === poolId)?.bondedPool.toHuman()

return Number(pool?.commission?.current?.[0]?.slice(0, -1) || 0)
}

return { getCurrentCommission }
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@w3ux/types": "^0.2.0",
"eslint": "^9.2.0",
"husky": "^8.0.3",
"prettier": "^2.8.8",
Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12620,6 +12620,13 @@ __metadata:
languageName: node
linkType: hard

"@w3ux/types@npm:^0.2.0":
version: 0.2.0
resolution: "@w3ux/types@npm:0.2.0"
checksum: 10c0/dae057b008f110f99da6c97c6f43849708234fbabeb5fa7370a3f9908513227f420ba48c6183b421ec9f8c758cd2c98e45a8a380efe6491012341bcc08b3063b
languageName: node
linkType: hard

"@wagmi/connectors@npm:5.0.21":
version: 5.0.21
resolution: "@wagmi/connectors@npm:5.0.21"
Expand Down Expand Up @@ -25472,6 +25479,7 @@ __metadata:
"@commitlint/cli": "npm:^18.4.3"
"@commitlint/config-conventional": "npm:^18.4.3"
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
"@w3ux/types": "npm:^0.2.0"
eslint: "npm:^9.2.0"
husky: "npm:^8.0.3"
prettier: "npm:^2.8.8"
Expand Down
Loading