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

Kl integration factory #41

Closed
wants to merge 9 commits 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
/ethereum/.openzeppelin
22 changes: 20 additions & 2 deletions ethereum/.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,36 @@ CHAIN_ETH_NETWORK=localhost
CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT=72000000
CONTRACTS_DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT=10000000
ETH_CLIENT_WEB3_URL=http://127.0.0.1:8545
CONTRACTS_MAILBOX_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_BRIDGEHEAD_PROXY_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_BRIDGEHEAD_IMPL_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_BRIDGEHEAD_PROXY_ADMIN_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_BRIDGEHEAD_CHAIN_IMPL_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_BRIDGEHEAD_CHAIN_PROXY_ADMIN_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_BRIDGEHEAD_CHAIN_PROXY_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_PROOF_SYSTEM_PROXY_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_PROOF_SYSTEM_IMPL_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_PROOF_SYSTEM_PROXY_ADMIN_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_VERIFIER_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_GOVERNANCE_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_DIAMOND_CUT_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_EXECUTOR_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_REGISTRY_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_GETTERS_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_DIAMOND_INIT_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_DIAMOND_UPGRADE_INIT_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_DIAMOND_PROXY_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_VERIFIER_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_DEFAULT_UPGRADE_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_ERA_GOVERNANCE_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_ERA_DIAMOND_CUT_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_ERA_EXECUTOR_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_ERA_GETTERS_FACET_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_ERA_DIAMOND_INIT_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_ERA_DIAMOND_UPGRADE_INIT_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_ERA_DIAMOND_PROXY_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_L1_ERC20_BRIDGE_IMPL_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_L1_ERC20_BRIDGE_PROXY_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_L1_ALLOW_LIST_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_CREATE2_FACTORY_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_VALIDATOR_TIMELOCK_ADDR=0x0000000000000000000000000000000000000000
CONTRACTS_RECURSION_LEAF_LEVEL_VK_HASH=0x0000000000000000000000000000000000000000000000000000000000000000
CONTRACTS_VALIDATOR_TIMELOCK_EXECUTION_DELAY=0
83 changes: 70 additions & 13 deletions ethereum/contracts/bridge/L1ERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import "./interfaces/IL2ERC20Bridge.sol";

import "./libraries/BridgeInitializationHelper.sol";

import "../zksync/interfaces/IZkSync.sol";
import "../bridgehead/bridgehead-interfaces/IBridgehead.sol";
import "../common/Messaging.sol";
import "../common/interfaces/IAllowList.sol";
import "../common/AllowListed.sol";
import "../common/libraries/UnsafeBytes.sol";
Expand All @@ -31,11 +32,11 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
IAllowList internal immutable allowList;

/// @dev zkSync smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication
IZkSync internal immutable zkSync;
IBridgehead internal immutable zkSync;

/// @dev A mapping L2 block number => message number => flag
/// @dev A mapping L2 _chainId => block number => message number => flag
/// @dev Used to indicate that zkSync L2 -> L1 message was already processed
mapping(uint256 => mapping(uint256 => bool)) public isWithdrawalFinalized;
mapping(uint256 => mapping(uint256 => mapping(uint256 => bool))) public isWithdrawalFinalized;

/// @dev A mapping account => L1 token address => L2 deposit transaction hash => amount
/// @dev Used for saving the number of deposited funds, to claim them in case the deposit transaction will fail
Expand All @@ -61,7 +62,7 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua

