Skip to content

Commit eaf9b9f

Browse files
authored
feat: wire up Boost tab (#5044)
1 parent b1e40a4 commit eaf9b9f

File tree

58 files changed

+3376
-245
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3376
-245
lines changed

.changeset/twenty-sites-smile.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@venusprotocol/evm": minor
3+
---
4+
5+
add base Boost tab

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,16 @@ export const useGetAccountTransactionHistory = vi.fn(() =>
613613
}),
614614
);
615615

616+
export const getSwapQuote = vi.fn(async () => ({
617+
swapQuote: undefined,
618+
}));
619+
export const useGetSwapQuote = vi.fn(() =>
620+
useQuery({
621+
queryKey: [FunctionKey.GET_SWAP_QUOTE],
622+
queryFn: getSwapQuote,
623+
}),
624+
);
625+
616626
// Mutations
617627
export const useApproveToken = vi.fn((_variables: never, options?: MutationObserverOptions) =>
618628
useMutation({
@@ -677,6 +687,14 @@ export const useBorrow = vi.fn((_variables: never, options?: MutationObserverOpt
677687
}),
678688
);
679689

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

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

Lines changed: 4 additions & 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';
@@ -226,3 +227,6 @@ export * from './queries/getAccountTransactionHistory/useGetAccountTransactionHi
226227

227228
export * from './queries/getSimulatedPool';
228229
export * from './queries/getSimulatedPool/useGetSimulatedPool';
230+
231+
export * from './queries/getSwapQuote';
232+
export * from './queries/getSwapQuote/useGetSwapQuote';
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`useOpenLeveragedPosition > calls useSendTransaction with correct parameters 'with single asset' 1`] = `
4+
{
5+
"abi": Any<Array>,
6+
"address": "0xfakeLeverageManagerContractAddress",
7+
"args": [
8+
"0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7",
9+
0n,
10+
100000000n,
11+
],
12+
"functionName": "enterSingleAssetLeverage",
13+
}
14+
`;
15+
16+
exports[`useOpenLeveragedPosition > calls useSendTransaction with correct parameters 'with single asset' 2`] = `
17+
[
18+
[
19+
{
20+
"queryKey": [
21+
"GET_POOLS",
22+
],
23+
},
24+
],
25+
]
26+
`;
27+
28+
exports[`useOpenLeveragedPosition > calls useSendTransaction with correct parameters 'with swapQuote' 1`] = `
29+
{
30+
"abi": Any<Array>,
31+
"address": "0xfakeLeverageManagerContractAddress",
32+
"args": [
33+
"0x170d3b2da05cc2124334240fB34ad1359e34C562",
34+
0n,
35+
"0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7",
36+
100000000n,
37+
100000000n,
38+
"0x",
39+
],
40+
"functionName": "enterLeverage",
41+
}
42+
`;
43+
44+
exports[`useOpenLeveragedPosition > calls useSendTransaction with correct parameters 'with swapQuote' 2`] = `
45+
[
46+
[
47+
{
48+
"queryKey": [
49+
"GET_POOLS",
50+
],
51+
},
52+
],
53+
]
54+
`;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { lisUsd, usdc } from '__mocks__/models/tokens';
2+
import { vLisUSD, vUsdc } from '__mocks__/models/vTokens';
3+
import { queryClient } from 'clients/api';
4+
import { useGetContractAddress } from 'hooks/useGetContractAddress';
5+
import { useSendTransaction } from 'hooks/useSendTransaction';
6+
import { renderHook } from 'testUtils/render';
7+
import type { ExactInSwapQuote } from 'types';
8+
import type { Mock } from 'vitest';
9+
import { useOpenLeveragedPosition } from '..';
10+
11+
const fakeSwapQuote: ExactInSwapQuote = {
12+
fromToken: usdc,
13+
toToken: lisUsd,
14+
direction: 'exact-in',
15+
priceImpactPercentage: 0.1,
16+
fromTokenAmountSoldMantissa: 100000000n,
17+
expectedToTokenAmountReceivedMantissa: 100000000n,
18+
minimumToTokenAmountReceivedMantissa: 100000000n,
19+
callData: '0x',
20+
};
21+
22+
vi.mock('libs/contracts');
23+
24+
describe('useOpenLeveragedPosition', () => {
25+
it.each([
26+
{
27+
label: 'with swapQuote',
28+
input: {
29+
borrowedVToken: vUsdc,
30+
suppliedVToken: vLisUSD,
31+
swapQuote: fakeSwapQuote,
32+
},
33+
},
34+
{
35+
label: 'with single asset',
36+
input: {
37+
vToken: vUsdc,
38+
amountMantissa: 100000000n,
39+
},
40+
},
41+
])('calls useSendTransaction with correct parameters $label', async ({ input }) => {
42+
renderHook(() => useOpenLeveragedPosition());
43+
44+
expect(useSendTransaction).toHaveBeenCalledWith({
45+
fn: expect.any(Function),
46+
onConfirmed: expect.any(Function),
47+
options: undefined,
48+
});
49+
50+
const { fn } = (useSendTransaction as jest.Mock).mock.calls[0][0];
51+
52+
expect(await fn(input)).toMatchSnapshot({
53+
abi: expect.any(Array),
54+
});
55+
56+
const { onConfirmed } = (useSendTransaction as jest.Mock).mock.calls[0][0];
57+
await onConfirmed();
58+
59+
expect((queryClient.invalidateQueries as Mock).mock.calls).toMatchSnapshot();
60+
});
61+
62+
it('throws error when LeverageManager contract address is not found', async () => {
63+
(useGetContractAddress as Mock).mockImplementation(() => ({ address: undefined }));
64+
65+
renderHook(() => useOpenLeveragedPosition());
66+
67+
const { fn } = (useSendTransaction as Mock).mock.calls[0][0];
68+
69+
await expect(async () =>
70+
fn({
71+
vToken: vUsdc,
72+
amountMantissa: 100000000n,
73+
}),
74+
).rejects.toThrow('somethingWentWrong');
75+
});
76+
});
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import type { Account, Address, Chain, Hex, WriteContractParameters } from 'viem';
2+
3+
import { queryClient } from 'clients/api';
4+
import FunctionKey from 'constants/functionKey';
5+
import { useGetContractAddress } from 'hooks/useGetContractAddress';
6+
import { type UseSendTransactionOptions, useSendTransaction } from 'hooks/useSendTransaction';
7+
import { leverageManagerAbi } from 'libs/contracts';
8+
import { VError } from 'libs/errors';
9+
import type { ExactInSwapQuote, VToken } from 'types';
10+
11+
type OpenLeveragedPositionWithSwapInput = {
12+
swapQuote: ExactInSwapQuote;
13+
borrowedVToken: VToken;
14+
suppliedVToken: VToken;
15+
};
16+
17+
type OpenLeveragedPositionWithSingleAssetInput = {
18+
vToken: VToken;
19+
amountMantissa: bigint;
20+
};
21+
22+
type OpenLeveragedPositionInput =
23+
| OpenLeveragedPositionWithSwapInput
24+
| OpenLeveragedPositionWithSingleAssetInput;
25+
26+
type Options = UseSendTransactionOptions<OpenLeveragedPositionInput>;
27+
28+
export const useOpenLeveragedPosition = (options?: Partial<Options>) => {
29+
const { address: leverageManagerContractAddress } = useGetContractAddress({
30+
name: 'LeverageManager',
31+
});
32+
33+
return useSendTransaction({
34+
fn: (input: OpenLeveragedPositionInput) => {
35+
if (!leverageManagerContractAddress) {
36+
throw new VError({ type: 'unexpected', code: 'somethingWentWrong' });
37+
}
38+
39+
if ('swapQuote' in input) {
40+
return {
41+
abi: leverageManagerAbi,
42+
address: leverageManagerContractAddress,
43+
functionName: 'enterLeverage',
44+
args: [
45+
input.suppliedVToken.address,
46+
0n,
47+
input.borrowedVToken.address,
48+
input.swapQuote.fromTokenAmountSoldMantissa,
49+
input.swapQuote.minimumToTokenAmountReceivedMantissa,
50+
input.swapQuote.callData,
51+
],
52+
} as WriteContractParameters<
53+
typeof leverageManagerAbi,
54+
'enterLeverage',
55+
readonly [Address, bigint, Address, bigint, bigint, Hex],
56+
Chain,
57+
Account
58+
>;
59+
}
60+
61+
return {
62+
abi: leverageManagerAbi,
63+
address: leverageManagerContractAddress,
64+
functionName: 'enterSingleAssetLeverage',
65+
args: [input.vToken.address, 0n, input.amountMantissa],
66+
} as WriteContractParameters<
67+
typeof leverageManagerAbi,
68+
'enterSingleAssetLeverage',
69+
readonly [Address, bigint, bigint],
70+
Chain,
71+
Account
72+
>;
73+
},
74+
onConfirmed: () => {
75+
// TODO: send analytic event
76+
77+
queryClient.invalidateQueries({
78+
queryKey: [FunctionKey.GET_POOLS],
79+
});
80+
},
81+
options,
82+
});
83+
};

0 commit comments

Comments
 (0)