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: new markets migration #2080

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"test:coverage": "jest --coverage"
},
"dependencies": {
"@aave/contract-helpers": "1.28.1",
"@aave/math-utils": "1.28.1",
"@aave/contract-helpers": "1.28.2-fb3b77e8484c11bb786b9872259d7bcff30597e7.0",
"@aave/math-utils": "1.28.2-fb3b77e8484c11bb786b9872259d7bcff30597e7.0",
"@bgd-labs/aave-address-book": "^2.26.1",
"@emotion/cache": "11.10.3",
"@emotion/react": "11.10.4",
Expand Down
23 changes: 15 additions & 8 deletions pages/v3-migration.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { ContentContainer } from 'src/components/ContentContainer';
import { getMarketInfoById } from 'src/components/MarketSwitcher';
import { useUserMigrationReserves } from 'src/hooks/migration/useUserMigrationReserves';
import { useUserSummaryAfterMigration } from 'src/hooks/migration/useUserSummaryAfterMigration';
import { useUserPoolReservesHumanized } from 'src/hooks/pool/useUserPoolReserves';
import { useUserSummaryAndIncentives } from 'src/hooks/pool/useUserSummaryAndIncentives';
import { MainLayout } from 'src/layouts/MainLayout';
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
Expand All @@ -23,6 +22,7 @@ import { selectCurrentChainIdV3MarketData } from 'src/store/poolSelectors';
import { useRootStore } from 'src/store/root';
import {
CustomMarket,
externalMarketsData,
getNetworkConfig,
MarketDataType,
marketsData,
Expand All @@ -34,6 +34,13 @@ const MigrateV3Modal = dynamic(() =>
)
);

const EXTERNAL_MARKETS_TO_MIGRATE = Object.keys(externalMarketsData).map((key) => {
const market = externalMarketsData[key];
return {
...market,
};
});

const AAVE_MARKETS_TO_MIGRATE = Object.keys(marketsData)
.map((key) => {
const market = marketsData[key];
Expand All @@ -48,6 +55,10 @@ const selectableMarkets = [
title: 'Aave V2 Markets',
markets: AAVE_MARKETS_TO_MIGRATE,
},
{
title: 'Spark Markets',
markets: EXTERNAL_MARKETS_TO_MIGRATE,
},
];

export default function V3Migration() {
Expand Down Expand Up @@ -91,20 +102,16 @@ export default function V3Migration() {

const { data: fromUserSummaryAndIncentives, isLoading: fromUserSummaryAndIncentivesLoading } =
useUserSummaryAndIncentives(fromMarketData);

const { data: toUserReservesData, isLoading: toUserReservesDataLoading } =
useUserPoolReservesHumanized(toMarketData);
const { data: toUserSummaryForMigration, isLoading: toUserSummaryForMigrationLoading } =
useUserSummaryAndIncentives(toMarketData);
const toUserEModeCategoryId = toUserReservesData?.userEmodeCategoryId || 0;

const { data: userSummaryAfterMigration, isLoading: userSummaryAfterMigrationLoading } =
useUserSummaryAfterMigration(fromMarketData, toMarketData);

const toUserEModeCategoryId = toUserSummaryForMigration?.userEmodeCategoryId || 0;
const loading =
userMigrationReservesLoading ||
fromUserSummaryAndIncentivesLoading ||
toUserReservesDataLoading ||
toUserSummaryForMigrationLoading ||
userSummaryAfterMigrationLoading;

Expand Down Expand Up @@ -138,7 +145,7 @@ export default function V3Migration() {
setFromMarketData(marketData);
};

const bottomPanelProps = fromUserSummaryAndIncentives &&
const userSummaryBeforeMigration = fromUserSummaryAndIncentives &&
toUserSummaryForMigration && {
fromUserSummaryBeforeMigration: fromUserSummaryAndIncentives,
toUserSummaryBeforeMigration: toUserSummaryForMigration,
Expand All @@ -159,7 +166,7 @@ export default function V3Migration() {
>
<MigrationBottomPanel
userSummaryAfterMigration={userSummaryAfterMigration}
userSummaryBeforeMigration={bottomPanelProps}
userSummaryBeforeMigration={userSummaryBeforeMigration}
disableButton={selectedSupplyAssets.length === 0 && selectedBorrowAssets.length === 0}
enteringIsolationMode={isolatedReserveV3?.enteringIsolationMode || false}
loading={loading}
Expand Down
Binary file added public/icons/externalMarkets/spark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
261 changes: 233 additions & 28 deletions src/components/transactions/MigrateV3/MigrateV3Actions.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import { ProtocolAction } from '@aave/contract-helpers';
import {
MigrationDelegationApproval,
V3MigrationHelperSignedCreditDelegationPermit,
V3MigrationHelperSignedPermit,
} from '@aave/contract-helpers/dist/esm/v3-migration-contract/v3MigrationTypes';
import { Trans } from '@lingui/macro';
import { useTransactionHandler } from 'src/helpers/useTransactionHandler';
import { useState } from 'react';
import { MOCK_SIGNED_HASH } from 'src/helpers/useTransactionHandler';
import { useMigrationApprovalTxs } from 'src/hooks/migration/useMigrationApprovalTxs';
import { UserMigrationReserves } from 'src/hooks/migration/useUserMigrationReserves';
import { UserSummaryForMigration } from 'src/hooks/migration/useUserSummaryForMigration';
import { useModalContext } from 'src/hooks/useModal';
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
import { useRootStore } from 'src/store/root';
import {
selectMigrationBorrowPermitPayloads,
selectMigrationRepayAssets,
selectUserSupplyAssetsForMigrationNoPermit,
} from 'src/store/v3MigrationSelectors';
import { ApprovalMethod } from 'src/store/walletSlice';
import { getErrorTextFromError, TxAction } from 'src/ui-config/errorMapping';
import { MarketDataType } from 'src/ui-config/marketsConfig';
import { useSharedDependencies } from 'src/ui-config/SharedDependenciesProvider';
import invariant from 'tiny-invariant';

import { TxActionsWrapper } from '../TxActionsWrapper';

Expand All @@ -12,51 +30,238 @@ export type MigrateV3ActionsProps = {
blocked: boolean;
userMigrationReserves: UserMigrationReserves;
toUserSummaryForMigration: UserSummaryForMigration;
fromMarket: MarketDataType;
toMarket: MarketDataType;
};

export const MigrateV3Actions = ({
isWrongNetwork,
blocked,
userMigrationReserves,
toUserSummaryForMigration,
fromMarket,
toMarket,
}: MigrateV3ActionsProps) => {
const migrateWithPermits = useRootStore((store) => store.migrateWithPermits);
const migrateWithoutPermits = useRootStore((store) => store.migrateWithoutPermits);
const getApprovePermitsForSelectedAssets = useRootStore(
(store) => store.getApprovePermitsForSelectedAssets
const [signatures, setSignatures] = useState<{
supply: V3MigrationHelperSignedPermit[];
borrow: V3MigrationHelperSignedCreditDelegationPermit[];
}>({
supply: [],
borrow: [],
});
const { signTxData, sendTx } = useWeb3Context();
const [
user,
walletApprovalMethodPreference,
selectedMigrationSupplyAssets,
selectedMigrationBorrowAssets,
generateCreditDelegationSignatureRequest,
generateSignatureRequest,
estimateGasLimit,
] = useRootStore((store) => [
store.account,
store.walletApprovalMethodPreference,
store.selectedMigrationSupplyAssets,
store.selectedMigrationBorrowAssets,
store.generateCreditDelegationSignatureRequest,
store.generateSignatureRequest,
store.estimateGasLimit,
]);
const { approvalTxState, mainTxState, setApprovalTxState, setTxError, setMainTxState } =
useModalContext();

const { migrationService } = useSharedDependencies();

const usePermit = walletApprovalMethodPreference === ApprovalMethod.PERMIT;

const supplyAssets = selectUserSupplyAssetsForMigrationNoPermit(
selectedMigrationSupplyAssets,
userMigrationReserves.supplyReserves,
userMigrationReserves.isolatedReserveV3
);

const repayAssets = selectMigrationRepayAssets(
selectedMigrationBorrowAssets,
userMigrationReserves.borrowReserves
);

const borrowPermitPayloads = selectMigrationBorrowPermitPayloads(
selectedMigrationBorrowAssets,
toUserSummaryForMigration,
userMigrationReserves.borrowReserves,
true
);
const { approval, action, loadingTxns, requiresApproval, mainTxState, approvalTxState } =
useTransactionHandler({
handleGetTxns: async () =>
await migrateWithoutPermits(toUserSummaryForMigration, userMigrationReserves),
handleGetPermitTxns: async (signatures, deadline) =>
await migrateWithPermits(
signatures,
deadline,
toUserSummaryForMigration,
userMigrationReserves
),
tryPermit: true,
permitAction: ProtocolAction.migrateV3,
});

const handleApproval = async () => {
const approvePermitsForSelectedAssets = await getApprovePermitsForSelectedAssets(
toUserSummaryForMigration,
userMigrationReserves
);
approval(approvePermitsForSelectedAssets);
const creditDelegationApprovals: MigrationDelegationApproval[] = borrowPermitPayloads.map(
({ underlyingAsset, amount }) => ({ debtTokenAddress: underlyingAsset, amount })
);

const { data: approvals, isLoading: approvalsLoading } = useMigrationApprovalTxs(
fromMarket,
toMarket,
supplyAssets,
creditDelegationApprovals
);

const requiresApproval = approvals
? approvals.supplyApprovalTxs.length > 0 ||
approvals.borrowCreditDelegationApprovalTxs.length > 0
: false;

const approval = async () => {
if (requiresApproval && approvals) {
try {
if (usePermit) {
setApprovalTxState({ ...approvalTxState, loading: true });
const supplyApprovals = approvals.supplyApprovalTxs;
const borrowCreditDelegationApprovals = approvals.borrowCreditDelegationApprovalTxs;
const supplySignatures = await Promise.all(
supplyApprovals.map(async (supplyApproval) => {
const supplyAsset = supplyAssets.find(
(supplyAsset) => supplyAsset.aToken === supplyApproval.to
);
invariant(supplyAsset, 'Supply asset not found');
const signatureRequest = await generateSignatureRequest(
{
token: supplyAsset.aToken,
amount: supplyAsset.amount,
deadline: supplyAsset.deadline.toString(),
spender: fromMarket.addresses.V3_MIGRATOR || '',
},
{ chainId: fromMarket.chainId }
);
return {
deadline: supplyAsset.deadline,
aToken: supplyAsset.aToken,
value: supplyAsset.amount,
signatureRequest,
};
})
);
const borrowCreditDelegationSignatures = await Promise.all(
borrowCreditDelegationApprovals.map(async (borrowCreditDelegationApproval) => {
const borrowAsset = borrowPermitPayloads.find(
(borrowAsset) => borrowAsset.underlyingAsset === borrowCreditDelegationApproval.to
);
invariant(borrowAsset, 'Borrow asset not found');
const deadline = Math.floor(Date.now() / 1000 + 3600).toString();
const signatureRequest = await generateCreditDelegationSignatureRequest(
{
...borrowAsset,
deadline,
spender: fromMarket.addresses.V3_MIGRATOR || '',
},
{ chainId: fromMarket.chainId }
);
return {
deadline,
debtToken: borrowAsset.underlyingAsset,
value: borrowAsset.amount,
signatureRequest,
};
})
);
const supplySigned: V3MigrationHelperSignedPermit[] = await Promise.all(
supplySignatures.map(async (signatureRequest) => {
const signature = await signTxData(signatureRequest.signatureRequest);
return {
deadline: signatureRequest.deadline,
aToken: signatureRequest.aToken,
value: signatureRequest.value,
signedPermit: signature,
};
})
);
const borrowSigned: V3MigrationHelperSignedCreditDelegationPermit[] = await Promise.all(
borrowCreditDelegationSignatures.map(async (signatureRequest) => {
const signature = await signTxData(signatureRequest.signatureRequest);
return {
deadline: signatureRequest.deadline,
debtToken: signatureRequest.debtToken,
value: signatureRequest.value,
signedPermit: signature,
};
})
);
setSignatures({
supply: supplySigned,
borrow: borrowSigned,
});
setTxError(undefined);
setApprovalTxState({
txHash: MOCK_SIGNED_HASH,
loading: false,
success: true,
});
} else {
setApprovalTxState({ ...approvalTxState, loading: true });
const supplyApprovalsResponse = await Promise.all(
approvals.supplyApprovalTxs.map((supplyApproval) => sendTx(supplyApproval))
);
const borrowApprovalsResponse = await Promise.all(
approvals.borrowCreditDelegationApprovalTxs.map((borrowCreditDelegationApproval) =>
sendTx(borrowCreditDelegationApproval)
)
);
await Promise.all(supplyApprovalsResponse.map((elem) => elem.wait(1)));
await Promise.all(borrowApprovalsResponse.map((elem) => elem.wait(1)));
setApprovalTxState({
txHash: supplyApprovalsResponse[0]?.hash || borrowApprovalsResponse[0]?.hash,
loading: false,
success: true,
});
setTxError(undefined);
}
} catch (error) {
const parsedError = getErrorTextFromError(error, TxAction.GAS_ESTIMATION, false);
setTxError(parsedError);
setApprovalTxState({
txHash: undefined,
loading: false,
});
}
}
};

const action = async () => {
try {
setMainTxState({ ...mainTxState, loading: true });
let tx = migrationService.getMigrationTx(
fromMarket,
toMarket,
user,
supplyAssets,
repayAssets,
signatures.supply,
signatures.borrow
);
tx = await estimateGasLimit(tx, fromMarket.chainId);
const response = await sendTx(tx);
await response.wait(1);
setMainTxState({
txHash: response.hash,
loading: false,
success: true,
});
} catch (error) {
const parsedError = getErrorTextFromError(error, TxAction.GAS_ESTIMATION, false);
setTxError(parsedError);
setMainTxState({
txHash: undefined,
loading: false,
});
}
};

return (
<TxActionsWrapper
requiresApproval={requiresApproval}
preparingTransactions={loadingTxns}
preparingTransactions={approvalsLoading}
mainTxState={mainTxState}
approvalTxState={approvalTxState}
isWrongNetwork={isWrongNetwork}
handleAction={action}
handleApproval={handleApproval}
handleApproval={approval}
blocked={blocked}
actionText={<Trans>Migrate</Trans>}
actionInProgressText={<Trans>Migrating</Trans>}
Expand Down
Loading
Loading