Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WETH Bridge Implementation #5

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 369 additions & 0 deletions ethereum/contracts/bridge/L1WethBridge.sol

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions ethereum/contracts/bridge/WETH9.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import { IWETH9 } from "./interfaces/IWETH9.sol";

contract WETH9 is IWETH9 {
string public name;
string public symbol;
uint8 public decimals;

mapping(address => uint) public override balanceOf;
mapping(address => mapping(address => uint)) public override allowance;

constructor() public {
name = "Wrapped Ether";
symbol = "WETH";
decimals = 18;
}

function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}

function withdraw(uint value) external override {
balanceOf[msg.sender] -= value;
(bool success, ) = msg.sender.call{value: value}("");
if (!success) {
revert WETH_ETHTransferFailed();
}
emit Withdrawal(msg.sender, value);
}

function totalSupply() external view override returns (uint) {
return address(this).balance;
}

function approve(
address spender,
uint value
) external override returns (bool) {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}

function transfer(
address to,
uint value
) external override ensuresRecipient(to) returns (bool) {
balanceOf[msg.sender] -= value;
balanceOf[to] += value;

emit Transfer(msg.sender, to, value);
return true;
}

function transferFrom(
address from,
address to,
uint value
) external override ensuresRecipient(to) returns (bool) {
if (from != msg.sender) {
uint _allowance = allowance[from][msg.sender];
if (_allowance != type(uint).max) {
allowance[from][msg.sender] -= value;
}
}

balanceOf[from] -= value;
balanceOf[to] += value;

emit Transfer(from, to, value);
return true;
}

modifier ensuresRecipient(address to) {
// Prevents from burning or sending WETH tokens to the contract.
if (to == address(0)) {
revert WETH_InvalidTransferRecipient();
}
if (to == address(this)) {
revert WETH_InvalidTransferRecipient();
}
_;
}
}
53 changes: 53 additions & 0 deletions ethereum/contracts/bridge/interfaces/IL1WethBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author Matter Labs
interface IL1WethBridge {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All bridges supposed to inherit the same interface as default bridge, so all the same functions/events should be inherit from the IL1Bridge. And all weth-spefic functions fine to be here.

struct FinalizeWithdrawalL2MessageIndexes {
uint256 ethL2MessageIndex;
uint256 wethL2MessageIndex;
}

struct FinalizeWithdrawalMessages {
bytes ethMessage;
bytes wethMessage;
}

struct FinalizeWithdrawalMerkleProofs {
bytes32[] ethProof;
bytes32[] wethProof;
}

event DepositInitiated(address indexed from, address indexed to, address indexed l1Token, uint256 amount);

event WithdrawalFinalized(address indexed to, address indexed l1Token, uint256 amount);

event ClaimedFailedDeposit(address indexed to, address indexed l1Token, uint256 amount);

function isWithdrawalFinalized(uint256 _l2BlockNumber, uint256 _l2MessageIndex) external view returns (bool);

function deposit(
address _l2Receiver,
uint256 _amount,
uint256 _l2TxGasLimit,
uint256 _l2TxGasPerPubdataByte
) external payable returns (bytes32 txHash);

function claimFailedDeposit(
address _depositSender,
bytes32 _l2TxHash,
uint256 _l2BlockNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBlock,
bytes32[] calldata _merkleProof
) external;

function finalizeWithdrawal(
uint256 _l2BlockNumber,
FinalizeWithdrawalL2MessageIndexes calldata _l2MessageIndexes,
uint16 _l2TxNumberInBlock,
FinalizeWithdrawalMessages calldata _messages,
FinalizeWithdrawalMerkleProofs calldata _merkleProofs
) external;
}
43 changes: 43 additions & 0 deletions ethereum/contracts/bridge/interfaces/IL2WethBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author Matter Labs
interface IL2WethBridge {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, please don't create another interface, but use IL2Bridge

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as here #5 (comment).
finalizeDeposit in our case needs to be payable, since we are transferring ETH to L2 bridge.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then make a finalizeDeposit from the IL2Bridge to be payable

function initialize(
address _l1Bridge,
address _l1WethAddress,
address _governor
) external;

event FinalizeDeposit(
address indexed l1Sender,
address indexed l2Receiver,
address indexed l2Weth,
uint256 amount
);

event WithdrawalInitiated(
address indexed l2Sender,
address indexed l1Receiver,
address indexed l2Weth,
uint256 amount
);

function finalizeDeposit(
address _l1Sender,
address _l2Receiver,
uint256 _amount
) external payable;

function withdraw(
address _l1Receiver,
uint256 _amount
) external;

function l1WethAddress() external view returns (address);

function l2WethAddress() external view returns (address);

function l1WethBridge() external view returns (address);
}
40 changes: 40 additions & 0 deletions ethereum/contracts/bridge/interfaces/IWETH9.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

