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/sweet-trams-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@venusprotocol/evm": minor
---

add Footer component to share between Operation forms
17 changes: 17 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
{
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit"
},
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"files.associations": {
"*.css": "tailwindcss"
},
"editor.quickSuggestions": {
"strings": "on"
},
"[tailwindcss]": {
"editor.defaultFormatter": "vscode.css-language-features"
},
"[css]": {
"editor.defaultFormatter": "vscode.css-language-features"
}
}
6 changes: 3 additions & 3 deletions apps/evm/src/components/ApproveTokenSteps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import type { Token } from 'types';

import { ApprovalSteps, type ApprovalStepsProps } from '../ApprovalSteps';

// TODO: remove in favor of using ApproveToken container

export interface ApproveTokenStepsProps
extends Pick<ApprovalStepsProps, 'className' | 'children' | 'secondStepButtonLabel'> {
token: Token;
approveToken: () => Promise<unknown>;
isWalletSpendingLimitLoading: boolean;
isApproveTokenLoading: boolean;
isTokenApproved?: boolean;
isUsingSwap?: boolean;
hideTokenEnablingStep?: boolean;
}

Expand All @@ -21,7 +22,6 @@ export const ApproveTokenSteps: React.FC<ApproveTokenStepsProps> = ({
isWalletSpendingLimitLoading,
isApproveTokenLoading,
hideTokenEnablingStep,
isUsingSwap = false,
className,
children,
...otherProps
Expand All @@ -37,7 +37,7 @@ export const ApproveTokenSteps: React.FC<ApproveTokenStepsProps> = ({
isApprovalActionLoading={isApproveTokenLoading}
approvalAction={approveToken}
firstStepLabel={t('approveTokenSteps.step1')}
firstStepTooltip={isUsingSwap ? t('approveTokenSteps.approveTokenButton.tooltip') : undefined}
firstStepTooltip={t('approveTokenSteps.approveTokenButton.tooltip')}
firstStepButtonLabel={t('approveTokenSteps.approveTokenButton.text', {
tokenSymbol: token.symbol,
})}
Expand Down
60 changes: 60 additions & 0 deletions apps/evm/src/containers/ApproveDelegate/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { Mock } from 'vitest';

import { fireEvent, waitFor } from '@testing-library/react';
import fakePoolComptrollerContractAddress, {
altAddress as delegateeAddress,
} from '__mocks__/models/address';
import useDelegateApproval from 'hooks/useDelegateApproval';
import { en } from 'libs/translations';
import { renderComponent } from 'testUtils/render';
import { ApproveDelegate, type ApproveDelegateProps } from '..';

const props: ApproveDelegateProps = {
poolComptrollerContractAddress: fakePoolComptrollerContractAddress,
delegateeAddress: delegateeAddress,
secondStepButtonLabel: 'Fake second step button label',
children: 'Fake children',
};

vi.mock('hooks/useDelegateApproval');

describe('ApproveDelegate', () => {
it('prompts user to approve delegatee if it is not', async () => {
const mockUpdatePoolDelegateStatus = vi.fn();

(useDelegateApproval as Mock).mockImplementation(() => ({
updatePoolDelegateStatus: mockUpdatePoolDelegateStatus,
isDelegateApproved: false,
isUpdateDelegateStatusLoading: false,
isDelegateApprovedLoading: false,
}));

const { getByText } = renderComponent(<ApproveDelegate {...props} />);

await waitFor(() => expect(getByText(en.approveDelegateSteps.approveDelegateButton.text)));

fireEvent.click(getByText(en.approveDelegateSteps.approveDelegateButton.text));

expect(mockUpdatePoolDelegateStatus).toHaveBeenCalledTimes(1);
expect((mockUpdatePoolDelegateStatus as Mock).mock.calls[0]).toMatchInlineSnapshot(`
[
{
"approvedStatus": true,
},
]
`);
});

it('displays children if delegate is approved', async () => {
(useDelegateApproval as Mock).mockImplementation(() => ({
updatePoolDelegateStatus: vi.fn(),
isDelegateApproved: true,
isUpdateDelegateStatusLoading: false,
isDelegateApprovedLoading: false,
}));

const { getByText } = renderComponent(<ApproveDelegate {...props} />);

await waitFor(() => expect(getByText(props.children as string)));
});
});
53 changes: 53 additions & 0 deletions apps/evm/src/containers/ApproveDelegate/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Address } from 'viem';

import { ApprovalSteps } from 'components';
import useDelegateApproval from 'hooks/useDelegateApproval';
import { useTranslation } from 'libs/translations';

export interface ApproveDelegateProps {
poolComptrollerContractAddress: Address;
delegateeAddress: Address;
secondStepButtonLabel: string;
children: React.ReactNode;
}

export const ApproveDelegate: React.FC<ApproveDelegateProps> = ({
poolComptrollerContractAddress,
delegateeAddress,
secondStepButtonLabel,
children,
}) => {
const { t } = useTranslation();

const {
isUpdateDelegateStatusLoading,
updatePoolDelegateStatus,
isDelegateApproved,
isDelegateApprovedLoading,
} = useDelegateApproval({
delegateeAddress,
poolComptrollerAddress: poolComptrollerContractAddress,
});

const approveDelegate = () => {
return updatePoolDelegateStatus({ approvedStatus: true });
};

const showApproveDelegateStep =
!isDelegateApprovedLoading && isDelegateApproved !== undefined && !isDelegateApproved;

return (
<ApprovalSteps
showApprovalSteps={showApproveDelegateStep}
isApprovalActionLoading={isUpdateDelegateStatusLoading}
approvalAction={approveDelegate}
firstStepLabel={t('approveDelegateSteps.step1')}
firstStepTooltip={t('approveDelegateSteps.approveDelegateButton.tooltip')}
firstStepButtonLabel={t('approveDelegateSteps.approveDelegateButton.text')}
secondStepLabel={t('approveTokenSteps.step2')}
secondStepButtonLabel={secondStepButtonLabel}
>
{children}
</ApprovalSteps>
);
};
57 changes: 57 additions & 0 deletions apps/evm/src/containers/ApproveToken/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { Mock } from 'vitest';

import { fireEvent, waitFor } from '@testing-library/react';
import fakeSpenderAddress from '__mocks__/models/address';
import { xvs } from '__mocks__/models/tokens';
import useTokenApproval from 'hooks/useTokenApproval';
import { en } from 'libs/translations';
import { renderComponent } from 'testUtils/render';
import { ApproveToken, type ApproveTokenProps } from '..';

const props: ApproveTokenProps = {
token: xvs,
spenderAddress: fakeSpenderAddress,
secondStepButtonLabel: 'Fake second step button label',
children: 'Fake children',
};

vi.mock('hooks/useTokenApproval');

describe('ApproveToken', () => {
it('prompts user to approve token if it is not', async () => {
const mockApproveToken = vi.fn();

(useTokenApproval as Mock).mockImplementation(() => ({
approveToken: mockApproveToken,
isTokenApproved: false,
isApproveTokenLoading: false,
isWalletSpendingLimitLoading: false,
}));

const { getByText } = renderComponent(<ApproveToken {...props} />);

const submitButtonLabel = en.approveTokenSteps.approveTokenButton.text.replace(
'{{tokenSymbol}}',
xvs.symbol,
);

await waitFor(() => expect(getByText(submitButtonLabel)));

fireEvent.click(getByText(submitButtonLabel));

expect(mockApproveToken).toHaveBeenCalledTimes(1);
});

it('displays children if token is approved', async () => {
(useTokenApproval as Mock).mockImplementation(() => ({
approveToken: vi.fn(),
isTokenApproved: true,
isApproveTokenLoading: false,
isWalletSpendingLimitLoading: false,
}));

const { getByText } = renderComponent(<ApproveToken {...props} />);

await waitFor(() => expect(getByText(props.children as string)));
});
});
51 changes: 51 additions & 0 deletions apps/evm/src/containers/ApproveToken/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Address } from 'viem';

import { ApprovalSteps } from 'components';
import useTokenApproval from 'hooks/useTokenApproval';
import { useTranslation } from 'libs/translations';
import { useAccountAddress } from 'libs/wallet';
import type { Token } from 'types';

export interface ApproveTokenProps {
token: Token;
spenderAddress: Address;
secondStepButtonLabel: string;
children: React.ReactNode;
}

export const ApproveToken: React.FC<ApproveTokenProps> = ({
token,
spenderAddress,
secondStepButtonLabel,
children,
}) => {
const { accountAddress } = useAccountAddress();
const { t } = useTranslation();

const { isTokenApproved, approveToken, isApproveTokenLoading, isWalletSpendingLimitLoading } =
useTokenApproval({
token,
spenderAddress,
accountAddress,
});

const showApproveTokenStep =
!isWalletSpendingLimitLoading && isTokenApproved !== undefined && !isTokenApproved;

return (
<ApprovalSteps
showApprovalSteps={showApproveTokenStep}
isApprovalActionLoading={isApproveTokenLoading}
approvalAction={approveToken}
firstStepLabel={t('approveTokenSteps.step1')}
firstStepTooltip={t('approveTokenSteps.approveTokenButton.tooltip')}
firstStepButtonLabel={t('approveTokenSteps.approveTokenButton.text', {
tokenSymbol: token.symbol,
})}
secondStepLabel={t('approveTokenSteps.step2')}
secondStepButtonLabel={secondStepButtonLabel}
>
{children}
</ApprovalSteps>
);
};

This file was deleted.

11 changes: 5 additions & 6 deletions apps/evm/src/containers/Form/RhfSubmitButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { ChainId } from '@venusprotocol/chains';
import { type Control, useFormState } from 'react-hook-form';

import { type ButtonProps, PrimaryButton } from 'components';
import { ApproveToken, type ApproveTokenProps } from 'containers/ApproveToken';
import { ConnectWallet } from 'containers/ConnectWallet';

import type { ChainId } from '@venusprotocol/chains';
import { SwitchChain } from 'containers/SwitchChain';
import { ApproveTokenSteps, type ApproveTokenStepsProps } from './ApproveTokenSteps';

export interface RhfSubmitButtonProps extends ButtonProps {
control: Control<any>;
Expand All @@ -16,7 +15,7 @@ export interface RhfSubmitButtonProps extends ButtonProps {
| {
chainId: ChainId;
};
spendingApproval?: Omit<ApproveTokenStepsProps, 'children' | 'secondStepButtonLabel'>;
spendingApproval?: Omit<ApproveTokenProps, 'children' | 'secondStepButtonLabel'>;
analyticVariant?: string;
}

Expand Down Expand Up @@ -48,9 +47,9 @@ export const RhfSubmitButton: React.FC<RhfSubmitButtonProps> = ({

if (formState.isValid && spendingApproval) {
dom = (
<ApproveTokenSteps secondStepButtonLabel={enabledLabel} {...spendingApproval}>
<ApproveToken secondStepButtonLabel={enabledLabel} {...spendingApproval}>
{dom}
</ApproveTokenSteps>
</ApproveToken>
);
}

Expand Down
4 changes: 2 additions & 2 deletions apps/evm/src/hooks/useDelegateApproval/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Address } from 'viem';
export interface UseDelegateApprovalInput {
poolComptrollerAddress: Address;
delegateeAddress: Address;
enabled: boolean;
enabled?: boolean;
}

export interface UseDelegateApprovalOutput {
Expand All @@ -19,7 +19,7 @@ export interface UseDelegateApprovalOutput {
const useDelegateApproval = ({
poolComptrollerAddress,
delegateeAddress,
enabled,
enabled = true,
}: UseDelegateApprovalInput): UseDelegateApprovalOutput => {
const { accountAddress } = useAccountAddress();

Expand Down
Loading
Loading