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

implement zksync gateway #130

Merged
merged 6 commits into from
Jan 6, 2024
Merged
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
8 changes: 4 additions & 4 deletions contracts/Storage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,21 @@ contract Storage is ZkLinkAcceptor, Config {
/// @dev Priority Requests mapping (request id - operation)
/// Contains op type, pubdata and expiration block of unsatisfied requests.
/// Numbers are in order of requests receiving
mapping(uint64 => Operations.PriorityOperation) internal priorityRequests;
mapping(uint64 => Operations.PriorityOperation) public priorityRequests;

/// @notice User authenticated fact hashes for some nonce.
mapping(address => mapping(uint32 => bytes32)) public authFacts;

/// @dev Timer for authFacts entry reset (address, nonce -> timer).
/// Used when user wants to reset `authFacts` for some nonce.
mapping(address => mapping(uint32 => uint256)) internal authFactsResetTimer;
mapping(address => mapping(uint32 => uint256)) public authFactsResetTimer;

/// @dev Stored hashed StoredBlockInfo for some block number
mapping(uint32 => bytes32) internal storedBlockHashes;
mapping(uint32 => bytes32) public storedBlockHashes;

/// @dev Store sync hash for slaver chains
/// chainId => syncHash
mapping(uint8 => bytes32) internal synchronizedChains;
mapping(uint8 => bytes32) public synchronizedChains;

/// @notice A set of permitted validators
mapping(address => bool) public validators;
Expand Down
28 changes: 26 additions & 2 deletions contracts/ZkLinkPeriphery.sol
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ contract ZkLinkPeriphery is ReentrancyGuard, Storage, Events {
for (uint8 i = 0; i < _block.syncHashs.length; ++i) {
SyncHash memory sync = _block.syncHashs[i];
bytes32 remoteSyncHash = synchronizedChains[sync.chainId];
if (remoteSyncHash == bytes32(0)) {
remoteSyncHash = EMPTY_STRING_KECCAK;
}
if (remoteSyncHash != sync.syncHash) {
return false;
}
Expand Down Expand Up @@ -523,6 +526,15 @@ contract ZkLinkPeriphery is ReentrancyGuard, Storage, Events {
}
// #endif
// =======================Withdraw to L1======================
/// @notice Estimate the fee to withdraw token to L1 for user by gateway
function estimateWithdrawToL1Fee(address owner, address token, uint128 amount, uint16 fastWithdrawFeeRate, uint32 accountIdOfNonce, uint8 subAccountIdOfNonce, uint32 nonce) public view returns (uint256 nativeFee) {
if (token == ETH_ADDRESS) {
nativeFee = gateway.estimateWithdrawETHFee(owner, amount, accountIdOfNonce, subAccountIdOfNonce, nonce, fastWithdrawFeeRate);
} else {
nativeFee = gateway.estimateWithdrawERC20Fee(owner, token, amount, accountIdOfNonce, subAccountIdOfNonce, nonce, fastWithdrawFeeRate);
}
}

/// @notice Withdraw token to L1 for user by gateway
/// @param owner User receive token on L1
/// @param token Token address
Expand All @@ -537,18 +549,30 @@ contract ZkLinkPeriphery is ReentrancyGuard, Storage, Events {
bytes32 withdrawHash = getWithdrawHash(accountIdOfNonce, subAccountIdOfNonce, nonce, owner, token, amount, fastWithdrawFeeRate);
require(pendingL1Withdraws[withdrawHash] == true, "M0");

// ensure supply fee
uint256 fee = estimateWithdrawToL1Fee(owner, token, amount, fastWithdrawFeeRate, accountIdOfNonce, subAccountIdOfNonce, nonce);
require(msg.value >= fee, "M1");

// ===Effects===
pendingL1Withdraws[withdrawHash] = false;

// ===Interactions===
// transfer token to gateway
// send msg.value as bridge fee to gateway
if (token == ETH_ADDRESS) {
gateway.withdrawETH{value: msg.value + amount}(owner, amount, accountIdOfNonce, subAccountIdOfNonce, nonce, fastWithdrawFeeRate);
gateway.withdrawETH{value: fee + amount}(owner, amount, accountIdOfNonce, subAccountIdOfNonce, nonce, fastWithdrawFeeRate);
} else {
IERC20(token).safeApprove(address(gateway), amount);
gateway.withdrawERC20{value: msg.value}(owner, token, amount, accountIdOfNonce, subAccountIdOfNonce, nonce, fastWithdrawFeeRate);
gateway.withdrawERC20{value: fee}(owner, token, amount, accountIdOfNonce, subAccountIdOfNonce, nonce, fastWithdrawFeeRate);
}

uint256 leftMsgValue = msg.value - fee;
if (leftMsgValue > 0) {
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = msg.sender.call{value: leftMsgValue}("");
require(success, "M2");
}

emit WithdrawalL1(withdrawHash);
}
}
12 changes: 12 additions & 0 deletions contracts/dev-contracts/L2GatewayMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ import "../interfaces/IL2Gateway.sol";

contract L2GatewayMock is IL2Gateway {

function estimateWithdrawETHFee(address /**_owner**/, uint128 /**_amount**/, uint32 /**_accountIdOfNonce**/, uint8 /**_subAccountIdOfNonce**/, uint32 /**_nonce**/, uint16 /**_fastWithdrawFeeRate**/) external view returns (uint256 nativeFee) {
nativeFee = 0;
}

function withdrawETH(address /**_owner*/, uint128 /**_amount*/, uint32 /**_accountIdOfNonce*/, uint8 /**_subAccountIdOfNonce*/, uint32 /**_nonce*/, uint16 /**_fastWithdrawFeeRate*/) external payable {
// do nothing
}

function estimateWithdrawERC20Fee(address /**_owner**/, address /**_token**/, uint128 /**_amount**/, uint32 /**_accountIdOfNonce**/, uint8 /**_subAccountIdOfNonce**/, uint32 /**_nonce**/, uint16 /**_fastWithdrawFeeRate**/) external view returns (uint256 nativeFee) {
nativeFee = 0;
}

function withdrawERC20(address /**_owner*/, address _token, uint128 _amount, uint32 /**_accountIdOfNonce*/, uint8 /**_subAccountIdOfNonce*/, uint32 /**_nonce*/, uint16 /**_fastWithdrawFeeRate*/) external payable {
// transfer token to self
IERC20(_token).transferFrom(msg.sender, address(this), _amount);
Expand All @@ -30,4 +38,8 @@ contract L2GatewayMock is IL2Gateway {
function sendMasterSyncHash(uint32, bytes32) external payable {
// do nothing
}

function claimBlockConfirmation(uint32 _blockNumber) external {
// do nothing
}
}
43 changes: 43 additions & 0 deletions contracts/gateway/AddressAliasHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: Apache-2.0

/*
* Copyright 2019-2021, Offchain Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

pragma solidity ^0.8.0;

library AddressAliasHelper {
uint160 internal constant offset = uint160(0x1111000000000000000000000000000000001111);

/// @notice Utility function converts the address that submitted a tx
/// to the inbox on L1 to the msg.sender viewed on L2
/// @param l1Address the address in the L1 that triggered the tx to L2
/// @return l2Address L2 address as viewed in msg.sender
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
unchecked {
l2Address = address(uint160(l1Address) + offset);
}
}

/// @notice Utility function that converts the msg.sender viewed on L2 to the
/// address that submitted a tx to the inbox on L1
/// @param l2Address L2 address as viewed in msg.sender
/// @return l1Address the address in the L1 that triggered the tx to L2
function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
unchecked {
l1Address = address(uint160(l2Address) - offset);
}
}
}
51 changes: 51 additions & 0 deletions contracts/gateway/BaseGateway.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.0;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

abstract contract BaseGateway is OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable {
/// @notice Gateway address on remote chain
address public remoteGateway;

/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;

event SetRemoteGateway(address remoteGateWay);

function __BaseGateway_init() internal onlyInitializing {
__BaseGateway_init_unchained();
}

function __BaseGateway_init_unchained() internal onlyInitializing {
__Ownable_init();
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
__Pausable_init();
}

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

/// @notice Set remote Gateway address
/// @param _remoteGateway remote gateway address
function setRemoteGateway(address _remoteGateway) external onlyOwner {
remoteGateway = _remoteGateway;
emit SetRemoteGateway(_remoteGateway);
}

/// @dev Pause the contract, can only be called by the owner
function pause() external onlyOwner {
_pause();
}

/// @dev Unpause the contract, can only be called by the owner
function unpause() external onlyOwner {
_unpause();
}
}
32 changes: 32 additions & 0 deletions contracts/gateway/L1BaseGateway.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.0;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {ZkLinkAcceptor} from "../ZkLinkAcceptor.sol";
import {IArbitrator} from "../interfaces/IArbitrator.sol";

abstract contract L1BaseGateway is ZkLinkAcceptor, OwnableUpgradeable {
/// @notice The arbitrator to confirm block
IArbitrator public arbitrator;

/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;

/// @dev Modifier to make sure the caller is the known arbitrator.
modifier onlyArbitrator() {
require(msg.sender == address(arbitrator), "Not arbitrator");
_;
}

event SetArbitrator(address arbitrator);

/// @notice Set arbitrator
function setArbitrator(IArbitrator _arbitrator) external onlyOwner {
arbitrator = _arbitrator;
emit SetArbitrator(address(_arbitrator));
}
}
32 changes: 32 additions & 0 deletions contracts/gateway/L2BaseGateway.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.0;

import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {IZkLink} from "../interfaces/IZkLink.sol";

abstract contract L2BaseGateway is UUPSUpgradeable {
/// @notice The zkLink contract
IZkLink public zkLink;

/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;

/// @dev Ensure withdraw come from zkLink
modifier onlyZkLink() {
require(msg.sender == address(zkLink), "Not zkLink contract");
_;
}

function __L2BaseGateway_init(IZkLink _zkLink) internal onlyInitializing {
__UUPSUpgradeable_init();
__L2BaseGateway_init_unchained(_zkLink);
}

function __L2BaseGateway_init_unchained(IZkLink _zkLink) internal onlyInitializing {
zkLink = _zkLink;
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.0;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IMessageService} from "../interfaces/linea/IMessageService.sol";
import {IUSDCBridge} from "../interfaces/linea/IUSDCBridge.sol";
import {ITokenBridge} from "../interfaces/linea/ITokenBridge.sol";
import {IMessageService} from "../../interfaces/linea/IMessageService.sol";
import {IUSDCBridge} from "../../interfaces/linea/IUSDCBridge.sol";
import {ITokenBridge} from "../../interfaces/linea/ITokenBridge.sol";

import {ILineaGateway} from "../interfaces/ILineaGateway.sol";
import {ILineaGateway} from "../../interfaces/linea/ILineaGateway.sol";
import {BaseGateway} from "../BaseGateway.sol";

abstract contract LineaGateway is OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable, ILineaGateway {
abstract contract LineaGateway is BaseGateway, ILineaGateway {
using SafeERC20 for IERC20;

/// @notice Linea message service on local chain
Expand All @@ -26,15 +23,12 @@ abstract contract LineaGateway is OwnableUpgradeable, UUPSUpgradeable, Reentranc
/// @notice Linea USDC bridge on local chain
IUSDCBridge public usdcBridge;

/// @notice Gateway address on remote chain
address public remoteGateway;

/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[46] private __gap;
uint256[47] private __gap;

/// @dev Modifier to make sure the caller is the known message service.
modifier onlyMessageService() {
Expand All @@ -49,22 +43,16 @@ abstract contract LineaGateway is OwnableUpgradeable, UUPSUpgradeable, Reentranc
}

function __LineaGateway_init(IMessageService _messageService, ITokenBridge _tokenBridge, IUSDCBridge _usdcBridge) internal onlyInitializing {
__BaseGateway_init();
__LineaGateway_init_unchained(_messageService, _tokenBridge, _usdcBridge);
}

function __LineaGateway_init_unchained(IMessageService _messageService, ITokenBridge _tokenBridge, IUSDCBridge _usdcBridge) internal onlyInitializing {
__Ownable_init();
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
__Pausable_init();

messageService = _messageService;
tokenBridge = _tokenBridge;
usdcBridge = _usdcBridge;
}

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

function claimETH(uint256 _value, bytes calldata _callData, uint256 _nonce) external override nonReentrant whenNotPaused {
// no fee on remote chain
messageService.claimMessage(remoteGateway, address(this), 0, _value, payable(msg.sender), _callData, _nonce);
Expand All @@ -83,23 +71,6 @@ abstract contract LineaGateway is OwnableUpgradeable, UUPSUpgradeable, Reentranc
messageService.claimMessage(remoteGateway, address(this), 0, 0, payable(msg.sender), _cbCallData, _cbNonce);
}

/// @notice Set remote Gateway address
/// @param _remoteGateway remote gateway address
function setRemoteGateway(address _remoteGateway) external onlyOwner {
remoteGateway = _remoteGateway;
emit SetRemoteGateway(_remoteGateway);
}

/// @dev Pause the contract, can only be called by the owner
function pause() external onlyOwner {
_pause();
}

/// @dev Unpause the contract, can only be called by the owner
function unpause() external onlyOwner {
_unpause();
}

/// @dev Bridge token to remote gateway
/// @param _token The token on local chain
/// @param _amount The token amount
Expand Down
Loading