Skip to content

Commit 47c052d

Browse files
committed
feat: wip
1 parent e6b8f35 commit 47c052d

14 files changed

Lines changed: 892 additions & 32 deletions

File tree

apps/evm/src/clients/api/__mocks__/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,14 @@ export const useBorrow = vi.fn((_variables: never, options?: MutationObserverOpt
687687
}),
688688
);
689689

690+
export const useOpenLeveragedPosition = vi.fn(
691+
(_variables: never, options?: MutationObserverOptions) =>
692+
useMutation({
693+
mutationFn: vi.fn(),
694+
...options,
695+
}),
696+
);
697+
690698
export const withdrawXvs = vi.fn();
691699
export const useWithdrawXvs = (options?: MutationObserverOptions) =>
692700
useMutation({

apps/evm/src/clients/api/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export * from './mutations/useSwapTokens';
3131
export * from './mutations/useWithdraw';
3232
export * from './mutations/useImportSupplyPosition';
3333
export * from './mutations/useSetEModeGroup';
34+
export * from './mutations/useOpenLeveragedPosition';
3435

3536
// Queries
3637
export * from './queries/getVaiTreasuryPercentage';
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { queryClient } from 'clients/api';
2+
import FunctionKey from 'constants/functionKey';
3+
import { useGetContractAddress } from 'hooks/useGetContractAddress';
4+
import { type UseSendTransactionOptions, useSendTransaction } from 'hooks/useSendTransaction';
5+
import { leverageManagerAbi } from 'libs/contracts';
6+
import { VError } from 'libs/errors';
7+
import type { ExactInSwapQuote, VToken } from 'types';
8+
9+
type OpenLeveragedPositionInput = {
10+
swapQuote: ExactInSwapQuote;
11+
borrowedVToken: VToken;
12+
suppliedVToken: VToken;
13+
};
14+
15+
type Options = UseSendTransactionOptions<OpenLeveragedPositionInput>;
16+
17+
// TODO: add tests
18+
19+
export const useOpenLeveragedPosition = (options?: Partial<Options>) => {
20+
const { address: leverageManagerContractAddress } = useGetContractAddress({
21+
name: 'LeverageManager',
22+
});
23+
24+
return useSendTransaction({
25+
fn: ({ swapQuote, suppliedVToken, borrowedVToken }: OpenLeveragedPositionInput) => {
26+
if (!leverageManagerContractAddress) {
27+
throw new VError({ type: 'unexpected', code: 'somethingWentWrong' });
28+
}
29+
30+
console.log(
31+
'_minAmountOutAfterSwap (sent to contract)',
32+
swapQuote.minimumToTokenAmountReceivedMantissa,
33+
);
34+
35+
console.log([
36+
suppliedVToken.address,
37+
borrowedVToken.address,
38+
0n,
39+
swapQuote.fromTokenAmountSoldMantissa,
40+
swapQuote.minimumToTokenAmountReceivedMantissa,
41+
swapQuote.callData,
42+
]);
43+
44+
return {
45+
abi: leverageManagerAbi,
46+
address: leverageManagerContractAddress,
47+
functionName: 'enterLeverageFromBorrow',
48+
args: [
49+
suppliedVToken.address,
50+
borrowedVToken.address,
51+
0n,
52+
swapQuote.fromTokenAmountSoldMantissa,
53+
swapQuote.minimumToTokenAmountReceivedMantissa,
54+
swapQuote.callData,
55+
],
56+
};
57+
},
58+
onConfirmed: () => {
59+
// TODO: send analytic event
60+
61+
queryClient.invalidateQueries({
62+
queryKey: [FunctionKey.GET_POOLS],
63+
});
64+
},
65+
options,
66+
});
67+
};

apps/evm/src/clients/api/queries/getSwapQuote/formatSwapQuote/index.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,63 @@ import type {
66
Token,
77
} from 'types';
88
import type { ApiSwapQuote } from '../types';
9+
import { subtractSlippagePercentage } from './subtractSlippagePercentage';
910

1011
export const formatSwapQuote = ({
1112
direction,
1213
fromToken,
1314
toToken,
1415
apiSwapQuote,
16+
slippagePercentage,
1517
}: {
1618
direction: SwapQuoteDirection;
1719
fromToken: Token;
1820
toToken: Token;
1921
apiSwapQuote: ApiSwapQuote;
22+
slippagePercentage: number;
2023
}) => {
2124
const sharedProps = {
2225
fromToken,
2326
toToken,
2427
priceImpactPercentage: apiSwapQuote.priceImpact,
28+
callData: apiSwapQuote.swapHelperMulticall.calldata.encodedCall,
2529
};
2630

2731
if (direction === 'exact-in') {
32+
const expectedToTokenAmountReceivedMantissa = BigInt(apiSwapQuote.amountOut);
33+
const minimumToTokenAmountReceivedMantissa = subtractSlippagePercentage({
34+
amount: expectedToTokenAmountReceivedMantissa,
35+
slippagePercentage,
36+
});
37+
38+
console.log('amountOut (API)', expectedToTokenAmountReceivedMantissa);
39+
console.log(
40+
'min amountOut (amountOut - slippage tolerance)',
41+
minimumToTokenAmountReceivedMantissa,
42+
);
43+
2844
const swapQuote: ExactInSwapQuote = {
2945
...sharedProps,
3046
fromTokenAmountSoldMantissa: BigInt(apiSwapQuote.amountIn),
31-
expectedToTokenAmountReceivedMantissa: BigInt(apiSwapQuote.amountOut),
32-
minimumToTokenAmountReceivedMantissa: BigInt(apiSwapQuote.amountOut), // TODO: replace with amountOutMin once API returns it
47+
expectedToTokenAmountReceivedMantissa,
48+
minimumToTokenAmountReceivedMantissa,
3349
direction: 'exact-in',
3450
};
3551

3652
return swapQuote;
3753
}
3854

3955
if (direction === 'exact-out') {
56+
const expectedFromTokenAmountSoldMantissa = BigInt(apiSwapQuote.amountIn);
57+
const maximumFromTokenAmountSoldMantissa = subtractSlippagePercentage({
58+
amount: expectedFromTokenAmountSoldMantissa,
59+
slippagePercentage,
60+
});
61+
4062
const swapQuote: ExactOutSwapQuote = {
4163
...sharedProps,
42-
expectedFromTokenAmountSoldMantissa: BigInt(apiSwapQuote.amountIn),
43-
maximumFromTokenAmountSoldMantissa: BigInt(apiSwapQuote.amountIn), // TODO: replace with amountInMax once API returns it
64+
expectedFromTokenAmountSoldMantissa,
65+
maximumFromTokenAmountSoldMantissa,
4466
toTokenAmountReceivedMantissa: BigInt(apiSwapQuote.amountOut),
4567
direction: 'exact-out',
4668
};
@@ -49,11 +71,17 @@ export const formatSwapQuote = ({
4971
}
5072

5173
// Approximate out swap
74+
const expectedToTokenAmountReceivedMantissa = BigInt(apiSwapQuote.amountOut);
75+
const minimumToTokenAmountReceivedMantissa = subtractSlippagePercentage({
76+
amount: expectedToTokenAmountReceivedMantissa,
77+
slippagePercentage,
78+
});
79+
5280
const swapQuote: ApproximateOutSwapQuote = {
5381
...sharedProps,
5482
fromTokenAmountSoldMantissa: BigInt(apiSwapQuote.amountIn),
55-
expectedToTokenAmountReceivedMantissa: BigInt(apiSwapQuote.amountOut),
56-
minimumToTokenAmountReceivedMantissa: BigInt(apiSwapQuote.amountOut), // TODO: replace with amountOutMin once API returns it
83+
expectedToTokenAmountReceivedMantissa,
84+
minimumToTokenAmountReceivedMantissa,
5785
direction: 'approximate-out',
5886
};
5987

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const subtractSlippagePercentage = ({
2+
amount,
3+
slippagePercentage,
4+
}: { amount: bigint; slippagePercentage: number }) =>
5+
BigInt((Number(amount) * (100 - slippagePercentage)) / 100);

apps/evm/src/clients/api/queries/getSwapQuote/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ export const getSwapQuote = async ({
1515
}: GetSwapQuoteInput): Promise<GetSwapQuoteOutput> => {
1616
const transactionDeadline = generateTransactionDeadline();
1717

18-
const params: Record<string, number | string> = {
18+
const params: Record<string, number | string | boolean> = {
1919
chainId,
2020
tokenInAddress: fromToken.address,
2121
tokenOutAddress: toToken.address,
2222
slippagePercentage,
2323
recipientAddress,
2424
deadlineTimestampSecs: Number(transactionDeadline),
2525
type: swapSpecificProps.direction,
26+
shouldTransferToReceiver: true,
2627
};
2728

2829
if (swapSpecificProps.direction === 'exact-in') {
@@ -70,6 +71,7 @@ export const getSwapQuote = async ({
7071
const swapQuote = formatSwapQuote({
7172
apiSwapQuote,
7273
direction: swapSpecificProps.direction,
74+
slippagePercentage: Number(params.slippagePercentage),
7375
fromToken,
7476
toToken,
7577
});

apps/evm/src/clients/api/queries/getSwapQuote/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface ApiSwapQuote {
1111
priceImpact: number;
1212
swapHelperMulticall: {
1313
target: Address;
14-
callData: {
14+
calldata: {
1515
encodedCall: Hex;
1616
};
1717
};

apps/evm/src/hooks/useSendTransaction/index.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,25 @@ import type {
33
GetSupertransactionReceiptPayloadWithReceipts,
44
} from '@biconomy/abstractjs';
55
import { type MutationObserverOptions, useMutation } from '@tanstack/react-query';
6+
import { getDefaultConfig } from 'connectkit';
7+
import { http, createConfig } from 'wagmi';
68

9+
import { MAIN_PRODUCTION_HOST } from 'constants/production';
710
import type { TransactionType } from 'types';
811

912
import { useGetPaymasterInfo } from 'clients/api';
1013
import { store as resendPayingGasModalStore } from 'containers/ResendPayingGasModal/store';
1114
import { useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled';
1215
import { useUserChainSettings } from 'hooks/useUserChainSettings';
1316
import { VError, logError } from 'libs/errors';
14-
import { useAccountAddress, useChainId, useMeeClient, usePublicClient } from 'libs/wallet';
17+
import {
18+
WALLET_CONNECT_PROJECT_ID,
19+
chains,
20+
useAccountAddress,
21+
useChainId,
22+
useMeeClient,
23+
usePublicClient,
24+
} from 'libs/wallet';
1525
import type {
1626
Abi,
1727
Account,
@@ -25,6 +35,33 @@ import { useWalletClient } from 'wagmi';
2535
import { sendTransaction } from './sendTransaction';
2636
import { useTrackTransaction } from './useTrackTransaction';
2737

38+
// DEV ONLY
39+
const connectKitConfig = getDefaultConfig({
40+
chains: chains as [Chain, ...Chain[]],
41+
transports: chains.reduce((acc, chain) => {
42+
return {
43+
...acc,
44+
[chain.id]: http(
45+
'https://virtual.binance.eu.rpc.tenderly.co/365d5150-fa4d-4dc6-a063-5992326a0fec',
46+
),
47+
};
48+
}, {}),
49+
walletConnectProjectId: WALLET_CONNECT_PROJECT_ID,
50+
appName: 'Venus',
51+
appUrl: `https://${MAIN_PRODUCTION_HOST}`,
52+
appDescription:
53+
'Venus is a decentralized finance (DeFi) algorithmic money market protocol on EVM networks.',
54+
appIcon: 'https://venus.io/180x180.png',
55+
batch: {
56+
multicall: {
57+
wait: 50,
58+
},
59+
},
60+
});
61+
62+
const config = createConfig(connectKitConfig);
63+
// END DEV ONLY
64+
2865
export interface UseSendTransactionOptions<TMutateInput extends Record<string, unknown> | void>
2966
extends MutationObserverOptions<unknown, Error, TMutateInput> {
3067
waitForConfirmation?: boolean;
@@ -80,7 +117,12 @@ export const useSendTransaction = <
80117
const tryGasless = options?.tryGasless ?? true;
81118

82119
const { accountAddress } = useAccountAddress();
83-
const { data: walletClient } = useWalletClient({ account: accountAddress });
120+
const { data: walletClient } = useWalletClient({
121+
account: accountAddress,
122+
// DEV ONLY
123+
config,
124+
// END DEV ONLY
125+
});
84126
const { publicClient } = usePublicClient();
85127
const { chainId } = useChainId();
86128

0 commit comments

Comments
 (0)