Skip to content

Commit 2e4f2ed

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

121 files changed

Lines changed: 4383 additions & 454 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: 135 additions & 7 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+
stakeBalanceMantissa: new BigNumber('415000000000000000000'),
28+
stakeBalanceCents: 41500,
2729
stakingAprPercentage: 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,
46+
stakeBalanceMantissa: new BigNumber('400000000000000000000000000'),
47+
stakeBalanceCents: 40000000000,
4548
stakingAprPercentage: 12.92281835063781,
46-
userStakedMantissa: new BigNumber('233000000000000000000'),
47-
userStakedCents: 23300,
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,135 @@ 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+
openEndTime: '2026-04-08T00:00:00.000Z',
160+
outstandingDebtMantissa: '0',
161+
reserveFactorMantissa: '0',
162+
settlementDeadline: '2026-09-01T00:00:00.000Z',
163+
shortfallMantissa: '0',
164+
supplyAssetAddress: '0x312e39c7641cE64BEccDe53613f07952258fa810',
165+
totalOwedMantissa: '0',
166+
totalRaisedMantissa: '500000000000',
167+
updatedAt: '2026-04-01T00:00:00.000Z',
168+
vaultState: 2,
169+
},
170+
underlyingToken: [
171+
{
172+
address: '0x312e39c7641cE64BEccDe53613f07952258fa810',
173+
chainId: '97',
174+
name: 'Mock USDC',
175+
symbol: 'MOCK_USDC',
176+
decimals: 6,
177+
maturityDate: '2026-09-01T00:00:00.000Z',
178+
createdAt: '2026-04-01T00:00:00.000Z',
179+
updatedAt: '2026-04-01T00:00:00.000Z',
180+
tokenPrices: [
181+
{
182+
id: 'fake-price-institutional',
183+
tokenAddress: '0x312e39c7641cE64BEccDe53613f07952258fa810',
184+
tokenWrappedAddress: null,
185+
chainId: '97',
186+
priceMantissa: '1000000000000000000000000000000',
187+
priceSource: 'oracle',
188+
priceOracleAddress: '0x0000000000000000000000000000000000000001',
189+
mainOracleAddress: '0x0000000000000000000000000000000000000001',
190+
mainOracleName: 'ResilientOracle',
191+
isPriceInvalid: false,
192+
hasErrorFetchingPrice: false,
193+
createdAt: '2026-04-01T00:00:00.000Z',
194+
updatedAt: '2026-04-01T00:00:00.000Z',
195+
},
196+
],
101197
},
102198
],
103199
},
104200
];
105201

202+
export const institutionalVault: InstitutionalVault = {
203+
vaultType: VaultType.Institutional,
204+
category: VaultCategory.STABLECOINS,
205+
manager: VaultManager.Ceffu,
206+
managerIcon: 'ceefu',
207+
managerAddress: '0x1111111111111111111111111111111111111111',
208+
managerLink: 'https://www.ceffu.com',
209+
status: VaultStatus.Pending,
210+
key: '97-institutional-0x5263D68786AaCfad74B9aa385A004c272548e8B7',
211+
stakedToken: usdc,
212+
rewardToken: usdc,
213+
stakedTokenPriceCents: new BigNumber(100),
214+
rewardTokenPriceCents: new BigNumber(100),
215+
stakingAprPercentage: 8,
216+
stakeBalanceMantissa: new BigNumber('500000000000'),
217+
stakeBalanceCents: 50000000,
218+
userStakeBalanceMantissa: new BigNumber('100000000'),
219+
userStakeBalanceCents: 10000,
220+
vaultAddress: '0x5263D68786AaCfad74B9aa385A004c272548e8B7',
221+
fixedApyDecimal: '0.08',
222+
vaultDeploymentDate: new Date('2026-04-01T00:00:00.000Z'),
223+
openEndDate: new Date('2026-04-08T00:00:00.000Z'),
224+
lockEndDate: new Date('2026-08-29T00:00:00.000Z'),
225+
maturityDate: new Date('2026-09-01T00:00:00.000Z'),
226+
settlementDate: new Date('2026-09-01T00:00:00.000Z'),
227+
stakeLimitMantissa: new BigNumber('1000000000000'),
228+
stakeMinMantissa: new BigNumber('100000000000'),
229+
userRedeemLimitMantissa: new BigNumber(0),
230+
userWithdrawLimitMantissa: new BigNumber(0),
231+
lockingPeriodMs: 2592000 * 1000,
232+
};
233+
106234
export const lockedDeposits: LockedDeposit[] = [
107235
{
108236
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: 7 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';
@@ -258,6 +261,10 @@ export * from './queries/getProposalCount/useGetProposalCount';
258261

259262
export * from './queries/getFixedRatedVaults';
260263
export * from './queries/getFixedRatedVaults/useGetFixedRatedVaults';
264+
export * from './queries/getFixedRatedVaultUserStakedTokens';
265+
export * from './queries/getFixedRatedVaultUserStakedTokens/useGetFixedRatedVaultUserStakedTokens';
266+
export * from './queries/getInstitutionalVaultUserMetrics';
267+
export * from './queries/getInstitutionalVaultUserMetrics/useGetInstitutionalVaultUserMetrics';
261268

262269
export * from './queries/getPendleSwapQuote';
263270
export * from './queries/getPendleSwapQuote/useGetPendleSwapQuote';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { queryClient } from 'clients/api/queryClient';
2+
import FunctionKey from 'constants/functionKey';
3+
4+
export const invalidateInstitutionalVaultQueries = () => {
5+
queryClient.invalidateQueries({ queryKey: [FunctionKey.GET_FIXED_RATED_VAULTS] });
6+
queryClient.invalidateQueries({
7+
queryKey: [FunctionKey.GET_TOKEN_BALANCES],
8+
});
9+
queryClient.invalidateQueries({
10+
queryKey: [FunctionKey.GET_FIXED_RATED_VAULTS_USER_STAKED_TOKENS],
11+
});
12+
queryClient.invalidateQueries({
13+
queryKey: [FunctionKey.GET_INSTITUTIONAL_VAULT_USER_METRICS],
14+
});
15+
};
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+
};
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 StakeIntoInstitutionalVaultInput = {
11+
amountMantissa: BigNumber;
12+
};
13+
14+
type Options = UseSendTransactionOptions<StakeIntoInstitutionalVaultInput>;
15+
16+
export const useStakeIntoInstitutionalVault = (
17+
{ vaultAddress }: { vaultAddress: Address },
18+
options?: Partial<Options>,
19+
) => {
20+
const { accountAddress } = useAccountAddress();
21+
const { captureAnalyticEvent } = useAnalytics();
22+
23+
return useSendTransaction({
24+
fn: ({ amountMantissa }: StakeIntoInstitutionalVaultInput) => {
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: 'deposit' as const,
36+
args: [BigInt(amountMantissa.toFixed()), accountAddress] as const,
37+
};
38+
},
39+
onConfirmed: () => {
40+
captureAnalyticEvent('Institutional vault deposit', {
41+
vaultAddress,
42+
accountAddress,
43+
});
44+
45+
invalidateInstitutionalVaultQueries();
46+
},
47+
options,
48+
});
49+
};

0 commit comments

Comments
 (0)