Skip to content

Commit

Permalink
Merge branch 'kl/sync-layer-reorg' into bh/full-merkle-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
benceharomi committed Jun 27, 2024
2 parents bec6540 + 66721c5 commit abb6685
Show file tree
Hide file tree
Showing 27 changed files with 326 additions and 437 deletions.
4 changes: 2 additions & 2 deletions contracts-review-prep.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Reason for changes

The goal was to be build a foundation to be able to support bridging of tokens with custom logic on receiving chain (not wrapped), as well as custom bridging logic (assets, which accrue value over time, like LRTs).
The goal was to be build a foundation to be able to support token bridging with custom logic on receiving chain (not wrapped), as well as custom bridging logic (assets, which accrue value over time, like LRTs).
For clarity, we only developed a framework, the exact logic for custom tokens and custom bridging will follow.

### Major changes
Expand All @@ -20,7 +20,7 @@ In order to achieve it, we separated the liquidity managing logic from the Share

### storage layout

L2SharedBridge will be a system contract, L2NativeTokenVault will replace it ( the storage layout is still not yet backwards compatible)
L2SharedBridge will be a system contract, L2NativeTokenVault will replace it (the storage layout is still not yet backwards compatible)

### bridgehubDeposit API change

Expand Down
100 changes: 55 additions & 45 deletions l1-contracts/contracts/bridge/L1AssetRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pragma solidity 0.8.24;
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

Expand All @@ -30,7 +31,7 @@ import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "../commo

/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @dev Bridges assets between L1 and hyperchains, supporting both ETH and ERC20 tokens.
/// @dev Bridges assets between L1 and ZK chain, supporting both ETH and ERC20 tokens.
/// @dev Designed for use with a proxy for upgradability.
contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeable, PausableUpgradeable {
using SafeERC20 for IERC20;
Expand Down Expand Up @@ -89,7 +90,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
// slither-disable-next-line uninitialized-state
mapping(uint256 chainId => bool enabled) public hyperbridgingEnabled;

/// @dev Maps token balances for each chain to prevent unauthorized spending across hyperchains.
/// @dev Maps token balances for each chain to prevent unauthorized spending across ZK chain.
/// This serves as a security measure until hyperbridging is implemented.
/// NOTE: this function may be removed in the future, don't rely on it!
mapping(uint256 chainId => mapping(address l1Token => uint256 balance)) public chainBalance;
Expand All @@ -100,7 +101,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
mapping(bytes32 assetId => address assetHandlerAddress) public assetHandlerAddress;

/// @dev A mapping assetId => the asset deployment tracker address
/// @dev Tracks the address of Deployment Tracker contract on L1, which sets Asset Handlers on L2s (hyperchains)
/// @dev Tracks the address of Deployment Tracker contract on L1, which sets Asset Handlers on L2s (ZK chain)
/// @dev for the asset and stores respective addresses
mapping(bytes32 assetId => address assetDeploymentTracker) public assetDeploymentTracker;

Expand Down Expand Up @@ -171,21 +172,26 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
}