/// @dev Contract is expected to be used as proxy implementation.
/// @dev Initialize the implementation to prevent Parity hack.
constructor(IZkSync _zkSync, IAllowList _allowList) reentrancyGuardInitializer {
constructor(IBridgehead _zkSync, IAllowList _allowList) reentrancyGuardInitializer {
zkSync = _zkSync;
allowList = _allowList;
}
Expand All @@ -79,6 +80,7 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
/// @param _deployBridgeImplementationFee How much of the sent value should be allocated to deploying the L2 bridge implementation
/// @param _deployBridgeProxyFee How much of the sent value should be allocated to deploying the L2 bridge proxy
function initialize(
uint256 _chainId,
bytes[] calldata _factoryDeps,
address _l2TokenBeacon,
address _governor,
Expand All @@ -98,7 +100,9 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
bytes32 l2BridgeProxyBytecodeHash = L2ContractHelper.hashL2Bytecode(_factoryDeps[1]);

// Deploy L2 bridge implementation contract
// KL todo we need to make the bridge L2 independent
address bridgeImplementationAddr = BridgeInitializationHelper.requestDeployTransaction(
_chainId,
zkSync,
_deployBridgeImplementationFee,
l2BridgeImplementationBytecodeHash,
Expand All @@ -118,7 +122,9 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
}

// Deploy L2 bridge proxy contract
//KL todo remove chainId, make it L2 independent
l2Bridge = BridgeInitializationHelper.requestDeployTransaction(
_chainId,
zkSync,
_deployBridgeProxyFee,
l2BridgeProxyBytecodeHash,
Expand All @@ -144,7 +150,39 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
uint256 _l2TxGasLimit,
uint256 _l2TxGasPerPubdataByte
) external payable returns (bytes32 l2TxHash) {
l2TxHash = deposit(_l2Receiver, _l1Token, _amount, _l2TxGasLimit, _l2TxGasPerPubdataByte, address(0));
// KL todo 9, get default Era chainId
l2TxHash = deposit(9, _l2Receiver, _l1Token, _amount, _l2TxGasLimit, _l2TxGasPerPubdataByte, address(0));
}

/// @notice Legacy deposit method with no chainId, use another `deposit` method instead.
/// @dev Initiates a deposit by locking funds on the contract and sending the request
/// of processing an L2 transaction where tokens would be minted
/// @param _l2Receiver The account address that should receive funds on L2
/// @param _l1Token The L1 token address which is deposited
/// @param _amount The total amount of tokens to be bridged
/// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction
/// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction
/// @return l2TxHash The L2 transaction hash of deposit finalization
/// @param _refundRecipient The address on L2 that will receive the refund for the transaction.
/// NOTE: the function doesn't use `nonreentrant` and `senderCanCallFunction` modifiers, because the inner method does.
function deposit(
address _l2Receiver,
address _l1Token,
uint256 _amount,
uint256 _l2TxGasLimit,
uint256 _l2TxGasPerPubdataByte,
address _refundRecipient
) external payable returns (bytes32 l2TxHash) {
// KL todo 9, get default Era chainIds
l2TxHash = deposit(
270,
_l2Receiver,
_l1Token,
_amount,
_l2TxGasLimit,
_l2TxGasPerPubdataByte,
_refundRecipient
);
}

/// @notice Initiates a deposit by locking funds on the contract and sending the request
Expand All @@ -166,6 +204,7 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
/// through the Mailbox to use or withdraw the funds from L2, and the funds would be lost.
/// @return l2TxHash The L2 transaction hash of deposit finalization
function deposit(
uint256 _chainId,
address _l2Receiver,
address _l1Token,
uint256 _amount,
Expand All @@ -188,6 +227,7 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
refundRecipient = msg.sender != tx.origin ? AddressAliasHelper.applyL1ToL2Alias(msg.sender) : msg.sender;
}
l2TxHash = zkSync.requestL2Transaction{value: msg.value}(
_chainId,
l2Bridge,
0, // L2 msg.value
l2TxCalldata,
Expand Down Expand Up @@ -249,6 +289,7 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
/// @param _l2TxNumberInBlock The L2 transaction number in a block, in which the log was sent
/// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization
function claimFailedDeposit(
uint256 _chainId,
address _depositSender,
address _l1Token,
bytes32 _l2TxHash,
Expand All @@ -258,6 +299,7 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
bytes32[] calldata _merkleProof
) external nonReentrant senderCanCallFunction(allowList) {
bool proofValid = zkSync.proveL1ToL2TransactionStatus(
_chainId,
_l2TxHash,
_l2BlockNumber,
_l2MessageIndex,
Expand Down Expand Up @@ -287,32 +329,47 @@ contract L1ERC20Bridge is IL1Bridge, IL1BridgeLegacy, AllowListed, ReentrancyGua
/// @param _message The L2 withdraw data, stored in an L2 -> L1 message
/// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization
function finalizeWithdrawal(
uint256 _chainId,
uint256 _l2BlockNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBlock,
bytes calldata _message,
bytes32[] calldata _merkleProof
) external nonReentrant senderCanCallFunction(allowList) {
require(!isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex], "pw");
{
require(!isWithdrawalFinalized[_chainId][_l2BlockNumber][_l2MessageIndex], "pw");
}

L2Message memory l2ToL1Message = L2Message({
txNumberInBlock: _l2TxNumberInBlock,
sender: l2Bridge,
data: _message
});

(address l1Receiver, address l1Token, uint256 amount) = _parseL2WithdrawalMessage(l2ToL1Message.data);
// Preventing the stack too deep error
{
bool success = zkSync.proveL2MessageInclusion(_l2BlockNumber, _l2MessageIndex, l2ToL1Message, _merkleProof);
bool success = zkSync.proveL2MessageInclusion(
_chainId,
_l2BlockNumber,
_l2MessageIndex,
l2ToL1Message,
_merkleProof
);
require(success, "nq");
}

isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex] = true;
// Withdraw funds
IERC20(l1Token).safeTransfer(l1Receiver, amount);
{
isWithdrawalFinalized[_chainId][_l2BlockNumber][_l2MessageIndex] = true;
}

emit WithdrawalFinalized(l1Receiver, l1Token, amount);
{
(address l1Receiver, address l1Token, uint256 amount) = _parseL2WithdrawalMessage(l2ToL1Message.data);

// Withdraw funds
IERC20(l1Token).safeTransfer(l1Receiver, amount);

emit WithdrawalFinalized(l1Receiver, l1Token, amount);
}
}

/// @dev Decode the withdraw message that came from L2
Expand Down
56 changes: 43 additions & 13 deletions ethereum/contracts/bridge/L1WethBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import "./interfaces/IL1Bridge.sol";
import "./interfaces/IL2WethBridge.sol";
import "./interfaces/IL2Bridge.sol";
import "./interfaces/IWETH9.sol";
import "../zksync/interfaces/IZkSync.sol";
import "../bridgehead/bridgehead-interfaces/IBridgehead.sol";
import "../common/interfaces/IAllowList.sol";

import "./libraries/BridgeInitializationHelper.sol";

import "../common/Messaging.sol";
import "../common/AllowListed.sol";
import "../common/libraries/UnsafeBytes.sol";
import "../common/ReentrancyGuard.sol";
Expand Down Expand Up @@ -46,7 +47,7 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {
IAllowList public immutable allowList;

/// @dev zkSync smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication
IZkSync public immutable zkSync;
IBridgehead public immutable zkSync;

/// @dev The address of deployed L2 WETH bridge counterpart
address public l2Bridge;
Expand All @@ -56,13 +57,13 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {

/// @dev A mapping L2 block number => message number => flag
/// @dev Used to indicate that zkSync L2 -> L1 WETH message was already processed
mapping(uint256 => mapping(uint256 => bool)) public isWithdrawalFinalized;
mapping(uint256 => mapping(uint256 => mapping(uint256 => bool))) public isWithdrawalFinalized;

/// @dev Contract is expected to be used as proxy implementation.
/// @dev Initialize the implementation to prevent Parity hack.
constructor(
address payable _l1WethAddress,
IZkSync _zkSync,
IBridgehead _zkSync,
IAllowList _allowList
) reentrancyGuardInitializer {
l1WethAddress = _l1WethAddress;
Expand All @@ -80,6 +81,8 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {
/// @param _deployBridgeImplementationFee The fee that will be paid for the L1 -> L2 transaction for deploying L2 bridge implementation
/// @param _deployBridgeProxyFee The fee that will be paid for the L1 -> L2 transaction for deploying L2 bridge proxy
function initialize(
// kl todo we need to split initialize and initialize chain
uint256 _chainId,
bytes[] calldata _factoryDeps,
address _l2WethAddress,
address _governor,
Expand All @@ -100,7 +103,9 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {
bytes32 l2WethBridgeProxyBytecodeHash = L2ContractHelper.hashL2Bytecode(_factoryDeps[1]);

// Deploy L2 bridge implementation contract
// KL todo remove chainId, make it L2 independent
address wethBridgeImplementationAddr = BridgeInitializationHelper.requestDeployTransaction(
_chainId,
zkSync,
_deployBridgeImplementationFee,
l2WethBridgeImplementationBytecodeHash,
Expand All @@ -125,6 +130,7 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {

// Deploy L2 bridge proxy contract
l2Bridge = BridgeInitializationHelper.requestDeployTransaction(
_chainId,
zkSync,
_deployBridgeProxyFee,
l2WethBridgeProxyBytecodeHash,
Expand Down Expand Up @@ -152,6 +158,7 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {
/// through the Mailbox to use or withdraw the funds from L2, and the funds would be lost.
/// @return txHash The L2 transaction hash of deposit finalization
function deposit(
uint256 _chainId,
address _l2Receiver,
address _l1Token,
uint256 _amount,
Expand All @@ -178,6 +185,7 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {
refundRecipient = msg.sender != tx.origin ? AddressAliasHelper.applyL1ToL2Alias(msg.sender) : msg.sender;
}
txHash = zkSync.requestL2Transaction{value: _amount + msg.value}(
_chainId,
l2Bridge,
_amount,
l2TxCalldata,
Expand Down Expand Up @@ -206,6 +214,7 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {
/// @notice Withdraw funds from the initiated deposit, that failed when finalizing on L2.
/// Note: Refund is performed by sending an equivalent amount of ETH on L2 to the specified deposit refund recipient address.
function claimFailedDeposit(
uint256, // _chainId,
address, // _depositSender,
address, // _l1Token,
bytes32, // _l2TxHash
Expand All @@ -224,38 +233,59 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {
/// @param _message The L2 withdraw data, stored in an L2 -> L1 message
/// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization
function finalizeWithdrawal(
uint256 _chainId,
uint256 _l2BlockNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBlock,
bytes calldata _message,
bytes32[] calldata _merkleProof
) external nonReentrant senderCanCallFunction(allowList) {
require(!isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex], "Withdrawal is already finalized");

(address l1WethWithdrawReceiver, uint256 amount) = _parseL2EthWithdrawalMessage(_message);
{
require(
!isWithdrawalFinalized[_chainId][_l2BlockNumber][_l2MessageIndex],
"Withdrawal is already finalized"
);
}

// Check if the withdrawal has already been finalized on L2.
bool alreadyFinalised = zkSync.isEthWithdrawalFinalized(_l2MessageIndex, _l2TxNumberInBlock);
bool alreadyFinalised = zkSync.isEthWithdrawalFinalized(_chainId, _l2MessageIndex, _l2TxNumberInBlock);
if (alreadyFinalised) {
// Check that the specified message was actually sent while withdrawing eth from L2.
L2Message memory l2ToL1Message = L2Message({
txNumberInBlock: _l2TxNumberInBlock,
sender: L2_ETH_TOKEN_SYSTEM_CONTRACT_ADDR,
data: _message
});
bool success = zkSync.proveL2MessageInclusion(_l2BlockNumber, _l2MessageIndex, l2ToL1Message, _merkleProof);
require(success, "vq");
{
bool success = zkSync.proveL2MessageInclusion(
_chainId,
_l2BlockNumber,
_l2MessageIndex,
l2ToL1Message,
_merkleProof
);
require(success, "vq");
}
} else {
// Finalize the withdrawal if it is not yet done.
zkSync.finalizeEthWithdrawal(_l2BlockNumber, _l2MessageIndex, _l2TxNumberInBlock, _message, _merkleProof);
zkSync.finalizeEthWithdrawal(
_chainId,
_l2BlockNumber,
_l2MessageIndex,
_l2TxNumberInBlock,
_message,
_merkleProof
);
}

(address l1WethWithdrawReceiver, uint256 amount) = _parseL2EthWithdrawalMessage(_message);

// Wrap ETH to WETH tokens (smart contract address receives the equivalent amount of WETH)
IWETH9(l1WethAddress).deposit{value: amount}();
// Transfer WETH tokens from the smart contract address to the withdrawal receiver
IERC20(l1WethAddress).safeTransfer(l1WethWithdrawReceiver, amount);

isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex] = true;
isWithdrawalFinalized[_chainId][_l2BlockNumber][_l2MessageIndex] = true;

emit WithdrawalFinalized(l1WethWithdrawReceiver, l1WethAddress, amount);
}
Expand All @@ -273,7 +303,7 @@ contract L1WethBridge is IL1Bridge, AllowListed, ReentrancyGuard {

(uint32 functionSignature, uint256 offset) = UnsafeBytes.readUint32(_message, 0);
require(
bytes4(functionSignature) == IMailbox.finalizeEthWithdrawal.selector,
bytes4(functionSignature) == IBridgeheadMailbox.finalizeEthWithdrawal.selector,
"Incorrect ETH message function selector"
);

Expand Down
Loading