Skip to content

Commit d25a80a

Browse files
committed
feat: add insitutional vaults
1 parent 5d563ac commit d25a80a

128 files changed

Lines changed: 4907 additions & 520 deletions

File tree

Some content is hidden

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

.changeset/puny-lamps-check.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@venusprotocol/chains": minor
3+
"@venusprotocol/evm": minor
4+
---
5+
6+
add institutional vaults

apps/evm/src/__mocks__/models/vaults.ts

Lines changed: 139 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ import BigNumber from 'bignumber.js';
22
import type { GetFixedRatedVaultsOutput } from 'clients/api/queries/getFixedRatedVaults';
33

44
import {
5+
type InstitutionalVault,
56
type LockedDeposit,
67
VaultCategory,
78
VaultManager,
89
VaultStatus,
10+
VaultType,
911
type VenusVault,
1012
} from 'types';
1113

1214
import type { Address } from 'viem';
13-
import { vai, xvs } from './tokens';
15+
import { usdc, vai, xvs } from './tokens';
1416

1517
export const vaults: VenusVault[] = [
1618
{
@@ -22,10 +24,11 @@ export const vaults: VenusVault[] = [
2224
rewardTokenPriceCents: new BigNumber(100),
2325
dailyEmissionMantissa: new BigNumber('144000000000000000000'),
2426
dailyEmissionCents: 14400,
25-
totalStakedMantissa: new BigNumber('415000000000000000000'),
26-
totalStakedCents: 41500,
27-
stakingAprPercentage: 12665.060240963856,
27+
stakeBalanceMantissa: new BigNumber('415000000000000000000'),
28+
stakeBalanceCents: 41500,
29+
stakeAprPercentage: 12665.060240963856,
2830
category: VaultCategory.STABLECOINS,
31+
vaultType: VaultType.Venus,
2932
manager: VaultManager.Venus,
3033
managerIcon: 'logoMobile',
3134
status: VaultStatus.Active,
@@ -40,12 +43,13 @@ export const vaults: VenusVault[] = [
4043
rewardTokenPriceCents: new BigNumber(100),
4144
dailyEmissionMantissa: new BigNumber('144000000000000000000'),
4245
dailyEmissionCents: 14400,
43-
totalStakedMantissa: new BigNumber('400000000000000000000000000'),
44-
totalStakedCents: 40000000000,
45-
stakingAprPercentage: 12.92281835063781,
46-
userStakedMantissa: new BigNumber('233000000000000000000'),
47-
userStakedCents: 23300,
46+
stakeBalanceMantissa: new BigNumber('400000000000000000000000000'),
47+
stakeBalanceCents: 40000000000,
48+
stakeAprPercentage: 12.92281835063781,
49+
userStakeBalanceMantissa: new BigNumber('233000000000000000000'),
50+
userStakeBalanceCents: 23300,
4851
category: VaultCategory.GOVERNANCE,
52+
vaultType: VaultType.Venus,
4953
manager: VaultManager.Venus,
5054
managerIcon: 'logoMobile',
5155
status: VaultStatus.Active,
@@ -98,11 +102,137 @@ export const fixedRatedVaults: GetFixedRatedVaultsOutput = [
98102
maturityDate: '2026-06-25T00:00:00.000Z',
99103
createdAt: '2026-01-21T20:14:15.000Z',
100104
updatedAt: '2026-01-21T20:14:15.000Z',
105+
tokenPrices: [
106+
{
107+
id: 'fake-price-pendle',
108+
tokenAddress: '0xe052823b4aefc6e230FAf46231A57d0905E30AE0',
109+
tokenWrappedAddress: null,
110+
chainId: '56',
111+
priceMantissa: '682687557196753800000000000000000000000',
112+
priceSource: 'oracle',
113+
priceOracleAddress: '0x0000000000000000000000000000000000000001',
114+
mainOracleAddress: '0x0000000000000000000000000000000000000001',
115+
mainOracleName: 'ResilientOracle',
116+
isPriceInvalid: false,
117+
hasErrorFetchingPrice: false,
118+
createdAt: '2026-01-21T20:14:15.000Z',
119+
updatedAt: '2026-01-21T20:14:15.000Z',
120+
},
121+
],
122+
},
123+
],
124+
},
125+
{
126+
id: '97-institutional-0x5263D68786AaCfad74B9aa385A004c272548e8B7',
127+
chainId: '97',
128+
protocol: 'institutional-vault',
129+
vaultAddress: '0x5263D68786AaCfad74B9aa385A004c272548e8B7',
130+
underlyingAssetAddress: '0x312e39c7641cE64BEccDe53613f07952258fa810',
131+
fixedApyDecimal: '0.08',
132+
maturityDate: '2026-09-01T00:00:00.000Z',
133+
protocolData: {
134+
collateralAssetAddress: '0xCC3933141a64E26C9317b19CE4BbB4ec2c333bc6',
135+
institutionOperatorAddress: '0x1111111111111111111111111111111111111111',
136+
latePenaltyRateMantissa: '0',
137+
lockDurationSeconds: 2592000,
138+
openDurationSeconds: 604800,
139+
settlementWindowSeconds: 259200,
140+
},
141+
createdAt: '2026-04-01T00:00:00.000Z',
142+
updatedAt: '2026-04-01T00:00:00.000Z',
143+
loanVaultDetail: {
144+
chainId: '97',
145+
collateralAssetAddress: '0xCC3933141a64E26C9317b19CE4BbB4ec2c333bc6',
146+
collateralValueCents: '0',
147+
createdAt: '2026-04-01T00:00:00.000Z',
148+
debtValueCents: '0',
149+
fixedRateVaultId: '97-institutional-0x5263D68786AaCfad74B9aa385A004c272548e8B7',
150+
id: 'loan-vault-detail-1',
151+
institutionAddress: '0x1111111111111111111111111111111111111111',
152+
latePenaltyRateMantissa: '0',
153+
liquidationIncentiveMantissa: '0',
154+
liquidationThresholdMantissa: '0',
155+
liquidityMantissa: '500000000000',
156+
lockEndTime: '2026-08-29T00:00:00.000Z',
157+
maxBorrowCapMantissa: '1000000000000',
158+
minBorrowCapMantissa: '100000000000',
159+
minSupplierDepositMantissa: '10000000',
160+
openEndTime: '2026-04-08T00:00:00.000Z',
161+
outstandingDebtMantissa: '0',
162+
reserveFactorMantissa: '0',
163+
settlementDeadline: '2026-09-01T00:00:00.000Z',
164+
shortfallMantissa: '0',
165+
supplyAssetAddress: '0x312e39c7641cE64BEccDe53613f07952258fa810',
166+
totalOwedMantissa: '0',
167+
totalRaisedMantissa: '500000000000',
168+
updatedAt: '2026-04-01T00:00:00.000Z',
169+
vaultState: 2,
170+
},
171+
underlyingToken: [
172+
{
173+
address: '0x312e39c7641cE64BEccDe53613f07952258fa810',
174+
chainId: '97',
175+
name: 'Mock USDC',
176+
symbol: 'MOCK_USDC',
177+
decimals: 6,
178+
maturityDate: '2026-09-01T00:00:00.000Z',
179+
createdAt: '2026-04-01T00:00:00.000Z',
180+
updatedAt: '2026-04-01T00:00:00.000Z',
181+
tokenPrices: [
182+
{
183+
id: 'fake-price-institutional',
184+
tokenAddress: '0x312e39c7641cE64BEccDe53613f07952258fa810',
185+
tokenWrappedAddress: null,
186+
chainId: '97',
187+
priceMantissa: '1000000000000000000000000000000',
188+
priceSource: 'oracle',
189+
priceOracleAddress: '0x0000000000000000000000000000000000000001',
190+
mainOracleAddress: '0x0000000000000000000000000000000000000001',
191+
mainOracleName: 'ResilientOracle',
192+
isPriceInvalid: false,
193+
hasErrorFetchingPrice: false,
194+
createdAt: '2026-04-01T00:00:00.000Z',
195+
updatedAt: '2026-04-01T00:00:00.000Z',
196+
},
197+
],
101198
},
102199
],
103200
},
104201
];
105202

203+
export const institutionalVault: InstitutionalVault = {
204+
vaultType: VaultType.Institutional,
205+
category: VaultCategory.STABLECOINS,
206+
manager: VaultManager.Ceffu,
207+
managerIcon: 'ceefu',
208+
managerAddress: '0x1111111111111111111111111111111111111111',
209+
managerLink: 'https://www.ceffu.com',
210+
status: VaultStatus.Pending,
211+
key: '97-institutional-0x5263D68786AaCfad74B9aa385A004c272548e8B7',
212+
stakedToken: usdc,
213+
rewardToken: usdc,
214+
stakedTokenPriceCents: new BigNumber(100),
215+
rewardTokenPriceCents: new BigNumber(100),
216+
stakeAprPercentage: 8,
217+
stakeBalanceMantissa: new BigNumber('500000000000'),
218+
stakeBalanceCents: 50000000,
219+
userStakeBalanceMantissa: new BigNumber('100000000'),
220+
userStakeBalanceCents: 10000,
221+
vaultAddress: '0x5263D68786AaCfad74B9aa385A004c272548e8B7',
222+
reserveFactor: 0,
223+
vaultDeploymentDate: new Date('2026-04-01T00:00:00.000Z'),
224+
openEndDate: new Date('2026-04-08T00:00:00.000Z'),
225+
lockEndDate: new Date('2026-08-29T00:00:00.000Z'),
226+
maturityDate: new Date('2026-09-01T00:00:00.000Z'),
227+
settlementDate: new Date('2026-09-01T00:00:00.000Z'),
228+
stakeLimitMantissa: new BigNumber('1000000000000'),
229+
stakeMinMantissa: new BigNumber('100000000000'),
230+
userMinIndividualStakeMantissa: new BigNumber('10000000'),
231+
userRedeemLimitMantissa: new BigNumber(0),
232+
userWithdrawLimitMantissa: new BigNumber(0),
233+
lockingPeriodMs: 2592000 * 1000,
234+
};
235+
106236
export const lockedDeposits: LockedDeposit[] = [
107237
{
108238
amountMantissa: new BigNumber('1000000000000000000'),

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,3 +1073,38 @@ export const useGetFixedRatedVaults = vi.fn(() => ({
10731073
}));
10741074

10751075
export const getFixedRatedVaults = vi.fn(async () => fixedRatedVaults);
1076+
1077+
export const useGetFixedRatedVaultUserStakedTokens = vi.fn(() => ({
1078+
data: [],
1079+
isLoading: false,
1080+
}));
1081+
1082+
export const getFixedRatedVaultUserStakedTokens = vi.fn(async () => []);
1083+
1084+
export const useGetInstitutionalVaultUserMetrics = vi.fn(() => ({
1085+
data: [],
1086+
isLoading: false,
1087+
}));
1088+
1089+
export const getInstitutionalVaultUserMetrics = vi.fn(async () => []);
1090+
1091+
export const useStakeIntoInstitutionalVault = vi.fn((options?: MutationObserverOptions) =>
1092+
useMutation({
1093+
mutationFn: vi.fn(),
1094+
...options,
1095+
}),
1096+
);
1097+
1098+
export const useRedeemFromInstitutionalVault = vi.fn((options?: MutationObserverOptions) =>
1099+
useMutation({
1100+
mutationFn: vi.fn(),
1101+
...options,
1102+
}),
1103+
);
1104+
1105+
export const useWithdrawFromInstitutionalVault = vi.fn((options?: MutationObserverOptions) =>
1106+
useMutation({
1107+
mutationFn: vi.fn(),
1108+
...options,
1109+
}),
1110+
);

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export * from './mutations/useWithdrawTradePositionCollateral';
4242
export * from './mutations/useRepayWithCollateral';
4343
export * from './mutations/useStakeInPendleVault';
4444
export * from './mutations/useWithdrawFromPendleVault';
45+
export * from './mutations/useStakeIntoInstitutionalVault';
46+
export * from './mutations/useRedeemFromInstitutionalVault';
47+
export * from './mutations/useWithdrawFromInstitutionalVault';
4548
// Queries
4649
export * from './queries/getVaiTreasuryPercentage';
4750
export * from './queries/getVaiTreasuryPercentage/useGetVaiTreasuryPercentage';
@@ -259,6 +262,15 @@ export * from './queries/getProposalCount/useGetProposalCount';
259262
export * from './queries/getFixedRatedVaults';
260263
export * from './queries/getFixedRatedVaults/useGetFixedRatedVaults';
261264

265+
export * from './queries/getInstitutionalVaultUserData';
266+
export * from './queries/getInstitutionalVaultUserData/useGetInstitutionalVaultUserData';
267+
268+
export * from './queries/getFixedRatedVaultUserStakedTokens';
269+
export * from './queries/getFixedRatedVaultUserStakedTokens/useGetFixedRatedVaultUserStakedTokens';
270+
271+
export * from './queries/getInstitutionalVaultUserMetrics';
272+
export * from './queries/getInstitutionalVaultUserMetrics/useGetInstitutionalVaultUserMetrics';
273+
262274
export * from './queries/getPendleSwapQuote';
263275
export * from './queries/getPendleSwapQuote/useGetPendleSwapQuote';
264276

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { queryClient } from 'clients/api/queryClient';
2+
import FunctionKey from 'constants/functionKey';
3+
import type { Mock } from 'vitest';
4+
5+
import { invalidateInstitutionalVaultQueries } from '..';
6+
7+
describe('clients/api/mutations/invalidateInstitutionalVaultQueries', () => {
8+
beforeEach(() => {
9+
vi.clearAllMocks();
10+
});
11+
12+
it('invalidates all institutional vault queries including token balances', () => {
13+
invalidateInstitutionalVaultQueries();
14+
15+
expect(queryClient.invalidateQueries).toHaveBeenCalledTimes(4);
16+
expect((queryClient.invalidateQueries as Mock).mock.calls).toEqual([
17+
[{ queryKey: [FunctionKey.GET_BALANCE_OF] }],
18+
[{ queryKey: [FunctionKey.GET_FIXED_RATED_VAULTS] }],
19+
[{ queryKey: [FunctionKey.GET_TOKEN_BALANCES] }],
20+
[{ queryKey: [FunctionKey.GET_INSTITUTIONAL_VAULT_USER_DATA] }],
21+
]);
22+
});
23+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { queryClient } from 'clients/api/queryClient';
2+
import FunctionKey from 'constants/functionKey';
3+
4+
export const invalidateInstitutionalVaultQueries = () => {
5+
queryClient.invalidateQueries({ queryKey: [FunctionKey.GET_BALANCE_OF] });
6+
queryClient.invalidateQueries({ queryKey: [FunctionKey.GET_FIXED_RATED_VAULTS] });
7+
queryClient.invalidateQueries({
8+
queryKey: [FunctionKey.GET_TOKEN_BALANCES],
9+
});
10+
queryClient.invalidateQueries({
11+
queryKey: [FunctionKey.GET_INSTITUTIONAL_VAULT_USER_DATA],
12+
});
13+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type BigNumber from 'bignumber.js';
2+
import { type UseSendTransactionOptions, useSendTransaction } from 'hooks/useSendTransaction';
3+
import { useAnalytics } from 'libs/analytics';
4+
import { institutionalVaultAbi } from 'libs/contracts/abis/institutionalVaultAbi';
5+
import { VError } from 'libs/errors';
6+
import { useAccountAddress } from 'libs/wallet';
7+
import type { Address } from 'viem';
8+
import { invalidateInstitutionalVaultQueries } from '../invalidateInstitutionalVaultQueries';
9+
10+
type RedeemFromInstitutionalVaultInput = {
11+
amountMantissa: BigNumber;
12+
};
13+
14+
type Options = UseSendTransactionOptions<RedeemFromInstitutionalVaultInput>;
15+
16+
export const useRedeemFromInstitutionalVault = (
17+
{ vaultAddress }: { vaultAddress: Address },
18+
options?: Partial<Options>,
19+
) => {
20+
const { accountAddress } = useAccountAddress();
21+
const { captureAnalyticEvent } = useAnalytics();
22+
23+
return useSendTransaction({
24+
fn: ({ amountMantissa }: RedeemFromInstitutionalVaultInput) => {
25+
if (!accountAddress) {
26+
throw new VError({
27+
type: 'unexpected',
28+
code: 'somethingWentWrong',
29+
});
30+
}
31+
32+
return {
33+
abi: institutionalVaultAbi,
34+
address: vaultAddress,
35+
functionName: 'redeem' as const,
36+
args: [BigInt(amountMantissa.toFixed()), accountAddress, accountAddress] as const,
37+
};
38+
},
39+
onConfirmed: () => {
40+
captureAnalyticEvent('Institutional vault redeem', {
41+
vaultAddress,
42+
accountAddress,
43+
});
44+
45+
invalidateInstitutionalVaultQueries();
46+
},
47+
options,
48+
});
49+
};

0 commit comments

Comments
 (0)