interface IWETH9 {
function deposit() external payable;

function withdraw(uint wad) external;

event Deposit(address indexed dst, uint wad);
event Withdrawal(address indexed src, uint wad);

error WETH_ETHTransferFailed();
error WETH_InvalidTransferRecipient();

// ERC20
// function name() external view returns (string memory);

// function symbol() external view returns (string memory);

// function decimals() external view returns (uint8);

function totalSupply() external view returns (uint);

function balanceOf(address guy) external view returns (uint);

function allowance(address src, address dst) external view returns (uint);

function approve(address spender, uint wad) external returns (bool);

function transfer(address dst, uint wad) external returns (bool);

function transferFrom(
address src,
address dst,
uint wad
) external returns (bool);

event Approval(address indexed src, address indexed dst, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
}
2 changes: 2 additions & 0 deletions ethereum/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
"test": "CONTRACT_TESTS=1 yarn run hardhat test test/unit_tests/*.spec.ts --network hardhat",
"test:fork": "CONTRACT_TESTS=1 TEST_CONTRACTS_FORK=1 yarn run hardhat test test/unit_tests/*.fork.ts --network hardhat",
"deploy-no-build": "ts-node scripts/deploy.ts",
"deploy-weth-bridges": "ts-node scripts/deploy-weth-bridges.ts",
"initialize-weth-bridges": "ts-node scripts/initialize-weth-bridges.ts",
"allow-list-manager": "ts-node scripts/allow-list-manager.ts",
"deploy-erc20": "ts-node scripts/deploy-erc20.ts",
"token-info": "ts-node scripts/token-info.ts",
Expand Down
60 changes: 60 additions & 0 deletions ethereum/scripts/deploy-weth-bridges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Command } from 'commander';
import { Wallet, ethers } from 'ethers';
import { Deployer } from '../src.ts/deploy';
import { formatUnits, parseUnits } from 'ethers/lib/utils';
import * as fs from 'fs';
import * as path from 'path';
import { web3Provider } from './utils';

const provider = web3Provider();
const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, `etc/test_config/constant`);
const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: 'utf-8' }));

async function main() {
const program = new Command();

program.version('0.1.0').name('deploy').description('deploy weth bridges');

program
.option('--private-key <private-key>')
.option('--gas-price <gas-price>')
.option('--nonce <nonce>')
.option('--governor-address <governor-address>')
.option('--create2-salt <create2-salt>')
.action(async (cmd) => {
const deployWallet = cmd.privateKey
? new Wallet(cmd.privateKey, provider)
: Wallet.fromMnemonic(
process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic,
"m/44'/60'/0'/0/0"
).connect(provider);
console.log(`Using deployer wallet: ${deployWallet.address}`);

const governorAddress = cmd.governorAddress ? cmd.governorAddress : deployWallet.address;
console.log(`Using governor address: ${governorAddress}`);

const gasPrice = cmd.gasPrice ? parseUnits(cmd.gasPrice, 'gwei') : await provider.getGasPrice();
console.log(`Using gas price: ${formatUnits(gasPrice, 'gwei')} gwei`);

let nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployWallet.getTransactionCount();
console.log(`Using nonce: ${nonce}`);

const create2Salt = cmd.create2Salt ? cmd.create2Salt : ethers.utils.hexlify(ethers.utils.randomBytes(32));

const deployer = new Deployer({
deployWallet,
governorAddress,
verbose: true
});

await deployer.deployWethBridgeContracts(create2Salt, gasPrice);
})
await program.parseAsync(process.argv);
}

main()
.then(() => process.exit(0))
.catch((err) => {
console.error('Error:', err);
process.exit(1);
});
1 change: 1 addition & 0 deletions ethereum/scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ async function main() {
await deployer.deployAllowList(create2Salt, { gasPrice, nonce });
await deployer.deployZkSyncContract(create2Salt, gasPrice, nonce + 1);
await deployer.deployBridgeContracts(create2Salt, gasPrice); // Do not pass nonce, since it was increment after deploying zkSync contracts
// await deployer.deployWethBridgeContracts(create2Salt, gasPrice);
});

await program.parseAsync(process.argv);
Expand Down
Loading