diff --git a/core/tests/ts-integration/src/context-owner.ts b/core/tests/ts-integration/src/context-owner.ts index 9e854417fbf..6ea3be7e8e0 100644 --- a/core/tests/ts-integration/src/context-owner.ts +++ b/core/tests/ts-integration/src/context-owner.ts @@ -197,7 +197,7 @@ export class TestContextOwner { const l2ETHAmountToDeposit = await this.ensureBalances(accountsAmount); const l2ERC20AmountToDeposit = ERC20_PER_ACCOUNT.mul(accountsAmount); const wallets = this.createTestWallets(suites); - await this.distributeL1BaseToken(l2ERC20AmountToDeposit); + await this.distributeL1BaseToken(wallets, l2ERC20AmountToDeposit); await this.cancelAllowances(); await this.distributeL1Tokens(wallets, l2ETHAmountToDeposit, l2ERC20AmountToDeposit); await this.distributeL2Tokens(wallets); @@ -257,7 +257,7 @@ export class TestContextOwner { * Sends L1 tokens to the test wallet accounts. * Additionally, deposits L1 tokens to the main account for further distribution on L2 (if required). */ - private async distributeL1BaseToken(l2erc20DepositAmount: ethers.BigNumber) { + private async distributeL1BaseToken(wallets: TestWallets, l2erc20DepositAmount: ethers.BigNumber) { this.reporter.startAction(`Distributing base tokens on L1`); const baseTokenAddress = process.env.CONTRACTS_BASE_TOKEN_ADDR!; if (baseTokenAddress != zksync.utils.ETH_ADDRESS_IN_CONTRACTS) { @@ -321,6 +321,20 @@ export class TestContextOwner { l1TxPromises.push(baseDepositPromise); this.reporter.debug(`Sent ${l1TxPromises.length} base token initial transactions on L1`); + + // Transfer base token to wallets + const baseTransfers = await sendTransfers( + baseTokenAddress, + this.mainEthersWallet, + wallets, + ERC20_PER_ACCOUNT, + nonce, + gasPrice, + this.reporter + ); + nonce += baseTransfers.length; + l1TxPromises.push(...baseTransfers); + await Promise.all(l1TxPromises); } this.reporter.finishAction(); diff --git a/core/tests/ts-integration/src/env.ts b/core/tests/ts-integration/src/env.ts index d74e378a464..458a0a6521e 100644 --- a/core/tests/ts-integration/src/env.ts +++ b/core/tests/ts-integration/src/env.ts @@ -4,6 +4,7 @@ import * as ethers from 'ethers'; import * as zksync from 'zksync-ethers'; import { TestEnvironment } from './types'; import { Reporter } from './reporter'; +import { L2_ETH_TOKEN_ADDRESS } from 'zksync-web3/build/src/utils'; /** * Attempts to connect to server. @@ -80,6 +81,7 @@ export async function loadTestEnvironment(): Promise { token = tokens[0]; } const weth = tokens.find((token: { symbol: string }) => token.symbol == 'WETH')!; + const baseToken = tokens.find((token: { symbol: string }) => token.symbol == 'BAT')!; // `waitForServer` is expected to be executed. Otherwise this call may throw. const l2TokenAddress = await new zksync.Wallet( @@ -114,6 +116,13 @@ export async function loadTestEnvironment(): Promise { decimals: weth.decimals, l1Address: weth.address, l2Address: l2WethAddress + }, + baseToken: { + name: baseToken.name, + symbol: baseToken.symbol, + decimals: baseToken.decimals, + l1Address: baseToken.address, + l2Address: L2_ETH_TOKEN_ADDRESS } }; } diff --git a/core/tests/ts-integration/src/test-master.ts b/core/tests/ts-integration/src/test-master.ts index 77985bd2374..ef1899a66ee 100644 --- a/core/tests/ts-integration/src/test-master.ts +++ b/core/tests/ts-integration/src/test-master.ts @@ -64,6 +64,7 @@ export class TestMaster { this.l2Provider.pollingInterval = 5000; } + // TODO: suiteWalletPK should be the same as the mainWalletPK used in context-owner.ts, otherwise, account won't have funds to operate with. this.mainWallet = new zksync.Wallet(suiteWalletPK, this.l2Provider, this.l1Provider); } diff --git a/core/tests/ts-integration/src/types.ts b/core/tests/ts-integration/src/types.ts index 20a7175cdd7..bd4300031b8 100644 --- a/core/tests/ts-integration/src/types.ts +++ b/core/tests/ts-integration/src/types.ts @@ -49,6 +49,10 @@ export interface TestEnvironment { * Description of the WETH token used in the tests. */ wethToken: Token; + /** + * Description of the "base" ERC20 token used in the tests. + */ + baseToken: Token; } /** diff --git a/core/tests/ts-integration/tests/native-erc20.test.ts b/core/tests/ts-integration/tests/native-erc20.test.ts new file mode 100644 index 00000000000..b9fdb05867e --- /dev/null +++ b/core/tests/ts-integration/tests/native-erc20.test.ts @@ -0,0 +1,97 @@ +/** + * This suite contains tests checking default ERC-20 contract behavior. + */ + +import { TestMaster } from '../src/index'; +import { Token } from '../src/types'; + +import * as zksync from 'zksync-ethers'; +import { BigNumber } from 'ethers'; +import * as ethers from 'ethers'; +import { scaledGasPrice } from '../src/helpers'; + +describe('ERC20 contract checks', () => { + let testMaster: TestMaster; + let alice: zksync.Wallet; + let baseTokenDetails: Token; + let aliceBaseErc20: ethers.Contract; + let chainId: ethers.BigNumberish; + + beforeAll(async () => { + testMaster = TestMaster.getInstance(__filename); + alice = testMaster.mainAccount(); + chainId = process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!; + + baseTokenDetails = testMaster.environment().baseToken; + aliceBaseErc20 = new ethers.Contract(baseTokenDetails.l1Address, zksync.utils.IERC20, alice._providerL1()); + }); + + test('Can perform a deposit', async () => { + const amount = 1; // 1 wei is enough. + const gasPrice = scaledGasPrice(alice); + + const initialEthBalance = await alice.getBalanceL1(); + const initialL1Balance = await alice.getBalanceL1(baseTokenDetails.l1Address); + const initialL2Balance = await alice.getBalance(); + + const depositTx = await alice.deposit({ + token: baseTokenDetails.l1Address, + amount: amount, + approveERC20: true, + approveBaseERC20: true, + approveBaseOverrides: { + gasPrice + }, + approveOverrides: { + gasPrice + }, + overrides: { + gasPrice + } + }); + const depositHash = depositTx.hash; + await depositTx.wait(); + + const receipt = await alice._providerL1().getTransactionReceipt(depositHash); + const fee = receipt.effectiveGasPrice.mul(receipt.gasUsed); + + // TODO: should all the following tests use strict equality? + + const finalEthBalance = await alice.getBalanceL1(); + expect(initialEthBalance).bnToBeGt(finalEthBalance.add(fee)); // Fee should be taken from the ETH balance on L1. + + const finalL1Balance = await alice.getBalanceL1(baseTokenDetails.l1Address); + expect(initialL1Balance).bnToBeGte(finalL1Balance.add(amount)); + + const finalL2Balance = await alice.getBalance(); + expect(initialL2Balance).bnToBeLte(finalL2Balance.add(amount)); + }); + + test('Not enough balance should revert', async () => { + const amount = BigNumber.from('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); + const gasPrice = scaledGasPrice(alice); + let errorMessage; + + await expect( + alice.deposit({ + token: baseTokenDetails.l1Address, + amount: amount, + approveERC20: true, + approveBaseERC20: true, + approveBaseOverrides: { + gasPrice + }, + approveOverrides: { + gasPrice + }, + overrides: { + gasPrice + } + }) + ).toBeRejected(errorMessage); + }); + + afterAll(async () => { + await testMaster.deinitialize(); + }); +});