Skip to content

Commit 3eeda26

Browse files
committed
wip, working refactor
1 parent 1cf8ada commit 3eeda26

File tree

2 files changed

+91
-99
lines changed

2 files changed

+91
-99
lines changed

core/tests/highlevel-test-tools/tests/token-balance-migration-tester.ts

Lines changed: 71 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ import { loadConfig } from 'utils/build/file-configs';
88
import { sleep } from 'zksync-ethers/build/utils';
99
import { L2_NATIVE_TOKEN_VAULT_ADDRESS } from 'utils/src/constants';
1010
import * as fs from 'fs';
11-
import { migrateToGatewayIfNeeded, startServer } from '../src';
11+
import { executeCommand, migrateToGatewayIfNeeded, startServer } from '../src';
1212
import { initTestWallet } from '../src/run-integration-tests';
1313

1414
const RICH_WALLET_L1_BALANCE = ethers.parseEther('10.0');
1515
const RICH_WALLET_L2_BALANCE = RICH_WALLET_L1_BALANCE;
1616
const TEST_SUITE_NAME = 'Token Balance Migration Test';
1717
const pathToHome = path.join(__dirname, '../../../..');
1818

19-
function readArtifact(contractName: string, outFolder: string = 'out') {
19+
function readArtifact(contractName: string, outFolder: string = 'out', fileName: string = contractName) {
2020
return JSON.parse(
2121
fs
2222
.readFileSync(
23-
path.join(pathToHome, `./contracts/l1-contracts/${outFolder}/${contractName}.sol/${contractName}.json`)
23+
path.join(pathToHome, `./contracts/l1-contracts/${outFolder}/${contractName}.sol/${fileName}.json`)
2424
)
2525
.toString()
2626
);
@@ -50,7 +50,7 @@ export async function generateChainRichWallet(chainName: string): Promise<zksync
5050

5151
if (contractsConfig.l1.base_token_addr !== zksync.utils.ETH_ADDRESS_IN_CONTRACTS) {
5252
const l1Token = new ethers.Contract(contractsConfig.l1.base_token_addr, ERC20_ABI, richWallet.ethWallet());
53-
const mintTx = await l1Token.mint(richWallet.address, RICH_WALLET_L2_BALANCE);
53+
const mintTx = await l1Token.mint(richWallet.address, 2n * RICH_WALLET_L2_BALANCE);
5454
await mintTx.wait();
5555
}
5656

@@ -76,6 +76,8 @@ export class ChainHandler {
7676
public inner: TestChain;
7777
public l2RichWallet: zksync.Wallet;
7878
public l1Ntv: ethers.Contract;
79+
public l1GettersContract: ethers.Contract;
80+
public gwGettersContract: zksync.Contract;
7981

8082
constructor(inner: TestChain, l2RichWallet: zksync.Wallet) {
8183
this.inner = inner;
@@ -87,44 +89,68 @@ export class ChainHandler {
8789
readArtifact('L1NativeTokenVault').abi,
8890
l2RichWallet.ethWallet()
8991
);
92+
93+
this.l1GettersContract = new ethers.Contract(
94+
contractsConfig.l1.diamond_proxy_addr,
95+
readArtifact('Getters', 'out', 'GettersFacet').abi,
96+
l2RichWallet.ethWallet()
97+
);
9098
}
9199

92100
async stopServer() {
93101
await this.inner.mainNode.kill();
94102
}
95103

96104
async migrateToGateway() {
97-
const pauseDepositsCmd = `zkstack chain pause-deposits --chain ${this.inner.chainName}`;
98-
await utils.spawn(pauseDepositsCmd);
105+
// Pause deposits before initiating migration
106+
await utils.spawn(`zkstack chain pause-deposits --chain ${this.inner.chainName}`);
107+
//await utils.spawn(`zkstack chain gateway notify-about-to-gateway-update --chain ${this.inner.chainName}`);
108+
// Wait for all batches to be executed and stop the server
109+
// Priority queue should be empty as all deposits have already been processed
110+
await this.inner.waitForAllBatchesToBeExecuted();
99111
await this.stopServer();
100-
// By now, the priority queue should be empty, so we can migrate straight away
112+
// We can now reliably migrate to gateway
101113
await migrateToGatewayIfNeeded(this.inner.chainName);
102-
// Restart the server
114+
103115
await startServer(this.inner.chainName);
116+
// We can now define the gateway getters contract
117+
const gatewayConfig = loadConfig({ pathToHome, chain: this.inner.chainName, config: 'gateway_chain.yaml' });
118+
this.gwGettersContract = new zksync.Contract(
119+
gatewayConfig.diamond_proxy_addr,
120+
readArtifact('Getters', 'out', 'GettersFacet').abi,
121+
this.l2RichWallet
122+
);
104123
}
105124

106125
async migrateFromGateway() {
107-
const pauseDepositsCmd = `zkstack chain pause-deposits --chain ${this.inner.chainName}`;
108-
await utils.spawn(pauseDepositsCmd);
126+
// Pause deposits before initiating migration
127+
await utils.spawn(`zkstack chain pause-deposits --chain ${this.inner.chainName}`);
128+
// Notify server
129+
// Wait for all batches to be executed
130+
await this.inner.waitForAllBatchesToBeExecuted();
109131
// Migrate from gateway
110132
await utils.spawn(
111133
`zkstack chain gateway migrate-from-gateway --gateway-chain-name gateway --chain ${this.inner.chainName}`
112134
);
113135
}
114136

115137
async migrateTokenBalancesToGateway() {
116-
const migrationCmd = `zkstack chain gateway migrate-token-balances --to-gateway true --gateway-chain-name gateway --chain ${this.inner.chainName}`;
117-
118-
// Migration might sometimes fail, so we retry a few times.
119-
for (let attempt = 1; attempt <= 3; attempt++) {
120-
try {
121-
await utils.spawn(migrationCmd);
122-
break;
123-
} catch (e) {
124-
if (attempt === 3) throw e;
125-
await utils.sleep(2 * attempt);
126-
}
127-
}
138+
await executeCommand(
139+
'zkstack',
140+
[
141+
'chain',
142+
'gateway',
143+
'migrate-token-balances',
144+
'--to-gateway',
145+
'true',
146+
'--gateway-chain-name',
147+
'gateway',
148+
'--chain',
149+
this.inner.chainName
150+
],
151+
this.inner.chainName,
152+
'token_balance_migration_to_gateway'
153+
);
128154
}
129155

130156
async migrateTokenBalancesToL1() {
@@ -145,10 +171,10 @@ export class ChainHandler {
145171

146172
static async createNewChain(chainType: ChainType): Promise<ChainHandler> {
147173
const testChain = await createChainAndStartServer(chainType, TEST_SUITE_NAME, false);
148-
await initTestWallet(testChain.chainName);
149174

150175
// Need to wait for a bit before the server works fully
151176
await sleep(2000);
177+
await initTestWallet(testChain.chainName);
152178

153179
return new ChainHandler(testChain, await generateChainRichWallet(testChain.chainName));
154180
}
@@ -189,7 +215,11 @@ export class ERC20Handler {
189215
return assetId;
190216
}
191217

192-
async deposit(chainHandler: ChainHandler, amount: bigint = DEFAULT_SMALL_AMOUNT): Promise<bigint> {
218+
async deposit(
219+
chainHandler: ChainHandler,
220+
awaitDeposit = false,
221+
amount: bigint = DEFAULT_SMALL_AMOUNT
222+
): Promise<bigint> {
193223
const depositAmount = amount ?? ethers.parseUnits((Math.floor(Math.random() * 900) + 100).toString(), 'gwei');
194224
const depositTx = await this.wallet.deposit({
195225
token: await this.l1Contract!.getAddress(),
@@ -200,6 +230,7 @@ export class ERC20Handler {
200230
await depositTx.wait();
201231

202232
await this.setL2Contract(chainHandler);
233+
if (awaitDeposit) await waitForBalanceNonZero(this.l2Contract!, this.wallet);
203234

204235
return depositAmount;
205236
}
@@ -307,7 +338,7 @@ export class WithdrawalHandler {
307338
throw new Error('Receipt');
308339
}
309340

310-
await waitForL2ToL1LogProof(l2Wallet.provider, receipt.blockNumber, this.txHash);
341+
await waitForL2ToL1LogProof(l2Wallet, receipt.blockNumber, this.txHash);
311342

312343
await (await l2Wallet.finalizeWithdrawal(this.txHash)).wait();
313344
}
@@ -323,87 +354,43 @@ export class MigrationHandler {
323354
async finalizeMigration(l1RichWallet: ethers.Wallet) {}
324355
}
325356

326-
export class Tester {
327-
public runningFee: Map<zksync.types.Address, bigint>;
328-
constructor(
329-
public ethProvider: ethers.Provider,
330-
public ethWallet: ethers.Wallet,
331-
public syncWallet: zksync.Wallet,
332-
public web3Provider: zksync.Provider
333-
) {
334-
this.runningFee = new Map();
335-
}
336-
337-
// prettier-ignore
338-
static async init(ethProviderAddress: string, web3JsonRpc: string) {
339-
const ethProvider = new ethers.JsonRpcProvider(ethProviderAddress);
340-
341-
const chainName = process.env.CHAIN_NAME!!;
342-
let ethWallet = new ethers.Wallet(getMainWalletPk(chainName));
343-
344-
ethWallet = ethWallet.connect(ethProvider);
345-
const web3Provider = new zksync.Provider(web3JsonRpc);
346-
web3Provider.pollingInterval = 100; // It's OK to keep it low even on stage.
347-
const syncWallet = new zksync.Wallet(ethWallet.privateKey, web3Provider, ethProvider);
348-
349-
350-
// Since some tx may be pending on stage, we don't want to get stuck because of it.
351-
// In order to not get stuck transactions, we manually cancel all the pending txs.
352-
const latestNonce = await ethWallet.getNonce('latest');
353-
const pendingNonce = await ethWallet.getNonce('pending');
354-
const cancellationTxs = [];
355-
for (let nonce = latestNonce; nonce != pendingNonce; nonce++) {
356-
// For each transaction to override it, we need to provide greater fee.
357-
// We would manually provide a value high enough (for a testnet) to be both valid
358-
// and higher than the previous one. It's OK as we'll only be charged for the bass fee
359-
// anyways. We will also set the miner's tip to 5 gwei, which is also much higher than the normal one.
360-
const maxFeePerGas = ethers.parseEther("0.00000025"); // 250 gwei
361-
const maxPriorityFeePerGas = ethers.parseEther("0.000000005"); // 5 gwei
362-
cancellationTxs.push(ethWallet.sendTransaction({ to: ethWallet.address, nonce, maxFeePerGas, maxPriorityFeePerGas }).then((tx) => tx.wait()));
363-
}
364-
if (cancellationTxs.length > 0) {
365-
await Promise.all(cancellationTxs);
366-
console.log(`Canceled ${cancellationTxs.length} pending transactions`);
367-
}
368-
369-
return new Tester(ethProvider, ethWallet, syncWallet, web3Provider);
370-
}
371-
372-
emptyWallet() {
373-
const walletHD = zksync.Wallet.createRandom();
374-
return new zksync.Wallet(walletHD.privateKey, this.web3Provider, this.ethProvider);
357+
async function waitForBalanceNonZero(contract: ethers.Contract | zksync.Contract, wallet: zksync.Wallet) {
358+
let balance;
359+
while (true) {
360+
balance = await contract.balanceOf(wallet.address);
361+
console.log('Waiting for balance to be non-zero', balance);
362+
if (balance !== 0n) break;
363+
await zksync.utils.sleep(wallet.provider.pollingInterval);
375364
}
376365
}
377366

378-
export async function waitUntilBlockFinalized(provider: zksync.Provider, blockNumber: number) {
379-
console.log('Waiting for block to be finalized...', blockNumber);
367+
async function waitUntilBlockFinalized(wallet: zksync.Wallet, blockNumber: number) {
380368
let printedBlockNumber = 0;
381369
while (true) {
382-
const block = await provider.getBlock('finalized');
370+
const block = await wallet.provider.getBlock('finalized');
371+
console.log('block number', block.number, blockNumber);
383372
if (blockNumber <= block.number) {
384373
break;
385374
} else {
386375
if (printedBlockNumber < block.number) {
387-
console.log('Waiting for block to be finalized...', blockNumber, block.number);
388-
console.log('time', new Date().toISOString());
389376
printedBlockNumber = block.number;
390377
}
391-
await zksync.utils.sleep(provider.pollingInterval);
378+
await zksync.utils.sleep(wallet.provider.pollingInterval);
392379
}
393380
}
394381
}
395382

396-
export async function waitForL2ToL1LogProof(provider: zksync.Provider, blockNumber: number, txHash: string) {
383+
async function waitForL2ToL1LogProof(wallet: zksync.Wallet, blockNumber: number, txHash: string) {
397384
console.log('waiting for block to be finalized');
398385
// First, we wait for block to be finalized.
399-
await waitUntilBlockFinalized(provider, blockNumber);
386+
await waitUntilBlockFinalized(wallet, blockNumber);
400387

401388
console.log('waiting for log proof');
402389
// Second, we wait for the log proof.
403390
let i = 0;
404-
while ((await provider.getLogProof(txHash)) == null) {
391+
while ((await wallet.provider.getLogProof(txHash)) == null) {
405392
console.log(`Waiting for log proof... ${i}`);
406-
await zksync.utils.sleep(provider.pollingInterval);
393+
await zksync.utils.sleep(wallet.provider.pollingInterval);
407394
i++;
408395
}
409396
}

core/tests/highlevel-test-tools/tests/token-balance-migration.test.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,22 @@ if (shouldSkip) {
109109
console.log('Creating a new chain with a custom token as the base token...');
110110
customTokenChainHandler = await ChainHandler.createNewChain('custom_token');
111111

112+
const withdrawalsToBeFinalized: WithdrawalHandler[] = [];
112113
// DEPLOY TOKENS THAT WILL BE TESTED
113-
const withdrawalHandlers: WithdrawalHandler[] = [];
114+
// We first deploy all tokens that will need to be withdrawn from L2 to make testing faster
115+
// Token B: Native to L1, deposited to L2, fully withdrawn from L2
116+
tokens.L1NativeWithdrawnFromL2 = { handler: await ERC20Handler.deployTokenOnL1(chainRichWallet), balance: 0n };
117+
const L1NativeWithdrawnFromL2Amount = await tokens.L1NativeWithdrawnFromL2.handler.deposit(chainHandler, true);
118+
withdrawalsToBeFinalized.push(
119+
await tokens.L1NativeWithdrawnFromL2.handler.withdraw(L1NativeWithdrawnFromL2Amount)
120+
);
121+
// Token G: Native to L2-A, fully withdrawn to L1
122+
tokens.L2NativeWithdrawnToL1 = { handler: await ERC20Handler.deployTokenOnL2(chainRichWallet), balance: 0n };
123+
const L2NativeWithdrawnToL1Amount = await tokens.L2NativeWithdrawnToL1.handler.getL2Balance();
124+
withdrawalsToBeFinalized.push(await tokens.L2NativeWithdrawnToL1.handler.withdraw(L2NativeWithdrawnToL1Amount));
114125
// Token A: Native to L1, deposited to L2
115126
tokens.L1NativeDepositedToL2 = { handler: await ERC20Handler.deployTokenOnL1(chainRichWallet), balance: 0n };
116127
tokens.L1NativeDepositedToL2.balance = await tokens.L1NativeDepositedToL2.handler.deposit(chainHandler);
117-
// Token B: Native to L1, deposited to L2, fully withdrawn from L2
118-
tokens.L1NativeWithdrawnFromL2 = { handler: await ERC20Handler.deployTokenOnL1(chainRichWallet), balance: 0n };
119-
const L1NativeWithdrawnFromL2Amount = await tokens.L1NativeWithdrawnFromL2.handler.deposit(chainHandler);
120-
const withdrawalHandler = await tokens.L1NativeWithdrawnFromL2.handler.withdraw(L1NativeWithdrawnFromL2Amount);
121-
await withdrawalHandler.finalizeWithdrawal(l1RichWallet);
122128
// Token C: Native to L1, deposited to L2, fully withdrawn from L2 but not finalized yet
123129

124130
// Token C: Native to L1, not deposited to L2 yet
@@ -129,23 +135,22 @@ if (shouldSkip) {
129135

130136
// Token F: Native to L2-A, partially withdrawn to L1
131137

132-
// Token G: Native to L2-A, fully withdrawn to L1
133-
tokens.L2NativeWithdrawnToL1 = { handler: await ERC20Handler.deployTokenOnL2(chainRichWallet), balance: 0n };
134-
const L2NativeWithdrawnToL1Amount = await tokens.L2NativeWithdrawnToL1.handler.getL2Balance();
135-
const L2NativeWithdrawnToL1Handler =
136-
await tokens.L2NativeWithdrawnToL1.handler.withdraw(L2NativeWithdrawnToL1Amount);
137-
await L2NativeWithdrawnToL1Handler.finalizeWithdrawal(l1RichWallet);
138-
await tokens.L2NativeWithdrawnToL1.handler.setL1Contract(chainHandler);
139138
// Token H: Native to L2-A, not withdrawn to L1
139+
140+
// Finalize all needed withdrawals
141+
for (const withdrawal of withdrawalsToBeFinalized) {
142+
await withdrawal.finalizeWithdrawal(chainRichWallet);
143+
}
144+
await tokens.L2NativeWithdrawnToL1.handler.setL1Contract(chainHandler);
140145
});
141146

142147
it('Correctly assigns chain token balances', async () => {
143148
// TODO
144149
});
145150

146151
it('Can migrate token balances to GW', async () => {
147-
// await chainHandler.migrateToGateway();
148-
// await chainHandler.migrateTokenBalancesToGateway();
152+
await chainHandler.migrateToGateway();
153+
await chainHandler.migrateTokenBalancesToGateway();
149154
});
150155

151156
it('Can do repeated migrations', async () => {

0 commit comments

Comments
 (0)