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 3 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
11 changes: 10 additions & 1 deletion l1-contracts/contracts/bridge/BridgedStandardERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ 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() {
Expand Down Expand Up @@ -74,14 +77,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
4 changes: 2 additions & 2 deletions l1-contracts/contracts/bridge/L1Nullifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable,
uint16 _l2TxNumberInBatch,
bytes32[] calldata _merkleProof
) external override {
bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).getAssetId(block.chainid, _l1Token);
bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).calculateAssetId(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 @@ -696,7 +696,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 = INativeTokenVault(address(l1NativeTokenVault)).calculateAssetId(block.chainid, _l1Asset);
kelemeno marked this conversation as resolved.
Show resolved Hide resolved

_verifyAndClearFailedTransfer({
_checkedInLegacyBridge: true,
Expand Down
8 changes: 8 additions & 0 deletions 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 token.
bytes32 public override assetId;
kelemeno marked this conversation as resolved.
Show resolved Hide resolved
kelemeno marked this conversation as resolved.
Show resolved Hide resolved

modifier onlyBridge() {
if (msg.sender != l2Bridge) {
revert Unauthorized(msg.sender);
Expand Down Expand Up @@ -70,6 +77,7 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri
}
l2Bridge = _l2Bridge;
l1Address = _l1Address;
nativeTokenVault = L2_NATIVE_TOKEN_VAULT_ADDR;

// Set decoded values for name and symbol.
__ERC20_init_unchained(name_, symbol_);
Expand Down
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,10 @@ 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);
function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) {
assetId = nativeTokenVault.calculateAssetId(block.chainid, _token);
kelemeno marked this conversation as resolved.
Show resolved Hide resolved
if (nativeTokenVault.tokenAddress(assetId) == address(0)) {
nativeTokenVault.registerToken(_token);
}
Expand Down
29 changes: 25 additions & 4 deletions l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter {
function setAssetHandlerAddressThisChain(
bytes32 _assetRegistrationData,
address _assetHandlerAddress
) external override(AssetRouterBase) {
) external override(AssetRouterBase, IAssetRouterBase) {
_setAssetHandlerAddressThisChain(L2_NATIVE_TOKEN_VAULT_ADDR, _assetRegistrationData, _assetHandlerAddress);
}

Expand All @@ -117,7 +117,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter {
uint256,
bytes32 _assetId,
bytes calldata _transferData
) public override onlyAssetRouterCounterpartOrSelf(L1_CHAIN_ID) {
) public override(AssetRouterBase, IAssetRouterBase) onlyAssetRouterCounterpartOrSelf(L1_CHAIN_ID) {
if (_assetId == BASE_TOKEN_ASSET_ID) {
revert AssetIdNotSupported(BASE_TOKEN_ASSET_ID);
}
Expand All @@ -126,6 +126,22 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter {
emit DepositFinalizedAssetRouter(L1_CHAIN_ID, _assetId, _transferData);
}

/*//////////////////////////////////////////////////////////////
Internal & Helpers
//////////////////////////////////////////////////////////////*/

// kl todo add handle Legaacy data here, which calls esureTokenRegisteredWithNTV
kelemeno marked this conversation as resolved.
Show resolved Hide resolved
// have handleLegacyData called from somewhere.

/// @inheritdoc AssetRouterBase
function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) {
IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR);
assetId = nativeTokenVault.calculateAssetId(block.chainid, _token);
if (nativeTokenVault.tokenAddress(assetId) == address(0)) {
nativeTokenVault.registerToken(_token);
}
}

/*//////////////////////////////////////////////////////////////
LEGACY FUNCTIONS
//////////////////////////////////////////////////////////////*/
Expand All @@ -139,6 +155,11 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter {
_withdrawSender(_assetId, _assetData, msg.sender, true);
}

function withdrawToken(address _l2NativeToken, bytes memory _assetData) public {
kelemeno marked this conversation as resolved.
Show resolved Hide resolved
bytes32 assetId = _ensureTokenRegisteredWithNTV(_l2NativeToken);
_withdrawSender(assetId, _assetData, msg.sender, true);
kelemeno marked this conversation as resolved.
Show resolved Hide resolved
}

/// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1
/// where tokens would be unlocked
/// @param _assetId The asset id of the withdrawn asset
Expand Down Expand Up @@ -250,15 +271,15 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter {
}

function _withdrawLegacy(address _l1Receiver, address _l2Token, uint256 _amount, address _sender) internal {
bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, getL1TokenAddress(_l2Token));
bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, l1TokenAddress(_l2Token));
bytes memory data = abi.encode(_amount, _l1Receiver);
_withdrawSender(assetId, data, _sender, false);
}

/// @notice Legacy getL1TokenAddress.
/// @param _l2Token The address of token on L2.
/// @return The address of token on L1.
function getL1TokenAddress(address _l2Token) public view returns (address) {
function l1TokenAddress(address _l2Token) public view returns (address) {
return IBridgedStandardToken(_l2Token).l1Address();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ interface IBridgedStandardToken {
function originToken() external view returns (address);

function l2Bridge() external view returns (address);

function assetId() external view returns (bytes32);

function nativeTokenVault() external view returns (address);
}
7 changes: 5 additions & 2 deletions l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,18 @@ interface INativeTokenVault {
/// @notice No access control is ok, since the bridging of tokens should be permissionless. This requires permissionless registration.
function registerToken(address _l1Token) external;

/// @notice Used to get the assetId of a token
function getAssetId(uint256 _chainId, address _tokenAddress) external view returns (bytes32);
/// @notice Used to calculate the assetId of a token
function calculateAssetId(uint256 _chainId, address _tokenAddress) external view returns (bytes32);

/// @notice Used to get the the ERC20 data for a token
function getERC20Getters(address _token, uint256 _originChainId) external view returns (bytes memory);

/// @notice Used to get the token address of an assetId
function tokenAddress(bytes32 assetId) external view returns (address);

/// @notice Used to get the assetId of a token
function assetId(address token) external view returns (bytes32);

/// @notice Used to get the expected bridged token address corresponding to its native counterpart
function calculateCreate2TokenAddress(uint256 _originChainId, address _originToken) external view returns (address);
}
8 changes: 5 additions & 3 deletions l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault {
/// @notice Sets the legacy token asset ID for the given L2 token address.
function setLegacyTokenAssetId(address _l2TokenAddress) public {
address l1TokenAddress = L2_LEGACY_SHARED_BRIDGE.l1TokenAddress(_l2TokenAddress);
bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, l1TokenAddress);
tokenAddress[assetId] = _l2TokenAddress;
originChainId[assetId] = L1_CHAIN_ID;
bytes32 newAssetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, l1TokenAddress);
tokenAddress[newAssetId] = _l2TokenAddress;
assetId[_l2TokenAddress] = newAssetId;
originChainId[newAssetId] = L1_CHAIN_ID;
}

/// @notice Ensures that the token is deployed.
Expand All @@ -114,6 +115,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault {
revert AddressMismatch(_originToken, l1LegacyToken);
}
tokenAddress[_assetId] = expectedToken;
assetId[expectedToken] = _assetId;
} else {
super._ensureTokenDeployedInner({
_originChainId: _originChainId,
Expand Down
24 changes: 17 additions & 7 deletions l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,15 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2
/// @dev A mapping assetId => tokenAddress
mapping(bytes32 assetId => address tokenAddress) public tokenAddress;

/// @dev A mapping tokenAddress => assetId
mapping(address tokenAddress => bytes32 assetId) public assetId;

/**
* @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[47] private __gap;
uint256[46] private __gap;

/// @notice Checks that the message sender is the bridgehub.
modifier onlyAssetRouter() {
Expand Down Expand Up @@ -322,18 +325,19 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2
/// @notice Returns the parsed assetId.
/// @param _nativeToken The address of the token to be parsed.
/// @dev Shows the assetId for a given chain and token address
function getAssetId(uint256 _chainId, address _nativeToken) external pure override returns (bytes32) {
function calculateAssetId(uint256 _chainId, address _nativeToken) external pure override returns (bytes32) {
kelemeno marked this conversation as resolved.
Show resolved Hide resolved
return DataEncoding.encodeNTVAssetId(_chainId, _nativeToken);
}

/// @notice Registers a native token address for the vault.
/// @dev It does not perform any checks for the correctnesss of the token contract.
/// @param _nativeToken The address of the token to be registered.
function _unsafeRegisterNativeToken(address _nativeToken) internal {
bytes32 assetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken);
bytes32 newAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken);
ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_nativeToken))), address(this));
tokenAddress[assetId] = _nativeToken;
originChainId[assetId] = block.chainid;
tokenAddress[newAssetId] = _nativeToken;
assetId[_nativeToken] = newAssetId;
originChainId[newAssetId] = block.chainid;
}

function _handleChainBalanceIncrease(
Expand Down Expand Up @@ -390,12 +394,13 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2
bytes memory _erc20Data,
address _expectedToken
) internal {
address deployedToken = _deployBridgedToken(_originChainId, _originToken, _erc20Data);
address deployedToken = _deployBridgedToken(_originChainId, _assetId, _originToken, _erc20Data);
if (deployedToken != _expectedToken) {
revert AddressMismatch(_expectedToken, deployedToken);
}

tokenAddress[_assetId] = _expectedToken;
assetId[_expectedToken] = _assetId;
}

/// @notice Calculates the bridged token address corresponding to native token counterpart.
Expand All @@ -412,13 +417,18 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2
/// @return The address of the beacon proxy (bridged token).
function _deployBridgedToken(
uint256 _originChainId,
bytes32 _assetId,
address _originToken,
bytes memory _erc20Data
) internal returns (address) {
bytes32 salt = _getCreate2Salt(_originChainId, _originToken);

BeaconProxy l2Token = _deployBeaconProxy(salt);
uint256 tokenOriginChainId = BridgedStandardERC20(address(l2Token)).bridgeInitialize(_originToken, _erc20Data);
uint256 tokenOriginChainId = BridgedStandardERC20(address(l2Token)).bridgeInitialize(
_assetId,
_originToken,
StanislavBreadless marked this conversation as resolved.
Show resolved Hide resolved
_erc20Data
);
tokenOriginChainId = tokenOriginChainId == 0 ? L1_CHAIN_ID : tokenOriginChainId;
kelemeno marked this conversation as resolved.
Show resolved Hide resolved
originChainId[DataEncoding.encodeNTVAssetId(tokenOriginChainId, _originToken)] = tokenOriginChainId;
return address(l2Token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable
require(_assetHandlerAddressOnCounterpart == L2_BRIDGEHUB_ADDR, "CTMDT: wrong counter part");
}

function getAssetId(address _l1CTM) public view override returns (bytes32) {
function calculateAssetId(address _l1CTM) public view override returns (bytes32) {
return keccak256(abi.encode(block.chainid, address(this), bytes32(uint256(uint160(_l1CTM)))));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ interface ICTMDeploymentTracker is IL1AssetDeploymentTracker {

function registerCTMAssetOnL1(address _ctmAddress) external;

function getAssetId(address _l1CTM) external view returns (bytes32);
function calculateAssetId(address _l1CTM) external view returns (bytes32);
}
4 changes: 3 additions & 1 deletion l1-contracts/src.ts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1351,7 +1351,9 @@ export class Deployer {
const alreadyRegisteredInCTM = (await chainTypeManager.getZKChain(inputChainId)) != ethers.constants.AddressZero;

if (l2LegacySharedBridge) {
console.log("Setting L2 legacy shared bridge in L1Nullifier");
if (this.verbose) {
console.log("Setting L2 legacy shared bridge in L1Nullifier");
}
await this.setL2LegacySharedBridgeInL1Nullifier(inputChainId);
nonce++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ contract L1AssetRouterTest is Test {
// vm.mockCall(
// address(bridgehubAddress),
// abi.encodeWithSelector(IBridgehub.baseTokenAssetId.selector, address(token)),
// abi.encode(nativeTokenVault.getAssetId(address(token)))
// abi.encode(nativeTokenVault.calculateAssetId(address(token)))
// );

token.mint(address(nativeTokenVault), amount);
Expand Down
Loading