Skip to content

Commit 80431ac

Browse files
authored
chore(tangle-dapp): Display TNT restake vault and fix transaction loading states (#3050)
1 parent 927ab19 commit 80431ac

File tree

24 files changed

+1111
-321
lines changed

24 files changed

+1111
-321
lines changed

apps/tangle-dapp/src/components/tables/Vaults/VaultsTable.tsx

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { BN_ZERO } from '@polkadot/util';
22
import { TokenIcon } from '@tangle-network/icons';
33
import { ChevronDown } from '@tangle-network/icons/ChevronDown';
44
import Spinner from '@tangle-network/icons/Spinner';
5-
import LsTokenIcon from '@tangle-network/tangle-shared-ui/components/LsTokenIcon';
5+
66
import HeaderCell from '@tangle-network/tangle-shared-ui/components/tables/HeaderCell';
77
import TableCellWrapper from '@tangle-network/tangle-shared-ui/components/tables/TableCellWrapper';
88
import TableStatus from '@tangle-network/tangle-shared-ui/components/tables/TableStatus';
@@ -48,6 +48,8 @@ const COLUMNS = [
4848
COLUMN_HELPER.accessor('id', {
4949
header: () => <HeaderCell title="Vault" />,
5050
cell: (props) => {
51+
const isNativeToken = props.row.original.isNativeToken;
52+
5153
return (
5254
<TableCellWrapper className="pl-3 flex items-center gap-2 justify-start">
5355
{props.row.original.logo ? (
@@ -58,7 +60,7 @@ const COLUMNS = [
5860
className="w-10 h-10"
5961
/>
6062
) : (
61-
<LsTokenIcon
63+
<TokenIcon
6264
name={props.row.original.representAssetSymbol}
6365
size="lg"
6466
/>
@@ -72,21 +74,29 @@ const COLUMNS = [
7274
variant="body3"
7375
className="text-mono-120 dark:text-mono-100"
7476
>
75-
ID: #{props.getValue()}
77+
{isNativeToken ? '' : `ID: #${props.getValue()}`}
7678
</Typography>
7779
</div>
7880
</TableCellWrapper>
7981
);
8082
},
81-
sortingFn: 'alphanumericCaseSensitive',
82-
sortDescFirst: true,
83+
sortingFn: (rowA, rowB) => {
84+
const aIsNative = rowA.original.isNativeToken;
85+
const bIsNative = rowB.original.isNativeToken;
86+
87+
if (aIsNative && !bIsNative) return -1;
88+
if (!aIsNative && bIsNative) return 1;
89+
90+
return String(rowA.original.id).localeCompare(String(rowB.original.id));
91+
},
92+
sortDescFirst: false,
8393
}),
8494
COLUMN_HELPER.accessor('totalDeposits', {
8595
sortingFn: sortByBnToDecimal(
8696
(row) => row.totalDeposits ?? BN_ZERO,
8797
(row) => row.decimals,
8898
),
89-
header: () => <HeaderCell title="Deposited Balance" />,
99+
header: () => <HeaderCell title="Deposited" />,
90100
cell: (props) => {
91101
const value = props.getValue();
92102

@@ -102,6 +112,27 @@ const COLUMNS = [
102112
return <TableCellWrapper>{fmtDeposits}</TableCellWrapper>;
103113
},
104114
}),
115+
COLUMN_HELPER.accessor('totalDelegated', {
116+
sortingFn: sortByBnToDecimal(
117+
(row) => row.totalDelegated,
118+
(row) => row.decimals,
119+
),
120+
header: () => <HeaderCell title="Delegated" />,
121+
cell: (props) => {
122+
const value = props.getValue();
123+
124+
const fmtDelegated =
125+
value === undefined
126+
? 0
127+
: formatDisplayAmount(
128+
value,
129+
props.row.original.decimals,
130+
AmountFormatStyle.SHORT,
131+
);
132+
133+
return <TableCellWrapper>{fmtDelegated}</TableCellWrapper>;
134+
},
135+
}),
105136
COLUMN_HELPER.accessor('tvl', {
106137
sortingFn: sortByBnToDecimal(
107138
(row) => row.tvl,
@@ -153,9 +184,11 @@ const COLUMNS = [
153184
)}
154185

155186
<Typography variant="body1" className="dark:text-mono-0">
156-
{fmtTvl === null
157-
? `${fmtDepositCap}`
158-
: `${fmtTvl}/${fmtDepositCap}`}
187+
{props.row.original.isNativeToken
188+
? fmtTvl
189+
: fmtTvl === null
190+
? `${fmtDepositCap}`
191+
: `${fmtTvl}/${fmtDepositCap}`}
159192
</Typography>
160193
</div>
161194
</TableCellWrapper>
@@ -234,8 +267,8 @@ export const VaultsTable: FC<VaultsTableProps> = ({
234267
columns: COLUMNS,
235268
initialState: {
236269
sorting: [
237-
{ id: 'totalDeposits', desc: true },
238270
{ id: 'id', desc: false },
271+
{ id: 'totalDeposits', desc: true },
239272
],
240273
},
241274
getCoreRowModel: getCoreRowModel(),

apps/tangle-dapp/src/components/tables/Vaults/useVaultsTableProps.tsx

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,62 @@ export const useVaultsTableProps = ({ delegatorDeposits, assets }: Options) => {
3737
}
3838

3939
const vaultId = row.original.id;
40+
const isNativeToken = row.original.isNativeToken;
4041

41-
const vaultAssets = Array.from(assets.values())
42-
.filter((asset) => asset.metadata.vaultId === vaultId)
43-
.map(
44-
({
45-
id: assetId,
46-
metadata: { decimals, symbol, name },
47-
balance,
48-
}) => {
49-
const available = balance ?? null;
42+
let vaultAssets: VaultAssetData[];
5043

51-
const deposited =
52-
typeof delegatorDeposits?.[assetId]?.amount === 'bigint'
53-
? new BN(delegatorDeposits[assetId].amount.toString())
54-
: null;
44+
if (isNativeToken) {
45+
// Special handling for TNT since TNT vault is not created.
46+
const NATIVE_ASSET_ID = '0' as RestakeAssetId;
47+
const tntAsset = assets.get(NATIVE_ASSET_ID);
5548

56-
return {
57-
id: assetId,
58-
name,
59-
symbol,
60-
decimals,
49+
if (tntAsset) {
50+
const available = tntAsset.balance ?? null;
51+
const deposited =
52+
typeof delegatorDeposits?.[NATIVE_ASSET_ID]?.amount === 'bigint'
53+
? new BN(delegatorDeposits[NATIVE_ASSET_ID].amount.toString())
54+
: null;
55+
56+
vaultAssets = [
57+
{
58+
id: NATIVE_ASSET_ID,
59+
name: tntAsset.metadata.name,
60+
symbol: tntAsset.metadata.symbol,
61+
decimals: tntAsset.metadata.decimals,
6162
available,
6263
deposited,
63-
} satisfies VaultAssetData;
64-
},
65-
);
64+
},
65+
];
66+
} else {
67+
vaultAssets = [];
68+
}
69+
} else {
70+
vaultAssets = Array.from(assets.values())
71+
.filter((asset) => asset.metadata.vaultId === vaultId)
72+
.map(
73+
({
74+
id: assetId,
75+
metadata: { decimals, symbol, name },
76+
balance,
77+
}) => {
78+
const available = balance ?? null;
79+
80+
const deposited =
81+
typeof delegatorDeposits?.[assetId]?.amount === 'bigint'
82+
? new BN(delegatorDeposits[assetId].amount.toString())
83+
: null;
84+
85+
return {
86+
id: assetId,
87+
name,
88+
symbol,
89+
decimals,
90+
available,
91+
deposited,
92+
} satisfies VaultAssetData;
93+
},
94+
);
95+
}
6696

6797
return (
6898
<VaultAssetsTable

apps/tangle-dapp/src/constants/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export enum TxName {
8383
RESTAKE_NATIVE_UNSTAKE_EXECUTE = 'restake execute undelegate native',
8484
RESTAKE_DEPOSITED_UNSTAKE_EXECUTE = 'restake execute undelegate deposited',
8585
RESTAKE_NATIVE_UNSTAKE_CANCEL = 'restake cancel undelegate native',
86+
RESTAKE_UNSTAKE_EXECUTE_ALL = 'restake execute all undelegate',
8687
}
8788

8889
export const PAYMENT_DESTINATION_OPTIONS: StakingRewardsDestinationDisplayText[] =

apps/tangle-dapp/src/containers/restaking/UnstakeRequestTable.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,13 @@ const COLUMNS = [
127127
type Props = {
128128
unstakeRequests: DelegatorUnstakeRequest[];
129129
operatorIdentities: Map<string, IdentityType | null>;
130+
onExecuted?: () => void;
130131
};
131132

132133
const UnstakeRequestTable: FC<Props> = ({
133134
unstakeRequests,
134135
operatorIdentities,
136+
onExecuted,
135137
}) => {
136138
const { assets } = useRestakeAssets();
137139
const { delegationBondLessDelay } = useRestakeConsts();
@@ -236,6 +238,11 @@ const UnstakeRequestTable: FC<Props> = ({
236238
<UnstakeRequestTableActions
237239
allRequests={requests}
238240
selectedRequests={selectedRequests}
241+
onExecuted={() => {
242+
if (onExecuted) {
243+
onExecuted();
244+
}
245+
}}
239246
/>
240247
</>
241248
);

apps/tangle-dapp/src/containers/restaking/UnstakeRequestTableActions.tsx

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,49 @@
11
import Button from '@tangle-network/ui-components/components/buttons/Button';
2-
import { FC, useCallback, useMemo, useState } from 'react';
2+
import { FC, useCallback, useEffect, useMemo } from 'react';
33
import { isScheduledRequestReady } from '../../pages/restake/utils';
44
import { UnstakeRequestTableRow } from './UnstakeRequestTable';
55
import { BN } from '@polkadot/util';
66
import useRestakeApi from '../../data/restake/useRestakeApi';
77
import { NATIVE_ASSET_ID } from '@tangle-network/tangle-shared-ui/constants/restaking';
8-
import useNativeRestakeUnstakeExecuteTx from '../../data/restake/useNativeRestakeUnstakeExecuteTx';
9-
import useDepositedRestakeUnstakeExecuteTx from '../../data/restake/useDepositedRestakeUnstakeExecuteTx';
8+
import useRestakeUnstakeExecuteAllTx from '../../data/restake/useRestakeUnstakeExecuteAllTx';
109
import { TxStatus } from '@tangle-network/tangle-shared-ui/hooks/useSubstrateTx';
1110
import useNativeRestakeUnstakeCancelTx from '../../data/restake/useNativeRestakeUnstakeCancelTx';
1211

13-
type Props = {
12+
type UnstakeRequestTableActionsProps = {
1413
allRequests: UnstakeRequestTableRow[];
1514
selectedRequests: UnstakeRequestTableRow[];
15+
onExecuted: () => void;
1616
};
1717

18-
const UnstakeRequestTableActions: FC<Props> = ({
18+
const UnstakeRequestTableActions: FC<UnstakeRequestTableActionsProps> = ({
1919
allRequests,
2020
selectedRequests,
21+
onExecuted,
2122
}) => {
22-
const [isTransacting, setIsTransacting] = useState(false);
2323
const restakeApi = useRestakeApi();
2424

25-
const { execute: executeNominatedExecute, status: nominatedExecuteStatus } =
26-
useNativeRestakeUnstakeExecuteTx();
27-
28-
const { execute: executeDepositedExecute, status: depositedExecuteStatus } =
29-
useDepositedRestakeUnstakeExecuteTx();
25+
const { execute: executeAll, status: executeAllStatus } =
26+
useRestakeUnstakeExecuteAllTx();
3027

3128
const { execute: executeCancel, status: cancelStatus } =
3229
useNativeRestakeUnstakeCancelTx();
3330

31+
const isExecuting = executeAllStatus === TxStatus.PROCESSING;
32+
const isCancelling = cancelStatus === TxStatus.PROCESSING;
33+
3434
const isReady =
3535
restakeApi !== null &&
36-
!isTransacting &&
37-
executeNominatedExecute !== null &&
38-
nominatedExecuteStatus !== TxStatus.PROCESSING &&
39-
executeDepositedExecute !== null &&
40-
depositedExecuteStatus !== TxStatus.PROCESSING &&
36+
executeAll !== null &&
37+
executeAllStatus !== TxStatus.PROCESSING &&
4138
executeCancel !== null &&
4239
cancelStatus !== TxStatus.PROCESSING;
4340

41+
useEffect(() => {
42+
if (executeAllStatus === TxStatus.COMPLETE && onExecuted) {
43+
onExecuted();
44+
}
45+
}, [executeAllStatus, onExecuted]);
46+
4447
const handleCancelUnstake = useCallback(async () => {
4548
if (!isReady) {
4649
return;
@@ -62,8 +65,6 @@ const UnstakeRequestTableActions: FC<Props> = ({
6265
}),
6366
);
6467

65-
setIsTransacting(true);
66-
6768
if (depositedUnstakeRequestsForApi.length > 0) {
6869
if (!restakeApi) return;
6970
await restakeApi.cancelUndelegate(depositedUnstakeRequestsForApi);
@@ -74,17 +75,13 @@ const UnstakeRequestTableActions: FC<Props> = ({
7475
nominatedUnstakeRequests.map((request) => request.operatorAccountId),
7576
);
7677
}
77-
78-
setIsTransacting(false);
7978
}, [executeCancel, isReady, restakeApi, selectedRequests]);
8079

8180
const handleExecuteUnstake = useCallback(async () => {
8281
if (!isReady) {
8382
return;
8483
}
8584

86-
setIsTransacting(true);
87-
8885
const nominatedNativeRequests = allRequests.filter(
8986
({ assetId, isNomination }) =>
9087
assetId === NATIVE_ASSET_ID && isNomination === true,
@@ -99,29 +96,14 @@ const UnstakeRequestTableActions: FC<Props> = ({
9996
({ assetId }) => assetId !== NATIVE_ASSET_ID,
10097
);
10198

102-
if (hasNonNativeUnstakeRequests) {
103-
if (!restakeApi) return;
104-
await restakeApi.executeUndelegate();
105-
}
106-
107-
if (nominatedNativeRequests.length > 0) {
108-
await executeNominatedExecute(
109-
nominatedNativeRequests.map((request) => request.operatorAccountId),
110-
);
111-
}
112-
113-
if (depositedNativeRequests.length > 0) {
114-
await executeDepositedExecute();
115-
}
116-
117-
setIsTransacting(false);
118-
}, [
119-
allRequests,
120-
executeNominatedExecute,
121-
executeDepositedExecute,
122-
isReady,
123-
restakeApi,
124-
]);
99+
await executeAll({
100+
nominatedOperators: nominatedNativeRequests.map(
101+
(request) => request.operatorAccountId,
102+
),
103+
hasDepositedRequests: depositedNativeRequests.length > 0,
104+
hasNonNativeRequests: hasNonNativeUnstakeRequests,
105+
});
106+
}, [allRequests, executeAll, isReady]);
125107

126108
const canCancelUnstake = selectedRequests.length > 0;
127109

@@ -138,7 +120,7 @@ const UnstakeRequestTableActions: FC<Props> = ({
138120
return (
139121
<div className="flex items-center gap-3">
140122
<Button
141-
isLoading={isTransacting}
123+
isLoading={isCancelling}
142124
isDisabled={!isReady || !canCancelUnstake}
143125
isFullWidth
144126
onClick={handleCancelUnstake}
@@ -148,10 +130,10 @@ const UnstakeRequestTableActions: FC<Props> = ({
148130
</Button>
149131

150132
<Button
151-
isLoading={isTransacting}
152-
isDisabled={!isReady || canCancelUnstake || !canExecuteUnstake}
153-
isFullWidth
133+
isLoading={isExecuting}
134+
isDisabled={!isReady || !canExecuteUnstake}
154135
onClick={handleExecuteUnstake}
136+
isFullWidth
155137
>
156138
Execute All
157139
</Button>

0 commit comments

Comments
 (0)