Skip to content

Commit

Permalink
Merge pull request #2313 from VenusProtocol/feat/unwrap-native-token-…
Browse files Browse the repository at this point in the history
…borrow-withdraw

feat: unwrap native token in borrow/withdraw operations
  • Loading branch information
gleiser-oliveira authored Mar 15, 2024
2 parents 25c1b52 + 9e5727e commit 5ac89b2
Show file tree
Hide file tree
Showing 33 changed files with 753 additions and 95 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-suns-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@venusprotocol/evm": minor
---

unwrap native token in borrow/withdraw operations
19 changes: 17 additions & 2 deletions apps/evm/src/clients/api/__mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ export const useGetPendingRewards = () =>
useQuery(FunctionKey.GET_PENDING_REWARDS, getPendingRewards);

export const getVTokenBalanceOf = vi.fn();
export const useGetVTokenBalanceOf = () =>
useQuery(FunctionKey.GET_V_TOKEN_BALANCE, getVTokenBalanceOf);
export const useGetVTokenBalanceOf = vi.fn(() =>
useQuery(FunctionKey.GET_V_TOKEN_BALANCE, getVTokenBalanceOf),
);

export const getAllowance = vi.fn();
export const useGetAllowance = () => useQuery(FunctionKey.GET_TOKEN_ALLOWANCE, getAllowance);
Expand Down Expand Up @@ -489,3 +490,17 @@ export const useWrapTokensAndRepay = (_variables: never, options?: MutationObser
export const updatePoolDelegateStatus = vi.fn();
export const useUpdatePoolDelegateStatus = (_variables: never, options?: MutationObserverOptions) =>
useMutation(FunctionKey.UPDATE_POOL_DELEGATE_STATUS, updatePoolDelegateStatus, options);

export const borrowAndUnwrap = vi.fn();
export const useBorrowAndUnwrap = (_variables: never, options?: MutationObserverOptions) =>
useMutation(FunctionKey.BORROW_AND_UNWRAP, borrowAndUnwrap, options);

export const redeemAndUnwrap = vi.fn();
export const useRedeemAndUnwrap = (_variables: never, options?: MutationObserverOptions) =>
useMutation(FunctionKey.REDEEM_AND_UNWRAP, redeemAndUnwrap, options);

export const redeemUnderlyingAndUnwrap = vi.fn();
export const useRedeemUnderlyingAndUnwrap = (
_variables: never,
options?: MutationObserverOptions,
) => useMutation(FunctionKey.REDEEM_UNDERLYING_AND_UNWRAP, redeemUnderlyingAndUnwrap, options);
12 changes: 12 additions & 0 deletions apps/evm/src/clients/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ export { default as updatePoolDelegateStatus } from './mutations/updatePoolDeleg
export * from './mutations/updatePoolDelegateStatus';
export { default as useUpdatePoolDelegateStatus } from './mutations/updatePoolDelegateStatus/useUpdatePoolDelegateStatus';

export { default as borrowAndUnwrap } from './mutations/borrowAndUnwrap';
export * from './mutations/borrowAndUnwrap';
export { default as useBorrowAndUnwrap } from './mutations/borrowAndUnwrap/useBorrowAndUnwrap';

export { default as redeemAndUnwrap } from './mutations/redeemAndUnwrap';
export * from './mutations/redeemAndUnwrap';
export { default as useRedeemAndUnwrap } from './mutations/redeemAndUnwrap/useRedeemAndUnwrap';

export { default as redeemUnderlyingAndUnwrap } from './mutations/redeemUnderlyingAndUnwrap';
export * from './mutations/redeemUnderlyingAndUnwrap';
export { default as useRedeemUnderlyingAndUnwrap } from './mutations/redeemUnderlyingAndUnwrap/useRedeemUnderlyingAndUnwrap';

// Queries
export { default as getVaiCalculateRepayAmount } from './queries/getVaiCalculateRepayAmount';
export * from './queries/getVaiCalculateRepayAmount';
Expand Down
31 changes: 31 additions & 0 deletions apps/evm/src/clients/api/mutations/borrowAndUnwrap/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import BigNumber from 'bignumber.js';

import fakeContractTransaction from '__mocks__/models/contractTransaction';

import type { NativeTokenGateway } from 'libs/contracts';

import borrowAndUnwrap from '.';

const fakeAmountMantissa = new BigNumber('10000000000000000');

vi.mock('libs/contracts');

describe('borrowAndUnwrap', () => {
it('returns transaction when request succeeds', async () => {
const borrowAndUnwrapMock = vi.fn(() => fakeContractTransaction);

const fakeNativeTokenGatewayContract = {
borrowAndUnwrap: borrowAndUnwrapMock,
} as unknown as NativeTokenGateway;

const response = await borrowAndUnwrap({
nativeTokenGatewayContract: fakeNativeTokenGatewayContract,
amountMantissa: fakeAmountMantissa,
});

expect(response).toBe(fakeContractTransaction);

expect(borrowAndUnwrapMock).toHaveBeenCalledTimes(1);
expect(borrowAndUnwrapMock).toHaveBeenCalledWith(fakeAmountMantissa.toFixed());
});
});
18 changes: 18 additions & 0 deletions apps/evm/src/clients/api/mutations/borrowAndUnwrap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type BigNumber from 'bignumber.js';
import type { ContractTransaction } from 'ethers';

import type { NativeTokenGateway } from 'libs/contracts';

export interface BorrowAndUnwrapInput {
nativeTokenGatewayContract: NativeTokenGateway;
amountMantissa: BigNumber;
}

export type BorrowAndUnwrapOutput = ContractTransaction;

const borrowAndUnwrap = async ({
nativeTokenGatewayContract,
amountMantissa,
}: BorrowAndUnwrapInput) => nativeTokenGatewayContract.borrowAndUnwrap(amountMantissa.toFixed());

export default borrowAndUnwrap;
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import borrowAndUnwrap, { type BorrowAndUnwrapInput } from 'clients/api/mutations/borrowAndUnwrap';
import queryClient from 'clients/api/queryClient';
import FunctionKey from 'constants/functionKey';
import { type UseSendTransactionOptions, useSendTransaction } from 'hooks/useSendTransaction';
import { useGetNativeTokenGatewayContract } from 'libs/contracts';
import { useChainId } from 'libs/wallet';
import type { VToken } from 'types';
import { callOrThrow } from 'utilities';

type TrimmedBorrowAndUnwrapInput = Omit<BorrowAndUnwrapInput, 'nativeTokenGatewayContract'>;
type Options = UseSendTransactionOptions<TrimmedBorrowAndUnwrapInput>;

const useBorrowAndUnwrap = (
{ vToken, poolComptrollerAddress }: { vToken: VToken; poolComptrollerAddress: string },
options?: Options,
) => {
const { chainId } = useChainId();
const nativeToken = vToken.underlyingToken.tokenWrapped;
const nativeTokenGatewayContract = useGetNativeTokenGatewayContract({
passSigner: true,
comptrollerContractAddress: poolComptrollerAddress,
});

return useSendTransaction({
fnKey: FunctionKey.BORROW_AND_UNWRAP,
fn: (input: TrimmedBorrowAndUnwrapInput) =>
callOrThrow({ nativeTokenGatewayContract }, params =>
borrowAndUnwrap({
...input,
...params,
}),
),
onConfirmed: async () => {
const accountAddress = await nativeTokenGatewayContract?.signer.getAddress();

queryClient.invalidateQueries(FunctionKey.GET_V_TOKEN_BALANCES_ALL);
queryClient.invalidateQueries([
FunctionKey.GET_BALANCE_OF,
{
chainId,
accountAddress,
vTokenAddress: vToken.address,
},
]);
queryClient.invalidateQueries([
FunctionKey.GET_BALANCE_OF,
{
chainId,
accountAddress,
tokenAddress: nativeToken?.address,
},
]);
queryClient.invalidateQueries(FunctionKey.GET_MAIN_MARKETS);
queryClient.invalidateQueries(FunctionKey.GET_ISOLATED_POOLS);
},
options,
});
};

export default useBorrowAndUnwrap;
31 changes: 31 additions & 0 deletions apps/evm/src/clients/api/mutations/redeemAndUnwrap/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import BigNumber from 'bignumber.js';

import fakeContractTransaction from '__mocks__/models/contractTransaction';

import type { NativeTokenGateway } from 'libs/contracts';

import redeemAndUnwrap from '.';

const fakeAmountMantissa = new BigNumber('10000000000000000');

vi.mock('libs/contracts');

describe('redeemAndUnwrap', () => {
it('returns transaction when request succeeds', async () => {
const redeemAndUnwrapMock = vi.fn(() => fakeContractTransaction);

const fakeNativeTokenGatewayContract = {
redeemAndUnwrap: redeemAndUnwrapMock,
} as unknown as NativeTokenGateway;

const response = await redeemAndUnwrap({
nativeTokenGatewayContract: fakeNativeTokenGatewayContract,
amountMantissa: fakeAmountMantissa,
});

expect(response).toBe(fakeContractTransaction);

expect(redeemAndUnwrapMock).toHaveBeenCalledTimes(1);
expect(redeemAndUnwrapMock).toHaveBeenCalledWith(fakeAmountMantissa.toFixed());
});
});
18 changes: 18 additions & 0 deletions apps/evm/src/clients/api/mutations/redeemAndUnwrap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type BigNumber from 'bignumber.js';
import type { ContractTransaction } from 'ethers';

import type { NativeTokenGateway } from 'libs/contracts';

export interface RedeemAndUnwrapInput {
nativeTokenGatewayContract: NativeTokenGateway;
amountMantissa: BigNumber;
}

export type RedeemAndUnwrapOutput = ContractTransaction;

const redeemAndUnwrap = async ({
nativeTokenGatewayContract,
amountMantissa,
}: RedeemAndUnwrapInput) => nativeTokenGatewayContract.redeemAndUnwrap(amountMantissa.toFixed());

export default redeemAndUnwrap;
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import redeemAndUnwrap, { type RedeemAndUnwrapInput } from 'clients/api/mutations/redeemAndUnwrap';
import queryClient from 'clients/api/queryClient';
import FunctionKey from 'constants/functionKey';
import { type UseSendTransactionOptions, useSendTransaction } from 'hooks/useSendTransaction';
import { useGetNativeTokenGatewayContract } from 'libs/contracts';
import { useChainId } from 'libs/wallet';
import type { VToken } from 'types';
import { callOrThrow } from 'utilities';

type TrimmedRedeemAndUnwrapInput = Omit<RedeemAndUnwrapInput, 'nativeTokenGatewayContract'>;
type Options = UseSendTransactionOptions<TrimmedRedeemAndUnwrapInput>;

const useRedeemAndUnwrap = (
{ vToken, poolComptrollerAddress }: { vToken: VToken; poolComptrollerAddress: string },
options?: Options,
) => {
const { chainId } = useChainId();
const nativeToken = vToken.underlyingToken.tokenWrapped;
const nativeTokenGatewayContract = useGetNativeTokenGatewayContract({
passSigner: true,
comptrollerContractAddress: poolComptrollerAddress,
});

return useSendTransaction({
fnKey: FunctionKey.REDEEM_AND_UNWRAP,
fn: (input: TrimmedRedeemAndUnwrapInput) =>
callOrThrow({ nativeTokenGatewayContract }, params =>
redeemAndUnwrap({
...input,
...params,
}),
),
onConfirmed: async () => {
const accountAddress = await nativeTokenGatewayContract?.signer.getAddress();

queryClient.invalidateQueries(FunctionKey.GET_V_TOKEN_BALANCES_ALL);
queryClient.invalidateQueries([
FunctionKey.GET_BALANCE_OF,
{
chainId,
accountAddress,
vTokenAddress: vToken.address,
},
]);
queryClient.invalidateQueries([
FunctionKey.GET_BALANCE_OF,
{
chainId,
accountAddress,
tokenAddress: nativeToken?.address,
},
]);
queryClient.invalidateQueries(FunctionKey.GET_MAIN_MARKETS);
queryClient.invalidateQueries(FunctionKey.GET_ISOLATED_POOLS);
},
options,
});
};

export default useRedeemAndUnwrap;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import BigNumber from 'bignumber.js';

import fakeContractTransaction from '__mocks__/models/contractTransaction';

import type { NativeTokenGateway } from 'libs/contracts';

import redeemAndUnwrap from '.';

const fakeAmountMantissa = new BigNumber('10000000000000000');

vi.mock('libs/contracts');

describe('redeemUnderlyingAndUnwrap', () => {
it('returns transaction when request succeeds', async () => {
const redeemUnderlyingAndUnwrapMock = vi.fn(() => fakeContractTransaction);

const fakeNativeTokenGatewayContract = {
redeemUnderlyingAndUnwrap: redeemUnderlyingAndUnwrapMock,
} as unknown as NativeTokenGateway;

const response = await redeemAndUnwrap({
nativeTokenGatewayContract: fakeNativeTokenGatewayContract,
amountMantissa: fakeAmountMantissa,
});

expect(response).toBe(fakeContractTransaction);

expect(redeemUnderlyingAndUnwrapMock).toHaveBeenCalledTimes(1);
expect(redeemUnderlyingAndUnwrapMock).toHaveBeenCalledWith(fakeAmountMantissa.toFixed());
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type BigNumber from 'bignumber.js';
import type { ContractTransaction } from 'ethers';

import type { NativeTokenGateway } from 'libs/contracts';

export interface RedeemUnderlyingAndUnwrapInput {
nativeTokenGatewayContract: NativeTokenGateway;
amountMantissa: BigNumber;
}

export type RedeemUnderlyingAndUnwrapOutput = ContractTransaction;

const redeemUnderlyingAndUnwrap = async ({
nativeTokenGatewayContract,
amountMantissa,
}: RedeemUnderlyingAndUnwrapInput) =>
nativeTokenGatewayContract.redeemUnderlyingAndUnwrap(amountMantissa.toFixed());

export default redeemUnderlyingAndUnwrap;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import redeemUnderlyingAndUnwrap, {
type RedeemUnderlyingAndUnwrapInput,
} from 'clients/api/mutations/redeemUnderlyingAndUnwrap';
import queryClient from 'clients/api/queryClient';
import FunctionKey from 'constants/functionKey';
import { type UseSendTransactionOptions, useSendTransaction } from 'hooks/useSendTransaction';
import { useGetNativeTokenGatewayContract } from 'libs/contracts';
import { useChainId } from 'libs/wallet';
import type { VToken } from 'types';
import { callOrThrow } from 'utilities';

type TrimmedRedeemUnderlyingAndUnwrapInput = Omit<
RedeemUnderlyingAndUnwrapInput,
'nativeTokenGatewayContract'
>;
type Options = UseSendTransactionOptions<TrimmedRedeemUnderlyingAndUnwrapInput>;

const useRedeemUnderlyingAndUnwrap = (
{ vToken, poolComptrollerAddress }: { vToken: VToken; poolComptrollerAddress: string },
options?: Options,
) => {
const { chainId } = useChainId();
const nativeToken = vToken.underlyingToken.tokenWrapped;
const nativeTokenGatewayContract = useGetNativeTokenGatewayContract({
passSigner: true,
comptrollerContractAddress: poolComptrollerAddress,
});

return useSendTransaction({
fnKey: FunctionKey.REDEEM_AND_UNWRAP,
fn: (input: TrimmedRedeemUnderlyingAndUnwrapInput) =>
callOrThrow({ nativeTokenGatewayContract }, params =>
redeemUnderlyingAndUnwrap({
...input,
...params,
}),
),
onConfirmed: async () => {
const accountAddress = await nativeTokenGatewayContract?.signer.getAddress();

queryClient.invalidateQueries(FunctionKey.GET_V_TOKEN_BALANCES_ALL);
queryClient.invalidateQueries([
FunctionKey.GET_BALANCE_OF,
{
chainId,
accountAddress,
vTokenAddress: vToken.address,
},
]);
queryClient.invalidateQueries([
FunctionKey.GET_BALANCE_OF,
{
chainId,
accountAddress,
tokenAddress: nativeToken?.address,
},
]);
queryClient.invalidateQueries(FunctionKey.GET_MAIN_MARKETS);
queryClient.invalidateQueries(FunctionKey.GET_ISOLATED_POOLS);
},
options,
});
};

export default useRedeemUnderlyingAndUnwrap;
Loading

0 comments on commit 5ac89b2

Please sign in to comment.