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

fix: l2-native-token, bridge fixes #811

Merged
merged 50 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
e0a27a3
bridge fixes
kelemeno Sep 17, 2024
18167a2
fixes
kelemeno Sep 18, 2024
56b4e1f
register Token new function new msg format
kelemeno Sep 19, 2024
90b5173
Merge branch 'sync-layer-stable' of ssh://github.com/matter-labs/era-…
kelemeno Sep 26, 2024
1e009c8
copied some test mods from interop branch
kelemeno Sep 26, 2024
acdf8e9
some fixes
kelemeno Sep 27, 2024
9791cec
linting
kelemeno Sep 27, 2024
c2c2dba
(feat): add legacy read for withdrawal finalization, update l2-l1 enc…
Sep 27, 2024
0c7cc45
fixed test
kelemeno Sep 27, 2024
18dc862
some fixes, enabling l2 integrationt tests
kelemeno Sep 28, 2024
4c4e3f6
lint
kelemeno Sep 28, 2024
e7cf415
diamond selectors
kelemeno Sep 28, 2024
5449b51
add back test for coverage
kelemeno Sep 28, 2024
ae57b24
trying to fix foundry deploy script tests
kelemeno Sep 28, 2024
da58aa0
fixing l1 and l2 tests
kelemeno Sep 29, 2024
acc2e77
renaming
kelemeno Sep 29, 2024
21d11f3
undo bh initialize change
kelemeno Sep 29, 2024
3d8b202
fmt
kelemeno Sep 29, 2024
72c83b0
getting tests to work
kelemeno Sep 29, 2024
8af590e
fmt
kelemeno Sep 29, 2024
4c40c89
chain withdrawal test
kelemeno Sep 29, 2024
8ce6905
some bridge fixes
kelemeno Sep 29, 2024
183589c
script
kelemeno Sep 29, 2024
546bd3f
Merge branch 'kl/sync-layer-reorg' of ssh://github.com/matter-labs/er…
kelemeno Sep 30, 2024
0b0e4a7
Merge branch 'kl/sync-layer-reorg' of ssh://github.com/matter-labs/er…
kelemeno Sep 30, 2024
aa87952
l1 test fixes
kelemeno Sep 30, 2024
f6e3538
gateway -> l1 migration fix
kelemeno Oct 1, 2024
ffcc50f
stas issue fixes
kelemeno Oct 1, 2024
40a556a
rename l2 dummy tests
kelemeno Oct 1, 2024
b88b1b6
renamed the dummy-tests folder
kelemeno Oct 1, 2024
5e55954
(fix): support in Nullifier L2 native tokens, correct recovery of fai…
Oct 2, 2024
47ed94c
Merge commit '90b517394e222c98ea99713e1ce9b5a1c4e0870e' into ra/fix-l…
Oct 2, 2024
0199a62
(merge): fix
Oct 2, 2024
905cc0d
(fix): merger
Oct 2, 2024
ec73c3f
(fix): formatting
Oct 2, 2024
885605e
(test)
Oct 2, 2024
069a34f
(fix): merge newer commit
Oct 2, 2024
d79b32b
(fix): typo
Oct 2, 2024
37dc6cd
missing broadcast
kelemeno Oct 2, 2024
340d506
contract ad vm.broadcast
kelemeno Oct 3, 2024
8e7af17
zks fmt
kelemeno Oct 3, 2024
21185a2
Merge branch 'kl/l2-native-token' of ssh://github.com/matter-labs/era…
kelemeno Oct 3, 2024
6bbcb1d
Merge pull request #837 from matter-labs/ra/l2-native
kelemeno Oct 3, 2024
5d60926
hopefully final cleanup
kelemeno Oct 4, 2024
4c9b96c
sys hashes
kelemeno Oct 4, 2024
fa030cd
stas issue fixes
kelemeno Oct 4, 2024
6afd801
compilation and test
kelemeno Oct 4, 2024
1d1e732
fmt
kelemeno Oct 4, 2024
24f6464
stas issues 2
kelemeno Oct 7, 2024
28c91ad
error selector typo
kelemeno Oct 7, 2024
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
1 change: 1 addition & 0 deletions l1-contracts/.env
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ TOKENS_CONFIG=/script-config/config-deploy-erc20.toml
ZK_CHAIN_CONFIG=/script-config/register-zk-chain.toml
ZK_CHAIN_OUTPUT=/script-out/output-deploy-zk-chain-era.toml
FORCE_DEPLOYMENTS_CONFIG=/script-config/generate-force-deployments-data.toml
GATEWAY_PREPARATION_L1_CONFIG=/script-config/gateway-preparation-l1.toml
StanislavBreadless marked this conversation as resolved.
Show resolved Hide resolved
16 changes: 15 additions & 1 deletion l1-contracts/contracts/bridge/BridgedStandardERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {IBridgedStandardToken} from "./interfaces/IBridgedStandardToken.sol";
import {Unauthorized, NonSequentialVersion, ZeroAddress} from "../common/L1ContractErrors.sol";
import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../common/L2ContractAddresses.sol";
import {DataEncoding} from "../common/libraries/DataEncoding.sol";
import {INativeTokenVault} from "../bridge/ntv/INativeTokenVault.sol";

