diff --git a/src/components/balance-input.tsx b/src/components/balance-input.tsx
index 9f8be37..5d87f61 100644
--- a/src/components/balance-input.tsx
+++ b/src/components/balance-input.tsx
@@ -2,11 +2,12 @@ import { formatBlanace } from "@/utils";
import Image from "next/image";
import InputLabel from "./input-label";
import { parseUnits } from "viem";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useMemo, useRef, useState } from "react";
export default function BalanceInput({
isReset,
balance,
+ max,
symbol,
decimals,
logoPath,
@@ -17,6 +18,7 @@ export default function BalanceInput({
}: {
isReset?: boolean;
balance: bigint;
+ max?: bigint;
symbol: string;
decimals: number;
logoPath?: string;
@@ -34,6 +36,14 @@ export default function BalanceInput({
}
}, [isReset]);
+ const placeholder = useMemo(() => {
+ if (typeof max === "bigint") {
+ return `Max: ${formatBlanace(max, decimals, { keepZero: false, precision: decimals })}`;
+ } else {
+ return `Balance: ${formatBlanace(balance, decimals, { keepZero: false, precision: decimals })}`;
+ }
+ }, [balance, decimals, max]);
+
return (
{label && }
@@ -43,11 +53,15 @@ export default function BalanceInput({
}`}
>
{
const _hasError = Number.isNaN(Number(e.target.value));
- setHasError(_hasError || balance < parseUnits(e.target.value, decimals));
+ setHasError(
+ _hasError ||
+ balance < parseUnits(e.target.value, decimals) ||
+ (typeof max === "bigint" && max < parseUnits(e.target.value, decimals))
+ );
if (!_hasError) {
onChange(parseUnits(e.target.value, decimals));
diff --git a/src/components/bond-more-deposit-modal.tsx b/src/components/bond-more-deposit-modal.tsx
index 0fa565d..47d5e0b 100644
--- a/src/components/bond-more-deposit-modal.tsx
+++ b/src/components/bond-more-deposit-modal.tsx
@@ -1,8 +1,8 @@
-import { Key, useCallback, useState } from "react";
+import { Key, useCallback, useEffect, useState } from "react";
import Modal from "./modal";
import CheckboxGroup from "./checkbox-group";
import { formatBlanace, getChainConfig, notifyTransaction } from "@/utils";
-import { useApp, useStaking } from "@/hooks";
+import { useApp, useDip6, useRateLimit, useStaking } from "@/hooks";
import { notification } from "./notification";
import { writeContract, waitForTransaction } from "@wagmi/core";
@@ -22,6 +22,14 @@ export default function BondMoreDepositModal({
const availableDeposits = deposits.filter(({ inUse }) => !inUse);
const { nativeToken } = getChainConfig(activeChain);
+ const { isDip6Implemented } = useDip6();
+ const { availableDeposit, updateRateLimit } = useRateLimit();
+ useEffect(() => {
+ if (isOpen) {
+ updateRateLimit();
+ }
+ }, [isOpen, updateRateLimit]);
+
const handleBond = useCallback(async () => {
setBusy(true);
const { contract, explorer } = getChainConfig(activeChain);
@@ -37,6 +45,7 @@ export default function BondMoreDepositModal({
if (receipt.status === "success") {
setCheckedDeposits([]);
+ updateRateLimit();
onClose();
}
notifyTransaction(receipt, explorer);
@@ -46,7 +55,7 @@ export default function BondMoreDepositModal({
}
setBusy(false);
- }, [activeChain, checkedDeposits, onClose]);
+ }, [activeChain, checkedDeposits, onClose, updateRateLimit]);
return (
{availableDeposits.length ? (
<>
+ {isDip6Implemented && (
+
+ Max in this session: {formatBlanace(availableDeposit, nativeToken.decimals, { keepZero: false })}{" "}
+ {nativeToken.symbol}
+
+ )}
({
value: id,
@@ -74,6 +89,7 @@ export default function BondMoreDepositModal({
})} ${nativeToken.symbol}`}
),
+ disabled: isDip6Implemented && availableDeposit < value,
}))}
checkedValues={checkedDeposits}
onChange={setCheckedDeposits as (values: Key[]) => void}
diff --git a/src/components/bond-more-kton-modal.tsx b/src/components/bond-more-kton-modal.tsx
index 29a50ca..6860b66 100644
--- a/src/components/bond-more-kton-modal.tsx
+++ b/src/components/bond-more-kton-modal.tsx
@@ -59,6 +59,7 @@ export default function BondMoreKtonModal({
symbol={ktonToken.symbol}
decimals={ktonToken.decimals}
balance={ktonBalance?.value || 0n}
+ max={0n}
busy={busy}
disabled={inputAmount <= 0n}
isReset={inputAmount <= 0}
diff --git a/src/components/bond-more-ring-modal.tsx b/src/components/bond-more-ring-modal.tsx
index 9ba3327..7cd1c4e 100644
--- a/src/components/bond-more-ring-modal.tsx
+++ b/src/components/bond-more-ring-modal.tsx
@@ -1,8 +1,8 @@
import { getChainConfig, notifyTransaction } from "@/utils";
import BondMoreTokenModal from "./bond-more-token-modal";
import { useAccount, useBalance } from "wagmi";
-import { useApp } from "@/hooks";
-import { useCallback, useState } from "react";
+import { useApp, useDip6, useRateLimit } from "@/hooks";
+import { useCallback, useEffect, useState } from "react";
import { notification } from "./notification";
import { writeContract, waitForTransaction } from "@wagmi/core";
@@ -22,6 +22,14 @@ export default function BondMoreRingModal({
const { nativeToken } = getChainConfig(activeChain);
+ const { isDip6Implemented } = useDip6();
+ const { availableDeposit, updateRateLimit } = useRateLimit();
+ useEffect(() => {
+ if (isOpen) {
+ updateRateLimit();
+ }
+ }, [isOpen, updateRateLimit]);
+
const handleBond = useCallback(async () => {
if ((ringBalance?.value || 0n) < inputAmount) {
notification.warn({ description: "Your balance is insufficient." });
@@ -40,6 +48,7 @@ export default function BondMoreRingModal({
if (receipt.status === "success") {
setInputAmount(0n);
+ updateRateLimit();
onClose();
}
notifyTransaction(receipt, explorer);
@@ -49,7 +58,7 @@ export default function BondMoreRingModal({
setBusy(false);
}
- }, [activeChain, inputAmount, ringBalance?.value, onClose]);
+ }, [activeChain, inputAmount, ringBalance?.value, onClose, updateRateLimit]);
return (
diff --git a/src/components/checkbox-group.tsx b/src/components/checkbox-group.tsx
index 2161b6b..ebddd6d 100644
--- a/src/components/checkbox-group.tsx
+++ b/src/components/checkbox-group.tsx
@@ -3,6 +3,7 @@ import { Key, ReactElement } from "react";
interface Props {
options: {
label: ReactElement;
+ disabled?: boolean;
value: Key;
}[];
checkedValues: Key[];
@@ -13,7 +14,7 @@ interface Props {
export default function CheckboxGroup({ options, checkedValues, className, onChange = () => undefined }: Props) {
return (
- {options.map(({ label, value }) => {
+ {options.map(({ label, value, disabled }) => {
const idx = checkedValues.findIndex((v) => v === value);
const checked = idx >= 0;
@@ -23,6 +24,7 @@ export default function CheckboxGroup({ options, checkedValues, className, onCha
className={`relative h-4 w-4 rounded-sm border transition hover:scale-105 active:scale-95 ${
checked ? "border-primary bg-primary" : "border-white bg-transparent"
}`}
+ disabled={disabled}
onClick={() => {
const checkeds = [...checkedValues];
if (checked) {
diff --git a/src/components/collator-select-modal.tsx b/src/components/collator-select-modal.tsx
index d42c7f4..5c2d3bf 100644
--- a/src/components/collator-select-modal.tsx
+++ b/src/components/collator-select-modal.tsx
@@ -5,10 +5,10 @@ import Image from "next/image";
import { useAccount } from "wagmi";
import Table, { ColumnType } from "./table";
import Jazzicon from "./jazzicon";
-import { prettyNumber } from "@/utils";
+import { formatBlanace, prettyNumber } from "@/utils";
import { notification } from "./notification";
import DisplayAccountName from "./display-account-name";
-import { useStaking } from "@/hooks";
+import { useDip6, useStaking } from "@/hooks";
import Tooltip from "./tooltip";
type TabKey = "active" | "waiting";
@@ -22,103 +22,101 @@ interface DataSource {
sessionKey: string | undefined;
}
-const columns: ColumnType
[] = [
- {
- key: "collator",
- dataIndex: "collator",
- width: "32%",
- title: Collator ,
- render: (row) => (
-
-
-
- {
- e.stopPropagation();
- try {
- await navigator.clipboard.writeText(row.collator);
- notification.success({
- title: "Copy address successfully",
- disabledCloseBtn: true,
- duration: 3000,
- });
- } catch (err) {
- console.error(err);
- }
- }}
- />
- {row.sessionKey ? null : (
-
-
-
- )}
-
- ),
- },
- {
- key: "power",
- dataIndex: "power",
- title: (
-
-
Total-staked
-
- {`The Collator's total-staked is a dynamic value, inversely proportional to the commission set by
- the Collator. Higher commission results in lower total-staked and vice versa. `}
-
- Learn More
-
-
- }
- enabledSafePolygon
- contentClassName="w-80"
- >
+function getColumns(activeTab: TabKey, isDip6Implemented: boolean) {
+ const columns: ColumnType[] = [
+ {
+ key: "collator",
+ dataIndex: "collator",
+ width: "32%",
+ title: Collator ,
+ render: (row) => (
+
+
+
{
+ e.stopPropagation();
+ try {
+ await navigator.clipboard.writeText(row.collator);
+ notification.success({
+ title: "Copy address successfully",
+ disabledCloseBtn: true,
+ duration: 3000,
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ }}
/>
-
-
- ),
- render: (row) => {prettyNumber(row.power)} ,
- },
- {
- key: "commission",
- dataIndex: "commission",
- width: "20%",
- title: Commission ,
- render: (row) => {row.commission} ,
- },
- {
- key: "blocks",
- dataIndex: "blocks",
- width: "20%",
- title: (
-
- Blocks
- Last session
-
- ),
- render: (row) => {row.blocks >= 0 ? row.blocks : "-"} ,
- },
-];
+ {row.sessionKey ? null : (
+
+
+
+ )}
+
+ ),
+ },
+ {
+ key: "power",
+ dataIndex: "power",
+ title: (
+
+ {activeTab === "active" ? "Vote" : "Total-staked"}
+ {activeTab === "active" && (
+ Vote = Total-staked * (1 - Commission)}
+ enabledSafePolygon
+ contentClassName="w-80"
+ >
+
+
+ )}
+
+ ),
+ render: (row) => {
+ if (activeTab === "active" && !isDip6Implemented) {
+ return {prettyNumber(row.power)} ;
+ }
+ return {formatBlanace(row.power, 18, { keepZero: false, precision: 0 })} ;
+ },
+ },
+ {
+ key: "commission",
+ dataIndex: "commission",
+ width: "20%",
+ title: Commission ,
+ render: (row) => {row.commission} ,
+ },
+ {
+ key: "blocks",
+ dataIndex: "blocks",
+ width: "20%",
+ title: (
+
+ Blocks
+ Last session
+
+ ),
+ render: (row) => {row.blocks >= 0 ? row.blocks : "-"} ,
+ },
+ ];
+
+ return columns;
+}
export default function CollatorSelectModal({
isOpen,
@@ -190,6 +188,8 @@ export default function CollatorSelectModal({
}
}, [address, nominatorCollators, isOpen]);
+ const { isDip6Implemented } = useDip6();
+
return (
{/* ring */}
- {row.bondedTokens.unbondingRing.length > 0 ? (
+ {row.bondedTokens.unbondingRing.length > 0 && !isDip6Implemented ? (
ringBusy ? (
) : (
@@ -131,7 +132,7 @@ export default function RecordsBondedTokens({ row }: { row: StakingRecordsDataSo
{/* deposit */}
- {row.bondedTokens.unbondingDeposits.length > 0 ? (
+ {row.bondedTokens.unbondingDeposits.length > 0 && !isDip6Implemented ? (
depositBusy ? (
) : (
@@ -160,7 +161,7 @@ export default function RecordsBondedTokens({ row }: { row: StakingRecordsDataSo
{/* kton */}
- {row.bondedTokens.unbondingKton.length > 0 ? (
+ {row.bondedTokens.unbondingKton.length > 0 && !isDip6Implemented ? (
ktonBusy ? (
) : (
diff --git a/src/components/unbond-deposit-modal.tsx b/src/components/unbond-deposit-modal.tsx
index 082b229..fead4f1 100644
--- a/src/components/unbond-deposit-modal.tsx
+++ b/src/components/unbond-deposit-modal.tsx
@@ -1,8 +1,8 @@
-import { Key, useCallback, useState } from "react";
+import { Key, useCallback, useEffect, useState } from "react";
import Modal from "./modal";
import CheckboxGroup from "./checkbox-group";
import { formatBlanace, getChainConfig, notifyTransaction } from "@/utils";
-import { useApp, useStaking } from "@/hooks";
+import { useApp, useDip6, useRateLimit, useStaking } from "@/hooks";
import { notification } from "./notification";
import { writeContract, waitForTransaction } from "@wagmi/core";
@@ -22,6 +22,14 @@ export default function UnbondDepositModal({
const availableDeposits = deposits.filter(({ id }) => stakedDeposits.includes(id));
const { nativeToken } = getChainConfig(activeChain);
+ const { isDip6Implemented } = useDip6();
+ const { availableWithdraw, updateRateLimit } = useRateLimit();
+ useEffect(() => {
+ if (isOpen) {
+ updateRateLimit();
+ }
+ }, [isOpen, updateRateLimit]);
+
const handleUnbond = useCallback(async () => {
setBusy(true);
const { contract, explorer } = getChainConfig(activeChain);
@@ -37,6 +45,7 @@ export default function UnbondDepositModal({
if (receipt.status === "success") {
setCheckedDeposits([]);
+ updateRateLimit();
onClose();
}
notifyTransaction(receipt, explorer);
@@ -46,7 +55,7 @@ export default function UnbondDepositModal({
}
setBusy(false);
- }, [activeChain, checkedDeposits, onClose]);
+ }, [activeChain, checkedDeposits, onClose, updateRateLimit]);
return (
{availableDeposits.length ? (
<>
+ {isDip6Implemented && (
+
+ Max in this session: {formatBlanace(availableWithdraw, nativeToken.decimals, { keepZero: false })}{" "}
+ {nativeToken.symbol}
+
+ )}
({
value: id,
@@ -74,6 +89,7 @@ export default function UnbondDepositModal({
})} ${nativeToken.symbol}`}
),
+ disabled: isDip6Implemented && availableWithdraw < value,
}))}
checkedValues={checkedDeposits}
onChange={setCheckedDeposits as (values: Key[]) => void}
diff --git a/src/components/unbond-ring-modal.tsx b/src/components/unbond-ring-modal.tsx
index dc5cdae..bf76aab 100644
--- a/src/components/unbond-ring-modal.tsx
+++ b/src/components/unbond-ring-modal.tsx
@@ -1,7 +1,7 @@
import { formatBlanace, getChainConfig, notifyTransaction } from "@/utils";
import UnbondTokenModal from "./unbond-token-modal";
-import { useApp, useStaking } from "@/hooks";
-import { useCallback, useState } from "react";
+import { useApp, useDip6, useRateLimit, useStaking } from "@/hooks";
+import { useCallback, useEffect, useState } from "react";
import { notification } from "./notification";
import { writeContract, waitForTransaction } from "@wagmi/core";
@@ -20,6 +20,14 @@ export default function UnbondRingModal({
const { nativeToken } = getChainConfig(activeChain);
+ const { isDip6Implemented } = useDip6();
+ const { availableWithdraw, updateRateLimit } = useRateLimit();
+ useEffect(() => {
+ if (isOpen) {
+ updateRateLimit();
+ }
+ }, [isOpen, updateRateLimit]);
+
const handleUnbond = useCallback(async () => {
if (stakedRing < inputAmount) {
notification.warn({
@@ -43,6 +51,7 @@ export default function UnbondRingModal({
if (receipt.status === "success") {
setInputAmount(0n);
+ updateRateLimit();
onClose();
}
notifyTransaction(receipt, explorer);
@@ -53,7 +62,7 @@ export default function UnbondRingModal({
setBusy(false);
}
- }, [activeChain, stakedRing, inputAmount, nativeToken, onClose]);
+ }, [activeChain, stakedRing, inputAmount, nativeToken, onClose, updateRateLimit]);
return (
void;
}) {
const isKton = symbol.endsWith("KTON");
+ const { isDip6Implemented } = useDip6();
return (
<>
-
+
This unbonding process will take 14 days to complete.
- {isKton && (
+ {(isKton || isDip6Implemented) && (
{`There is no longer a 14-day period for unbonding ${symbol}.`}
)}
@@ -55,6 +59,7 @@ export default function UnbondTokenModal({
decimals={decimals}
symbol={symbol}
balance={balance}
+ max={max}
isReset={isReset}
onChange={onChange}
/>
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index 36e2ca2..559d64f 100644
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -9,7 +9,8 @@ export * from "./use-nominator-collators";
export * from "./use-collator-nominators";
export * from "./use-collator-commission";
export * from "./use-collator-power";
-export * from "./use-pool";
export * from "./use-active-collators";
export * from "./use-collator-last-session-blocks";
export * from "./use-collators-session-key";
+export * from "./use-rate-limit";
+export * from "./use-dip6";
diff --git a/src/hooks/use-collator-power.ts b/src/hooks/use-collator-power.ts
index 2607db6..0dde9f7 100644
--- a/src/hooks/use-collator-power.ts
+++ b/src/hooks/use-collator-power.ts
@@ -2,7 +2,6 @@ import { useEffect, useState } from "react";
import { from, of, forkJoin, switchMap, Subscription } from "rxjs";
import { useApi } from "./use-api";
import { DarwiniaStakingLedger } from "@/types";
-import { commissionWeightedPower, stakingToPower } from "@/utils";
interface DepositJson {
id: number;
@@ -13,11 +12,6 @@ interface DepositJson {
}
interface ExposuresJson {
- nominators: { who: string; value: string }[];
- total: string;
-}
-
-interface ExposuresJsonCache {
nominators: { who: string; vote: string }[];
vote: string;
}
@@ -29,26 +23,8 @@ interface DefaultValue {
type ExposureCacheState = "Previous" | "Current" | "Next";
-function isExposuresJsonCache(data: any): data is ExposuresJsonCache {
- return data.vote;
-}
-
-function formatExposuresData(data: unknown) {
- if (isExposuresJsonCache(data)) {
- return {
- total: data.vote,
- nominators: data.nominators.map(({ who, vote }) => ({ who, value: vote })),
- } as ExposuresJson;
- } else {
- return data as ExposuresJson;
- }
-}
-
export const useCollatorPower = (
collatorNominators: { [collator: string]: string[] | undefined },
- collatorCommission: { [collator: string]: string | undefined },
- ringPool: bigint,
- ktonPool: bigint,
defaultValue: DefaultValue
) => {
const [collatorPower, setCollatorPower] = useState(defaultValue.collatorPower);
@@ -80,7 +56,7 @@ export const useCollatorPower = (
next: ([exposures, ledgers, deposits]) => {
const parsedExposures = exposures.reduce((acc, cur) => {
const address = (cur[0].toHuman() as string[])[0];
- const data = formatExposuresData(cur[1].toJSON() as unknown);
+ const data = cur[1].toJSON() as unknown as ExposuresJson;
return { ...acc, [address]: data };
}, {} as { [address: string]: ExposuresJson | undefined });
@@ -101,34 +77,31 @@ export const useCollatorPower = (
collators.reduce((acc, cur) => {
if (parsedExposures[cur]) {
// active collator
- return { ...acc, [cur]: BigInt(parsedExposures[cur]?.total || 0) };
+ return { ...acc, [cur]: BigInt(parsedExposures[cur]?.vote || 0) };
}
const nominators = collatorNominators[cur] || [];
- const { stakedDeposit, stakedRing, stakedKton } = nominators.reduce(
+ const { stakedDeposit, stakedRing } = nominators.reduce(
(acc, cur) => {
const ledger = parsedLedgers[cur];
const deposits = parsedDeposits[cur] || [];
if (ledger) {
const stakedDeposit = deposits
- .filter(({ id }) => ledger.stakedDeposits?.includes(id))
+ .filter(({ id }) => (ledger.stakedDeposits || ledger.deposits)?.includes(id))
.reduce((acc, cur) => acc + BigInt(cur.value), 0n);
return {
stakedDeposit: acc.stakedDeposit + stakedDeposit,
- stakedRing: acc.stakedRing + BigInt(ledger.stakedRing),
- stakedKton: acc.stakedKton + BigInt(ledger.stakedKton),
+ stakedRing: acc.stakedRing + BigInt(ledger.stakedRing ?? ledger.ring ?? 0n),
};
}
return acc;
},
- { stakedDeposit: 0n, stakedRing: 0n, stakedKton: 0n }
+ { stakedDeposit: 0n, stakedRing: 0n }
);
- const power = stakingToPower(stakedRing + stakedDeposit, stakedKton, ringPool, ktonPool);
- const commission = collatorCommission[cur] || "0.00%";
- return { ...acc, [cur]: commissionWeightedPower(power, commission) };
+ return { ...acc, [cur]: stakedRing + stakedDeposit };
}, {} as { [collator: string]: bigint | undefined })
);
},
@@ -140,7 +113,7 @@ export const useCollatorPower = (
}
return () => sub$$?.unsubscribe();
- }, [polkadotApi, collatorNominators, collatorCommission, ringPool, ktonPool]);
+ }, [polkadotApi, collatorNominators]);
return { collatorPower, isCollatorPowerInitialized };
};
diff --git a/src/hooks/use-dip6.ts b/src/hooks/use-dip6.ts
new file mode 100644
index 0000000..efbf7f6
--- /dev/null
+++ b/src/hooks/use-dip6.ts
@@ -0,0 +1,16 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useApi } from "./use-api";
+import { isFunction } from "@polkadot/util";
+
+export function useDip6() {
+ const [isDip6Implemented, setIsDip6Implemented] = useState(false);
+ const { polkadotApi } = useApi();
+
+ useEffect(() => {
+ setIsDip6Implemented(isFunction(polkadotApi?.query.darwiniaStaking?.rateLimit));
+ }, [polkadotApi?.query.darwiniaStaking?.rateLimit]);
+
+ return { isDip6Implemented };
+}
diff --git a/src/hooks/use-ledger.ts b/src/hooks/use-ledger.ts
index b1a618e..c70cfb7 100644
--- a/src/hooks/use-ledger.ts
+++ b/src/hooks/use-ledger.ts
@@ -87,13 +87,13 @@ export const useLedger = (deposits: Deposit[], defaultValue: DefaultValue) => {
setStakedDeposit(
deposits
- .filter(({ id }) => ledgerData.stakedDeposits?.includes(id))
+ .filter(({ id }) => (ledgerData.stakedDeposits || ledgerData.deposits)?.includes(id))
.reduce((acc, cur) => acc + cur.value, 0n)
);
- setStakedDeposits(ledgerData.stakedDeposits || []);
+ setStakedDeposits(ledgerData.stakedDeposits || ledgerData.deposits || []);
- setStakedRing(BigInt(ledgerData.stakedRing));
- setStakedKton(BigInt(ledgerData.stakedKton));
+ setStakedRing(BigInt(ledgerData.stakedRing ?? ledgerData.ring ?? 0));
+ setStakedKton(BigInt(ledgerData.stakedKton ?? 0));
setUnbondingRing(_unbondingRing);
setUnbondingKton(_unbondingKton);
diff --git a/src/hooks/use-pool.ts b/src/hooks/use-pool.ts
deleted file mode 100644
index 93e1d91..0000000
--- a/src/hooks/use-pool.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { useEffect, useState } from "react";
-import { useApi } from "./use-api";
-import type { Balance } from "@polkadot/types/interfaces";
-
-interface DefaultValue {
- ringPool: bigint;
- ktonPool: bigint;
- isRingPoolInitialized: boolean;
- isKtonPoolInitialized: boolean;
-}
-
-export const usePool = (defaultValue: DefaultValue) => {
- const [ringPool, setRingPool] = useState(defaultValue.ringPool);
- const [ktonPool, setKtonPool] = useState(defaultValue.ktonPool);
- const [isRingPoolInitialized, setIsRingPoolInitialized] = useState(false);
- const [isKtonPoolInitialized, setIsKtonPoolInitialized] = useState(false);
- const { polkadotApi } = useApi();
-
- // ring pool
- useEffect(() => {
- let unsub = () => undefined;
-
- polkadotApi?.query.darwiniaStaking
- .ringPool((value: Balance) => setRingPool(value.toBigInt()))
- .then((_unsub) => {
- unsub = _unsub as unknown as typeof unsub;
- })
- .catch(console.error)
- .finally(() => setIsRingPoolInitialized(true));
-
- return () => unsub();
- }, [polkadotApi]);
-
- // kton pool
- useEffect(() => {
- let unsub = () => undefined;
-
- polkadotApi?.query.darwiniaStaking
- .ktonPool((value: Balance) => setKtonPool(value.toBigInt()))
- .then((_unsub) => {
- unsub = _unsub as unknown as typeof unsub;
- })
- .catch(console.error)
- .finally(() => setIsKtonPoolInitialized(true));
-
- return () => unsub();
- }, [polkadotApi]);
-
- return { ringPool, ktonPool, isRingPoolInitialized, isKtonPoolInitialized };
-};
diff --git a/src/hooks/use-rate-limit.ts b/src/hooks/use-rate-limit.ts
new file mode 100644
index 0000000..f0c8fab
--- /dev/null
+++ b/src/hooks/use-rate-limit.ts
@@ -0,0 +1,59 @@
+"use client";
+
+import { useCallback, useEffect, useState } from "react";
+import { useApi } from "./use-api";
+import { isFunction } from "@polkadot/util";
+import { forkJoin } from "rxjs";
+import type { u128 } from "@polkadot/types";
+import { DarwiniaStakingRateLimiter } from "@/types";
+
+const defaultValue = BigInt(Number.MAX_SAFE_INTEGER) * 10n ** 18n; // Token decimals: 18
+
+export function useRateLimit() {
+ const [availableWithdraw, setAvailableWithdraw] = useState(defaultValue);
+ const [availableDeposit, setAvailableDeposit] = useState(defaultValue);
+ const { polkadotApi } = useApi();
+
+ const updateRateLimit = useCallback(() => {
+ if (
+ isFunction(polkadotApi?.query.darwiniaStaking?.rateLimitState) &&
+ isFunction(polkadotApi?.query.darwiniaStaking?.rateLimit)
+ ) {
+ return forkJoin([
+ polkadotApi?.query.darwiniaStaking.rateLimitState() as unknown as Promise,
+ polkadotApi?.query.darwiniaStaking.rateLimit() as Promise,
+ ]).subscribe({
+ next: ([rls, rl]) => {
+ const limit = rl.toBigInt();
+ if (rls.isPos) {
+ const pos = rls.asPos.toBigInt();
+ setAvailableWithdraw(limit + pos);
+ setAvailableDeposit(limit - pos);
+ } else {
+ const neg = rls.asNeg.toBigInt();
+ setAvailableWithdraw(limit - neg);
+ setAvailableDeposit(limit + neg);
+ }
+ },
+ error: (err) => {
+ console.error(err);
+
+ setAvailableWithdraw(0n);
+ setAvailableDeposit(0n);
+ },
+ });
+ } else {
+ setAvailableWithdraw(defaultValue);
+ setAvailableDeposit(defaultValue);
+ }
+ }, [polkadotApi?.query.darwiniaStaking]);
+
+ useEffect(() => {
+ const sub$$ = updateRateLimit();
+ return () => {
+ sub$$?.unsubscribe();
+ };
+ }, [updateRateLimit]);
+
+ return { availableWithdraw, availableDeposit, updateRateLimit };
+}
diff --git a/src/providers/staking-provider.tsx b/src/providers/staking-provider.tsx
index 9a355e4..795eb7d 100644
--- a/src/providers/staking-provider.tsx
+++ b/src/providers/staking-provider.tsx
@@ -11,9 +11,7 @@ import {
useDeposits,
useLedger,
useNominatorCollators,
- usePool,
} from "@/hooks";
-import { stakingToPower } from "@/utils";
import { PropsWithChildren, createContext, useCallback, useEffect, useMemo, useState } from "react";
import type { Deposit, UnbondingInfo } from "@/types";
@@ -21,8 +19,6 @@ interface StakingCtx {
deposits: Deposit[];
stakedDeposits: number[];
power: bigint;
- ringPool: bigint;
- ktonPool: bigint;
stakedRing: bigint;
stakedKton: bigint;
stakedDeposit: bigint;
@@ -41,8 +37,6 @@ interface StakingCtx {
isLedgersInitialized: boolean;
isDepositsInitialized: boolean;
- isRingPoolInitialized: boolean;
- isKtonPoolInitialized: boolean;
isActiveCollatorsInitialized: boolean;
isCollatorPowerInitialized: boolean;
isCollatorSessionKeyInitialized: boolean;
@@ -63,8 +57,6 @@ const defaultValue: StakingCtx = {
deposits: [],
stakedDeposits: [],
power: 0n,
- ringPool: 0n,
- ktonPool: 0n,
stakedRing: 0n,
stakedKton: 0n,
stakedDeposit: 0n,
@@ -83,8 +75,6 @@ const defaultValue: StakingCtx = {
isLedgersInitialized: false,
isDepositsInitialized: false,
- isRingPoolInitialized: false,
- isKtonPoolInitialized: false,
isActiveCollatorsInitialized: false,
isCollatorPowerInitialized: false,
isCollatorSessionKeyInitialized: false,
@@ -110,7 +100,6 @@ export function StakingProvider({ children }: PropsWithChildren) {
const { collatorLastSessionBlocks, isCollatorLastSessionBlocksInitialized } =
useCollatorLastSessionBlocks(defaultValue);
- const { ringPool, ktonPool, isRingPoolInitialized, isKtonPoolInitialized } = usePool(defaultValue);
const { activeCollators, isActiveCollatorsInitialized } = useActiveCollators(defaultValue);
const { deposits, isDepositsInitialized } = useDeposits(defaultValue);
const {
@@ -129,25 +118,11 @@ export function StakingProvider({ children }: PropsWithChildren) {
const { nominatorCollators, isNominatorCollatorsInitialized, isNominatorCollatorsLoading, updateNominatorCollators } =
useNominatorCollators(defaultValue);
const { collatorNominators, isCollatorNominatorsInitialized } = useCollatorNominators(defaultValue);
- const { collatorPower, isCollatorPowerInitialized } = useCollatorPower(
- collatorNominators,
- collatorCommission,
- ringPool,
- ktonPool,
- defaultValue
- );
+ const { collatorPower, isCollatorPowerInitialized } = useCollatorPower(collatorNominators, defaultValue);
- const power = useMemo(
- () => stakingToPower(stakedRing + stakedDeposit, stakedKton, ringPool, ktonPool),
- [stakedRing, stakedKton, ringPool, ktonPool, stakedDeposit]
- );
+ const power = useMemo(() => stakedRing + stakedDeposit, [stakedRing, stakedDeposit]);
- const calcExtraPower = useCallback(
- (stakingRing: bigint, stakingKton: bigint) =>
- stakingToPower(stakingRing, stakingKton, ringPool + stakingRing, ktonPool + stakingKton) -
- stakingToPower(0n, 0n, ringPool, ktonPool),
- [ringPool, ktonPool]
- );
+ const calcExtraPower = useCallback((stakingRing: bigint, stakingKton: bigint) => stakingRing + stakingKton, []);
useEffect(() => {
setMinimumDeposit(BigInt(polkadotApi?.consts.deposit.minLockingAmount.toString() || 0));
@@ -160,8 +135,6 @@ export function StakingProvider({ children }: PropsWithChildren) {
power,
deposits,
stakedDeposits,
- ringPool,
- ktonPool,
stakedRing,
stakedKton,
stakedDeposit,
@@ -180,8 +153,6 @@ export function StakingProvider({ children }: PropsWithChildren) {
isLedgersInitialized,
isDepositsInitialized,
- isRingPoolInitialized,
- isKtonPoolInitialized,
isActiveCollatorsInitialized,
isCollatorPowerInitialized,
isCollatorSessionKeyInitialized,
diff --git a/src/types/staking.ts b/src/types/staking.ts
index 6fd38b9..bdb5226 100644
--- a/src/types/staking.ts
+++ b/src/types/staking.ts
@@ -1,13 +1,16 @@
-import type { Struct, u16, u128, bool } from "@polkadot/types-codec";
+import type { Struct, Enum, u16, u128, bool } from "@polkadot/types-codec";
import type { Balance } from "@polkadot/types/interfaces";
export interface DarwiniaStakingLedger extends Struct {
- stakedRing: string;
- stakedKton: string;
+ stakedRing?: string;
+ stakedKton?: string;
stakedDeposits?: number[];
unstakingDeposits?: [number, number][];
unstakingRing?: [number, number][];
unstakingKton?: [number, number][];
+
+ ring?: string;
+ deposits?: number[];
}
export interface DepositCodec extends Struct {
@@ -36,3 +39,10 @@ export interface UnbondingInfo {
expiredTimestamp: number; // millisecond
isExpired: boolean;
}
+
+export interface DarwiniaStakingRateLimiter extends Enum {
+ readonly isPos: boolean;
+ readonly isNeg: boolean;
+ readonly asPos: Balance;
+ readonly asNeg: Balance;
+}