/// @dev transfer token to shared bridge as part of upgrade
function transferEthToNTV() external {
address ntvAddress = address(nativeTokenVault);
require(msg.sender == ntvAddress, "ShB: not NTV");
uint256 amount = address(this).balance;
bool callSuccess;
// Low-level assembly call, to avoid any memory copying (save gas)
assembly {
callSuccess := call(gas(), ntvAddress, amount, 0, 0, 0, 0)
function transferTokenToNTV(address _token) external {
require(msg.sender == address(nativeTokenVault), "ShB: not NTV");
if (ETH_TOKEN_ADDRESS == _token) {
address ntvAddress = address(nativeTokenVault);
uint256 amount = address(this).balance;
bool callSuccess;
// Low-level assembly call, to avoid any memory copying (save gas)
assembly {
callSuccess := call(gas(), ntvAddress, amount, 0, 0, 0, 0)
}
require(callSuccess, "ShB: eth transfer failed");
} else {
IERC20(_token).safeTransfer(address(nativeTokenVault), IERC20(_token).balanceOf(address(this)));
}
}

/// @dev transfer token to shared bridge as part of upgrade
function transferTokenToNTV(address _token) external {
/// @dev transfer balance to native token vault as part of upgrade
function transferBalanceToNTV(uint256 _chainId, address _token) external {
require(msg.sender == address(nativeTokenVault), "ShB: not NTV");
IERC20(_token).safeTransfer(address(nativeTokenVault), IERC20(_token).balanceOf(address(this)));
chainBalance[_chainId][_token] = 0;
}

/// @dev Sets the L1ERC20Bridge contract address. Should be called only once.
Expand Down Expand Up @@ -243,7 +249,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab

/// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions.
/// @dev If the corresponding L2 transaction fails, refunds are issued to a refund recipient on L2.
/// @param _chainId The chain ID of the hyperchain to which deposit.
/// @param _chainId The chain ID of the ZK chain to which deposit.
/// @param _assetId The deposited asset ID.
/// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one.
/// @param _amount The total amount of tokens to be bridged.
Expand Down Expand Up @@ -332,7 +338,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
}

/// @notice Initiates a deposit transaction within Bridgehub, used by `requestL2TransactionTwoBridges`.
/// @param _chainId The chain ID of the hyperchain to which deposit.
/// @param _chainId The chain ID of the ZK chain to which deposit.
/// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one.
/// @param _l2Value The L2 `msg.value` from the L1 -> L2 deposit transaction.
/// @param _data The calldata for the second bridge deposit.
Expand All @@ -349,41 +355,41 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
whenNotPaused
returns (L2TransactionRequestTwoBridgesInner memory request)
{
bytes32 _assetId;
bytes memory _transferData;
bytes32 assetId;
bytes memory transferData;
bool legacyDeposit = false;
try this.handleLegacyData(_data, _prevMsgSender) returns (
bytes32 _assetIdDecoded,
bytes memory _transferDataDecoded
bytes32 assetIdDecoded,
bytes memory transferDataDecoded
) {
(_assetId, _transferData) = (_assetIdDecoded, _transferDataDecoded);
(assetId, transferData) = (assetIdDecoded, transferDataDecoded);
legacyDeposit = true;
} catch {
(_assetId, _transferData) = abi.decode(_data, (bytes32, bytes));
(assetId, transferData) = abi.decode(_data, (bytes32, bytes));
}

require(BRIDGE_HUB.baseTokenAssetId(_chainId) != _assetId, "ShB: baseToken deposit not supported");
require(BRIDGE_HUB.baseTokenAssetId(_chainId) != assetId, "ShB: baseToken deposit not supported");

bytes memory bridgeMintCalldata = _burn({
_chainId: _chainId,
_l2Value: _l2Value,
_assetId: _assetId,
_assetId: assetId,
_prevMsgSender: _prevMsgSender,
_transferData: _transferData
_transferData: transferData
});
bytes32 txDataHash;

if (legacyDeposit) {
(uint256 _depositAmount, ) = abi.decode(_transferData, (uint256, address));
txDataHash = keccak256(abi.encode(_prevMsgSender, nativeTokenVault.tokenAddress(_assetId), _depositAmount));
(uint256 _depositAmount, ) = abi.decode(transferData, (uint256, address));
txDataHash = keccak256(abi.encode(_prevMsgSender, nativeTokenVault.tokenAddress(assetId), _depositAmount));
} else {
txDataHash = keccak256(abi.encode(_prevMsgSender, _assetId, _transferData));
txDataHash = keccak256(abi.encode(_prevMsgSender, assetId, transferData));
}

request = _requestToBridge({
_chainId: _chainId,
_prevMsgSender: _prevMsgSender,
_assetId: _assetId,
_assetId: assetId,
_bridgeMintCalldata: bridgeMintCalldata,
_txDataHash: txDataHash
});
Expand All @@ -392,7 +398,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
chainId: _chainId,
txDataHash: txDataHash,
from: _prevMsgSender,
assetId: _assetId,
assetId: assetId,
bridgeMintCalldata: bridgeMintCalldata
});
}
Expand Down Expand Up @@ -438,7 +444,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab

