Skip to content

Commit

Permalink
Merge branch 'kl-factory' into bh-executor-proof-test
Browse files Browse the repository at this point in the history
  • Loading branch information
vladbochok authored Mar 5, 2024
2 parents 1ebace5 + e5dc407 commit 0b4ecbd
Show file tree
Hide file tree
Showing 17 changed files with 443 additions and 281 deletions.
8 changes: 8 additions & 0 deletions l1-contracts/contracts/bridge/L1ERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard {
/// @dev Initializes the reentrancy guard. Expected to be used in the proxy.
function initialize() external reentrancyGuardInitializer {}

/// @dev transfer token to shared bridge as part of upgrade
function tranferTokenToSharedBridge(address _token, uint256 _amount) external {
require(msg.sender == address(sharedBridge), "Not shared bridge");

Check warning on line 64 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 64 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 64 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
uint256 amount = IERC20(_token).balanceOf(address(this));
require(amount == _amount, "Incorrect amount");

Check warning on line 66 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 66 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements

Check warning on line 66 in l1-contracts/contracts/bridge/L1ERC20Bridge.sol

View workflow job for this annotation

GitHub Actions / lint

Use Custom Errors instead of require statements
IERC20(_token).safeTransfer(address(sharedBridge), amount);
}

/*//////////////////////////////////////////////////////////////
ERA LEGACY GETTERS
//////////////////////////////////////////////////////////////*/
Expand Down
26 changes: 25 additions & 1 deletion l1-contracts/contracts/bridge/L1SharedBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,27 @@ contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Initializable, Owna
l2BridgeAddress[ERA_CHAIN_ID] = ERA_ERC20_BRIDGE_ADDRESS;
}

/// @dev tranfer tokens from legacy erc20 bridge and set chainBalance as part of migration process
function transferTokenFromERC20Bridge(address _token) external onlyOwner {
if (_token == ETH_TOKEN_ADDRESS) {
IMailbox(ERA_DIAMOND_PROXY).transferEthToSharedBridge();
} else {
uint256 balanceBefore = IERC20(_token).balanceOf(address(this));
uint256 amount = IERC20(_token).balanceOf(address(legacyBridge));
require(amount > 0, "ShB: 0 amount to transfer");
legacyBridge.tranferTokenToSharedBridge(_token, amount);
uint256 balanceAfter = IERC20(_token).balanceOf(address(this));
require(balanceAfter - balanceBefore == amount, "ShB: wrong amount transferred");
chainBalance[ERA_CHAIN_ID][_token] = chainBalance[ERA_CHAIN_ID][_token] + amount;
}
}

/// @dev only used to receive funds from Era diamond Proxy as part of migration process
function receive() external payable {
require(msg.sender == ERA_DIAMOND_PROXY, "ShB: not era diamond proxy");
chainBalance[ERA_CHAIN_ID][ETH_TOKEN_ADDRESS] = chainBalance[ERA_CHAIN_ID][ETH_TOKEN_ADDRESS] + msg.value;
}

