Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lucky-pugs-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@venusprotocol/evm": minor
---

Replace the Hub contract row with the operator address in the Liquidity Hub info section
11 changes: 11 additions & 0 deletions apps/evm/src/clients/api/__mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,17 @@ export const useGetLiquidityHubHistory = vi.fn(() => ({
},
}));

export const getLiquidityHubOperatorAddress = vi.fn(async () => ({
operatorAddress: fakeAddress,
}));

export const useGetLiquidityHubOperatorAddress = vi.fn(() => ({
isLoading: false,
data: {
operatorAddress: fakeAddress,
},
}));

export const useGetSimulatedPool = vi.fn(() => ({
isLoading: false,
data: {
Expand Down
3 changes: 3 additions & 0 deletions apps/evm/src/clients/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export * from './queries/getLiquidityHub/useGetLiquidityHub';
export * from './queries/getLiquidityHubHistory';
export * from './queries/getLiquidityHubHistory/useGetLiquidityHubHistory';

export * from './queries/getLiquidityHubOperatorAddress';
export * from './queries/getLiquidityHubOperatorAddress/useGetLiquidityHubOperatorAddress';

export * from './queries/getMarketHistory';
export * from './queries/getMarketHistory/useGetMarketHistory';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`getLiquidityHubOperatorAddress > returns the operator address in the right format on success 1`] = `
{
"operatorAddress": "0x3d759121234cd36F8124C21aFe1c6852d2bEd848",
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { PublicClient } from 'viem';

import fakeAddress from '__mocks__/models/address';
import { liquidityHubAbi } from 'libs/contracts';

import { getLiquidityHubOperatorAddress } from '..';

describe('getLiquidityHubOperatorAddress', () => {
it('returns the operator address in the right format on success', async () => {
const readContractMock = vi.fn().mockResolvedValue(fakeAddress);

const fakePublicClient = {
readContract: readContractMock,
} as unknown as PublicClient;

const res = await getLiquidityHubOperatorAddress({
publicClient: fakePublicClient,
vhTokenAddress: fakeAddress,
});

expect(readContractMock).toHaveBeenCalledWith({
address: fakeAddress,
abi: liquidityHubAbi,
functionName: 'owner',
});
expect(res).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { liquidityHubAbi } from 'libs/contracts';
import type { Address, PublicClient } from 'viem';

export interface GetLiquidityHubOperatorAddressInput {
publicClient: PublicClient;
vhTokenAddress: Address;
}

export type GetLiquidityHubOperatorAddressOutput = {
operatorAddress: Address;
};

export const getLiquidityHubOperatorAddress = async ({
publicClient,
vhTokenAddress,
}: GetLiquidityHubOperatorAddressInput): Promise<GetLiquidityHubOperatorAddressOutput> => {
// The hub exposes no operator getter: it is owned by the Venus DAO timelock, which is the
// entity operating it
const operatorAddress = await publicClient.readContract({
address: vhTokenAddress,
abi: liquidityHubAbi,
functionName: 'owner',
});

return {
operatorAddress,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { QueryClient } from '@tanstack/react-query';
import { waitFor } from '@testing-library/react';
import fakeAddress, { altAddress } from '__mocks__/models/address';
import FunctionKey from 'constants/functionKey';
import { usePublicClient } from 'libs/wallet';
import { renderHook } from 'testUtils/render';
import { ChainId } from 'types';
import type { Address } from 'viem';
import type { Mock } from 'vitest';
import { useGetLiquidityHubOperatorAddress } from '..';
import * as getLiquidityHubOperatorAddressQueries from '../..';

describe('useGetLiquidityHubOperatorAddress', () => {
it('uses the expected query key and calls getLiquidityHubOperatorAddress with the right parameters', async () => {
const fakePublicClient = {
readContract: vi.fn(),
};

const fakeOutput = {
operatorAddress: altAddress as Address,
};

const getLiquidityHubOperatorAddressSpy = vi
.spyOn(getLiquidityHubOperatorAddressQueries, 'getLiquidityHubOperatorAddress')
.mockResolvedValue(fakeOutput);

(usePublicClient as Mock).mockImplementation(() => ({
publicClient: fakePublicClient,
}));

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
gcTime: 0,
},
},
});

const { result } = renderHook(
() => useGetLiquidityHubOperatorAddress({ vhTokenAddress: fakeAddress }),
{
chainId: ChainId.BSC_MAINNET,
queryClient,
},
);

await waitFor(() => expect(result.current.isSuccess).toBe(true));

expect(getLiquidityHubOperatorAddressSpy).toHaveBeenCalledWith({
publicClient: fakePublicClient,
vhTokenAddress: fakeAddress,
});

expect(
queryClient.getQueryData([
FunctionKey.GET_LIQUIDITY_HUB_OPERATOR_ADDRESS,
{ chainId: ChainId.BSC_MAINNET, vhTokenAddress: fakeAddress },
]),
).toEqual(fakeOutput);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { type QueryObserverOptions, useQuery } from '@tanstack/react-query';

import FunctionKey from 'constants/functionKey';
import { useChainId, usePublicClient } from 'libs/wallet';
import type { ChainId } from 'types';
import type { Address } from 'viem';
import { type GetLiquidityHubOperatorAddressOutput, getLiquidityHubOperatorAddress } from '..';

export type UseGetLiquidityHubOperatorAddressInput = {
vhTokenAddress: Address;
};

export type UseGetLiquidityHubOperatorAddressQueryKey = [
FunctionKey.GET_LIQUIDITY_HUB_OPERATOR_ADDRESS,
{ chainId: ChainId; vhTokenAddress: Address },
];

type Options = QueryObserverOptions<
GetLiquidityHubOperatorAddressOutput,
Error,
GetLiquidityHubOperatorAddressOutput,
GetLiquidityHubOperatorAddressOutput,
UseGetLiquidityHubOperatorAddressQueryKey
>;

export const useGetLiquidityHubOperatorAddress = (
{ vhTokenAddress }: UseGetLiquidityHubOperatorAddressInput,
options?: Partial<Options>,
) => {
const { chainId } = useChainId();
const { publicClient } = usePublicClient();

return useQuery({
queryKey: [FunctionKey.GET_LIQUIDITY_HUB_OPERATOR_ADDRESS, { chainId, vhTokenAddress }],
queryFn: () => getLiquidityHubOperatorAddress({ publicClient, vhTokenAddress }),
...options,
});
};
1 change: 1 addition & 0 deletions apps/evm/src/constants/functionKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ enum FunctionKey {
GET_HAS_ISOLATED_POOL_POSITION = 'GET_HAS_ISOLATED_POOL_POSITION',
GET_LIQUIDITY_HUB = 'GET_LIQUIDITY_HUB',
GET_LIQUIDITY_HUB_HISTORY = 'GET_LIQUIDITY_HUB_HISTORY',
GET_LIQUIDITY_HUB_OPERATOR_ADDRESS = 'GET_LIQUIDITY_HUB_OPERATOR_ADDRESS',
GET_ADDRESS_DOMAIN_NAME = 'GET_ADDRESS_DOMAIN_NAME',
GET_BURNED_BNB = 'GET_BURNED_BNB',
GET_IMPORTABLE_POSITIONS = 'GET_IMPORTABLE_POSITIONS',
Expand Down
2 changes: 1 addition & 1 deletion apps/evm/src/libs/translations/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,8 +991,8 @@
"title": "Exchange rate",
"value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}"
},
"hubContract": "Hub contract",
"operator": "Operator",
"operatorAddress": "Operator address",
"operatorName": "Venus DAO",
"performanceFee": "Performance fee",
"redeemFee": "Redeem fee",
Expand Down
2 changes: 1 addition & 1 deletion apps/evm/src/libs/translations/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,8 @@
"title": "交換レート",
"value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}"
},
"hubContract": "ハブコントラクト",
"operator": "オペレーター",
"operatorAddress": "TRANSLATION NEEDED",
"operatorName": "Venus DAO",
"performanceFee": "パフォーマンス手数料",
"redeemFee": "償還手数料",
Expand Down
2 changes: 1 addition & 1 deletion apps/evm/src/libs/translations/translations/th.json
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,8 @@
"title": "อัตราแลกเปลี่ยน",
"value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}"
},
"hubContract": "สัญญาศูนย์",
"operator": "ผู้ดำเนินการ",
"operatorAddress": "TRANSLATION NEEDED",
"operatorName": "Venus DAO",
"performanceFee": "ค่าธรรมเนียมผลการดำเนินงาน",
"redeemFee": "ค่าธรรมเนียมไถ่ถอน",
Expand Down
2 changes: 1 addition & 1 deletion apps/evm/src/libs/translations/translations/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,8 +991,8 @@
"title": "Döviz kuru",
"value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}"
},
"hubContract": "Merkez sözleşmesi",
"operator": "Operatör",
"operatorAddress": "TRANSLATION NEEDED",
"operatorName": "Venus DAO",
"performanceFee": "Performans ücreti",
"redeemFee": "İtfa ücreti",
Expand Down
2 changes: 1 addition & 1 deletion apps/evm/src/libs/translations/translations/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,8 @@
"title": "Tỷ giá hối đoái",
"value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}"
},
"hubContract": "Hợp đồng hub",
"operator": "Nhà vận hành",
"operatorAddress": "TRANSLATION NEEDED",
"operatorName": "Venus DAO",
"performanceFee": "Phí hiệu suất",
"redeemFee": "Phí đổi lại",
Expand Down
2 changes: 1 addition & 1 deletion apps/evm/src/libs/translations/translations/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,8 @@
"title": "汇率",
"value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}"
},
"hubContract": "中心合约",
"operator": "运营方",
"operatorAddress": "TRANSLATION NEEDED",
"operatorName": "Venus DAO",
"performanceFee": "绩效费",
"redeemFee": "赎回费",
Expand Down
2 changes: 1 addition & 1 deletion apps/evm/src/libs/translations/translations/zh-Hant.json
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,8 @@
"title": "匯率",
"value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}"
},
"hubContract": "中心合約",
"operator": "營運方",
"operatorAddress": "TRANSLATION NEEDED",
"operatorName": "Venus DAO",
"performanceFee": "績效費",
"redeemFee": "贖回費",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { screen } from '@testing-library/react';
import type { Mock } from 'vitest';

import fakeAddress from '__mocks__/models/address';
import { liquidityHubs } from '__mocks__/models/liquidityHubs';
import { useGetLiquidityHubOperatorAddress } from 'clients/api';
import { PLACEHOLDER_KEY } from 'constants/placeholders';
import { en } from 'libs/translations';
import { renderComponent } from 'testUtils/render';
import { ChainId } from 'types';
import { generateExplorerUrl, truncateAddress } from 'utilities';
import { LiquidityHubInfo } from '..';

const liquidityHub = liquidityHubs[0];

const renderLiquidityHubInfo = () =>
renderComponent(<LiquidityHubInfo liquidityHub={liquidityHub} />, {
chainId: ChainId.BSC_TESTNET,
});

describe('LiquidityHubInfo', () => {
const mockUseGetLiquidityHubOperatorAddress = useGetLiquidityHubOperatorAddress as Mock;

it('renders the operator address, linking to the chain explorer', () => {
renderLiquidityHubInfo();

expect(mockUseGetLiquidityHubOperatorAddress).toHaveBeenCalledWith({
vhTokenAddress: liquidityHub.vhToken.address,
});
expect(screen.getByText(en.liquidityHub.info.stats.operatorAddress)).toBeInTheDocument();
expect(screen.getByText(truncateAddress(fakeAddress))).toBeInTheDocument();

const operatorAddressLink = screen
.getByText(truncateAddress(fakeAddress))
.closest('a') as HTMLAnchorElement;

expect(operatorAddressLink).toHaveAttribute(
'href',
generateExplorerUrl({ hash: fakeAddress, chainId: ChainId.BSC_TESTNET }),
);
});

it('does not render the hub contract row anymore', () => {
renderLiquidityHubInfo();

// The hub contract address is identical to the vhToken contract address, so the only remaining
// row displaying it is the vhToken one
expect(screen.getAllByText(truncateAddress(liquidityHub.vhToken.address)).length).toBe(1);
expect(
screen.getByText(
en.liquidityHub.info.stats.vhTokenContract.replace(
'{{ vhTokenSymbol }}',
liquidityHub.vhToken.symbol,
),
),
).toBeInTheDocument();
});

it('renders a placeholder while the operator address has not been fetched', () => {
mockUseGetLiquidityHubOperatorAddress.mockReturnValue({
isLoading: true,
data: undefined,
});

renderLiquidityHubInfo();

expect(screen.getByText(en.liquidityHub.info.stats.operatorAddress)).toBeInTheDocument();
expect(screen.getByText(PLACEHOLDER_KEY)).toBeInTheDocument();
expect(screen.queryByText(truncateAddress(fakeAddress))).toBeNull();
});
});
19 changes: 12 additions & 7 deletions apps/evm/src/pages/LiquidityHub/LiquidityHubInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { cn } from '@venusprotocol/ui';

import { useGetLiquidityHubOperatorAddress } from 'clients/api';
import { MarketInfo, type MarketInfoProps } from 'components';
import { PLACEHOLDER_KEY } from 'constants/placeholders';
import { routes } from 'constants/routing';
import { DAYS_PER_YEAR } from 'constants/time';
import { ChainExplorerLink } from 'containers/ChainExplorerLink';
Expand All @@ -22,6 +24,11 @@ export const LiquidityHubInfo: React.FC<LiquidityHubInfoProps> = ({ liquidityHub
const { t, Trans } = useTranslation();
const { chainId } = useChainId();

const { data: getLiquidityHubOperatorAddressData } = useGetLiquidityHubOperatorAddress({
vhTokenAddress: liquidityHub.vhToken.address,
});
const operatorAddress = getLiquidityHubOperatorAddressData?.operatorAddress;

const { totalApyPercentage } = getCombinedApy({
type: 'supply',
baseApyPercentage: liquidityHub.supplyApyPercentage,
Expand All @@ -41,13 +48,11 @@ export const LiquidityHubInfo: React.FC<LiquidityHubInfoProps> = ({ liquidityHub
children: t('liquidityHub.info.stats.operatorName'),
},
{
label: t('liquidityHub.info.stats.hubContract'),
children: (
<ChainExplorerLink
hash={liquidityHub.vhToken.address}
text={liquidityHub.vhToken.address}
chainId={chainId}
/>
label: t('liquidityHub.info.stats.operatorAddress'),
children: operatorAddress ? (
<ChainExplorerLink hash={operatorAddress} text={operatorAddress} chainId={chainId} />
) : (
PLACEHOLDER_KEY
),
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`LiquidityHub > loads the liquidity hub from the route and renders its content 1`] = `"SupplyWithdrawWalletCollateralXVSMAXSupply APY6.2%Distribution APY1.2%Total supply APY7.4%Connect walletSupply info61%Total supplied$109.03K / $178.75K15.25K / 25K XVSAverage APY4.1%Current APY7.4%Position unit price (vhXVS)1.06Allocation detailsVenus CoreAlloc.$8KLiquidity$6.2KAPY5.7%Cap55%Eff. cap$16KVenus FluxAlloc.$7.25KLiquidity$5.8KAPY6.8%Cap45%Eff. cap$14.5KSourceAlloc.sorted descendingLiquidityAPYCapEff. capVenus Core$8K$6.2K5.7%55%$16KVenus Flux$7.25K$5.8K6.8%45%$14.5KSort byAlloc.Venus CoreAlloc.$8KLiquidity$6.2KAPY5.7%Cap55%Eff. cap$16KVenus FluxAlloc.$7.25KLiquidity$5.8KAPY6.8%Cap45%Eff. cap$14.5KHub infoOperatorVenus DAOHub contract0x20...0001vhXVS contract0x20...0001Performance fee10%Redeem fee1%Daily supplying interests$22.1Exchange rate1 vhXVS = 1.06 XVSRisk disclosuresBy supplying to, withdrawing from, or otherwise interacting with the Venus Liquidity Hub, you acknowledge and agree that:You are interacting with autonomous smart contracts. Venus and its affiliates do not guarantee the performance, security, or availability of the Liquidity Hub or any related software, smart contracts, blockchain networks, or third-party integrations.Supplying assets to the Liquidity Hub does not grant borrowing power and does not constitute a deposit, loan, or investment product. You may not be able to withdraw at all times, and returns, if any, are not guaranteed.You assume all risks associated with your use of the Liquidity Hub and are solely responsible for determining whether it is appropriate for your financial circumstances, risk tolerance, and technical capabilities. You are responsible for conducting your own independent evaluation, including reviewing the relevant source code and on-chain configuration.Venus is a technology provider only and does not provide investment, advisory, fiduciary, or custodial services, and does not manage or hold your assets.Your interaction with the Liquidity Hub is governed by the Terms of Use."`;
exports[`LiquidityHub > loads the liquidity hub from the route and renders its content 1`] = `"SupplyWithdrawWalletCollateralXVSMAXSupply APY6.2%Distribution APY1.2%Total supply APY7.4%Connect walletSupply info61%Total supplied$109.03K / $178.75K15.25K / 25K XVSAverage APY4.1%Current APY7.4%Position unit price (vhXVS)1.06Allocation detailsVenus CoreAlloc.$8KLiquidity$6.2KAPY5.7%Cap55%Eff. cap$16KVenus FluxAlloc.$7.25KLiquidity$5.8KAPY6.8%Cap45%Eff. cap$14.5KSourceAlloc.sorted descendingLiquidityAPYCapEff. capVenus Core$8K$6.2K5.7%55%$16KVenus Flux$7.25K$5.8K6.8%45%$14.5KSort byAlloc.Venus CoreAlloc.$8KLiquidity$6.2KAPY5.7%Cap55%Eff. cap$16KVenus FluxAlloc.$7.25KLiquidity$5.8KAPY6.8%Cap45%Eff. cap$14.5KHub infoOperatorVenus DAOOperator address0x3d...d848vhXVS contract0x20...0001Performance fee10%Redeem fee1%Daily supplying interests$22.1Exchange rate1 vhXVS = 1.06 XVSRisk disclosuresBy supplying to, withdrawing from, or otherwise interacting with the Venus Liquidity Hub, you acknowledge and agree that:You are interacting with autonomous smart contracts. Venus and its affiliates do not guarantee the performance, security, or availability of the Liquidity Hub or any related software, smart contracts, blockchain networks, or third-party integrations.Supplying assets to the Liquidity Hub does not grant borrowing power and does not constitute a deposit, loan, or investment product. You may not be able to withdraw at all times, and returns, if any, are not guaranteed.You assume all risks associated with your use of the Liquidity Hub and are solely responsible for determining whether it is appropriate for your financial circumstances, risk tolerance, and technical capabilities. You are responsible for conducting your own independent evaluation, including reviewing the relevant source code and on-chain configuration.Venus is a technology provider only and does not provide investment, advisory, fiduciary, or custodial services, and does not manage or hold your assets.Your interaction with the Liquidity Hub is governed by the Terms of Use."`;
Loading