/// @author Matter Labs
/// @custom:security-contact [email protected]
Expand Down Expand Up @@ -43,13 +44,20 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken,
/// @dev Address of the native token vault that is used as trustee who can mint/burn tokens
address public nativeTokenVault;

/// @dev The assetId of the token.
bytes32 public assetId;
kelemeno marked this conversation as resolved.
Show resolved Hide resolved

/// @dev This also sets the native token vault to the default value if it is not set.
/// It is not set only on the L2s for legacy tokens.
modifier onlyNTV() {
address ntv = nativeTokenVault;
if (ntv == address(0)) {
ntv = L2_NATIVE_TOKEN_VAULT_ADDR;
nativeTokenVault = L2_NATIVE_TOKEN_VAULT_ADDR;
assetId = DataEncoding.encodeNTVAssetId(
INativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).L1_CHAIN_ID(),
originToken
);
}
if (msg.sender != ntv) {
revert Unauthorized(msg.sender);
Expand All @@ -74,14 +82,20 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken,

/// @notice Initializes a contract token for later use. Expected to be used in the proxy.
/// @dev Stores the L1 address of the bridge and set `name`/`symbol`/`decimals` getters that L1 token has.
/// @param _assetId The assetId of the token.
/// @param _originToken Address of the origin token that can be deposited to mint this bridged token
/// @param _data The additional data that the L1 bridge provide for initialization.
/// In this case, it is packed `name`/`symbol`/`decimals` of the L1 token.
function bridgeInitialize(address _originToken, bytes calldata _data) external initializer returns (uint256) {
function bridgeInitialize(
bytes32 _assetId,
address _originToken,
bytes calldata _data
) external initializer returns (uint256) {
if (_originToken == address(0)) {
revert ZeroAddress();
}
originToken = _originToken;
assetId = _assetId;

nativeTokenVault = msg.sender;

Expand Down
15 changes: 9 additions & 6 deletions l1-contracts/contracts/bridge/L1Nullifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {IL1NativeTokenVault} from "./ntv/IL1NativeTokenVault.sol";
import {IL1ERC20Bridge} from "./interfaces/IL1ERC20Bridge.sol";
import {IL1AssetRouter} from "./asset-router/IL1AssetRouter.sol";
import {IAssetRouterBase} from "./asset-router/IAssetRouterBase.sol";
import {INativeTokenVault} from "./ntv/INativeTokenVault.sol";

import {IL1Nullifier, FinalizeL1DepositParams} from "./interfaces/IL1Nullifier.sol";

Expand Down Expand Up @@ -511,7 +510,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable,
/// @return transferData The transfer data used to finalize withdawal.
function _verifyWithdrawal(
FinalizeL1DepositParams calldata _finalizeWithdrawalParams
) internal view returns (bytes32 assetId, bytes memory transferData) {
) internal returns (bytes32 assetId, bytes memory transferData) {
(assetId, transferData) = _parseL2WithdrawalMessage(
_finalizeWithdrawalParams.chainId,
_finalizeWithdrawalParams.message
Expand Down Expand Up @@ -560,7 +559,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable,
function _parseL2WithdrawalMessage(
uint256 _chainId,
bytes memory _l2ToL1message
) internal view returns (bytes32 assetId, bytes memory transferData) {
) internal returns (bytes32 assetId, bytes memory transferData) {
// Please note that there are three versions of the message:
// 1. The message that is sent from `L2BaseToken` to withdraw base token.
// 2. The message that is sent from L2 Legacy Shared Bridge to withdraw ERC20 tokens or base token.
Expand Down Expand Up @@ -604,6 +603,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable,
// slither-disable-next-line unused-return
(amount, ) = UnsafeBytes.readUint256(_l2ToL1message, offset);

l1NativeTokenVault.ensureTokenIsRegistered(l1Token);
assetId = DataEncoding.encodeNTVAssetId(block.chainid, l1Token);
transferData = DataEncoding.encodeBridgeMintData({
_originalCaller: address(0),
Expand All @@ -616,7 +616,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable,
// The data is expected to be at least 36 bytes long to contain assetId.
require(_l2ToL1message.length >= 36, "L1N: wrong msg len"); // wrong message length
// slither-disable-next-line unused-return
(, offset) = UnsafeBytes.readBytes32(_l2ToL1message, offset); // originChainId, not used for L2->L1 txs
(, offset) = UnsafeBytes.readUint256(_l2ToL1message, offset); // originChainId, not used for L2->L1 txs
kelemeno marked this conversation as resolved.
Show resolved Hide resolved
(assetId, offset) = UnsafeBytes.readBytes32(_l2ToL1message, offset);
transferData = UnsafeBytes.readRemainingBytes(_l2ToL1message, offset);
} else {
Expand Down Expand Up @@ -648,7 +648,10 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external override {
bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).getAssetId(block.chainid, _l1Token);
bytes32 assetId = l1NativeTokenVault.assetId(_l1Token);
if (assetId == bytes32(0)) {
assetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Token);
}
// For legacy deposits, the l2 receiver is not required to check tx data hash
// bytes memory transferData = abi.encode(_amount, _depositSender);
bytes memory assetData = abi.encode(_amount, address(0));
Expand Down Expand Up @@ -702,7 +705,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable,
) external override onlyLegacyBridge {
bytes memory assetData = abi.encode(_amount, _depositSender);
/// the legacy bridge can only be used with L1 native tokens.
bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).getAssetId(block.chainid, _l1Asset);
bytes32 assetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Asset);

_verifyAndClearFailedTransfer({
_checkedInLegacyBridge: true,
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable {
proxy = abi.decode(returndata, (address));
}

function sendMessageToL1(bytes calldata _message) external override onlyAssetRouter {
function sendMessageToL1(bytes calldata _message) external override onlyAssetRouter returns (bytes32) {
// slither-disable-next-line unused-return
L2ContractHelper.sendMessageToL1(_message);
return L2ContractHelper.sendMessageToL1(_message);
}
}
19 changes: 18 additions & 1 deletion l1-contracts/contracts/bridge/L2WrappedBaseToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/tok

import {IL2WrappedBaseToken} from "./interfaces/IL2WrappedBaseToken.sol";
import {IBridgedStandardToken} from "./interfaces/IBridgedStandardToken.sol";
import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../common/L2ContractAddresses.sol";

import {ZeroAddress, Unauthorized, BridgeMintNotImplemented, WithdrawFailed} from "../common/L1ContractErrors.sol";

Expand All @@ -29,6 +30,12 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri
/// @dev Address of the L1 base token. It can be deposited to mint this L2 token.
address public override l1Address;

/// @dev Address of the native token vault.
address public override nativeTokenVault;

/// @dev The assetId of the base token. The wrapped token does not have its own assetId.
bytes32 public baseTokenAssetId;

modifier onlyBridge() {
if (msg.sender != l2Bridge) {
revert Unauthorized(msg.sender);
Expand Down Expand Up @@ -59,7 +66,8 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri
string calldata name_,
string calldata symbol_,
address _l2Bridge,
address _l1Address
address _l1Address,
bytes32 _baseTokenAssetId
) external reinitializer(2) {
if (_l2Bridge == address(0)) {
revert ZeroAddress();
Expand All @@ -68,8 +76,13 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri
if (_l1Address == address(0)) {
revert ZeroAddress();
}
if (_baseTokenAssetId == bytes32(0)) {
revert ZeroAddress();
}
l2Bridge = _l2Bridge;
l1Address = _l1Address;
nativeTokenVault = L2_NATIVE_TOKEN_VAULT_ADDR;
baseTokenAssetId = _baseTokenAssetId;

// Set decoded values for name and symbol.
__ERC20_init_unchained(name_, symbol_);
Expand Down Expand Up @@ -131,4 +144,8 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri
function originToken() external view override returns (address) {
return l1Address;
}

function assetId() external view override returns (bytes32) {
return baseTokenAssetId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ abstract contract AssetRouterBase is IAssetRouterBase, Ownable2StepUpgradeable,
});
}

/// @notice Ensures that token is registered with native token vault.
/// @dev Only used when deposit is made with legacy data encoding format.
/// @param _token The native token address which should be registered with native token vault.
/// @return assetId The asset ID of the token provided.
function _ensureTokenRegisteredWithNTV(address _token) internal virtual returns (bytes32 assetId);

/*//////////////////////////////////////////////////////////////
PAUSE
//////////////////////////////////////////////////////////////*/
Expand Down
29 changes: 29 additions & 0 deletions l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,29 @@ interface IL1AssetRouter is IAssetRouterBase, IL1SharedBridgeLegacy {
bytes calldata _assetData
) external;

/// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2.
/// @param _chainId The ZK chain id to which deposit was initiated.
/// @param _depositSender The address of the entity that initiated the deposit.
/// @param _assetId The unique identifier of the deposited L1 token.
/// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information.
/// @param _l2TxHash The L2 transaction hash of the failed deposit finalization.
/// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed.
/// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
/// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent.
/// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization.
/// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system.
function bridgeRecoverFailedTransfer(
uint256 _chainId,
address _depositSender,
bytes32 _assetId,
bytes memory _assetData,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external;

/// @notice Transfers funds to Native Token Vault, if the asset is registered with it. Does nothing for ETH or non-registered tokens.
/// @dev assetId is not the padded address, but the correct encoded id (NTV stores respective format for IDs)
/// @param _amount The asset amount to be transferred to native token vault.
Expand Down Expand Up @@ -169,4 +192,10 @@ interface IL1AssetRouter is IAssetRouterBase, IL1SharedBridgeLegacy {
/// @param _txDataHash The keccak256 hash of 0x01 || abi.encode(bytes32, bytes) to identify deposits.
/// @param _txHash The hash of the L1->L2 transaction to confirm the deposit.
function bridgehubConfirmL2Transaction(uint256 _chainId, bytes32 _txDataHash, bytes32 _txHash) external;

function isWithdrawalFinalized(
uint256 _chainId,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex
) external view returns (bool);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

pragma solidity ^0.8.20;

import {IAssetRouterBase} from "./IAssetRouterBase.sol";

/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IL2AssetRouter {
interface IL2AssetRouter is IAssetRouterBase {
event WithdrawalInitiatedAssetRouter(
uint256 chainId,
address indexed l2Sender,
bytes32 indexed assetId,
bytes assetData
);

function withdraw(bytes32 _assetId, bytes calldata _transferData) external;
function withdraw(bytes32 _assetId, bytes calldata _transferData) external returns (bytes32);

function l1AssetRouter() external view returns (address);

Expand Down
46 changes: 41 additions & 5 deletions l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,31 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard {

emit ClaimedFailedDepositAssetRouter(_chainId, _assetId, _assetData);
}

function bridgeRecoverFailedTransfer(
uint256 _chainId,
address _depositSender,
bytes32 _assetId,
bytes calldata _assetData,
bytes32 _l2TxHash,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external {
L1_NULLIFIER.bridgeRecoverFailedTransfer({
_chainId: _chainId,
_depositSender: _depositSender,
_assetId: _assetId,
_assetData: _assetData,
_l2TxHash: _l2TxHash,
_l2BatchNumber: _l2BatchNumber,
_l2MessageIndex: _l2MessageIndex,
_l2TxNumberInBatch: _l2TxNumberInBatch,
_merkleProof: _merkleProof
});
}

/*//////////////////////////////////////////////////////////////
Internal & Helpers
//////////////////////////////////////////////////////////////*/
Expand All @@ -354,13 +379,15 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard {

/// @notice Ensures that token is registered with native token vault.
/// @dev Only used when deposit is made with legacy data encoding format.
/// @param _token The L1 token address which should be registered with native token vault.
/// @param _token The native token address which should be registered with native token vault.
/// @return assetId The asset ID of the token provided.
function _ensureTokenRegisteredWithNTV(address _token) internal returns (bytes32 assetId) {
assetId = nativeTokenVault.getAssetId(block.chainid, _token);
if (nativeTokenVault.tokenAddress(assetId) == address(0)) {
nativeTokenVault.registerToken(_token);
function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) {
assetId = nativeTokenVault.assetId(_token);
if (assetId != bytes32(0)) {
return assetId;
}
nativeTokenVault.ensureTokenIsRegistered(_token);
assetId = nativeTokenVault.assetId(_token);
}

/// @inheritdoc IL1AssetRouter
Expand Down Expand Up @@ -586,6 +613,15 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard {
});
}

/// @notice Legacy read method, which forwards the call to L1Nullifier to check if withdrawal was finalized
function isWithdrawalFinalized(
uint256 _chainId,
uint256 _l2BatchNumber,
uint256 _l2MessageIndex
) external view returns (bool) {
return L1_NULLIFIER.isWithdrawalFinalized(_chainId, _l2BatchNumber, _l2MessageIndex);
}

/// @notice Legacy function to get the L2 shared bridge address for a chain.
/// @dev In case the chain has been deployed after the gateway release,
/// the returned value is 0.
Expand Down
Loading
Loading