Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions apps/main/src/api/locks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { useAccount } from "@galacticcouncil/web3-connect"
import { useQuery } from "@tanstack/react-query"

import { TokenLockType, useNativeTokenLocks } from "@/api/balances"
import { bestNumberQuery } from "@/api/chain"
import {
accountUnlockClassesQuery,
openGovUnlockedTokensQuery,
TUnlockableVote,
} from "@/api/democracy"
import {
gigaStakeConstantsQuery,
gigaUnstakePositionsQuery,
} from "@/api/gigaStake"
import { useProxyUrl } from "@/api/provider"
import { useDisplayAssetPrice } from "@/components/AssetPrice"
import { useAssets } from "@/providers/assetsProvider"
import { useRpcProvider } from "@/providers/rpcProvider"
import { PARACHAIN_BLOCK_TIME } from "@/utils/consts"
import { scaleHuman } from "@/utils/formatting"

export const useUnlockableNativeTokens = () => {
const { account } = useAccount()
const rpc = useRpcProvider()
const { native } = useAssets()
const indexerUrl = useProxyUrl()
const address = account?.address ?? ""
const { data: locks, isSuccess } = useNativeTokenLocks()

const referendaLock = locks?.get(TokenLockType.OpenGov) ?? 0n
const gigaLock = locks?.get(TokenLockType.GigaStaking) ?? 0n

const { data, isLoading } = useQuery({
enabled: rpc.isApiLoaded && !!address && isSuccess,
queryKey: [
"unlockable-native-tokens",
address,
referendaLock.toString(),
gigaLock.toString(),
],
queryFn: async () => {
let referendaUnlockable = 0n
let votesToRemove: TUnlockableVote[] = []
let lockedReferendaSeconds = 0
let classIds: number[] = []

if (referendaLock > 0n) {
const unlockedTokens = await rpc.queryClient.ensureQueryData(
openGovUnlockedTokensQuery(rpc, address, indexerUrl),
)

const stillLockedByVotes = BigInt(unlockedTokens.maxLockedValue)

referendaUnlockable =
referendaLock > stillLockedByVotes
? referendaLock - stillLockedByVotes
: 0n

votesToRemove = unlockedTokens.votesToRemove
lockedReferendaSeconds =
(unlockedTokens.maxLockedBlock ?? 0) * PARACHAIN_BLOCK_TIME

classIds = await rpc.queryClient.ensureQueryData(
accountUnlockClassesQuery(rpc, address),
)
}

let gigaUnlockable = 0n
let unlockableGigaPendingPositions: Array<{
voteAtBlock: number
amount: bigint
}> = []

if (gigaLock > 0n) {
const [pendingPositions, bestNumber, gigaStakeConstants] =
await Promise.all([
rpc.queryClient.ensureQueryData(
gigaUnstakePositionsQuery(rpc, address),
),
rpc.queryClient.ensureQueryData(bestNumberQuery(rpc)),
rpc.queryClient.ensureQueryData(gigaStakeConstantsQuery(rpc)),
])

unlockableGigaPendingPositions = pendingPositions.filter(
(position) =>
position.voteAtBlock + gigaStakeConstants.cooldownPeriod <
bestNumber.parachainBlockNumber,
)

gigaUnlockable = unlockableGigaPendingPositions.reduce(
(acc, position) => acc + position.amount,
0n,
)
}

const effectiveLockBefore =
referendaLock > gigaLock ? referendaLock : gigaLock
const referendaLockAfter = referendaLock - referendaUnlockable
const gigaLockAfter = gigaLock - gigaUnlockable
const effectiveLockAfter =
referendaLockAfter > gigaLockAfter ? referendaLockAfter : gigaLockAfter
const maxUnlockable = effectiveLockBefore - effectiveLockAfter

return {
maxUnlockable: scaleHuman(maxUnlockable.toString(), native.decimals),
lockedReferendaSeconds,
unlockableGigaPendingPositions,
votesToRemove,
classIds,
}
},
})

const [displayMaxUnlockable] = useDisplayAssetPrice(
native.id,
data?.maxUnlockable ?? "0",
)

return {
maxUnlockable: data?.maxUnlockable ?? "0",
displayMaxUnlockable,
lockedReferendaSeconds: data?.lockedReferendaSeconds ?? 0,
unlockableGigaPendingPositions: data?.unlockableGigaPendingPositions ?? [],
votesToRemove: data?.votesToRemove ?? [],
classIds: data?.classIds ?? [],
isLoading,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ import { FC } from "react"
import { useTranslation } from "react-i18next"

import { TokenReserveType, useAccountTokenReserves } from "@/api/balances"
import { useUnlockableNativeTokens } from "@/api/locks"
import { SAssetDetailMobileSeparator } from "@/modules/wallet/assets/MyAssets/AssetDetailNativeMobileModal.styled"
import { AssetDetailUnlock } from "@/modules/wallet/assets/MyAssets/AssetDetailUnlock"
import {
useNativeAssetLocks,
useUnlockableNativeTokens,
} from "@/modules/wallet/assets/MyAssets/ExpandedNativeRow.data"
import { useNativeAssetLocks } from "@/modules/wallet/assets/MyAssets/ExpandedNativeRow.data"
import { FullExpiration } from "@/modules/wallet/assets/MyAssets/FullExpiration"
import { MyAsset } from "@/modules/wallet/assets/MyAssets/MyAssetsTable.columns"
import { useRpcProvider } from "@/providers/rpcProvider"
Expand All @@ -37,15 +35,16 @@ export const AssetDetailNativeMobileModalBalances: FC<Props> = ({ asset }) => {
const { account } = useAccount()

const locks = useNativeAssetLocks()
const unlockable = useUnlockableNativeTokens(locks.lockedInOpenGov)
const { data: reserves } = useAccountTokenReserves(asset.id, !!asset.reserved)
const unlockable = useUnlockableNativeTokens()
const { data: reserves } = useAccountTokenReserves(asset.id)

const { data: identity } = useQuery({
...getIdentityQuery(rpc.papi, account?.address ?? ""),
enabled: !!account?.address && !!asset.reserved,
})

const identityReserves = identity?.deposit ?? 0n

const dca = reserves?.get(TokenReserveType.DCA) ?? 0n
const otc = reserves?.get(TokenReserveType.OTC) ?? 0n
const xcm = reserves?.get(TokenReserveType.XCM) ?? 0n
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,8 @@
import { useAccount } from "@galacticcouncil/web3-connect"
import { useQuery } from "@tanstack/react-query"
import Big from "big.js"

import { TokenLockType, useNativeTokenLocks } from "@/api/balances"
import { bestNumberQuery } from "@/api/chain"
import {
accountUnlockClassesQuery,
openGovUnlockedTokensQuery,
} from "@/api/democracy"
import {
gigaStakeConstantsQuery,
gigaUnstakePositionsQuery,
} from "@/api/gigaStake"
import { useProxyUrl } from "@/api/provider"
import { useDisplayAssetPrice } from "@/components/AssetPrice"
import { useAssets } from "@/providers/assetsProvider"
import { useRpcProvider } from "@/providers/rpcProvider"
import { useAssetPrice } from "@/states/displayAsset"
import { PARACHAIN_BLOCK_TIME } from "@/utils/consts"
import { scaleHuman } from "@/utils/formatting"

export const useNativeAssetLocks = () => {
Expand Down Expand Up @@ -71,71 +56,3 @@ export const useNativeAssetLocks = () => {
isLoading,
}
}

export const useUnlockableNativeTokens = (lockedInReferenda: string) => {
const rpc = useRpcProvider()
const { account } = useAccount()
const { native } = useAssets()
const indexerUrl = useProxyUrl()

const { data: bestNumber } = useQuery(bestNumberQuery(rpc))
const { data: gigaStakeConstants, isLoading: gigaStakeConstantsLoading } =
useQuery(gigaStakeConstantsQuery(rpc))

const cooldownPeriod = gigaStakeConstants?.cooldownPeriod ?? 0
const parachainBlockNumber = bestNumber?.parachainBlockNumber ?? 0

const { data: unlockedTokens, isLoading: unlockedTokensLoading } = useQuery(
openGovUnlockedTokensQuery(rpc, account?.address ?? "", indexerUrl),
)

const { data: unlockClasses, isLoading: unlockClassesLoading } = useQuery(
accountUnlockClassesQuery(rpc, account?.address ?? ""),
)

const { data: pendingPositions = [], isLoading: pendingPositionsLoading } =
useQuery(gigaUnstakePositionsQuery(rpc, account?.address ?? ""))

const lockedInReferendaBig = new Big(lockedInReferenda)

const maxLocked = scaleHuman(
unlockedTokens?.maxLockedValue ?? "0",
native.decimals,
)

const unlockableReferendaLocks = lockedInReferendaBig.lte(0)
? "0"
: lockedInReferendaBig.minus(maxLocked).toString()

const unlockableGigaPendingPositions = pendingPositions.filter(
(position) => position.voteAtBlock + cooldownPeriod < parachainBlockNumber,
)

const unlockableGigaStakingLocks = unlockableGigaPendingPositions
.reduce((acc, position) => acc + position.amount, 0n)
.toString()

const lockedReferendaSeconds =
(unlockedTokens?.maxLockedBlock ?? 0) * PARACHAIN_BLOCK_TIME

const maxUnlockable = Big.min(
unlockableReferendaLocks,
unlockableGigaStakingLocks,
).toString()

const [displayMaxUnlockable] = useDisplayAssetPrice(native.id, maxUnlockable)

return {
maxUnlockable,
displayMaxUnlockable,
lockedReferendaSeconds: lockedReferendaSeconds,
unlockableGigaPendingPositions,
votesToRemove: unlockedTokens?.votesToRemove ?? [],
classIds: unlockClasses ?? [],
isLoading:
unlockedTokensLoading ||
pendingPositionsLoading ||
gigaStakeConstantsLoading ||
unlockClassesLoading,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ import { FC } from "react"
import { useTranslation } from "react-i18next"

import { TokenReserveType, useAccountTokenReserves } from "@/api/balances"
import { useUnlockableNativeTokens } from "@/api/locks"
import { AssetDetailUnlock } from "@/modules/wallet/assets/MyAssets/AssetDetailUnlock"
import {
useNativeAssetLocks,
useUnlockableNativeTokens,
} from "@/modules/wallet/assets/MyAssets/ExpandedNativeRow.data"
import { useNativeAssetLocks } from "@/modules/wallet/assets/MyAssets/ExpandedNativeRow.data"
import { ExpandedRowSeparator } from "@/modules/wallet/assets/MyAssets/ExpandedRowSeparator"
import { MyAsset } from "@/modules/wallet/assets/MyAssets/MyAssetsTable.columns"
import { useRpcProvider } from "@/providers/rpcProvider"
Expand All @@ -35,16 +33,17 @@ export const ExpandedNativeRow: FC<Props> = ({ asset }) => {
const { account } = useAccount()

const locks = useNativeAssetLocks()
const unlockable = useUnlockableNativeTokens(locks.lockedInOpenGov)
const { data: reserves } = useAccountTokenReserves(asset.id, !!asset.reserved)
const unlockable = useUnlockableNativeTokens()

const { data: reserves } = useAccountTokenReserves(asset.id)
const xcm = reserves?.get(TokenReserveType.XCM) ?? 0n

const { data: identity } = useQuery({
...getIdentityQuery(rpc.papi, account?.address ?? ""),
enabled: !!account?.address && !!asset.reserved,
})

const identityReserves = identity?.deposit ?? 0n
const xcm = reserves?.get(TokenReserveType.XCM) ?? 0n
const { price: assetPrice } = useAssetPrice(asset.id)

const dca = reserves?.get(TokenReserveType.DCA) ?? 0n
Expand Down
Loading