/// @dev Initializes the l2Bridge address by governance for a specific chain.
function initializeChainGovernance(uint256 _chainId, address _l2BridgeAddress) external onlyOwner {
l2BridgeAddress[_chainId] = _l2BridgeAddress;
Expand Down Expand Up @@ -227,7 +248,10 @@ contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Initializable, Owna
/// @dev Receives and parses (name, symbol, decimals) from the token contract
function _getERC20Getters(address _token) internal view returns (bytes memory) {
if (_token == ETH_TOKEN_ADDRESS) {
return abi.encode("Ether", "ETH", uint8(18)); // when depositing eth to a non-eth based chain it is an ERC20
bytes memory name = bytes("Ether");
bytes memory symbol = bytes("ETH");
bytes memory decimals = abi.encode(uint8(18));
return abi.encode(name, symbol, decimals); // when depositing eth to a non-eth based chain it is an ERC20
}

(, bytes memory data1) = _token.staticcall(abi.encodeCall(IERC20Metadata.name, ()));
Expand Down
2 changes: 2 additions & 0 deletions l1-contracts/contracts/bridge/interfaces/IL1ERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,6 @@ interface IL1ERC20Bridge {
address _l1Token,
bytes32 _depositL2TxHash
) external returns (uint256 amount);

function tranferTokenToSharedBridge(address _token, uint256 _amount) external;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import "../../state-transition/libraries/Diamond.sol";
import "../../state-transition/chain-deps/facets/Getters.sol";

contract DiamondCutTestContract is GettersFacet {
// add this to be excluded from coverage report
function test() internal virtual {}

function diamondCut(Diamond.DiamondCutData memory _diamondCut) external {
Diamond.diamondCut(_diamondCut);
}
Expand Down
3 changes: 0 additions & 3 deletions l1-contracts/contracts/dev-contracts/test/MerkleTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ pragma solidity 0.8.20;
import "../../state-transition/libraries/Merkle.sol";

contract MerkleTest {
// add this to be excluded from coverage report
function test() internal virtual {}

function calculateRoot(
bytes32[] calldata _path,
uint256 _index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ struct ZkSyncStateTransitionStorage {
address stateTransitionManager;
/// @dev The address of the baseToken contract. Eth is address(1)
address baseToken;
/// @dev The address of the baseTokenbridge. Eth uses the weth bridge.
/// @dev The address of the baseTokenbridge. Eth also uses the shared bridge
address baseTokenBridge;
/// @notice gasPriceMultiplier for each baseToken, so that each L1->L2 transaction pays for its transaction on the destination
/// we multiply by the nominator, and divide by the denominator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ contract MailboxFacet is ZkSyncStateTransitionBase, IMailbox {
/// @inheritdoc IZkSyncStateTransitionBase
string public constant override getName = "MailboxFacet";

/// @inheritdoc IMailbox
function transferEthToSharedBridge() external onlyBaseTokenBridge {
require(s.chainId == ERA_CHAIN_ID, "transferEthToSharedBridge only available for Era on mailbox");

uint256 amount = address(this).balance;
bool callSuccess;
address sharedBridgeAddress = s.baseTokenBridge;
// Low-level assembly call, to avoid any memory copying (save gas)
assembly {
callSuccess := call(gas(), sharedBridgeAddress, amount, 0, 0, 0, 0)
}
require(callSuccess, "ShB: transferEthToSharedBridge failed");
}

/// @notice when requesting transactions through the bridgehub
function bridgehubRequestL2Transaction(
BridgehubL2TransactionRequest memory _request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ contract ZkSyncStateTransitionBase is ReentrancyGuard {
);
_;
}

modifier onlyBaseTokenBridge() {
require(msg.sender == s.baseTokenBridge, "Only shared bridge can call this function");
_;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ interface IMailbox is IZkSyncStateTransitionBase {
uint256 _l2GasPerPubdataByteLimit
) external view returns (uint256);

/// @notice tranfer Eth to shared bridge as part of migration process
function transferEthToSharedBridge() external;

/// @notice New priority request event. Emitted when a request is placed into the priority queue
/// @param txId Serial number of the priority operation
/// @param txHash keccak256 hash of encoded transaction representation
Expand Down
7 changes: 5 additions & 2 deletions l1-contracts/scripts/deploy-erc20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Wallet } from "ethers";
import { web3Provider } from "./utils";

import type { TokenDescription } from "../src.ts/deploy-token";
import { deployTokens } from "../src.ts/deploy-token";
import { deployTokens, deployContracts, mintTokens } from "../src.ts/deploy-token";

import { ethTestConfig } from "../src.ts/utils";

Expand Down Expand Up @@ -54,7 +54,10 @@ async function main() {
? new Wallet(cmd.privateKey, provider)
: Wallet.fromMnemonic(ethTestConfig.mnemonic, "m/44'/60'/0'/0/1").connect(provider);

console.log(JSON.stringify(await deployTokens(tokens, wallet, ethTestConfig.mnemonic, true, false), null, 2));
const nonce = await deployContracts(tokens, wallet);
const result = await mintTokens(tokens, wallet, nonce, ethTestConfig.mnemonic);

console.log(JSON.stringify(result, null, 2));
});

await program.parseAsync(process.argv);
Expand Down
72 changes: 67 additions & 5 deletions l1-contracts/src.ts/deploy-token.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as hardhat from "hardhat";
import "@nomiclabs/hardhat-ethers";
import { Wallet } from "ethers";
import type { Contract } from "ethers";
import { parseEther } from "ethers/lib/utils";

import * as fs from "fs";

const DEFAULT_ERC20 = "TestnetERC20Token";
Expand All @@ -15,8 +17,65 @@ export type L1Token = {

export type TokenDescription = L1Token & {
implementation?: string;
contract?: Contract;
};

export async function deployContracts(tokens: TokenDescription[], wallet: Wallet): Promise<number> {
let nonce = await wallet.getTransactionCount("pending");

for (const token of tokens) {
token.implementation = token.implementation || DEFAULT_ERC20;
const tokenFactory = await hardhat.ethers.getContractFactory(token.implementation, wallet);
const args = token.implementation !== "WETH9" ? [token.name, token.symbol, token.decimals] : [];

token.contract = await tokenFactory.deploy(...args, { gasLimit: 5000000, nonce: nonce++ });
}

await Promise.all(tokens.map(async (token) => token.contract.deployTransaction.wait()));

return nonce;
}

function getTestAddresses(mnemonic: string): string[] {
return Array.from({ length: 10 }, (_, i) => Wallet.fromMnemonic(mnemonic as string, `m/44'/60'/0'/0/${i}`).address);
}

function unwrapToken(token: TokenDescription): L1Token {
token.address = token.contract.address;

delete token.contract;
if (token.implementation) {
delete token.implementation;
}

return token;
}

export async function mintTokens(
tokens: TokenDescription[],
wallet: Wallet,
nonce: number,
mnemonic: string
): Promise<L1Token[]> {
const targetAddresses = [wallet.address, ...getTestAddresses(mnemonic)];

const results = [];
const promises = [];
for (const token of tokens) {
if (token.implementation !== "WETH9") {
for (const address of targetAddresses) {
const tx = await token.contract.mint(address, parseEther("3000000000"), { nonce: nonce++ });
promises.push(tx.wait());
}
}

results.push(unwrapToken(token));
}
await Promise.all(promises);

return results;
}

export function getTokens(): L1Token[] {
const network = process.env.CHAIN_ETH_NETWORK || "localhost";
const configPath =
Expand All @@ -31,15 +90,15 @@ export function getTokens(): L1Token[] {
}

export async function deployTokens(
tokens: L1Token[],
tokens: TokenDescription[],
wallet: Wallet,
mnemonic: string,
mintTokens: boolean = false,
verbose: boolean = false
): Promise<L1Token[]> {
const result: L1Token[] = [];
for (const token of tokens) {
const implementation = token.symbol != "WETH" ? DEFAULT_ERC20 : "WETH9";
const implementation = token.implementation || token.symbol != "WETH" ? DEFAULT_ERC20 : "WETH9";
const tokenFactory = await hardhat.ethers.getContractFactory(implementation, wallet);
const args =
token.symbol != "WETH" ? [`${token.name} (${process.env.CHAIN_ETH_NETWORK})`, token.symbol, token.decimals] : [];
Expand All @@ -58,13 +117,16 @@ export async function deployTokens(
}
if (mintTokens) {
for (let i = 0; i < 10; ++i) {
const testWallet = Wallet.fromMnemonic(mnemonic as string, "m/44'/60'/0'/0/" + i).connect(wallet.provider);
const testWalletAddress = Wallet.fromMnemonic(mnemonic as string, "m/44'/60'/0'/0/" + i).address;
if (token.symbol !== "WETH") {
await erc20.mint(testWallet.address, parseEther("3000000000"));
await erc20.mint(testWalletAddress, parseEther("3000000000"));
}
}
}

// Remove the unneeded field
// if (token.implementation) {
// delete token.implementation;
// }
result.push(token);
}
return result;
Expand Down
Loading

0 comments on commit 0b4ecbd

Please sign in to comment.