Skip to content

Commit f82b555

Browse files
authored
fix: rusdt migration (#1137)
1 parent a3a7c85 commit f82b555

6 files changed

Lines changed: 286 additions & 63 deletions

File tree

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import React, { FC, useCallback, useMemo, useState } from 'react';
2+
3+
import classNames from 'classnames';
4+
import { t } from 'i18next';
5+
6+
import { getAssetData } from '@sovryn/contracts';
7+
import {
8+
Button,
9+
ButtonSize,
10+
ButtonStyle,
11+
Dialog,
12+
DialogBody,
13+
DialogHeader,
14+
Paragraph,
15+
ParagraphSize,
16+
} from '@sovryn/ui';
17+
import { Decimal } from '@sovryn/utils';
18+
19+
import { RSK_CHAIN_ID } from '../../../config/chains';
20+
21+
import {
22+
TransactionType,
23+
Transaction,
24+
} from '../../3_organisms/TransactionStepDialog/TransactionStepDialog.types';
25+
import { useTransactionContext } from '../../../contexts/TransactionContext';
26+
import { useAccount } from '../../../hooks/useAccount';
27+
import { useAssetBalance } from '../../../hooks/useAssetBalance';
28+
import { translations } from '../../../locales/i18n';
29+
import { COMMON_SYMBOLS } from '../../../utils/asset';
30+
import { bigNumberic, decimalic } from '../../../utils/math';
31+
import { AmountRenderer } from '../AmountRenderer/AmountRenderer';
32+
33+
const RUSDT_MIGRATION_CONTRACT = '0xd143f576b8e889b1c90ebef8d0c4bfbb3316fdd2';
34+
35+
type RusdtMigrationNoticeProps = {
36+
className?: string;
37+
buttonClassName?: string;
38+
dataAttributePrefix?: string;
39+
};
40+
41+
export const RusdtMigrationNotice: FC<RusdtMigrationNoticeProps> = ({
42+
className,
43+
buttonClassName,
44+
dataAttributePrefix = 'rusdt-migration',
45+
}) => {
46+
const { account, signer } = useAccount();
47+
const { balance, weiBalance, loading } = useAssetBalance(
48+
COMMON_SYMBOLS.RUSDT,
49+
RSK_CHAIN_ID,
50+
);
51+
52+
// Convert rUSDT balance to the correct decimals for the migration
53+
const outputWeiBalance = useMemo(
54+
() => bigNumberic(weiBalance).div(1e12).toString(),
55+
[weiBalance],
56+
);
57+
58+
const { setTransactions, setTitle, setIsOpen } = useTransactionContext();
59+
60+
const [isModalOpen, setIsModalOpen] = useState(false);
61+
62+
const hasBalance = useMemo(() => balance.gt(Decimal.ZERO), [balance]);
63+
64+
const canMigrate =
65+
!!account && !!signer && hasBalance && outputWeiBalance !== '0' && !loading;
66+
67+
const handleOpenModal = useCallback(() => setIsModalOpen(true), []);
68+
const handleCloseModal = useCallback(() => setIsModalOpen(false), []);
69+
70+
const handleMigrate = useCallback(async () => {
71+
if (!canMigrate || !signer) {
72+
return;
73+
}
74+
75+
try {
76+
const { contract } = await getAssetData(
77+
COMMON_SYMBOLS.RUSDT,
78+
RSK_CHAIN_ID,
79+
);
80+
const rusdtContract = contract(signer);
81+
82+
const transactions: Transaction[] = [
83+
{
84+
title: t(translations.rusdtMigration.tx.migrate),
85+
request: {
86+
type: TransactionType.signTransaction,
87+
contract: rusdtContract,
88+
fnName: 'transfer',
89+
args: [RUSDT_MIGRATION_CONTRACT, weiBalance],
90+
gasLimit: 100000, // Set a reasonable gas limit for the migration transaction
91+
},
92+
},
93+
];
94+
95+
setTransactions(transactions);
96+
setTitle(t(translations.rusdtMigration.tx.title));
97+
setIsOpen(true);
98+
setIsModalOpen(false);
99+
} catch (error) {
100+
console.error('rUSDT migration setup failed', error);
101+
}
102+
}, [canMigrate, setIsOpen, setTitle, setTransactions, signer, weiBalance]);
103+
104+
return (
105+
<>
106+
<Button
107+
style={ButtonStyle.ghost}
108+
size={ButtonSize.small}
109+
text={t(translations.rusdtMigration.cta)}
110+
onClick={handleOpenModal}
111+
className={classNames('w-auto h-auto py-0 px-0.5', buttonClassName)}
112+
dataAttribute={`${dataAttributePrefix}-open-button`}
113+
/>
114+
115+
<Dialog disableFocusTrap isOpen={isModalOpen}>
116+
<DialogHeader
117+
title={t(translations.rusdtMigration.modal.title)}
118+
onClose={handleCloseModal}
119+
/>
120+
<DialogBody>
121+
<Paragraph className="text-gray-30" size={ParagraphSize.base}>
122+
{t(translations.rusdtMigration.modal.description)}
123+
</Paragraph>
124+
125+
<div className="bg-gray-90 rounded p-4 mt-4">
126+
<Paragraph size={ParagraphSize.small} className="text-gray-30">
127+
{t(translations.rusdtMigration.modal.currentBalance)}
128+
</Paragraph>
129+
<Paragraph className="font-medium mt-1">
130+
<AmountRenderer
131+
value={account ? balance : '0'}
132+
suffix={COMMON_SYMBOLS.RUSDT}
133+
/>
134+
&nbsp;&nbsp;-&gt;&nbsp;&nbsp;
135+
<AmountRenderer
136+
value={account ? decimalic(outputWeiBalance).toUnits(6) : '0'}
137+
suffix={COMMON_SYMBOLS.USDT}
138+
isAnimated
139+
/>
140+
</Paragraph>
141+
</div>
142+
143+
{!canMigrate && (
144+
<Paragraph size={ParagraphSize.small} className="text-gray-30 mt-3">
145+
{t(translations.rusdtMigration.modal.noBalance)}
146+
</Paragraph>
147+
)}
148+
149+
<Button
150+
style={ButtonStyle.primary}
151+
text={t(translations.rusdtMigration.modal.migrate)}
152+
onClick={handleMigrate}
153+
className="w-full mt-6"
154+
disabled={!canMigrate}
155+
dataAttribute={`${dataAttributePrefix}-submit-button`}
156+
/>
157+
</DialogBody>
158+
</Dialog>
159+
</>
160+
);
161+
};

apps/frontend/src/app/5_pages/MarketMakingPage/components/PoolsTable/PoolsTable.constants.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { HelperButton } from '@sovryn/ui';
88

99
import { AssetPairRenderer } from '../../../../2_molecules/AssetPairRenderer/AssetPairRenderer';
1010
import { DeprecatedBadge } from '../../../../2_molecules/DeprecatedBadge/DeprecatedBadge';
11+
import { RusdtMigrationNotice } from '../../../../2_molecules/RusdtMigrationNotice/RusdtMigrationNotice';
1112
import { getRskDeprecatedAssetTooltips } from '../../../../../constants/tokens';
1213
import { translations } from '../../../../../locales/i18n';
13-
import { findAsset } from '../../../../../utils/asset';
14+
import { COMMON_SYMBOLS, findAsset } from '../../../../../utils/asset';
1415
import { AmmLiquidityPool } from '../../utils/AmmLiquidityPool';
1516
import { BlockedPoolConfig } from './PoolsTable.types';
1617
import { CurrentBalanceRenderer } from './components/CurrentBalanceRenderer/CurrentBalanceRenderer';
@@ -27,6 +28,9 @@ export const COLUMNS_CONFIG = [
2728
const isDeprecated =
2829
!!getRskDeprecatedAssetTooltips(pool.assetA) ||
2930
!!getRskDeprecatedAssetTooltips(pool.assetB);
31+
const isRusdtPool = [pool.assetA, pool.assetB].some(
32+
asset => asset.toUpperCase() === COMMON_SYMBOLS.RUSDT,
33+
);
3034
return (
3135
<div
3236
data-pool-key={pool.key}
@@ -47,7 +51,16 @@ export const COLUMNS_CONFIG = [
4751
{findAsset(pool.assetA, pool.chainId)?.symbol}/
4852
{findAsset(pool.assetB, pool.chainId)?.symbol}
4953
</span>
50-
{isDeprecated && <DeprecatedBadge />}
54+
<span className="flex flex-row justify-start items-center gap-1">
55+
{isDeprecated && <DeprecatedBadge />}
56+
{isRusdtPool && (
57+
<RusdtMigrationNotice
58+
className="justify-center lg:justify-end text-center lg:text-right"
59+
buttonClassName="prevent-row-click"
60+
dataAttributePrefix="market-making-rusdt-migration"
61+
/>
62+
)}
63+
</span>
5164
</div>
5265
</div>
5366
);

apps/frontend/src/app/5_pages/MarketMakingPage/components/PoolsTable/components/PoolsTableAction/PoolsTableAction.tsx

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -73,67 +73,69 @@ export const PoolsTableAction: FC<PoolsTableActionProps> = ({ pool }) => {
7373
}, [refetch, block]);
7474

7575
return (
76-
<div className="flex items-center justify-center lg:justify-end w-full">
77-
<Tooltip
78-
trigger={TooltipTrigger.click}
79-
content={<>{t(translations.maintenanceMode.featureDisabled)}</>}
80-
disabled={!actionLocked}
81-
children={
82-
<>
83-
{!account ||
84-
(poolBalanceA.lte(Decimal.ZERO) &&
85-
poolBalanceB.lte(Decimal.ZERO)) ? (
86-
poolBlocked.isBlocked ? (
87-
<Tooltip
88-
children={
89-
<div>
90-
<Button
91-
style={ButtonStyle.primary}
92-
size={ButtonSize.small}
93-
text={t(translations.common.deposit)}
94-
dataAttribute="pools-table-deposit-button"
95-
className="w-full lg:w-auto prevent-row-click"
96-
disabled
97-
/>
98-
</div>
99-
}
100-
content={
101-
poolBlocked.message ??
102-
t(
103-
translations.marketMakingPage.marketMakingOperations
104-
.depositNotAllowed,
105-
)
106-
}
107-
dataAttribute="pools-table-deposit-button-tooltip"
108-
className="w-full lg:w-auto prevent-row-click"
109-
/>
76+
<div className="flex flex-col items-center lg:items-end gap-1.5 w-full">
77+
<div className="flex items-center justify-center lg:justify-end w-full">
78+
<Tooltip
79+
trigger={TooltipTrigger.click}
80+
content={<>{t(translations.maintenanceMode.featureDisabled)}</>}
81+
disabled={!actionLocked}
82+
children={
83+
<>
84+
{!account ||
85+
(poolBalanceA.lte(Decimal.ZERO) &&
86+
poolBalanceB.lte(Decimal.ZERO)) ? (
87+
poolBlocked.isBlocked ? (
88+
<Tooltip
89+
children={
90+
<div>
91+
<Button
92+
style={ButtonStyle.primary}
93+
size={ButtonSize.small}
94+
text={t(translations.common.deposit)}
95+
dataAttribute="pools-table-deposit-button"
96+
className="w-full lg:w-auto prevent-row-click"
97+
disabled
98+
/>
99+
</div>
100+
}
101+
content={
102+
poolBlocked.message ??
103+
t(
104+
translations.marketMakingPage.marketMakingOperations
105+
.depositNotAllowed,
106+
)
107+
}
108+
dataAttribute="pools-table-deposit-button-tooltip"
109+
className="w-full lg:w-auto prevent-row-click"
110+
/>
111+
) : (
112+
<Button
113+
style={ButtonStyle.primary}
114+
size={ButtonSize.small}
115+
text={t(translations.common.deposit)}
116+
dataAttribute="pools-table-deposit-button"
117+
className="w-full lg:w-auto prevent-row-click"
118+
disabledStyle={actionLocked}
119+
disabled={!account}
120+
onClick={handleDepositClick}
121+
/>
122+
)
110123
) : (
111124
<Button
112-
style={ButtonStyle.primary}
125+
style={ButtonStyle.secondary}
113126
size={ButtonSize.small}
114-
text={t(translations.common.deposit)}
115-
dataAttribute="pools-table-deposit-button"
127+
text={t(translations.common.adjust)}
128+
dataAttribute="pools-table-adjust-button"
116129
className="w-full lg:w-auto prevent-row-click"
117130
disabledStyle={actionLocked}
118131
disabled={!account}
119-
onClick={handleDepositClick}
132+
onClick={handleAdjustClick}
120133
/>
121-
)
122-
) : (
123-
<Button
124-
style={ButtonStyle.secondary}
125-
size={ButtonSize.small}
126-
text={t(translations.common.adjust)}
127-
dataAttribute="pools-table-adjust-button"
128-
className="w-full lg:w-auto prevent-row-click"
129-
disabledStyle={actionLocked}
130-
disabled={!account}
131-
onClick={handleAdjustClick}
132-
/>
133-
)}
134-
</>
135-
}
136-
/>
134+
)}
135+
</>
136+
}
137+
/>
138+
</div>
137139

138140
<AdjustAndDepositModal
139141
isOpen={isModalOpen}

apps/frontend/src/app/5_pages/PortfolioPage/components/AssetSection/components/AssetBalanceRow/AssetBalanceRow.tsx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
1-
import React, { FC, useCallback, useEffect } from 'react';
1+
import React, { FC, useCallback, useEffect, useMemo } from 'react';
22

33
import { Icon, IconNames, Paragraph, prettyTx } from '@sovryn/ui';
44

55
import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer';
66
import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer';
77
import { DeprecatedBadge } from '../../../../../../2_molecules/DeprecatedBadge/DeprecatedBadge';
8+
import { RusdtMigrationNotice } from '../../../../../../2_molecules/RusdtMigrationNotice/RusdtMigrationNotice';
89
import { USD } from '../../../../../../../constants/currencies';
9-
import { getBobDeprecatedAssetTooltips } from '../../../../../../../constants/tokens';
10+
import {
11+
getBobDeprecatedAssetTooltips,
12+
getRskDeprecatedAssetTooltips,
13+
} from '../../../../../../../constants/tokens';
1014
import { useAccount } from '../../../../../../../hooks/useAccount';
1115
import { useAssetBalance } from '../../../../../../../hooks/useAssetBalance';
1216
import { useCurrentChain } from '../../../../../../../hooks/useChainStore';
1317
import { useCopyAddress } from '../../../../../../../hooks/useCopyAddress';
1418
import { useDollarValue } from '../../../../../../../hooks/useDollarValue';
15-
import { findAsset } from '../../../../../../../utils/asset';
16-
import { isBobChain } from '../../../../../../../utils/chain';
19+
import { COMMON_SYMBOLS, findAsset } from '../../../../../../../utils/asset';
20+
import { isBobChain, isRskChain } from '../../../../../../../utils/chain';
1721
import { getCurrencyPrecision } from '../../../ProtocolSection/ProtocolSection.utils';
1822
import styles from './AssetBalanceRow.module.css';
1923
import { SdexBalance } from './SdexBalance';
@@ -38,8 +42,22 @@ export const AssetBalanceRow: FC<AssetBalanceRowProps> = ({
3842
updateUsdValue(usdValue);
3943
}, [usdValue, updateUsdValue]);
4044

41-
const isDeprecated =
42-
isBobChain(chainId) && !!getBobDeprecatedAssetTooltips(token);
45+
const isDeprecated = useMemo(() => {
46+
if (isBobChain(chainId)) {
47+
return !!getBobDeprecatedAssetTooltips(token);
48+
}
49+
50+
if (isRskChain(chainId)) {
51+
return !!getRskDeprecatedAssetTooltips(token);
52+
}
53+
54+
return false;
55+
}, [chainId, token]);
56+
57+
const isRskRusdtAsset = useMemo(
58+
() => isRskChain(chainId) && token.toUpperCase() === COMMON_SYMBOLS.RUSDT,
59+
[chainId, token],
60+
);
4361

4462
const copyAddress = useCallback(async () => {
4563
await navigator.clipboard.writeText(asset.address);
@@ -70,6 +88,12 @@ export const AssetBalanceRow: FC<AssetBalanceRowProps> = ({
7088
</span>
7189
</span>
7290
<DeprecatedBadge />
91+
{isRskRusdtAsset && (
92+
<RusdtMigrationNotice
93+
className="w-full mt-0.5 text-left"
94+
dataAttributePrefix="portfolio-rusdt-migration"
95+
/>
96+
)}
7397
</div>
7498
)}
7599
</AssetRenderer>

0 commit comments

Comments
 (0)