Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: unwrap native token in borrow/withdraw operations #2313

Merged
merged 20 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
51c669d
feat: add borrowAndUnwrap
gleiser-oliveira Mar 7, 2024
ef51bc3
feat: integrate borrow form with borrowAndUnwrap
gleiser-oliveira Mar 7, 2024
c163b83
feat: add redeemAndUnwrap
gleiser-oliveira Mar 7, 2024
6a4f38f
feat: integrate withdraw form with redeemAndUnwrap
gleiser-oliveira Mar 7, 2024
e362302
feat: add NativeTokenGateway addresses for opBNB and BSC mainnet
gleiser-oliveira Mar 7, 2024
0d94af3
feat: update WBNB to wrap BNB on BSC mainnet
gleiser-oliveira Mar 7, 2024
ea315f8
feat: update WBNB to wrap BNB on opBNB mainnet
gleiser-oliveira Mar 7, 2024
6cceaf1
feat: separate redeemAndUnwrap and redeemUnderlyingAndUnwrap
gleiser-oliveira Mar 13, 2024
a22a3c3
feat: add getUniqueTokenBalances
gleiser-oliveira Mar 13, 2024
e254f51
feat: update NativeTokenGateway addresses
gleiser-oliveira Mar 13, 2024
2ce3098
feat: add useRedeemUnderlyingAndUnwrap to WithdrawForm
gleiser-oliveira Mar 13, 2024
faa394e
feat: hook up RepayForm with getUniqueTokenBalances
gleiser-oliveira Mar 13, 2024
dc28849
feat: hook up SupplyForm with getUniqueTokenBalances
gleiser-oliveira Mar 13, 2024
b25390f
test: update integrated swap tests to select a different token
gleiser-oliveira Mar 13, 2024
eb990da
chore: add changeset
gleiser-oliveira Mar 14, 2024
938cad6
test: add tests for borrow/withdraw to native tokens
gleiser-oliveira Mar 14, 2024
7313e81
fix: isDelegateApproved is undefined when query is disabled
gleiser-oliveira Mar 14, 2024
55e40e0
refactor: prefix variables with userWallet
gleiser-oliveira Mar 15, 2024
4616427
feat: receiveNativeToken on by default in wrapped native token markets
gleiser-oliveira Mar 15, 2024
9e5727e
feat: use gt instead of gte
gleiser-oliveira Mar 15, 2024
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/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
Loading