996c4ac4d5
* 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>
304 lines
8.9 KiB
TypeScript
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,
|
|
]);
|
|
});
|
|
});
|