diff --git a/core/tests/ts-tests/tests/api.test.ts b/core/tests/ts-tests/tests/api.test.ts index fada97d2c7..c0ad3eb9fe 100644 --- a/core/tests/ts-tests/tests/api.test.ts +++ b/core/tests/ts-tests/tests/api.test.ts @@ -1,12 +1,12 @@ import { Wallet, RestProvider, getDefaultRestProvider, types, utils } from 'zksync'; -import { Tester } from './tester'; +import { Tester } from './tester/tester'; import * as ethers from 'ethers'; -import './priority-ops'; -import './change-pub-key'; -import './transfer'; -import './withdraw'; -import './forced-exit'; -import './mint-nft'; +import './tester/priority-ops'; +import './tester/change-pub-key'; +import './tester/transfer'; +import './tester/withdraw'; +import './tester/forced-exit'; +import './tester/mint-nft'; import { expect } from 'chai'; import path from 'path'; diff --git a/core/tests/ts-tests/tests/main.test.ts b/core/tests/ts-tests/tests/main.test.ts index 4281582a0f..1d2555c4ab 100644 --- a/core/tests/ts-tests/tests/main.test.ts +++ b/core/tests/ts-tests/tests/main.test.ts @@ -1,520 +1,6 @@ -import { expect, use } from 'chai'; -import { BigNumber, utils } from 'ethers'; -import { Wallet, types, crypto, Signer, No2FAWalletSigner } from 'zksync'; -import chaiAsPromised from 'chai-as-promised'; -import { Tester } from './tester'; -import './priority-ops'; -import './change-pub-key'; -import './transfer'; -import './withdraw'; -import './mint-nft'; -import './forced-exit'; -import './misc'; -import './batch-builder'; -import './create2'; -import './swap'; -import './register-factory'; -import './token-listing'; - -use(chaiAsPromised); - -const TX_AMOUNT = utils.parseEther('10.0'); -// should be enough for ~200 test transactions (excluding fees), increase if needed -const DEPOSIT_AMOUNT = TX_AMOUNT.mul(200); - -// prettier-ignore -/// We don't want to run tests with all tokens, so we highlight basic operations such as: Deposit, Withdrawal, Forced Exit -/// We want to check basic operations with all tokens, and other operations only if it's necessary -const TestSuite = (token: types.TokenSymbol, transport: 'HTTP' | 'WS', providerType: 'REST' | 'RPC', onlyBasic: boolean = false) => -describe(`ZkSync integration tests (token: ${token}, transport: ${transport}, provider: ${providerType})`, () => { - let tester: Tester; - let alice: Wallet; - let bob: Wallet; - let chuck: Wallet; - let david: Wallet; - let frank: Wallet; - let judy: Wallet; - let chris: Wallet; - let operatorBalance: BigNumber; - let nft: types.NFT; - - before('create tester and test wallets', async () => { - tester = await Tester.init('localhost', transport, providerType); - alice = await tester.fundedWallet('5.0'); - bob = await tester.emptyWallet(); - chuck = await tester.emptyWallet(); - david = await tester.fundedWallet('1.0'); - frank = await tester.fundedWallet('1.0'); - judy = await tester.emptyWallet(); - chris = await tester.emptyWallet(); - operatorBalance = await tester.operatorBalance(token); - }); - - after('disconnect tester', async () => { - await tester.disconnect(); - }); - - step('should execute an auto-approved deposit', async () => { - await tester.testDeposit(alice, token, DEPOSIT_AMOUNT, true); - }); - - step('should execute a normal deposit', async () => { - if (token == 'ETH') { - await tester.testDeposit(alice, token, DEPOSIT_AMOUNT); - } else { - expect(await tester.syncWallet.isERC20DepositsApproved(token), 'Token should not be approved').to.be - .false; - const approveERC20 = await tester.syncWallet.approveERC20TokenDeposits(token, DEPOSIT_AMOUNT); - await approveERC20.wait(); - expect( - await tester.syncWallet.isERC20DepositsApproved(token, DEPOSIT_AMOUNT), - 'Token should be approved' - ).to.be.true; - await tester.testDeposit(alice, token, DEPOSIT_AMOUNT); - // It should not be approved because we have approved only DEPOSIT_AMOUNT, not the maximum possible amount of deposit - expect( - await tester.syncWallet.isERC20DepositsApproved(token, DEPOSIT_AMOUNT), - 'Token should not be approved' - ).to.be.false; - const approveERC20Next = await tester.syncWallet.approveERC20TokenDeposits(token); - await approveERC20Next.wait(); - expect(await tester.syncWallet.isERC20DepositsApproved(token), 'The second deposit should be approved') - .to.be.true; - } - }); - - step('should change pubkey onchain', async () => { - await tester.testChangePubKey(alice, token, true); - }); - - step('should execute a transfer to new account', async () => { - await tester.testTransfer(alice, chuck, token, TX_AMOUNT); - }); - - step('should execute a mintNFT', async () => { - nft = await tester.testMintNFT(alice, chuck, token); - }); - step('should execute a getNFT', async () => { - if (onlyBasic) { - return - } - await tester.testGetNFT(alice, token); - }).timeout(500000); - - step('should execute a transfer to existing account', async () => { - if (onlyBasic) { - return; - } - await tester.testTransfer(alice, chuck, token, TX_AMOUNT); - }); - - it('should execute a transfer to self', async () => { - if (onlyBasic) { - return; - } - await tester.testTransfer(alice, alice, token, TX_AMOUNT); - }); - - step('should change pubkey offchain', async () => { - await tester.testChangePubKey(chuck, token, false); - }); - - step('should test multi-transfers', async () => { - await tester.testBatch(alice, bob, token, TX_AMOUNT); - await tester.testIgnoredBatch(alice, bob, token, TX_AMOUNT); - await tester.testRejectedBatch(alice, bob, token, TX_AMOUNT, providerType); - await tester.testInvalidFeeBatch(alice, bob, token, TX_AMOUNT, providerType); - }); - - step('should test batch-builder', async () => { - // We will pay with different token. - const feeToken = token == 'ETH' ? 'wBTC' : 'ETH'; - // Add these accounts to the network. - await tester.testTransfer(alice, david, token, TX_AMOUNT.mul(10)); - await tester.testTransfer(alice, judy, token, TX_AMOUNT.mul(10)); - await tester.testTransfer(alice, frank, token, TX_AMOUNT.mul(10)); - await tester.testTransfer(alice, chris, token, TX_AMOUNT.mul(10)); - - // Also deposit another token to pay with. - await tester.testDeposit(frank, feeToken, DEPOSIT_AMOUNT, true); - - await tester.testBatchBuilderInvalidUsage(david, alice, token); - await tester.testBatchBuilderChangePubKey(david, token, TX_AMOUNT, true); - await tester.testBatchBuilderSignedChangePubKey(chris, token, TX_AMOUNT); - await tester.testBatchBuilderChangePubKey(frank, token, TX_AMOUNT, false); - await tester.testBatchBuilderTransfers(david, frank, token, TX_AMOUNT); - await tester.testBatchBuilderPayInDifferentToken(frank, david, token, feeToken, TX_AMOUNT); - await tester.testBatchBuilderNFT(frank, david, token); - // Finally, transfer, withdraw and forced exit in a single batch. - await tester.testBatchBuilderGenericUsage(david, frank, judy, token, TX_AMOUNT); - }); - - - step('should test swaps and limit orders', async () => { - if (onlyBasic) { - return; - } - const secondToken = token == 'ETH' ? 'wBTC' : 'ETH'; - await tester.testSwap(alice, frank, token, secondToken, TX_AMOUNT); - await tester.testSwapBatch(alice, frank, david, token, secondToken, TX_AMOUNT); - await tester.testSwapMissingSignatures(alice, frank, token, secondToken, TX_AMOUNT); - }); - - step('should swap NFT for fungible tokens', async () => { - if (onlyBasic) { - return; - } - await tester.testSwapNFT(alice, chuck, token, nft.id, TX_AMOUNT); - }); - - step('should test multi-signers', async () => { - // At this point, all these wallets already have their public keys set. - await tester.testMultipleBatchSigners([alice, david, frank], token, TX_AMOUNT); - await tester.testMultipleWalletsWrongSignature(alice, david, token, TX_AMOUNT, providerType); - }); - - step('should test backwards compatibility', async () => { - await tester.testBackwardCompatibleEthMessages(alice, david, token, TX_AMOUNT); - }); - - step('should execute a withdrawal', async () => { - await tester.testVerifiedWithdraw(alice, token, TX_AMOUNT); - }); - - step('should execute NFT transfer', async () => { - if (onlyBasic) { - return; - } - await tester.testTransferNFT(alice, chuck, token); - }); - - step('should execute NFT withdraw', async () => { - await tester.testWithdrawNFT(chuck, token); - }); - - step('should execute a forced exit', async () => { - await tester.testVerifiedForcedExit(alice, bob, token); - }); - - step('should register factory and withdraw nft', async () => { - if (onlyBasic) { - return; - } - await tester.testRegisterFactory(alice, token); - }); - - it('should check collected fees', async () => { - const collectedFee = (await tester.operatorBalance(token)).sub(operatorBalance); - expect(collectedFee.eq(tester.runningFee), `Fee collection failed, expected: ${tester.runningFee.toString()}, got: ${collectedFee.toString()}`).to.be.true; - }); - - it('should fail trying to send tx with wrong signature', async () => { - if (onlyBasic) { - return; - } - await tester.testWrongSignature(alice, bob, token, TX_AMOUNT, providerType); - }); - - describe('Full Exit tests', () => { - let carl: Wallet; - - before('create a test wallet', async () => { - carl = await tester.fundedWallet('5.0'); - }); - - step('should execute full-exit on random wallet', async () => { - if (onlyBasic) { - return; - } - await tester.testFullExit(carl, token, 145); - }); - - step('should fail full-exit with wrong eth-signer', async () => { - if (onlyBasic) { - return; - } - // make a deposit so that wallet is assigned an accountId - await tester.testDeposit(carl, token, DEPOSIT_AMOUNT, true); - - const oldSigner = carl.ethSigner; - carl.ethSigner = tester.ethWallet; - const [before, after] = await tester.testFullExit(carl, token); - expect(before.eq(0), 'Balance before Full Exit must be non-zero').to.be.false; - expect(before.eq(after), 'Balance after incorrect Full Exit should not change').to.be.true; - carl.ethSigner = oldSigner; - }); - - step('should execute NFT full-exit', async () => { - if (onlyBasic) { - return; - } - await tester.testMintNFT(alice, carl, token, true); - await tester.testFullExitNFT(carl); - }); - - step('should execute a normal full-exit', async () => { - if (onlyBasic) { - return; - } - const [before, after] = await tester.testFullExit(carl, token); - expect(before.eq(0), 'Balance before Full Exit must be non-zero').to.be.false; - expect(after.eq(0), 'Balance after Full Exit must be zero').to.be.true; - }); - - step('should execute full-exit on an empty wallet', async () => { - if (onlyBasic) { - return; - } - const [before, after] = await tester.testFullExit(carl, token); - expect(before.eq(0), "Balance before Full Exit must be zero (we've already withdrawn all the funds)").to - .be.true; - expect(after.eq(0), 'Balance after Full Exit must be zero').to.be.true; - }); - }); - - describe('CREATE2 tests', () => { - let hilda: Wallet; - let frida: Wallet; - - step('should setup a create2 account', async () => { - if (onlyBasic) { - return; - } - hilda = await tester.create2Wallet(); - await tester.testDeposit(hilda, token, DEPOSIT_AMOUNT, true); - const cpk = await hilda.setSigningKey({ - feeToken: token, - ethAuthType: 'CREATE2' - }); - await cpk.awaitReceipt(); - const accountState = await hilda.getAccountState(); - expect(accountState.accountType, 'Incorrect account type').to.be.eql('CREATE2'); - }); - - step('should make transfers from create2 account', async () => { - if (onlyBasic) { - return; - } - - await tester.testTransfer(hilda, david, token, TX_AMOUNT); - await tester.testBatch(hilda, david, token, TX_AMOUNT); - }); - - step('should set pubkey and transfer in single batch', async () => { - if (onlyBasic) { - return; - } - frida = await tester.create2Wallet(); - await tester.testDeposit(frida, token, DEPOSIT_AMOUNT, true); - await tester.testCreate2CPKandTransfer(frida, david, token, TX_AMOUNT); - }); - - step('should fail eth-signed tx from create2 account', async () => { - if (onlyBasic) { - return; - } - await tester.testCreate2TxFail(hilda, david, token, TX_AMOUNT); - }); - - step('should fail eth-signed batch from create2 account', async () => { - if (onlyBasic) { - return; - } - // here we have a signle eth signature for the whole batch - await tester.testCreate2SignedBatchFail(hilda, david, token, TX_AMOUNT); - if(providerType === 'RPC') { - // REST provider always expects Ethereum signed message for the whole batch, skip this test. - await tester.testCreate2BatchFail(hilda, david, token, TX_AMOUNT); - } - }); - }); - - describe('No2FA tests', () => { - let hilda: Wallet; - let hildaWithEthSigner: Wallet; - let frida: Wallet; - - // The private key which won't require 2FA - let zkPrivateKey: Uint8Array; - // The private key whcih will require 2FA - let zkPrivateKeyWith2FA: Uint8Array; - - step('should setup an account without 2fa', async () => { - if (onlyBasic) { - return; - } - - // The 2FA will be off only for this L2 private key - zkPrivateKey = await crypto.privateKeyFromSeed(utils.randomBytes(32)); - zkPrivateKeyWith2FA = await crypto.privateKeyFromSeed(utils.randomBytes(32)); - - // Even the wallets with no 2fa should sign message for CPK with their private key - hilda = await tester.fundedWallet('1.0'); - frida = await tester.fundedWallet('1.0'); - hilda.signer = Signer.fromPrivateKey(zkPrivateKeyWith2FA); - await tester.testDeposit(hilda, token, DEPOSIT_AMOUNT, true); - await tester.testDeposit(frida, token, DEPOSIT_AMOUNT, true); - await (await hilda.setSigningKey({ - feeToken: token, - ethAuthType: 'ECDSA' - })).awaitReceipt(); - await (await frida.setSigningKey({ - feeToken: token, - ethAuthType: 'ECDSA' - })).awaitReceipt(); - const pubKeyHash = await crypto.privateKeyToPubKeyHash(zkPrivateKey); - await hilda.toggle2FA(false, pubKeyHash); - - const accountState = await hilda.getAccountState(); - expect(accountState.accountType, 'Incorrect account type').to.be.eql({ - No2FA: pubKeyHash - }); - }); - - step('Test No2FA with wrong PubKeyHash', async () => { - if(onlyBasic) { - return; - } - - hildaWithEthSigner = hilda; - - // Making sure that the wallet has no Ethereum private key - // but has wrong l2 private key - hilda = await Wallet.fromSyncSigner( - new No2FAWalletSigner(hilda.address(), hilda.ethSigner.provider), - Signer.fromPrivateKey(zkPrivateKeyWith2FA), - hilda.provider - ); - - // Here the transfer without Ethereum signature, but with wrong l2 private key - await expect(tester.testTransfer(hilda, frida, token, TX_AMOUNT)).to.be.rejected; - - // No let's go back to the correct l2 private key - hildaWithEthSigner.signer = Signer.fromPrivateKey(zkPrivateKey); - await (await hildaWithEthSigner.setSigningKey({ - feeToken: token, - ethAuthType: 'ECDSA' - })).awaitReceipt(); - - // Making sure that hilda has correct signer - hilda.signer = Signer.fromPrivateKey(zkPrivateKey); - }); - - step('Test No2FA transfers', async () => { - if (onlyBasic) { - return; - } - - await tester.testTransfer(hilda, frida, token, TX_AMOUNT); - await tester.testBatch(hilda, frida, token, TX_AMOUNT); - await tester.testBatchBuilderTransfersWithoutSignatures(hilda, frida, token, TX_AMOUNT); - }) - - step('Test No2FA Swaps', async () => { - if(onlyBasic) { - return; - } - - const secondToken = token == 'ETH' ? 'wBTC' : 'ETH'; - await tester.testDeposit(frida, secondToken, DEPOSIT_AMOUNT, true); - await tester.testSwap(hilda, frida, token, secondToken, TX_AMOUNT); - }) - - step('Test No2FA Withdrawals', async () => { - if(onlyBasic) { - return; - } - - await tester.testWithdraw(hilda, token, TX_AMOUNT); - }) - - step('Test switching 2FA on', async () => { - if(onlyBasic) { - return; - } - - await hildaWithEthSigner.toggle2FA(true); - const accountState = await hilda.getAccountState(); - expect(accountState.accountType, 'Incorrect account type').to.be.eql('Owned'); - }) - }); - - describe('Permissionless token listing tests', () => { - step('Test ERC20 token listing', async () => { - if(onlyBasic || providerType == 'RPC') { - return; - } - await tester.testERC20Listing(); - }) - step('Test non-ERC20 token listing', async () => { - if(onlyBasic || providerType == 'RPC') { - return; - } - await tester.testNonERC20Listing(); - }) - }) -}); - -// wBTC is chosen because it has decimals different from ETH (8 instead of 18). -// Using this token will help us to detect decimals-related errors. -const defaultERC20 = 'wBTC'; -const defaultProviderType = 'REST'; - -let tokenAndTransport = []; -if (process.env.TEST_TRANSPORT) { - if (process.env.TEST_TOKEN) { - // Both transport and token are set, use config from env. - const envTransport = process.env.TEST_TRANSPORT.toUpperCase(); - const envToken = process.env.TEST_TOKEN; - tokenAndTransport = [ - { - transport: envTransport, - token: envToken, - providerType: process.env.TEST_PROVIDER ? process.env.TEST_PROVIDER : defaultProviderType - } - ]; - } else { - // Only transport is set, use wBTC as default token for this transport. - const envTransport = process.env.TEST_TRANSPORT.toUpperCase(); - tokenAndTransport = [ - { - transport: envTransport, - token: defaultERC20, - providerType: process.env.TEST_PROVIDER ? process.env.TEST_PROVIDER : defaultProviderType - } - ]; - } -} else { - // Default case: run HTTPÐ / HTTP&wBTC. - tokenAndTransport = [ - { - transport: 'HTTP', - token: 'ETH', - providerType: 'RPC', - onlyBasic: false - }, - { - transport: 'HTTP', - token: defaultERC20, - providerType: 'RPC', - onlyBasic: false - }, - { - transport: 'HTTP', - token: 'ETH', - providerType: 'REST', - onlyBasic: true - }, - { - transport: 'HTTP', - token: defaultERC20, - providerType: 'REST', - onlyBasic: false - } - ]; -} - -for (const input of tokenAndTransport) { - // @ts-ignore - TestSuite(input.token, input.transport, input.providerType, input.onlyBasic); -} +import './suits/basic'; +import './suits/extended'; +import './suits/full-exit'; +import './suits/create2'; +import './suits/no2fa'; +import './suits/token-listing'; diff --git a/core/tests/ts-tests/tests/suits/basic.ts b/core/tests/ts-tests/tests/suits/basic.ts new file mode 100644 index 0000000000..c57f7907a6 --- /dev/null +++ b/core/tests/ts-tests/tests/suits/basic.ts @@ -0,0 +1,138 @@ +import { expect, use } from 'chai'; +import { BigNumber, utils } from 'ethers'; +import { Wallet, types, crypto, Signer, No2FAWalletSigner } from 'zksync'; +import chaiAsPromised from 'chai-as-promised'; +import { Tester } from '../tester/tester'; +import '../tester/priority-ops'; +import '../tester/change-pub-key'; +import '../tester/transfer'; +import '../tester/withdraw'; +import '../tester/mint-nft'; +import '../tester/forced-exit'; +import '../tester/misc'; +import '../tester/batch-builder'; +import '../tester/create2'; +import '../tester/swap'; +import '../tester/register-factory'; +import '../tester/token-listing'; + +use(chaiAsPromised); + +const TX_AMOUNT = utils.parseEther('10.0'); +// should be enough for ~200 test transactions (excluding fees), increase if needed +const DEPOSIT_AMOUNT = TX_AMOUNT.mul(200); + +// wBTC is chosen because it has decimals different from ETH (8 instead of 18). +// Using this token will help us to detect decimals-related errors. +const defaultERC20 = 'wBTC'; + +let tokenAndTransport = [ + { + transport: 'HTTP', + token: 'ETH', + providerType: 'RPC' + }, + { + transport: 'HTTP', + token: defaultERC20, + providerType: 'RPC' + }, + { + transport: 'HTTP', + token: 'ETH', + providerType: 'REST' + }, + { + transport: 'HTTP', + token: defaultERC20, + providerType: 'REST' + } +]; + +// prettier-ignore +/// We don't want to run tests with all tokens, so we highlight basic operations such as: Deposit, Withdrawal, Forced Exit +/// We want to check basic operations with all tokens, and other operations only if it's necessary +const BasicTestSuite = (token: types.TokenSymbol, transport: 'HTTP' | 'WS', providerType: 'REST' | 'RPC') => + describe(`ZkSync integration tests (token: ${token}, transport: ${transport}, provider: ${providerType})`, () => { + let tester: Tester; + let alice: Wallet; + let bob: Wallet; + let chuck: Wallet; + let operatorBalance: BigNumber; + + before('create tester and test wallets', async () => { + tester = await Tester.init('localhost', transport, providerType); + alice = await tester.fundedWallet('5.0'); + bob = await tester.emptyWallet(); + chuck = await tester.emptyWallet(); + operatorBalance = await tester.operatorBalance(token); + }); + + after('disconnect tester', async () => { + await tester.disconnect(); + }); + + step('should execute an auto-approved deposit', async () => { + await tester.testDeposit(alice, token, DEPOSIT_AMOUNT, true); + }); + + step('should change pubkey offchain', async () => { + await tester.testChangePubKey(alice, token, false); + }); + + step('should execute a transfer to new account', async () => { + await tester.testTransfer(alice, bob, token, TX_AMOUNT); + }); + + step('should execute a mintNFT', async () => { + await tester.testMintNFT(alice, bob, token); + }); + + step('should execute a transfer to existing account', async () => { + await tester.testTransfer(alice, bob, token, TX_AMOUNT); + }); + + step('should test multi-transfers', async () => { + await tester.testBatch(alice, bob, token, TX_AMOUNT); + await tester.testIgnoredBatch(alice, bob, token, TX_AMOUNT); + await tester.testRejectedBatch(alice, bob, token, TX_AMOUNT, providerType); + await tester.testInvalidFeeBatch(alice, bob, token, TX_AMOUNT, providerType); + }); + + step('should test multi-signers', async () => { + await tester.testTransfer(alice, chuck, token, TX_AMOUNT); + await tester.testChangePubKey(bob, token, false); + await tester.testChangePubKey(chuck, token, false); + + await tester.testMultipleBatchSigners([alice, bob, chuck], token, TX_AMOUNT); + await tester.testMultipleWalletsWrongSignature(alice, bob, token, TX_AMOUNT, providerType); + }); + + step('should test backwards compatibility', async () => { + await tester.testBackwardCompatibleEthMessages(alice, bob, token, TX_AMOUNT); + }); + + step('should execute a withdrawal', async () => { + await tester.testVerifiedWithdraw(alice, token, TX_AMOUNT); + }); + + step('should execute NFT withdraw', async () => { + await tester.testWithdrawNFT(bob, token); + }); + + step('should execute a forced exit', async () => { + const forcedExitWallet = await tester.emptyWallet(); + await tester.testTransfer(alice, forcedExitWallet, token, TX_AMOUNT); + await tester.testVerifiedForcedExit(alice, forcedExitWallet, token); + }); + + it('should check collected fees', async () => { + const collectedFee = (await tester.operatorBalance(token)).sub(operatorBalance); + expect(collectedFee.eq(tester.runningFee), `Fee collection failed, expected: ${tester.runningFee.toString()}, got: ${collectedFee.toString()}`).to.be.true; + }); + }); + +for (const input of tokenAndTransport) { + // @ts-ignore + BasicTestSuite(input.token, input.transport, input.providerType, input.onlyBasic); +} diff --git a/core/tests/ts-tests/tests/suits/create2.ts b/core/tests/ts-tests/tests/suits/create2.ts new file mode 100644 index 0000000000..5ee7d11f25 --- /dev/null +++ b/core/tests/ts-tests/tests/suits/create2.ts @@ -0,0 +1,87 @@ +import { expect, use } from 'chai'; +import { BigNumber, utils } from 'ethers'; +import { Wallet, types, crypto, Signer, No2FAWalletSigner } from 'zksync'; +import chaiAsPromised from 'chai-as-promised'; +import { Tester } from '../tester/tester'; +import '../tester/priority-ops'; +import '../tester/change-pub-key'; +import '../tester/transfer'; +import '../tester/withdraw'; +import '../tester/mint-nft'; +import '../tester/forced-exit'; +import '../tester/misc'; +import '../tester/batch-builder'; +import '../tester/create2'; +import '../tester/swap'; +import '../tester/register-factory'; +import '../tester/token-listing'; + +use(chaiAsPromised); + +const TX_AMOUNT = utils.parseEther('10.0'); +// should be enough for ~200 test transactions (excluding fees), increase if needed +const DEPOSIT_AMOUNT = TX_AMOUNT.mul(200); + +// wBTC is chosen because it has decimals different from ETH (8 instead of 18). +// Using this token will help us to detect decimals-related errors. +const defaultERC20 = 'wBTC'; + +describe(`CREATE2 tests`, () => { + const transport = 'HTTP'; + const providerType = 'REST'; + const token = 'wBTC'; + let hilda: Wallet; + let frida: Wallet; + let david: Wallet; + + let tester: Tester; + + before('create tester and test wallets', async () => { + tester = await Tester.init('localhost', transport, providerType); + david = await tester.fundedWallet('1.0'); + }); + + after('disconnect tester', async () => { + await tester.disconnect(); + }); + + step('should setup a create2 account', async () => { + hilda = await tester.create2Wallet(); + await tester.testDeposit(hilda, token, DEPOSIT_AMOUNT, true); + const cpk = await hilda.setSigningKey({ + feeToken: token, + ethAuthType: 'CREATE2' + }); + await cpk.awaitReceipt(); + const accountState = await hilda.getAccountState(); + expect(accountState.accountType, 'Incorrect account type').to.be.eql('CREATE2'); + }); + + step('should make transfers from create2 account', async () => { + await tester.testTransfer(hilda, david, token, TX_AMOUNT); + await tester.testBatch(hilda, david, token, TX_AMOUNT); + }); + + step('should set pubkey and transfer in single batch', async () => { + frida = await tester.create2Wallet(); + await tester.testDeposit(frida, token, DEPOSIT_AMOUNT, true); + await tester.testCreate2CPKandTransfer(frida, david, token, TX_AMOUNT); + }); + + step('should fail eth-signed tx from create2 account', async () => { + await tester.testCreate2TxFail(hilda, david, token, TX_AMOUNT); + }); + + step('should fail eth-signed batch from create2 account', async () => { + // here we have a signle eth signature for the whole batch + await tester.testCreate2SignedBatchFail(hilda, david, token, TX_AMOUNT); + + // Switch provider to RPC, because REST provider always expects + // Ethereum signed message for the whole batch, skip this test. + const oldProvider = tester.syncWallet.provider; + tester.syncWallet.provider = await Tester.createSyncProvider('localhost', 'HTTP', 'RPC'); + await tester.testCreate2BatchFail(hilda, david, token, TX_AMOUNT); + // Restore provider in tester + tester.syncWallet.provider = oldProvider; + }); +}); diff --git a/core/tests/ts-tests/tests/suits/extended.ts b/core/tests/ts-tests/tests/suits/extended.ts new file mode 100644 index 0000000000..2c1a00612c --- /dev/null +++ b/core/tests/ts-tests/tests/suits/extended.ts @@ -0,0 +1,168 @@ +import { expect, use } from 'chai'; +import { BigNumber, utils } from 'ethers'; +import { Wallet, types, crypto, Signer, No2FAWalletSigner } from 'zksync'; +import chaiAsPromised from 'chai-as-promised'; +import { Tester } from '../tester/tester'; +import '../tester/priority-ops'; +import '../tester/change-pub-key'; +import '../tester/transfer'; +import '../tester/withdraw'; +import '../tester/mint-nft'; +import '../tester/forced-exit'; +import '../tester/misc'; +import '../tester/batch-builder'; +import '../tester/create2'; +import '../tester/swap'; +import '../tester/register-factory'; +import '../tester/token-listing'; + +use(chaiAsPromised); + +const TX_AMOUNT = utils.parseEther('10.0'); +// should be enough for ~200 test transactions (excluding fees), increase if needed +const DEPOSIT_AMOUNT = TX_AMOUNT.mul(200); + +// wBTC is chosen because it has decimals different from ETH (8 instead of 18). +// Using this token will help us to detect decimals-related errors. +const defaultERC20 = 'wBTC'; + +describe(`Extended tests`, () => { + const transport = 'HTTP'; + const providerType = 'REST'; + const token = 'wBTC'; + const secondToken = 'ETH'; + + let tester: Tester; + let alice: Wallet; + let bob: Wallet; + let chuck: Wallet; + let david: Wallet; + let frank: Wallet; + let judy: Wallet; + let chris: Wallet; + let operatorBalance: BigNumber; + let nft: types.NFT; + + before('create tester and test wallets', async () => { + tester = await Tester.init('localhost', transport, providerType); + alice = await tester.fundedWallet('5.0'); + bob = await tester.emptyWallet(); + chuck = await tester.emptyWallet(); + david = await tester.fundedWallet('1.0'); + frank = await tester.fundedWallet('1.0'); + judy = await tester.emptyWallet(); + chris = await tester.emptyWallet(); + operatorBalance = await tester.operatorBalance(token); + }); + + after('disconnect tester', async () => { + await tester.disconnect(); + }); + + step('should execute a manually approved deposit', async () => { + // Start by setting approved amount to 0. + const resetApproveERC20 = await tester.syncWallet.approveERC20TokenDeposits(token, BigNumber.from(0)); + await resetApproveERC20.wait(); + + // Now token must not be approved. + expect(await tester.syncWallet.isERC20DepositsApproved(token), 'Token should not be approved').to.be.false; + const approveERC20 = await tester.syncWallet.approveERC20TokenDeposits(token, DEPOSIT_AMOUNT); + await approveERC20.wait(); + expect(await tester.syncWallet.isERC20DepositsApproved(token, DEPOSIT_AMOUNT), 'Token should be approved').to.be + .true; + await tester.testDeposit(alice, token, DEPOSIT_AMOUNT); + // It should not be approved because we have approved only DEPOSIT_AMOUNT, not the maximum possible amount of deposit + expect(await tester.syncWallet.isERC20DepositsApproved(token, DEPOSIT_AMOUNT), 'Token should not be approved') + .to.be.false; + const approveERC20Next = await tester.syncWallet.approveERC20TokenDeposits(token); + await approveERC20Next.wait(); + expect(await tester.syncWallet.isERC20DepositsApproved(token), 'The second deposit should be approved').to.be + .true; + }); + + step('should change pubkey onchain', async () => { + await tester.testChangePubKey(alice, token, true); + }); + + step('should execute a mintNFT', async () => { + // Recipient account must exist, so create it by performing a transfer. + await tester.testTransfer(alice, chuck, token, TX_AMOUNT); + + nft = await tester.testMintNFT(alice, chuck, token); + }); + + step('should execute a getNFT', async () => { + await tester.testGetNFT(alice, token); + }); + + step('should execute a transfer to existing account', async () => { + await tester.testTransfer(alice, chuck, token, TX_AMOUNT); + }); + + it('should execute a transfer to self', async () => { + await tester.testTransfer(alice, alice, token, TX_AMOUNT); + }); + + step('should test batch-builder', async () => { + // We will pay with different token. + const feeToken = secondToken; + // Add these accounts to the network. + await tester.testTransfer(alice, david, token, TX_AMOUNT.mul(10)); + await tester.testTransfer(alice, judy, token, TX_AMOUNT.mul(10)); + await tester.testTransfer(alice, frank, token, TX_AMOUNT.mul(10)); + await tester.testTransfer(alice, chris, token, TX_AMOUNT.mul(10)); + + // Also deposit another token to pay with. + await tester.testDeposit(frank, feeToken, DEPOSIT_AMOUNT, true); + + await tester.testBatchBuilderInvalidUsage(david, alice, token); + await tester.testBatchBuilderChangePubKey(david, token, TX_AMOUNT, true); + await tester.testBatchBuilderSignedChangePubKey(chris, token, TX_AMOUNT); + await tester.testBatchBuilderChangePubKey(frank, token, TX_AMOUNT, false); + await tester.testBatchBuilderTransfers(david, frank, token, TX_AMOUNT); + await tester.testBatchBuilderPayInDifferentToken(frank, david, token, feeToken, TX_AMOUNT); + await tester.testBatchBuilderNFT(frank, david, token); + // Finally, transfer, withdraw and forced exit in a single batch. + await tester.testBatchBuilderGenericUsage(david, frank, judy, token, TX_AMOUNT); + }); + + step('should test swaps and limit orders', async () => { + await tester.testSwap(alice, frank, token, secondToken, TX_AMOUNT); + await tester.testSwapBatch(alice, frank, david, token, secondToken, TX_AMOUNT); + await tester.testSwapMissingSignatures(alice, frank, token, secondToken, TX_AMOUNT); + }); + + step('should swap NFT for fungible tokens', async () => { + await tester.testChangePubKey(chuck, token, false); + + await tester.testSwapNFT(alice, chuck, token, nft.id, TX_AMOUNT); + }); + + step('should test backwards compatibility', async () => { + await tester.testBackwardCompatibleEthMessages(alice, david, token, TX_AMOUNT); + }); + + step('should execute NFT transfer', async () => { + await tester.testTransferNFT(alice, chuck, token); + }); + + step('should execute NFT withdraw', async () => { + await tester.testWithdrawNFT(chuck, token); + }); + + step('should register factory and withdraw nft', async () => { + await tester.testRegisterFactory(alice, token); + }); + + it('should check collected fees', async () => { + const collectedFee = (await tester.operatorBalance(token)).sub(operatorBalance); + expect( + collectedFee.eq(tester.runningFee), + `Fee collection failed, expected: ${tester.runningFee.toString()}, got: ${collectedFee.toString()}` + ).to.be.true; + }); + + it('should fail trying to send tx with wrong signature', async () => { + await tester.testWrongSignature(alice, bob, token, TX_AMOUNT, providerType); + }); +}); diff --git a/core/tests/ts-tests/tests/suits/full-exit.ts b/core/tests/ts-tests/tests/suits/full-exit.ts new file mode 100644 index 0000000000..0552ec2fe3 --- /dev/null +++ b/core/tests/ts-tests/tests/suits/full-exit.ts @@ -0,0 +1,87 @@ +import { expect, use } from 'chai'; +import { BigNumber, utils } from 'ethers'; +import { Wallet, types, crypto, Signer, No2FAWalletSigner } from 'zksync'; +import chaiAsPromised from 'chai-as-promised'; +import { Tester } from '../tester/tester'; +import '../tester/priority-ops'; +import '../tester/change-pub-key'; +import '../tester/transfer'; +import '../tester/withdraw'; +import '../tester/mint-nft'; +import '../tester/forced-exit'; +import '../tester/misc'; +import '../tester/batch-builder'; +import '../tester/create2'; +import '../tester/swap'; +import '../tester/register-factory'; +import '../tester/token-listing'; + +use(chaiAsPromised); + +const TX_AMOUNT = utils.parseEther('10.0'); +// should be enough for ~200 test transactions (excluding fees), increase if needed +const DEPOSIT_AMOUNT = TX_AMOUNT.mul(200); + +// wBTC is chosen because it has decimals different from ETH (8 instead of 18). +// Using this token will help us to detect decimals-related errors. +const defaultERC20 = 'wBTC'; + +const FullExitTestSuite = (token: types.TokenSymbol) => + describe(`Full Exit tests`, () => { + const transport = 'HTTP'; + const providerType = 'REST'; + + let tester: Tester; + let alice: Wallet; + let carl: Wallet; + + before('create tester and test wallets', async () => { + tester = await Tester.init('localhost', transport, providerType); + alice = await tester.fundedWallet('5.0'); + carl = await tester.fundedWallet('5.0'); + }); + + after('disconnect tester', async () => { + await tester.disconnect(); + }); + + step('should execute full-exit on random wallet', async () => { + await tester.testFullExit(carl, token, 145); + }); + + step('should fail full-exit with wrong eth-signer', async () => { + // make a deposit so that wallet is assigned an accountId + await tester.testDeposit(carl, token, DEPOSIT_AMOUNT, true); + + const oldSigner = carl.ethSigner; + carl.ethSigner = tester.ethWallet; + const [before, after] = await tester.testFullExit(carl, token); + expect(before.eq(0), 'Balance before Full Exit must be non-zero').to.be.false; + expect(before.eq(after), 'Balance after incorrect Full Exit should not change').to.be.true; + carl.ethSigner = oldSigner; + }); + + step('should execute NFT full-exit', async () => { + await tester.testDeposit(alice, token, DEPOSIT_AMOUNT, true); + await tester.testChangePubKey(alice, token, false); + + await tester.testMintNFT(alice, carl, token, true); + await tester.testFullExitNFT(carl); + }); + + step('should execute a normal full-exit', async () => { + const [before, after] = await tester.testFullExit(carl, token); + expect(before.eq(0), 'Balance before Full Exit must be non-zero').to.be.false; + expect(after.eq(0), 'Balance after Full Exit must be zero').to.be.true; + }); + + step('should execute full-exit on an empty wallet', async () => { + const [before, after] = await tester.testFullExit(carl, token); + expect(before.eq(0), "Balance before Full Exit must be zero (we've already withdrawn all the funds)").to.be + .true; + expect(after.eq(0), 'Balance after Full Exit must be zero').to.be.true; + }); + }); + +FullExitTestSuite(defaultERC20); +FullExitTestSuite('ETH'); diff --git a/core/tests/ts-tests/tests/suits/no2fa.ts b/core/tests/ts-tests/tests/suits/no2fa.ts new file mode 100644 index 0000000000..1492aa0d84 --- /dev/null +++ b/core/tests/ts-tests/tests/suits/no2fa.ts @@ -0,0 +1,132 @@ +import { expect, use } from 'chai'; +import { BigNumber, utils } from 'ethers'; +import { Wallet, types, crypto, Signer, No2FAWalletSigner } from 'zksync'; +import chaiAsPromised from 'chai-as-promised'; +import { Tester } from '../tester/tester'; +import '../tester/priority-ops'; +import '../tester/change-pub-key'; +import '../tester/transfer'; +import '../tester/withdraw'; +import '../tester/mint-nft'; +import '../tester/forced-exit'; +import '../tester/misc'; +import '../tester/batch-builder'; +import '../tester/create2'; +import '../tester/swap'; +import '../tester/register-factory'; +import '../tester/token-listing'; + +use(chaiAsPromised); + +const TX_AMOUNT = utils.parseEther('10.0'); +// should be enough for ~200 test transactions (excluding fees), increase if needed +const DEPOSIT_AMOUNT = TX_AMOUNT.mul(200); + +// wBTC is chosen because it has decimals different from ETH (8 instead of 18). +// Using this token will help us to detect decimals-related errors. +const defaultERC20 = 'wBTC'; + +describe(`No2FA tests`, () => { + const transport = 'HTTP'; + const providerType = 'REST'; + const token = 'wBTC'; + const secondToken = 'ETH'; + + let tester: Tester; + let hilda: Wallet; + let hildaWithEthSigner: Wallet; + let frida: Wallet; + + // The private key which won't require 2FA + let zkPrivateKey: Uint8Array; + // The private key whcih will require 2FA + let zkPrivateKeyWith2FA: Uint8Array; + + before('create tester and test wallets', async () => { + tester = await Tester.init('localhost', transport, providerType); + }); + + after('disconnect tester', async () => { + await tester.disconnect(); + }); + + step('should setup an account without 2fa', async () => { + // The 2FA will be off only for this L2 private key + zkPrivateKey = await crypto.privateKeyFromSeed(utils.randomBytes(32)); + zkPrivateKeyWith2FA = await crypto.privateKeyFromSeed(utils.randomBytes(32)); + + // Even the wallets with no 2fa should sign message for CPK with their private key + hilda = await tester.fundedWallet('1.0'); + frida = await tester.fundedWallet('1.0'); + hilda.signer = Signer.fromPrivateKey(zkPrivateKeyWith2FA); + await tester.testDeposit(hilda, token, DEPOSIT_AMOUNT, true); + await tester.testDeposit(frida, token, DEPOSIT_AMOUNT, true); + await ( + await hilda.setSigningKey({ + feeToken: token, + ethAuthType: 'ECDSA' + }) + ).awaitReceipt(); + await ( + await frida.setSigningKey({ + feeToken: token, + ethAuthType: 'ECDSA' + }) + ).awaitReceipt(); + const pubKeyHash = await crypto.privateKeyToPubKeyHash(zkPrivateKey); + await hilda.toggle2FA(false, pubKeyHash); + + const accountState = await hilda.getAccountState(); + expect(accountState.accountType, 'Incorrect account type').to.be.eql({ + No2FA: pubKeyHash + }); + }); + + step('Test No2FA with wrong PubKeyHash', async () => { + hildaWithEthSigner = hilda; + + // Making sure that the wallet has no Ethereum private key + // but has wrong l2 private key + hilda = await Wallet.fromSyncSigner( + new No2FAWalletSigner(hilda.address(), hilda.ethSigner.provider), + Signer.fromPrivateKey(zkPrivateKeyWith2FA), + hilda.provider + ); + + // Here the transfer without Ethereum signature, but with wrong l2 private key + await expect(tester.testTransfer(hilda, frida, token, TX_AMOUNT)).to.be.rejected; + + // No let's go back to the correct l2 private key + hildaWithEthSigner.signer = Signer.fromPrivateKey(zkPrivateKey); + await ( + await hildaWithEthSigner.setSigningKey({ + feeToken: token, + ethAuthType: 'ECDSA' + }) + ).awaitReceipt(); + + // Making sure that hilda has correct signer + hilda.signer = Signer.fromPrivateKey(zkPrivateKey); + }); + + step('Test No2FA transfers', async () => { + await tester.testTransfer(hilda, frida, token, TX_AMOUNT); + await tester.testBatch(hilda, frida, token, TX_AMOUNT); + await tester.testBatchBuilderTransfersWithoutSignatures(hilda, frida, token, TX_AMOUNT); + }); + + step('Test No2FA Swaps', async () => { + await tester.testDeposit(frida, secondToken, DEPOSIT_AMOUNT, true); + await tester.testSwap(hilda, frida, token, secondToken, TX_AMOUNT); + }); + + step('Test No2FA Withdrawals', async () => { + await tester.testWithdraw(hilda, token, TX_AMOUNT); + }); + + step('Test switching 2FA on', async () => { + await hildaWithEthSigner.toggle2FA(true); + const accountState = await hilda.getAccountState(); + expect(accountState.accountType, 'Incorrect account type').to.be.eql('Owned'); + }); +}); diff --git a/core/tests/ts-tests/tests/suits/token-listing.ts b/core/tests/ts-tests/tests/suits/token-listing.ts new file mode 100644 index 0000000000..abec958600 --- /dev/null +++ b/core/tests/ts-tests/tests/suits/token-listing.ts @@ -0,0 +1,50 @@ +import { expect, use } from 'chai'; +import { BigNumber, utils } from 'ethers'; +import { Wallet, types, crypto, Signer, No2FAWalletSigner } from 'zksync'; +import chaiAsPromised from 'chai-as-promised'; +import { Tester } from '../tester/tester'; +import '../tester/priority-ops'; +import '../tester/change-pub-key'; +import '../tester/transfer'; +import '../tester/withdraw'; +import '../tester/mint-nft'; +import '../tester/forced-exit'; +import '../tester/misc'; +import '../tester/batch-builder'; +import '../tester/create2'; +import '../tester/swap'; +import '../tester/register-factory'; +import '../tester/token-listing'; + +use(chaiAsPromised); + +const TX_AMOUNT = utils.parseEther('10.0'); +// should be enough for ~200 test transactions (excluding fees), increase if needed +const DEPOSIT_AMOUNT = TX_AMOUNT.mul(200); + +// wBTC is chosen because it has decimals different from ETH (8 instead of 18). +// Using this token will help us to detect decimals-related errors. +const defaultERC20 = 'wBTC'; + +describe(`Permissionless token listing tests`, () => { + const transport = 'HTTP'; + const providerType = 'REST'; + + let tester: Tester; + + before('create tester and test wallets', async () => { + tester = await Tester.init('localhost', transport, providerType); + }); + + after('disconnect tester', async () => { + await tester.disconnect(); + }); + + step('Test ERC20 token listing', async () => { + await tester.testERC20Listing(); + }); + + step('Test non-ERC20 token listing', async () => { + await tester.testNonERC20Listing(); + }); +}); diff --git a/core/tests/ts-tests/tests/batch-builder.ts b/core/tests/ts-tests/tests/tester/batch-builder.ts similarity index 100% rename from core/tests/ts-tests/tests/batch-builder.ts rename to core/tests/ts-tests/tests/tester/batch-builder.ts diff --git a/core/tests/ts-tests/tests/change-pub-key.ts b/core/tests/ts-tests/tests/tester/change-pub-key.ts similarity index 100% rename from core/tests/ts-tests/tests/change-pub-key.ts rename to core/tests/ts-tests/tests/tester/change-pub-key.ts diff --git a/core/tests/ts-tests/tests/create2.ts b/core/tests/ts-tests/tests/tester/create2.ts similarity index 100% rename from core/tests/ts-tests/tests/create2.ts rename to core/tests/ts-tests/tests/tester/create2.ts diff --git a/core/tests/ts-tests/tests/forced-exit-requests.ts b/core/tests/ts-tests/tests/tester/forced-exit-requests.ts similarity index 100% rename from core/tests/ts-tests/tests/forced-exit-requests.ts rename to core/tests/ts-tests/tests/tester/forced-exit-requests.ts diff --git a/core/tests/ts-tests/tests/forced-exit.ts b/core/tests/ts-tests/tests/tester/forced-exit.ts similarity index 100% rename from core/tests/ts-tests/tests/forced-exit.ts rename to core/tests/ts-tests/tests/tester/forced-exit.ts diff --git a/core/tests/ts-tests/tests/mint-nft.ts b/core/tests/ts-tests/tests/tester/mint-nft.ts similarity index 100% rename from core/tests/ts-tests/tests/mint-nft.ts rename to core/tests/ts-tests/tests/tester/mint-nft.ts diff --git a/core/tests/ts-tests/tests/misc.ts b/core/tests/ts-tests/tests/tester/misc.ts similarity index 100% rename from core/tests/ts-tests/tests/misc.ts rename to core/tests/ts-tests/tests/tester/misc.ts diff --git a/core/tests/ts-tests/tests/priority-ops.ts b/core/tests/ts-tests/tests/tester/priority-ops.ts similarity index 100% rename from core/tests/ts-tests/tests/priority-ops.ts rename to core/tests/ts-tests/tests/tester/priority-ops.ts diff --git a/core/tests/ts-tests/tests/register-factory.ts b/core/tests/ts-tests/tests/tester/register-factory.ts similarity index 89% rename from core/tests/ts-tests/tests/register-factory.ts rename to core/tests/ts-tests/tests/tester/register-factory.ts index ce0f1aba30..4148272858 100644 --- a/core/tests/ts-tests/tests/register-factory.ts +++ b/core/tests/ts-tests/tests/tester/register-factory.ts @@ -9,9 +9,12 @@ type TokenLike = types.TokenLike; function readContractCode(name: string) { const fileName = name.split('/').pop(); return JSON.parse( - fs.readFileSync(`../../../contracts/artifacts/cache/solpp-generated-contracts/${name}.sol/${fileName}.json`, { - encoding: 'utf-8' - }) + fs.readFileSync( + `${process.env['ZKSYNC_HOME']}/contracts/artifacts/cache/solpp-generated-contracts/${name}.sol/${fileName}.json`, + { + encoding: 'utf-8' + } + ) ); } function readFactoryCode() { @@ -88,6 +91,10 @@ Tester.prototype.testRegisterFactory = async function (wallet: Wallet, feeToken: this.runningFee = this.runningFee.add(withdrawFee); nftInfo = await wallet.provider.getNFT(nft.id); - expect(nftInfo.currentFactory, 'NFT info after withdrawing is wrong (current factory)').to.eql(contract.address.toLowerCase()); - expect(nftInfo.withdrawnFactory, 'NFT info after withdrawing is wrong (withdrawn factory)').to.eql(contract.address.toLowerCase()); + expect(nftInfo.currentFactory, 'NFT info after withdrawing is wrong (current factory)').to.eql( + contract.address.toLowerCase() + ); + expect(nftInfo.withdrawnFactory, 'NFT info after withdrawing is wrong (withdrawn factory)').to.eql( + contract.address.toLowerCase() + ); }; diff --git a/core/tests/ts-tests/tests/swap.ts b/core/tests/ts-tests/tests/tester/swap.ts similarity index 100% rename from core/tests/ts-tests/tests/swap.ts rename to core/tests/ts-tests/tests/tester/swap.ts diff --git a/core/tests/ts-tests/tests/tester.ts b/core/tests/ts-tests/tests/tester/tester.ts similarity index 88% rename from core/tests/ts-tests/tests/tester.ts rename to core/tests/ts-tests/tests/tester/tester.ts index c08701bfc1..25e99bada0 100644 --- a/core/tests/ts-tests/tests/tester.ts +++ b/core/tests/ts-tests/tests/tester/tester.ts @@ -5,7 +5,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as crypto from 'crypto'; -const zksyncAbi = require('../../../../contracts/artifacts/cache/solpp-generated-contracts/ZkSync.sol/ZkSync.json').abi; +const zksyncAbi = + require('../../../../../contracts/artifacts/cache/solpp-generated-contracts/ZkSync.sol/ZkSync.json').abi; type Network = 'localhost' | 'rinkeby' | 'ropsten'; const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, `etc/test_config/constant`); @@ -49,13 +50,7 @@ export class Tester { if (network == 'localhost') { ethProvider.pollingInterval = 100; } - const syncProvider = providerType === 'REST' - ? await zksync.getDefaultRestProvider(network) - : await zksync.getDefaultProvider(network, transport); - - if (network == 'localhost' && transport == 'HTTP') { - syncProvider.pollIntervalMilliSecs = 50; - } + const syncProvider = await Tester.createSyncProvider(network, transport, providerType); const ethWallet = ethers.Wallet.fromMnemonic( ethTestConfig.test_mnemonic as string, "m/44'/60'/0'/0/0" @@ -64,6 +59,18 @@ export class Tester { return new Tester(network, ethProvider, syncProvider, ethWallet, syncWallet); } + static async createSyncProvider(network: Network, transport: 'WS' | 'HTTP', providerType: 'REST' | 'RPC') { + const syncProvider = + providerType === 'REST' + ? await zksync.getDefaultRestProvider(network) + : await zksync.getDefaultProvider(network, transport); + + if (network == 'localhost' && transport == 'HTTP') { + syncProvider.pollIntervalMilliSecs = 50; + } + return syncProvider; + } + async disconnect() { await this.syncProvider.disconnect(); } diff --git a/core/tests/ts-tests/tests/token-listing.ts b/core/tests/ts-tests/tests/tester/token-listing.ts similarity index 87% rename from core/tests/ts-tests/tests/token-listing.ts rename to core/tests/ts-tests/tests/tester/token-listing.ts index 034d0bcfd7..cd680a33ae 100644 --- a/core/tests/ts-tests/tests/token-listing.ts +++ b/core/tests/ts-tests/tests/tester/token-listing.ts @@ -13,7 +13,7 @@ declare module './tester' { Tester.prototype.testERC20Listing = async function () { // Simple ERC20 contract const bytecode = - require('../../../../contracts/artifacts/cache/solpp-generated-contracts/dev-contracts/TestnetERC20Token.sol/TestnetERC20Token.json').bytecode; + require('../../../../../contracts/artifacts/cache/solpp-generated-contracts/dev-contracts/TestnetERC20Token.sol/TestnetERC20Token.json').bytecode; const tokenAbi = ['constructor(string memory name, string memory symbol, uint8 decimals)']; const factory = new ContractFactory(tokenAbi, bytecode, this.ethWallet); const tokenContract = await factory.deploy('Test Token', 'TTT', 18); @@ -37,7 +37,7 @@ Tester.prototype.testERC20Listing = async function () { Tester.prototype.testNonERC20Listing = async function () { // Non-ERC20 contract const bytecode = - require('../../../../contracts/artifacts/cache/solpp-generated-contracts/Config.sol/Config.json').bytecode; + require('../../../../../contracts/artifacts/cache/solpp-generated-contracts/Config.sol/Config.json').bytecode; const factory = new ContractFactory([], bytecode, this.ethWallet); const tokenContract = await factory.deploy(); await tokenContract.deployTransaction.wait(); diff --git a/core/tests/ts-tests/tests/transfer.ts b/core/tests/ts-tests/tests/tester/transfer.ts similarity index 99% rename from core/tests/ts-tests/tests/transfer.ts rename to core/tests/ts-tests/tests/tester/transfer.ts index 4c8b675bcf..a702da8ec1 100644 --- a/core/tests/ts-tests/tests/transfer.ts +++ b/core/tests/ts-tests/tests/tester/transfer.ts @@ -2,7 +2,7 @@ import { Tester } from './tester'; import { expect } from 'chai'; import { Wallet, types } from 'zksync'; import { BigNumber } from 'ethers'; -import { closestPackableTransactionFee } from '../../../../sdk/zksync.js'; +import { closestPackableTransactionFee } from 'zksync'; type TokenLike = types.TokenLike; diff --git a/core/tests/ts-tests/tests/withdraw.ts b/core/tests/ts-tests/tests/tester/withdraw.ts similarity index 100% rename from core/tests/ts-tests/tests/withdraw.ts rename to core/tests/ts-tests/tests/tester/withdraw.ts diff --git a/core/tests/ts-tests/tests/withdrawal-helpers.ts b/core/tests/ts-tests/tests/tester/withdrawal-helpers.ts similarity index 99% rename from core/tests/ts-tests/tests/withdrawal-helpers.ts rename to core/tests/ts-tests/tests/tester/withdrawal-helpers.ts index 2117d09455..2bca9c9ee0 100644 --- a/core/tests/ts-tests/tests/withdrawal-helpers.ts +++ b/core/tests/ts-tests/tests/tester/withdrawal-helpers.ts @@ -5,7 +5,7 @@ import { BigNumber, ethers } from 'ethers'; import { Address } from 'zksync/build/types'; import { sleep } from 'zksync/build/utils'; -import { RevertReceiveAccountFactory, RevertTransferERC20Factory } from '../../../../contracts/typechain'; +import { RevertReceiveAccountFactory, RevertTransferERC20Factory } from '../../../../../contracts/typechain'; import { loadTestConfig } from 'reading-tool'; const TEST_CONFIG = loadTestConfig(true); diff --git a/core/tests/ts-tests/tests/withdrawal-helpers.test.ts b/core/tests/ts-tests/tests/withdrawal-helpers.test.ts index 51c22dcfd4..cfd80c26f0 100644 --- a/core/tests/ts-tests/tests/withdrawal-helpers.test.ts +++ b/core/tests/ts-tests/tests/withdrawal-helpers.test.ts @@ -1,10 +1,10 @@ import { Wallet } from 'zksync'; -import { Tester } from './tester'; +import { Tester } from './tester/tester'; import { utils } from 'ethers'; -import './priority-ops'; -import './change-pub-key'; -import './withdrawal-helpers'; -import './forced-exit-requests'; +import './tester/priority-ops'; +import './tester/change-pub-key'; +import './tester/withdrawal-helpers'; +import './tester/forced-exit-requests'; import { loadTestConfig } from 'reading-tool'; @@ -18,66 +18,66 @@ const erc20Token = 'wBTC'; // prettier-ignore const TestSuite = (providerType: 'REST' | 'RPC') => -describe(`Withdrawal helpers tests (provider: ${providerType})`, () => { - let tester: Tester; - let alice: Wallet; - let bob: Wallet; - let chuck: Wallet; + describe(`Withdrawal helpers tests (provider: ${providerType})`, () => { + let tester: Tester; + let alice: Wallet; + let bob: Wallet; + let chuck: Wallet; - before('create tester and test wallets', async () => { - tester = await Tester.init('localhost', 'HTTP', providerType); - alice = await tester.fundedWallet('10.0'); - bob = await tester.fundedWallet('10.0'); - chuck = await tester.emptyWallet(); + before('create tester and test wallets', async () => { + tester = await Tester.init('localhost', 'HTTP', providerType); + alice = await tester.fundedWallet('10.0'); + bob = await tester.fundedWallet('10.0'); + chuck = await tester.emptyWallet(); - for (const token of ['ETH', erc20Token]) { - await tester.testDeposit(alice, token, DEPOSIT_AMOUNT, true); - await tester.testChangePubKey(alice, token, false); - } + for (const token of ['ETH', erc20Token]) { + await tester.testDeposit(alice, token, DEPOSIT_AMOUNT, true); + await tester.testChangePubKey(alice, token, false); + } - // This is needed to interact with blockchain - alice.ethSigner.connect(tester.ethProvider); - }); + // This is needed to interact with blockchain + alice.ethSigner.connect(tester.ethProvider); + }); - after('disconnect tester', async () => { - await tester.disconnect(); - }); + after('disconnect tester', async () => { + await tester.disconnect(); + }); - it('should recover failed ETH withdraw', async () => { - await tester.testRecoverETHWithdrawal(alice, TEST_CONFIG.withdrawalHelpers.revert_receive_address, TX_AMOUNT); - }); + it('should recover failed ETH withdraw', async () => { + await tester.testRecoverETHWithdrawal(alice, TEST_CONFIG.withdrawalHelpers.revert_receive_address, TX_AMOUNT); + }); - it('should recover failed ERC20 withdraw', async () => { - await tester.testRecoverERC20Withdrawal( - alice, - TEST_CONFIG.withdrawalHelpers.revert_receive_address, - erc20Token, - TX_AMOUNT - ); - }); - - it('should recover multiple withdrawals', async () => { - await tester.testRecoverMultipleWithdrawals( - alice, - [ + it('should recover failed ERC20 withdraw', async () => { + await tester.testRecoverERC20Withdrawal( + alice, TEST_CONFIG.withdrawalHelpers.revert_receive_address, - TEST_CONFIG.withdrawalHelpers.revert_receive_address - ], - ['ETH', erc20Token], - [TX_AMOUNT, TX_AMOUNT] - ); - }); + erc20Token, + TX_AMOUNT + ); + }); + + it('should recover multiple withdrawals', async () => { + await tester.testRecoverMultipleWithdrawals( + alice, + [ + TEST_CONFIG.withdrawalHelpers.revert_receive_address, + TEST_CONFIG.withdrawalHelpers.revert_receive_address + ], + ['ETH', erc20Token], + [TX_AMOUNT, TX_AMOUNT] + ); + }); - it('forced_exit_request should recover mutiple tokens', async () => { - await tester.testForcedExitRequestMultipleTokens( - alice, - bob.ethSigner, - chuck.address(), - ['ETH', erc20Token], - [TX_AMOUNT, TX_AMOUNT.mul(2)] - ); + it('forced_exit_request should recover mutiple tokens', async () => { + await tester.testForcedExitRequestMultipleTokens( + alice, + bob.ethSigner, + chuck.address(), + ['ETH', erc20Token], + [TX_AMOUNT, TX_AMOUNT.mul(2)] + ); + }); }); -}); for (const providerType of ['RPC', 'REST']) { // @ts-ignore diff --git a/infrastructure/zk/src/down.ts b/infrastructure/zk/src/down.ts index 48560c515c..f2e905237a 100644 --- a/infrastructure/zk/src/down.ts +++ b/infrastructure/zk/src/down.ts @@ -2,7 +2,6 @@ import { Command } from 'commander'; import * as utils from './utils'; export async function down() { - await utils.spawn('docker-compose stop tesseracts'); await utils.spawn('docker-compose stop postgres geth dev-ticker dev-liquidity-token-watcher'); }