/// @notice Confirms the acceptance of a transaction by the Mailbox, as part of the L2 transaction process within Bridgehub.
/// This function is utilized by `requestL2TransactionTwoBridges` to validate the execution of a transaction.
/// @param _chainId The chain ID of the hyperchain to which confirm the deposit.
/// @param _chainId The chain ID of the ZK chain to which confirm the deposit.
/// @param _txDataHash The keccak256 hash of abi.encode(msgSender, l1Token, amount)
/// @param _txHash The hash of the L1->L2 transaction to confirm the deposit.
function bridgehubConfirmL2Transaction(
Expand Down Expand Up @@ -518,14 +524,9 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
}
delete depositHappened[_chainId][_l2TxHash];

IL1AssetHandler(assetHandlerAddress[_assetId]).bridgeRecoverFailedTransfer(
_chainId,
_assetId,
_depositSender,
_assetData
);
IL1AssetHandler(assetHandlerAddress[_assetId]).bridgeRecoverFailedTransfer(_chainId, _assetId, _assetData);

emit ClaimedFailedDepositSharedBridge(_chainId, _depositSender, _assetId, keccak256(_assetData));
emit ClaimedFailedDepositSharedBridge(_chainId, _depositSender, _assetId, _assetData);
}

/// @dev Determines if an eth withdrawal was initiated on zkSync Era before the upgrade to the Shared Bridge.
Expand Down Expand Up @@ -713,6 +714,21 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
}
}

/// @dev Receives and parses (name, symbol, decimals) from the token contract
function getERC20Getters(address _token) public view returns (bytes memory) {
if (_token == ETH_TOKEN_ADDRESS) {
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, ()));
(, bytes memory data2) = _token.staticcall(abi.encodeCall(IERC20Metadata.symbol, ()));
(, bytes memory data3) = _token.staticcall(abi.encodeCall(IERC20Metadata.decimals, ()));
return abi.encode(data1, data2, data3);
}

/*//////////////////////////////////////////////////////////////
SHARED BRIDGE TOKEN BRIDGING LEGACY FUNCTIONS
//////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -799,13 +815,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab
_assetId = _ensureTokenRegisteredWithNTV(_l1Token);

// solhint-disable-next-line func-named-parameters
bridgeMintCalldata = abi.encode(
_amount,
_prevMsgSender,
_l2Receiver,
nativeTokenVault.getERC20Getters(_l1Token),
_l1Token
);
bridgeMintCalldata = abi.encode(_amount, _prevMsgSender, _l2Receiver, getERC20Getters(_l1Token), _l1Token);
}

{
Expand Down
16 changes: 7 additions & 9 deletions l1-contracts/contracts/bridge/L1ERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol";

/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @notice Smart contract that allows depositing ERC20 tokens from Ethereum to hyperchains
/// @notice Smart contract that allows depositing ERC20 tokens from Ethereum to ZK chains
/// @dev It is a legacy bridge from zkSync Era, that was deprecated in favour of shared bridge.
/// It is needed for backward compatibility with already integrated projects.
contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard {
Expand All @@ -25,7 +25,7 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard {
/// @dev The shared bridge that is now used for all bridging, replacing the legacy contract.
IL1AssetRouter public immutable override SHARED_BRIDGE;

/// @dev The native token vault, which handles the token transfers. We should deposit to it
/// @dev The native token vault, which holds deposited tokens.
IL1NativeTokenVault public immutable override NATIVE_TOKEN_VAULT;

/// @dev A mapping L2 batch number => message number => flag.
Expand Down Expand Up @@ -70,13 +70,6 @@ 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 transferTokenToSharedBridge(address _token) external {
require(msg.sender == address(SHARED_BRIDGE), "Not shared bridge");
uint256 amount = IERC20(_token).balanceOf(address(this));
IERC20(_token).safeTransfer(address(SHARED_BRIDGE), amount);
}

/*//////////////////////////////////////////////////////////////
ERA LEGACY GETTERS
//////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -248,4 +241,9 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard {
});
emit WithdrawalFinalized(l1Receiver, l1Token, amount);
}

/// @notice View-only function for backward compatibility
function l2Bridge() external view returns (address) {
return l2NativeTokenVault;
}
}
Loading

0 comments on commit abb6685

Please sign in to comment.