Skip to content

Commit fe8a113

Browse files
feat: paraswap permit (#1399)
* feat: add permit params to paraswap methods in poolSlice * feat: update paraswap actions and transaction handler to support permit * feat: add utility for calculating signature amount for aToken * feat: add signature request helper function to slice * feat: configure paraswap tx handler to support permit * feat: update repay with collateral action with permit parameters * feat: update collateral swap action with permit parameters * feat: add comments * feat: add slippage and amount tip for Paraswap gas estimation error * chore: run i18n * feat: export variable for percentage increase of signature amount * fix: use collateral asset in useEffect depedency for collateral swap * feat: correctly display approval and error states based on dependency changes * chore: update range for re-using signature on amount update * fix: swap and collateral repay tests by tx approve * chore: remove console log Co-authored-by: NikitaY <[email protected]>
1 parent 74bd1ac commit fe8a113

File tree

13 files changed

+303
-37
lines changed

13 files changed

+303
-37
lines changed

cypress/support/steps/main.steps.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,10 @@ export const repay = (
231231
break;
232232
}
233233
});
234+
234235
if (repayableAsset) {
235236
it(`Choose ${repayableAsset.shortName} as option to repay`, () => {
236-
cy.get('[data-cy=Modal] ').as('Modal');
237+
cy.get('[data-cy=Modal]').as('Modal');
237238
cy.get('@Modal').get('[data-cy=assetSelect]').click();
238239
cy.get('@Modal')
239240
.get(`[data-cy='assetsSelectOption_${repayableAsset.shortName.toUpperCase()}']`)
@@ -245,6 +246,13 @@ export const repay = (
245246
isMaxAmount ? 'MAX' : amount
246247
} amount for ${_shortName}, with ${repayOption} repay option`, () => {
247248
cy.setAmount(amount, isMaxAmount);
249+
if (repayOption == constants.repayType.collateral) {
250+
cy.get('[data-cy=Modal]')
251+
.find('[data-cy=approveButtonChange]')
252+
.click()
253+
.get('[data-cy=approveOption_Transaction]')
254+
.click();
255+
}
248256
cy.doConfirm(hasApproval, _actionName, _shortName);
249257
});
250258
doCloseModal();
@@ -440,6 +448,11 @@ export const swap = (
440448
});
441449
it(`Make approve for ${isMaxAmount ? 'MAX' : amount} amount`, () => {
442450
cy.setAmount(amount, isMaxAmount);
451+
cy.get('[data-cy=Modal]')
452+
.find('[data-cy=approveButtonChange]')
453+
.click()
454+
.get('[data-cy=approveOption_Transaction]')
455+
.click();
443456
cy.wait(2000);
444457
cy.doConfirm(hasApproval, _actionName);
445458
});

scripts/populate-cache.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11252,15 +11252,8 @@ var require_base = __commonJS({
1125211252
comb[2] = points[a].toJ().mixedAdd(points[b].neg());
1125311253
}
1125411254
var index = [
11255-
-3, /* -1 -1 */
11256-
-1, /* -1 0 */
11257-
-5, /* -1 1 */
11258-
-7, /* 0 -1 */
11259-
0, /* 0 0 */
11260-
7, /* 0 1 */
11261-
5, /* 1 -1 */
11262-
1, /* 1 0 */
11263-
3,
11255+
-3 /* -1 -1 */, -1 /* -1 0 */, -5 /* -1 1 */, -7 /* 0 -1 */, 0 /* 0 0 */, 7 /* 0 1 */,
11256+
5 /* 1 -1 */, 1 /* 1 0 */, 3,
1126411257
/* 1 1 */
1126511258
];
1126611259
var jsf = getJSF(coeffs[a], coeffs[b]);

src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ export const ApprovalMethodToggleButton = ({
3333

3434
return (
3535
<>
36-
<Box onClick={handleClick} sx={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }}>
36+
<Box
37+
onClick={handleClick}
38+
sx={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }}
39+
data-cy={`approveButtonChange`}
40+
>
3741
<Typography variant="subheader2" color="info.main">
3842
<Trans>{currentMethod}</Trans>
3943
</Typography>
@@ -53,6 +57,7 @@ export const ApprovalMethodToggleButton = ({
5357
data-cy={`approveMenu_${currentMethod}`}
5458
>
5559
<MenuItem
60+
data-cy={`approveOption_${ApprovalMethod.PERMIT}`}
5661
selected={currentMethod === ApprovalMethod.PERMIT}
5762
value={ApprovalMethod.PERMIT}
5863
onClick={() => {
@@ -71,6 +76,7 @@ export const ApprovalMethodToggleButton = ({
7176
</MenuItem>
7277

7378
<MenuItem
79+
data-cy={`approveOption_${ApprovalMethod.APPROVE}`}
7480
selected={currentMethod === ApprovalMethod.APPROVE}
7581
value={ApprovalMethod.APPROVE}
7682
onClick={() => {

src/components/transactions/Repay/CollateralRepayActions.tsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import {
44
InterestRate,
55
ProtocolAction,
66
} from '@aave/contract-helpers';
7+
import { SignatureLike } from '@ethersproject/bytes';
78
import { Trans } from '@lingui/macro';
89
import { BoxProps } from '@mui/material';
910
import { useParaSwapTransactionHandler } from 'src/helpers/useParaSwapTransactionHandler';
1011
import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider';
11-
import { SwapTransactionParams } from 'src/hooks/paraswap/common';
12+
import { calculateSignedAmount, SwapTransactionParams } from 'src/hooks/paraswap/common';
1213
import { useRootStore } from 'src/store/root';
1314

1415
import { TxActionsWrapper } from '../TxActionsWrapper';
@@ -26,6 +27,9 @@ interface CollateralRepayBaseProps extends BoxProps {
2627
useFlashLoan: boolean;
2728
blocked: boolean;
2829
loading?: boolean;
30+
signature?: SignatureLike;
31+
signedAmount?: string;
32+
deadline?: string;
2933
}
3034

3135
// Used in poolSlice
@@ -50,11 +54,11 @@ export const CollateralRepayActions = ({
5054
buildTxFn,
5155
...props
5256
}: CollateralRepayBaseProps & { buildTxFn: () => Promise<SwapTransactionParams> }) => {
53-
const paraswapRepayWithCollateral = useRootStore((state) => state.paraswapRepayWithCollateral);
57+
const { paraswapRepayWithCollateral, currentMarketData } = useRootStore();
5458

55-
const { approval, action, requiresApproval, loadingTxns, approvalTxState, mainTxState } =
59+
const { approval, action, loadingTxns, approvalTxState, mainTxState, requiresApproval } =
5660
useParaSwapTransactionHandler({
57-
handleGetTxns: async () => {
61+
handleGetTxns: async (signature, deadline) => {
5862
const route = await buildTxFn();
5963
return paraswapRepayWithCollateral({
6064
repayAllDebt,
@@ -69,6 +73,9 @@ export const CollateralRepayActions = ({
6973
blocked,
7074
swapCallData: route.swapCallData,
7175
augustus: route.augustus,
76+
signature,
77+
deadline,
78+
signedAmount: calculateSignedAmount(repayWithAmount, fromAssetData.decimals),
7279
});
7380
},
7481
handleGetApprovalTxns: async () => {
@@ -89,12 +96,13 @@ export const CollateralRepayActions = ({
8996
},
9097
gasLimitRecommendation: gasLimitRecommendations[ProtocolAction.repayCollateral].limit,
9198
skip: loading || !repayAmount || parseFloat(repayAmount) === 0 || blocked,
99+
spender: currentMarketData.addresses.REPAY_WITH_COLLATERAL_ADAPTER ?? '',
100+
deps: [fromAssetData.symbol, repayWithAmount],
92101
});
93102

94103
return (
95104
<TxActionsWrapper
96105
preparingTransactions={loadingTxns}
97-
symbol={fromAssetData.symbol}
98106
mainTxState={mainTxState}
99107
approvalTxState={approvalTxState}
100108
requiresAmount
@@ -104,7 +112,12 @@ export const CollateralRepayActions = ({
104112
sx={sx}
105113
{...props}
106114
handleAction={action}
107-
handleApproval={() => approval()}
115+
handleApproval={() =>
116+
approval({
117+
amount: calculateSignedAmount(repayWithAmount, fromAssetData.decimals),
118+
underlyingAsset: fromAssetData.aTokenAddress,
119+
})
120+
}
108121
actionText={<Trans>Repay {symbol}</Trans>}
109122
actionInProgressText={<Trans>Repaying {symbol}</Trans>}
110123
fetchingData={loading}
@@ -114,6 +127,7 @@ export const CollateralRepayActions = ({
114127
content: <Trans>Repay {symbol}</Trans>,
115128
handleClick: action,
116129
}}
130+
tryPermit
117131
/>
118132
);
119133
};

src/components/transactions/Repay/CollateralRepayModalContent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { ListSlippageButton } from 'src/modules/dashboard/lists/SlippageList';
2020
import { calculateHFAfterRepay } from 'src/utils/hfUtils';
2121

2222
import { Asset, AssetInput } from '../AssetInput';
23-
import { GasEstimationError } from '../FlowCommons/GasEstimationError';
2423
import { ModalWrapperProps } from '../FlowCommons/ModalWrapper';
2524
import { TxSuccessView } from '../FlowCommons/Success';
2625
import {
@@ -29,6 +28,7 @@ import {
2928
TxModalDetails,
3029
} from '../FlowCommons/TxModalDetails';
3130
import { ErrorType, flashLoanNotAvailable, useFlashloan } from '../utils';
31+
import { ParaswapErrorDisplay } from '../Warnings/ParaswapErrorDisplay';
3232
import { CollateralRepayActions } from './CollateralRepayActions';
3333

3434
export function CollateralRepayModalContent({
@@ -302,7 +302,7 @@ export function CollateralRepayModalContent({
302302
/>
303303
</TxModalDetails>
304304

305-
{txError && <GasEstimationError txError={txError} />}
305+
{txError && <ParaswapErrorDisplay txError={txError} />}
306306

307307
<CollateralRepayActions
308308
poolReserve={poolReserve}

src/components/transactions/Swap/SwapActions.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import {
33
gasLimitRecommendations,
44
ProtocolAction,
55
} from '@aave/contract-helpers';
6+
import { SignatureLike } from '@ethersproject/bytes';
67
import { Trans } from '@lingui/macro';
78
import { BoxProps } from '@mui/material';
89
import { useParaSwapTransactionHandler } from 'src/helpers/useParaSwapTransactionHandler';
910
import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider';
10-
import { SwapTransactionParams } from 'src/hooks/paraswap/common';
11+
import { calculateSignedAmount, SwapTransactionParams } from 'src/hooks/paraswap/common';
1112
import { useRootStore } from 'src/store/root';
1213

1314
import { TxActionsWrapper } from '../TxActionsWrapper';
@@ -24,6 +25,9 @@ interface SwapBaseProps extends BoxProps {
2425
isMaxSelected: boolean;
2526
useFlashLoan: boolean;
2627
loading?: boolean;
28+
signature?: SignatureLike;
29+
deadline?: string;
30+
signedAmount?: string;
2731
}
2832

2933
export interface SwapActionProps extends SwapBaseProps {
@@ -46,11 +50,11 @@ export const SwapActions = ({
4650
buildTxFn,
4751
...props
4852
}: SwapBaseProps & { buildTxFn: () => Promise<SwapTransactionParams> }) => {
49-
const swapCollateral = useRootStore((state) => state.swapCollateral);
53+
const { swapCollateral, currentMarketData } = useRootStore();
5054

51-
const { approval, action, requiresApproval, approvalTxState, mainTxState, loadingTxns } =
55+
const { approval, action, approvalTxState, mainTxState, loadingTxns, requiresApproval } =
5256
useParaSwapTransactionHandler({
53-
handleGetTxns: async () => {
57+
handleGetTxns: async (signature, deadline) => {
5458
const route = await buildTxFn();
5559
return swapCollateral({
5660
amountToSwap: route.inputAmount,
@@ -64,6 +68,9 @@ export const SwapActions = ({
6468
useFlashLoan,
6569
swapCallData: route.swapCallData,
6670
augustus: route.augustus,
71+
signature,
72+
deadline,
73+
signedAmount: calculateSignedAmount(amountToSwap, poolReserve.decimals),
6774
});
6875
},
6976
handleGetApprovalTxns: async () => {
@@ -83,6 +90,8 @@ export const SwapActions = ({
8390
},
8491
gasLimitRecommendation: gasLimitRecommendations[ProtocolAction.swapCollateral].limit,
8592
skip: loading || !amountToSwap || parseFloat(amountToSwap) === 0,
93+
spender: currentMarketData.addresses.SWAP_COLLATERAL_ADAPTER ?? '',
94+
deps: [targetReserve.symbol, amountToSwap],
8695
});
8796

8897
return (
@@ -94,7 +103,12 @@ export const SwapActions = ({
94103
handleAction={action}
95104
requiresAmount
96105
amount={amountToSwap}
97-
handleApproval={() => approval()}
106+
handleApproval={() =>
107+
approval({
108+
amount: calculateSignedAmount(amountToSwap, poolReserve.decimals),
109+
underlyingAsset: poolReserve.aTokenAddress,
110+
})
111+
}
98112
requiresApproval={requiresApproval}
99113
actionText={<Trans>Swap</Trans>}
100114
actionInProgressText={<Trans>Swapping</Trans>}
@@ -106,6 +120,7 @@ export const SwapActions = ({
106120
content: <Trans>Swap</Trans>,
107121
handleClick: action,
108122
}}
123+
tryPermit
109124
{...props}
110125
/>
111126
);

src/components/transactions/Swap/SwapModalContent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import BigNumber from 'bignumber.js';
55
import React, { useRef, useState } from 'react';
66
import { PriceImpactTooltip } from 'src/components/infoTooltips/PriceImpactTooltip';
77
import { Asset, AssetInput } from 'src/components/transactions/AssetInput';
8-
import { GasEstimationError } from 'src/components/transactions/FlowCommons/GasEstimationError';
98
import { TxModalDetails } from 'src/components/transactions/FlowCommons/TxModalDetails';
109
import { useCollateralSwap } from 'src/hooks/paraswap/useCollateralSwap';
1110
import { useModalContext } from 'src/hooks/useModal';
@@ -22,6 +21,7 @@ import {
2221
import { ModalWrapperProps } from '../FlowCommons/ModalWrapper';
2322
import { TxSuccessView } from '../FlowCommons/Success';
2423
import { ErrorType, flashLoanNotAvailable, useFlashloan } from '../utils';
24+
import { ParaswapErrorDisplay } from '../Warnings/ParaswapErrorDisplay';
2525
import { SwapActions } from './SwapActions';
2626
import { SwapModalDetails } from './SwapModalDetails';
2727

@@ -237,7 +237,7 @@ export const SwapModalContent = ({
237237
/>
238238
</TxModalDetails>
239239

240-
{txError && <GasEstimationError txError={txError} />}
240+
{txError && <ParaswapErrorDisplay txError={txError} />}
241241

242242
<SwapActions
243243
isMaxSelected={isMaxSelected}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Trans } from '@lingui/macro';
2+
import { Box, Typography } from '@mui/material';
3+
import { Warning } from 'src/components/primitives/Warning';
4+
import { TxErrorType } from 'src/ui-config/errorMapping';
5+
6+
import { GasEstimationError } from '../FlowCommons/GasEstimationError';
7+
8+
const USER_DENIED_SIGNATURE = 'MetaMask Message Signature: User denied message signature.';
9+
const USER_DENIED_TRANSACTION = 'MetaMask Message Signature: User denied message signature.';
10+
11+
interface ErrorProps {
12+
txError: TxErrorType;
13+
}
14+
export const ParaswapErrorDisplay: React.FC<ErrorProps> = ({ txError }) => {
15+
return (
16+
<Box>
17+
<GasEstimationError txError={txError} />
18+
{txError.rawError.message !== USER_DENIED_SIGNATURE &&
19+
txError.rawError.message !== USER_DENIED_TRANSACTION && (
20+
<Box sx={{ pt: 4 }}>
21+
<Warning severity="info">
22+
<Typography variant="description">
23+
{' '}
24+
<Trans> Tip: Try increasing slippage or reduce input amount</Trans>
25+
</Typography>
26+
</Warning>
27+
</Box>
28+
)}
29+
</Box>
30+
);
31+
};

0 commit comments

Comments
 (0)