diff --git a/.eslintrc.js b/.eslintrc.js index 3ef588d401..66b7d0fbb5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -84,6 +84,12 @@ module.exports = { message: 'Please import from src/dependencies to make this dependency more testable', }, + { + group: ['@/services/web3/transactions/concerns/contract.concern'], + importNames: ['ContractConcern'], + message: + 'Please import from src/dependencies to make this dependency more testable', + }, // { // group: ['@/services/multicalls/multicaller'], // importNames: ['Multicaller'], diff --git a/src/composables/queries/usePoolsQuery.spec.ts b/src/composables/queries/usePoolsQuery.spec.ts index a3099d0a34..bd4a78c1df 100644 --- a/src/composables/queries/usePoolsQuery.spec.ts +++ b/src/composables/queries/usePoolsQuery.spec.ts @@ -5,7 +5,6 @@ import { defaultPool2, initPoolsFallbackRepositoryWithDefaultMocks, } from '@/dependencies/PoolsFallbackRepository.mocks'; -import { initRpcProviderServiceWithDefaultMocks } from '@/dependencies/rpc-provider.service.mocks'; import { mountComposableWithFakeTokensProvider as mountComposable, waitForQueryData, @@ -15,7 +14,6 @@ import usePoolsQuery from './usePoolsQuery'; initPoolsFallbackRepositoryWithDefaultMocks(); initMulticallWithDefaultMocks(); -initRpcProviderServiceWithDefaultMocks(); async function mountPoolsQuery(poolsSortField = '') { const filterTokens = ref([]); diff --git a/src/dependencies/contract.concern.mocks.ts b/src/dependencies/contract.concern.mocks.ts new file mode 100644 index 0000000000..8a15151a9f --- /dev/null +++ b/src/dependencies/contract.concern.mocks.ts @@ -0,0 +1,30 @@ +import { + // eslint-disable-next-line no-restricted-imports + ContractConcern, + SendTransactionOpts, +} from '@/services/web3/transactions/concerns/contract.concern'; +import { initContractConcern } from './contract.concern'; +import { aSigner } from '@tests/unit/builders/signer'; +import { TransactionResponse } from '@ethersproject/abstract-provider'; +import { mock } from 'vitest-mock-extended'; + +export const defaultContractTransactionResponse = mock(); +defaultContractTransactionResponse.chainId = 5; + +export const sendTransactionMock = vi.fn( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (opts: SendTransactionOpts) => + Promise.resolve(defaultContractTransactionResponse) +); + +export class MockedContractConcern extends ContractConcern { + constructor() { + super(aSigner()); + } + + sendTransaction = (opts: SendTransactionOpts) => sendTransactionMock(opts); +} + +export function initContractConcernWithDefaultMocks() { + initContractConcern(MockedContractConcern); +} diff --git a/src/dependencies/contract.concern.ts b/src/dependencies/contract.concern.ts new file mode 100644 index 0000000000..766fcfe1c3 --- /dev/null +++ b/src/dependencies/contract.concern.ts @@ -0,0 +1,23 @@ +// eslint-disable-next-line no-restricted-imports +import { ContractConcern } from '@/services/web3/transactions/concerns/contract.concern'; +import { handleDependencyError } from '.'; + +let contractConcern: typeof ContractConcern | undefined; + +/** + * initContractConcern uses the real Contract Concern instance by default but allows injecting Contract Concern mocks from tests + */ +export function initContractConcern( + contractConcernInstance: typeof ContractConcern = ContractConcern +) { + contractConcern = contractConcernInstance; +} + +export function getContractConcern() { + if (!contractConcern) { + handleDependencyError('Contract Concern'); + } + return contractConcern; +} + +export type ContractConcernType = ContractConcern; diff --git a/src/dependencies/default-mocks.ts b/src/dependencies/default-mocks.ts index 57cef60a09..0867cd7882 100644 --- a/src/dependencies/default-mocks.ts +++ b/src/dependencies/default-mocks.ts @@ -7,6 +7,7 @@ import { initMulticallerWithDefaultMocks } from './Multicaller.mocks'; import { initWalletConnectorsWithDefaultMocks } from './wallets/default-mocks'; import { initBalancerApiWithDefaultMocks } from './balancer-api.mocks'; import { initPoolsFallbackRepositoryWithDefaultMocks } from './PoolsFallbackRepository.mocks'; +import { initContractConcernWithDefaultMocks } from './contract.concern.mocks'; export function initDependenciesWithDefaultMocks() { initMulticallWithDefaultMocks(); @@ -19,4 +20,5 @@ export function initDependenciesWithDefaultMocks() { initEthersContractWithDefaultMocks(); initWalletConnectorsWithDefaultMocks(); initPoolsFallbackRepositoryWithDefaultMocks(); + initContractConcernWithDefaultMocks(); } diff --git a/src/dependencies/index.ts b/src/dependencies/index.ts index 20dcd64fc8..6b146750a8 100644 --- a/src/dependencies/index.ts +++ b/src/dependencies/index.ts @@ -6,6 +6,7 @@ import { initOldMulticaller } from './OldMulticaller'; import { initRpcProviderService } from './rpc-provider.service'; import { initWalletConnectors } from './wallets'; import { initWeb3Provider } from './wallets/Web3Provider'; +import { initContractConcern } from './contract.concern'; export function initDependencies() { // We exclude the heavy dependencies to save bundle size: @@ -20,6 +21,7 @@ export function initDependencies() { initEthersContract(); initWeb3Provider(); initWalletConnectors(); + initContractConcern(); } export function handleDependencyError(dependencyName: string): never { diff --git a/src/providers/local/exit-pool.provider.spec.ts b/src/providers/local/exit-pool.provider.spec.ts index 5aae25200f..064bd255de 100644 --- a/src/providers/local/exit-pool.provider.spec.ts +++ b/src/providers/local/exit-pool.provider.spec.ts @@ -1,6 +1,5 @@ import { initBalancerSdkWithDefaultMocks } from '@/dependencies/balancer-sdk.mocks'; import { initEthersContractWithDefaultMocks } from '@/dependencies/EthersContract.mocks'; -import { initRpcProviderServiceWithDefaultMocks } from '@/dependencies/rpc-provider.service.mocks'; import { hasFetchedPoolsForSor } from '@/lib/balancer.sdk'; import { poolIdThatRequiresInternalBalanceExit } from '@/lib/config/goerli/pools'; import { ExactInExitHandler } from '@/services/balancer/pools/exits/handlers/exact-in-exit.handler'; @@ -12,10 +11,11 @@ import { mountComposableWithFakeTokensProvider as mountComposable } from '@tests import { groAddress, wethAddress } from '@tests/unit/builders/address'; import waitForExpect from 'wait-for-expect'; import { exitPoolProvider } from './exit-pool.provider'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; initEthersContractWithDefaultMocks(); initBalancerSdkWithDefaultMocks(); -initRpcProviderServiceWithDefaultMocks(); +initContractConcernWithDefaultMocks(); async function mountExitPoolProvider(pool: Pool) { // Pretend that pools for SOR were fetched diff --git a/src/providers/local/join-pool.provider.spec.ts b/src/providers/local/join-pool.provider.spec.ts index fa39625a45..bef41618c0 100644 --- a/src/providers/local/join-pool.provider.spec.ts +++ b/src/providers/local/join-pool.provider.spec.ts @@ -7,9 +7,11 @@ import { anAmountIn } from '@tests/unit/builders/join-exit.builders'; import waitForExpect from 'wait-for-expect'; import { joinPoolProvider } from './join-pool.provider'; import { groAddress, wethAddress } from '@tests/unit/builders/address'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; initEthersContractWithDefaultMocks(); initBalancerSdkWithDefaultMocks(); +initContractConcernWithDefaultMocks(); async function mountJoinPoolProvider(pool: Pool) { const { result } = await mountComposable(() => joinPoolProvider(ref(pool))); diff --git a/src/providers/wallet.provider.spec.ts b/src/providers/wallet.provider.spec.ts index 6e5897e900..ebfa3c949e 100644 --- a/src/providers/wallet.provider.spec.ts +++ b/src/providers/wallet.provider.spec.ts @@ -23,6 +23,9 @@ import { lsGet } from '@/lib/utils'; import { mountComposable } from '@tests/mount-helpers'; import { SANCTIONED_ADDRESS } from '@tests/msw/rest-handlers'; import { isBlockedAddress, useWallets } from './wallet.provider'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; + +initContractConcernWithDefaultMocks(); describe('Given that VITE_WALLET_SCREENING is false', () => { beforeAll(() => { @@ -54,8 +57,6 @@ describe('Given that VITE_WALLET_SCREENING is true', () => { }); }); -// initDependenciesWithDefaultMocks(); - async function mountWalletProvider() { const { result } = mountComposable(() => useWallets()); return result; diff --git a/src/services/balancer/pools/exits/handlers/exact-in-exit.handler.spec.ts b/src/services/balancer/pools/exits/handlers/exact-in-exit.handler.spec.ts index b9b572dca9..0b4774bdae 100644 --- a/src/services/balancer/pools/exits/handlers/exact-in-exit.handler.spec.ts +++ b/src/services/balancer/pools/exits/handlers/exact-in-exit.handler.spec.ts @@ -10,10 +10,11 @@ import { defaultGasLimit, defaultTransactionResponse, } from '@tests/unit/builders/signer'; -import { ref } from 'vue'; import { ExactInExitHandler } from './exact-in-exit.handler'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; initBalancerSdkWithDefaultMocks(); +initContractConcernWithDefaultMocks(); async function mountExactInExitHandler(pool: Pool) { return new ExactInExitHandler(ref(pool), getBalancerSDK()); diff --git a/src/services/balancer/pools/exits/handlers/exact-out-exit.handler.spec.ts b/src/services/balancer/pools/exits/handlers/exact-out-exit.handler.spec.ts index 8a8d747994..dc40b812d0 100644 --- a/src/services/balancer/pools/exits/handlers/exact-out-exit.handler.spec.ts +++ b/src/services/balancer/pools/exits/handlers/exact-out-exit.handler.spec.ts @@ -12,10 +12,11 @@ import { defaultGasLimit, defaultTransactionResponse, } from '@tests/unit/builders/signer'; -import { ref } from 'vue'; import { ExactOutExitHandler } from './exact-out-exit.handler'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; initBalancerSdkWithDefaultMocks(); +initContractConcernWithDefaultMocks(); async function mountExactOutExitHandler(pool: Pool) { return new ExactOutExitHandler(ref(pool), getBalancerSDK()); diff --git a/src/services/balancer/pools/exits/handlers/generalised-exit.handler.spec.ts b/src/services/balancer/pools/exits/handlers/generalised-exit.handler.spec.ts index fc14c5270e..ac8d244ce5 100644 --- a/src/services/balancer/pools/exits/handlers/generalised-exit.handler.spec.ts +++ b/src/services/balancer/pools/exits/handlers/generalised-exit.handler.spec.ts @@ -10,11 +10,12 @@ import { defaultGasLimit, defaultTransactionResponse, } from '@tests/unit/builders/signer'; -import { ref } from 'vue'; import { GeneralisedExitHandler } from './generalised-exit.handler'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; initBalancerSdkWithDefaultMocks(); +initContractConcernWithDefaultMocks(); async function mountGeneralizedExitHandler(pool: Pool) { return new GeneralisedExitHandler(ref(pool), getBalancerSDK()); diff --git a/src/services/balancer/pools/exits/handlers/recovery-exit.handler.spec.ts b/src/services/balancer/pools/exits/handlers/recovery-exit.handler.spec.ts index 170bc5884e..24c86720ed 100644 --- a/src/services/balancer/pools/exits/handlers/recovery-exit.handler.spec.ts +++ b/src/services/balancer/pools/exits/handlers/recovery-exit.handler.spec.ts @@ -17,8 +17,10 @@ import { } from '@tests/unit/builders/signer'; import { ExitParams, ExitType } from './exit-pool.handler'; import { RecoveryExitHandler } from './recovery-exit.handler'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; initBalancerSdkWithDefaultMocks(); +initContractConcernWithDefaultMocks(); async function mountRecoveryExitHandler(pool: Pool) { return new RecoveryExitHandler(ref(pool), getBalancerSDK()); diff --git a/src/services/balancer/pools/exits/handlers/swap-exit.handler.spec.ts b/src/services/balancer/pools/exits/handlers/swap-exit.handler.spec.ts index b5cd6898ed..3e41dba974 100644 --- a/src/services/balancer/pools/exits/handlers/swap-exit.handler.spec.ts +++ b/src/services/balancer/pools/exits/handlers/swap-exit.handler.spec.ts @@ -1,3 +1,4 @@ +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; import { getBalancerSDK } from '@/dependencies/balancer-sdk'; import { initBalancerSdkWithDefaultMocks } from '@/dependencies/balancer-sdk.mocks'; import { Web3ProviderMock } from '@/dependencies/wallets/wallet-connector-mocks'; @@ -5,12 +6,15 @@ import { vaultService } from '@/services/contracts/vault.service'; import { Pool } from '@/services/pool/types'; import { aWeightedPool } from '@/__mocks__/weighted-pool'; import { buildExitParams } from '@tests/unit/builders/join-exit.builders'; -import { ref } from 'vue'; import { ExitType } from './exit-pool.handler'; import { SwapExitHandler } from './swap-exit.handler'; import { defaultTransactionResponse } from '@tests/unit/builders/signer'; +import { silenceConsoleLog } from '@tests/unit/console'; initBalancerSdkWithDefaultMocks(); +initContractConcernWithDefaultMocks(); + +silenceConsoleLog(vi, message => message.includes('sendTransaction')); async function mountSwapExitHandler(pool: Pool) { return new SwapExitHandler(ref(pool), getBalancerSDK()); diff --git a/src/services/balancer/pools/joins/handlers/exact-in-join.handler.spec.ts b/src/services/balancer/pools/joins/handlers/exact-in-join.handler.spec.ts index a45229b81d..cc1774dfc1 100644 --- a/src/services/balancer/pools/joins/handlers/exact-in-join.handler.spec.ts +++ b/src/services/balancer/pools/joins/handlers/exact-in-join.handler.spec.ts @@ -17,8 +17,10 @@ import { } from '@tests/unit/builders/signer'; import { ExactInJoinHandler } from './exact-in-join.handler'; import { JoinParams } from './join-pool.handler'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; initBalancerSdkWithDefaultMocks(); +initContractConcernWithDefaultMocks(); async function mountExactInJoinHandler(pool: Pool) { return new ExactInJoinHandler(ref(pool), getBalancerSDK()); diff --git a/src/services/balancer/pools/joins/handlers/generalised-join.handler.spec.ts b/src/services/balancer/pools/joins/handlers/generalised-join.handler.spec.ts index 7666a46cc6..e24d938514 100644 --- a/src/services/balancer/pools/joins/handlers/generalised-join.handler.spec.ts +++ b/src/services/balancer/pools/joins/handlers/generalised-join.handler.spec.ts @@ -10,10 +10,11 @@ import { defaultGasLimit, defaultTransactionResponse, } from '@tests/unit/builders/signer'; -import { ref } from 'vue'; import { GeneralisedJoinHandler } from './generalised-join.handler'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; initBalancerSdkWithDefaultMocks(); +initContractConcernWithDefaultMocks(); async function mountGeneralizedJoinHandler(pool: Pool) { return new GeneralisedJoinHandler(ref(pool), getBalancerSDK()); diff --git a/src/services/contracts/vault.service.spec.ts b/src/services/contracts/vault.service.spec.ts index 6006123422..fa9838a735 100644 --- a/src/services/contracts/vault.service.spec.ts +++ b/src/services/contracts/vault.service.spec.ts @@ -1,4 +1,3 @@ -import { initEthersContractWithDefaultMocks } from '@/dependencies/EthersContract.mocks'; import { FundManagement, SingleSwap, @@ -7,27 +6,26 @@ import { } from '@balancer-labs/sdk'; import { BigNumber } from '@ethersproject/bignumber'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; import { walletService } from '@/services/web3/wallet.service'; +import { silenceConsoleLog } from '@tests/unit/console'; import { configService } from '../config/config.service'; import { SwapToken, SwapTokenType } from '../swap/swap.service'; import VaultService from './vault.service'; import { walletProviderMock } from './vault.service.mocks'; -// @ts-ignore -walletService.setUserProvider(ref(walletProviderMock)); +initContractConcernWithDefaultMocks(); -const vaultService = new VaultService(configService, walletService); +walletService.setUserProvider(computed(() => walletProviderMock)); -initEthersContractWithDefaultMocks(); +const vaultService = new VaultService(configService, walletService); const userAddress = '0xAAA00fB39c06E7b41bEdFf8A6a4e013666141d40'; -//TODO: extract to helper that accepts rules callback -// Silence Contract and Params debug -vi.spyOn(console, 'log').mockImplementation(args => { - if (!args.startsWith('Contract') && !args.startsWith('Params')) - return console.warn(args); -}); +silenceConsoleLog( + vi, + message => message.startsWith('Contract') && !message.startsWith('Params') +); describe('vault.service', () => { let swaps: SwapV2[] = []; diff --git a/src/services/swap/swap.service.spec.ts b/src/services/swap/swap.service.spec.ts index 6b76beff88..facc08ab61 100644 --- a/src/services/swap/swap.service.spec.ts +++ b/src/services/swap/swap.service.spec.ts @@ -9,7 +9,6 @@ import { AddressZero } from '@ethersproject/constants'; import { configService } from '@/services/config/config.service'; -import { initEthersContractWithDefaultMocks } from '@/dependencies/EthersContract.mocks'; import { lidoRelayerService } from '@/services/contracts/lido-relayer.service'; import { vaultService } from '@/services/contracts/vault.service'; import { walletService } from '@/services/web3/wallet.service'; @@ -19,11 +18,12 @@ import { } from '../contracts/vault.service.mocks'; import { setUserAddress } from '../web3/__mocks__/web3.service'; import { SwapService, SwapToken, SwapTokenType } from './swap.service'; +import { initContractConcernWithDefaultMocks } from '@/dependencies/contract.concern.mocks'; vi.mock('@/lib/utils/balancer/lido'); vi.mock('@/services/contracts/lido-relayer.service'); -initEthersContractWithDefaultMocks(); +initContractConcernWithDefaultMocks(); walletService.setUserProvider(computed(() => walletProviderMock)); diff --git a/src/services/web3/transactions/concerns/contract.concern.ts b/src/services/web3/transactions/concerns/contract.concern.ts index b33454ebc0..1ea78c6549 100644 --- a/src/services/web3/transactions/concerns/contract.concern.ts +++ b/src/services/web3/transactions/concerns/contract.concern.ts @@ -17,7 +17,7 @@ import { getEthersContract, } from '@/dependencies/EthersContract'; -type SendTransactionOpts = { +export type SendTransactionOpts = { contractAddress: string; abi: ContractInterface; action: string; diff --git a/src/services/web3/transactions/transaction.builder.spec.ts b/src/services/web3/transactions/transaction.builder.spec.ts index e9b47af6b8..54eaabb2be 100644 --- a/src/services/web3/transactions/transaction.builder.spec.ts +++ b/src/services/web3/transactions/transaction.builder.spec.ts @@ -1,4 +1,5 @@ import { initEthersContract } from '@/dependencies/EthersContract'; +import { initContractConcern } from '@/dependencies/contract.concern'; import { MockedContractWithSigner } from '@/dependencies/EthersContract.mocks'; import { initOldMulticallerWithDefaultMocks } from '@/dependencies/OldMulticaller.mocks'; import { AddressZero } from '@ethersproject/constants'; @@ -7,6 +8,8 @@ import { ContractConcern } from './concerns/contract.concern'; import { RawConcern } from './concerns/raw.concern'; import { TransactionBuilder } from './transaction.builder'; +initContractConcern(); + vi.mock('@ethersproject/providers', () => { return { JsonRpcSigner: vi.fn().mockImplementation(() => { @@ -21,7 +24,7 @@ vi.mock('@ethersproject/providers', () => { }), }; }); -vi.mock('@/services/rpc-provider/rpc-provider.service'); + vi.mock('@/services/gas/gas.service', () => { return { gasService: { @@ -34,15 +37,6 @@ vi.mock('@/services/gas/gas.service', () => { }, }; }); -vi.mock('ethers', () => { - return { - Contract: vi.fn().mockImplementation(() => { - return { - test: vi.fn(), - }; - }), - }; -}); const contractActionMock = vi.fn(() => 1e5); @@ -51,9 +45,10 @@ class ContractMock extends MockedContractWithSigner { } initOldMulticallerWithDefaultMocks(); - //@ts-ignore initEthersContract(ContractMock); +// Init real implementation to be tested +initContractConcern(); const SignerMock = JsonRpcSigner; diff --git a/src/services/web3/transactions/transaction.builder.ts b/src/services/web3/transactions/transaction.builder.ts index b9303b4354..f9284f3812 100644 --- a/src/services/web3/transactions/transaction.builder.ts +++ b/src/services/web3/transactions/transaction.builder.ts @@ -1,11 +1,17 @@ import { JsonRpcSigner } from '@ethersproject/providers'; -import { ContractConcern } from './concerns/contract.concern'; import { RawConcern } from './concerns/raw.concern'; +import { + getContractConcern, + ContractConcernType, +} from '@/dependencies/contract.concern'; export class TransactionBuilder { + contract!: ContractConcernType; constructor( public readonly signer: JsonRpcSigner, - public readonly contract = new ContractConcern(signer), public readonly raw = new RawConcern(signer) - ) {} + ) { + const ContractConcernConstructor = getContractConcern(); + this.contract = new ContractConcernConstructor(this.signer); + } }