vega-frontend-monorepo/libs/web3/src/lib/use-ethereum-withdraw-approvals-manager.spec.tsx
Edd 996c4ac4d5
chore: regenerate types from version 66 (#2568)
* chore: regenerate types from version 66

* chore: fix transfer type mappings

* chore: update triggering ratio to use strings

* chore: regen explorer types

* build(explorer): fix linting error in regenerated explorer types

* chore: remove expiry fields from tests and fix imports to use updated generated code

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-01-10 14:21:54 -08:00

304 lines
8.9 KiB
TypeScript

import { useEthWithdrawApprovalsManager } from './use-ethereum-withdraw-approvals-manager';
import { renderHook } from '@testing-library/react';
import type { MockedResponse } from '@apollo/client/testing';
import type { ReactNode } from 'react';
import { MockedProvider } from '@apollo/client/testing';
import waitForNextTick from 'flush-promises';
import * as Schema from '@vegaprotocol/types';
import { ApprovalStatus } from './use-ethereum-withdraw-approvals-store';
import BigNumber from 'bignumber.js';
import type {
EthWithdrawApprovalStore,
EthWithdrawalApprovalState,
} from './use-ethereum-withdraw-approvals-store';
import type { EthTransactionStore } from './use-ethereum-transaction-store';
import { WithdrawalApprovalDocument } from '@vegaprotocol/wallet';
import type { WithdrawalApprovalQuery } from '@vegaprotocol/wallet';
import { NetworkParamsDocument } from '@vegaprotocol/react-helpers';
import type { NetworkParamsQuery } from '@vegaprotocol/react-helpers';
const mockWeb3Provider = jest.fn();
jest.mock('@web3-react/core', () => ({
useWeb3React: () => ({
provider: mockWeb3Provider(),
}),
}));
const mockEthTransactionStoreState = jest.fn<
Partial<EthTransactionStore>,
[]
>();
jest.mock('./use-ethereum-transaction-store', () => ({
...jest.requireActual('./use-ethereum-transaction-store'),
useEthTransactionStore: (
selector: (state: Partial<EthTransactionStore>) => void
) => selector(mockEthTransactionStoreState()),
}));
const mockEthWithdrawApprovalsStoreState = jest.fn<
Partial<EthWithdrawApprovalStore>,
[]
>();
jest.mock('./use-ethereum-withdraw-approvals-store', () => ({
...jest.requireActual('./use-ethereum-withdraw-approvals-store'),
useEthWithdrawApprovalsStore: (
selector: (state: Partial<EthWithdrawApprovalStore>) => void
) => selector(mockEthWithdrawApprovalsStoreState()),
}));
const mockUseGetWithdrawThreshold = jest.fn();
jest.mock('./use-get-withdraw-threshold', () => ({
useGetWithdrawThreshold: () => mockUseGetWithdrawThreshold(),
}));
const mockUseGetWithdrawDelay = jest.fn();
jest.mock('./use-get-withdraw-delay', () => ({
useGetWithdrawDelay: () => mockUseGetWithdrawDelay(),
}));
const mockUseEthereumConfig = jest.fn(() => ({
collateral_bridge_contract: {
address: 'address',
},
}));
jest.mock('./use-ethereum-config', () => ({
useEthereumConfig: () => ({
config: mockUseEthereumConfig(),
}),
}));
jest.mock('@vegaprotocol/smart-contracts', () => ({
CollateralBridge: jest.fn().mockImplementation(),
}));
const update = jest.fn();
const withdrawalId = 'withdrawalId';
const createWithdrawTransaction = (
transaction?: Partial<EthWithdrawalApprovalState>
): EthWithdrawalApprovalState => ({
id: 0,
status: ApprovalStatus.Idle,
createdAt: new Date('2022-12-12T11:24:40.301Z'),
dialogOpen: true,
withdrawal: {
id: withdrawalId,
status: Schema.WithdrawalStatus.STATUS_OPEN,
createdTimestamp: '2022-12-12T11:24:40.301Z',
pendingOnForeignChain: false,
amount: '50',
asset: {
__typename: 'Asset',
id: 'fdf0ec118d98393a7702cf72e46fc87ad680b152f64b2aac59e093ac2d688fbb',
name: 'USDT-T',
symbol: 'USDT-T',
decimals: 18,
status: Schema.AssetStatus.STATUS_ENABLED,
source: {
__typename: 'ERC20',
contractAddress: 'contractAddress',
},
},
},
...transaction,
});
const create = jest.fn();
const getSigner = jest.fn();
mockWeb3Provider.mockReturnValue({
getSigner,
});
mockUseGetWithdrawDelay.mockReturnValue(() => Promise.resolve(60));
mockUseGetWithdrawThreshold.mockReturnValue(() =>
Promise.resolve(new BigNumber(100))
);
let dateNowSpy: jest.SpyInstance<number, []>;
const erc20WithdrawalApproval = {
assetSource: 'asset-source',
amount: '100',
nonce: '1',
creation: '1',
signatures: 'signatures',
targetAddress: 'target-address',
};
const mockedNetworkParams: MockedResponse<NetworkParamsQuery> = {
request: {
query: NetworkParamsDocument,
variables: {},
},
result: {
data: {
networkParametersConnection: {
edges: [
{
node: {
key: 'blockchains.ethereumConfig',
value: JSON.stringify({
collateral_bridge_contract: { address: '' },
}),
},
},
],
},
},
},
};
const mockedWithdrawalApproval: MockedResponse<WithdrawalApprovalQuery> = {
request: {
query: WithdrawalApprovalDocument,
variables: { withdrawalId },
},
result: {
data: { erc20WithdrawalApproval },
},
};
const render = (
mocks: MockedResponse[] = [mockedWithdrawalApproval, mockedNetworkParams]
) => {
const wrapper = ({ children }: { children: ReactNode }) => (
<MockedProvider mocks={mocks}>{children}</MockedProvider>
);
return renderHook(() => useEthWithdrawApprovalsManager(), { wrapper });
};
describe('useEthWithdrawApprovalsManager', () => {
beforeEach(() => {
update.mockReset();
create.mockReset();
mockEthTransactionStoreState.mockReset();
mockEthWithdrawApprovalsStoreState.mockReset();
});
afterEach(() => {
if (dateNowSpy) {
dateNowSpy.mockRestore();
}
});
it('sendTx of first pending transaction', async () => {
mockEthTransactionStoreState.mockReturnValue({ create });
mockEthWithdrawApprovalsStoreState.mockReturnValue({
transactions: [
createWithdrawTransaction(),
createWithdrawTransaction({ id: 1 }),
],
update,
});
const { rerender } = render();
expect(update.mock.calls[0][0]).toEqual(0);
expect(update.mock.calls[0][1].status).toEqual(ApprovalStatus.Pending);
rerender();
expect(update.mock.calls[1][0]).toEqual(1);
expect(update.mock.calls[1][1].status).toEqual(ApprovalStatus.Pending);
});
it('sets status to error if wrong asset type', async () => {
const transaction = createWithdrawTransaction();
transaction.withdrawal.asset.source.__typename = 'BuiltinAsset';
mockEthTransactionStoreState.mockReturnValue({ create });
mockEthWithdrawApprovalsStoreState.mockReturnValue({
transactions: [transaction],
update,
});
render();
expect(update.mock.calls[0][1].status).toEqual(ApprovalStatus.Error);
});
it('sets status to pending', async () => {
mockEthWithdrawApprovalsStoreState.mockReturnValue({
transactions: [createWithdrawTransaction()],
update,
});
mockEthTransactionStoreState.mockReturnValue({ create });
render();
expect(update.mock.calls[0][1].status).toEqual(ApprovalStatus.Pending);
});
it('sets status to delayed if amount is greater than threshold', async () => {
const transaction = createWithdrawTransaction();
mockUseGetWithdrawThreshold.mockReturnValueOnce(() =>
Promise.resolve(
new BigNumber(transaction.withdrawal.amount)
.dividedBy(Math.pow(10, transaction.withdrawal.asset.decimals))
.dividedBy(2)
)
);
mockEthWithdrawApprovalsStoreState.mockReturnValue({
transactions: [transaction],
update,
});
mockEthTransactionStoreState.mockReturnValue({ create });
dateNowSpy = jest
.spyOn(Date, 'now')
.mockImplementation(() =>
new Date(transaction.withdrawal.createdTimestamp).valueOf()
);
render();
await waitForNextTick();
expect(update.mock.calls[1][1].status).toEqual(ApprovalStatus.Delayed);
});
it('fetch approval if not provided', async () => {
const transaction = createWithdrawTransaction();
mockEthWithdrawApprovalsStoreState.mockReturnValue({
transactions: [transaction],
update,
});
mockEthTransactionStoreState.mockReturnValue({ create });
render();
await waitForNextTick();
await waitForNextTick();
expect(update.mock.calls[1][1].approval).toEqual(erc20WithdrawalApproval);
});
it('sets status to error if withdraw dependencies not met', async () => {
const transaction = createWithdrawTransaction();
transaction.approval = {
...erc20WithdrawalApproval,
signatures: '',
};
mockEthWithdrawApprovalsStoreState.mockReturnValue({
transactions: [transaction],
update,
});
mockEthTransactionStoreState.mockReturnValue({ create });
render();
await waitForNextTick();
expect(update.mock.calls[1][1].status).toEqual(ApprovalStatus.Error);
});
it('sets status to ready and creates eth transaction', async () => {
const transaction = createWithdrawTransaction();
transaction.approval = erc20WithdrawalApproval;
mockEthWithdrawApprovalsStoreState.mockReturnValue({
transactions: [transaction],
update,
});
mockEthTransactionStoreState.mockReturnValue({ create });
render();
await waitForNextTick();
expect(create).toBeCalledWith({}, 'withdraw_asset', [
erc20WithdrawalApproval.assetSource,
erc20WithdrawalApproval.amount,
erc20WithdrawalApproval.targetAddress,
erc20WithdrawalApproval.creation,
erc20WithdrawalApproval.nonce,
erc20WithdrawalApproval.signatures,
]);
});
});