diff --git a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol index 09f087c79..2f15cdc88 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -30,12 +30,34 @@ contract ExperimentalBridgeTest is Test { address secondBridgeAddress; L1SharedBridge secondBridge; TestnetERC20Token testToken; + TestnetERC20Token testToken6; + TestnetERC20Token testToken8; + TestnetERC20Token testToken18; + address mockL2Contract; uint256 eraChainId; event NewChain(uint256 indexed chainId, address stateTransitionManager, address indexed chainGovernance); + modifier useRandomToken(uint256 randomValue) { + _setRandomToken(randomValue); + + _; + } + + function _setRandomToken(uint256 randomValue) internal { + uint256 tokenIndex = randomValue % 3; + TestnetERC20Token token; + if (tokenIndex == 0) { + testToken = testToken18; + } else if (tokenIndex == 1) { + testToken = testToken6; + } else { + testToken = testToken8; + } + } + function setUp() public { eraChainId = 9; bridgeHub = new Bridgehub(); @@ -64,7 +86,9 @@ contract ExperimentalBridgeTest is Test { sharedBridgeAddress = address(sharedBridge); secondBridgeAddress = address(secondBridge); - testToken = new TestnetERC20Token("ZKSTT", "ZkSync Test Token", 18); + testToken18 = new TestnetERC20Token("ZKSTT", "ZkSync Test Token", 18); + testToken6 = new TestnetERC20Token("USDC", "USD Coin", 6); + testToken8 = new TestnetERC20Token("WBTC", "Wrapped Bitcoin", 8); // test if the ownership of the bridgeHub is set correctly or not defaultOwner = bridgeHub.owner(); @@ -280,7 +304,7 @@ contract ExperimentalBridgeTest is Test { } } - function test_addToken(address, address randomAddress) public { + function test_addToken(address, address randomAddress, uint256 randomValue) public useRandomToken(randomValue) { assertTrue(!bridgeHub.tokenIsRegistered(randomAddress), "This random address is not registered as a token"); vm.prank(bridgeOwner); @@ -305,7 +329,11 @@ contract ExperimentalBridgeTest is Test { bridgeHub.addToken(randomAddress); } - function test_addToken_cannotBeCalledByRandomAddress(address randomAddress, address randomCaller) public { + function test_addToken_cannotBeCalledByRandomAddress( + address randomAddress, + address randomCaller, + uint256 randomValue + ) public useRandomToken(randomValue) { if (randomCaller != bridgeOwner) { vm.prank(randomCaller); vm.expectRevert(bytes("Ownable: caller is not the owner")); @@ -377,7 +405,12 @@ contract ExperimentalBridgeTest is Test { uint256 newChainId; address admin; - function test_pause_createNewChain() public { + function test_pause_createNewChain( + uint256 chainId, + uint256 salt, + uint256 randomValue + ) public useRandomToken(randomValue) { + chainId = bound(chainId, 1, type(uint48).max); address deployerAddress = makeAddr("DEPLOYER_ADDRESS"); admin = makeAddr("NEW_CHAIN_ADMIN"); @@ -391,10 +424,10 @@ contract ExperimentalBridgeTest is Test { vm.expectRevert("Pausable: paused"); vm.prank(deployerAddress); bridgeHub.createNewChain({ - _chainId: 1, + _chainId: chainId, _stateTransitionManager: address(mockSTM), _baseToken: address(testToken), - _salt: uint256(123), + _salt: salt, _admin: admin, _initData: bytes("") }); @@ -414,7 +447,12 @@ contract ExperimentalBridgeTest is Test { }); } - function test_RevertWhen_STMNotRegisteredOnCreate(uint256 chainId) public { + function test_RevertWhen_STMNotRegisteredOnCreate( + uint256 chainId, + uint256 salt, + uint256 randomValue + ) public useRandomToken(randomValue) { + chainId = bound(chainId, 1, type(uint48).max); address deployerAddress = makeAddr("DEPLOYER_ADDRESS"); admin = makeAddr("NEW_CHAIN_ADMIN"); @@ -430,13 +468,18 @@ contract ExperimentalBridgeTest is Test { _chainId: chainId, _stateTransitionManager: address(mockSTM), _baseToken: address(testToken), - _salt: uint256(123), + _salt: salt, _admin: admin, _initData: bytes("") }); } - function test_RevertWhen_wrongChainIdOnCreate(uint256 chainId) public { + function test_RevertWhen_wrongChainIdOnCreate( + uint256 chainId, + uint256 salt, + uint256 randomValue + ) public useRandomToken(randomValue) { + chainId = bound(chainId, 1, type(uint48).max); address deployerAddress = makeAddr("DEPLOYER_ADDRESS"); admin = makeAddr("NEW_CHAIN_ADMIN"); @@ -452,7 +495,7 @@ contract ExperimentalBridgeTest is Test { _chainId: chainId, _stateTransitionManager: address(mockSTM), _baseToken: address(testToken), - _salt: uint256(123), + _salt: salt, _admin: admin, _initData: bytes("") }); @@ -464,13 +507,18 @@ contract ExperimentalBridgeTest is Test { _chainId: chainId, _stateTransitionManager: address(mockSTM), _baseToken: address(testToken), - _salt: uint256(123), + _salt: salt, _admin: admin, _initData: bytes("") }); } - function test_RevertWhen_tokenNotRegistered() public { + function test_RevertWhen_tokenNotRegistered( + uint256 chainId, + uint256 salt, + uint256 randomValue + ) public useRandomToken(randomValue) { + chainId = bound(chainId, 1, type(uint48).max); address deployerAddress = makeAddr("DEPLOYER_ADDRESS"); admin = makeAddr("NEW_CHAIN_ADMIN"); @@ -486,16 +534,21 @@ contract ExperimentalBridgeTest is Test { vm.expectRevert("Bridgehub: token not registered"); vm.prank(deployerAddress); bridgeHub.createNewChain({ - _chainId: 1, + _chainId: chainId, _stateTransitionManager: address(mockSTM), _baseToken: address(testToken), - _salt: uint256(123), + _salt: salt, _admin: admin, _initData: bytes("") }); } - function test_RevertWhen_wethBridgeNotSet() public { + function test_RevertWhen_wethBridgeNotSet( + uint256 chainId, + uint256 salt, + uint256 randomValue + ) public useRandomToken(randomValue) { + chainId = bound(chainId, 1, type(uint48).max); address deployerAddress = makeAddr("DEPLOYER_ADDRESS"); admin = makeAddr("NEW_CHAIN_ADMIN"); @@ -512,19 +565,22 @@ contract ExperimentalBridgeTest is Test { vm.expectRevert("Bridgehub: weth bridge not set"); vm.prank(deployerAddress); bridgeHub.createNewChain({ - _chainId: 1, + _chainId: chainId, _stateTransitionManager: address(mockSTM), _baseToken: address(testToken), - _salt: uint256(123), + _salt: salt, _admin: admin, _initData: bytes("") }); } - function test_RevertWhen_chainIdAlreadyRegistered(uint256 chainId) public { + function test_RevertWhen_chainIdAlreadyRegistered( + uint256 chainId, + uint256 salt, + uint256 randomValue + ) public useRandomToken(randomValue) { address deployerAddress = makeAddr("DEPLOYER_ADDRESS"); admin = makeAddr("NEW_CHAIN_ADMIN"); - vm.prank(bridgeOwner); bridgeHub.setPendingAdmin(deployerAddress); vm.prank(deployerAddress); @@ -547,7 +603,7 @@ contract ExperimentalBridgeTest is Test { _chainId: chainId, _stateTransitionManager: address(mockSTM), _baseToken: address(testToken), - _salt: uint256(123), + _salt: salt, _admin: admin, _initData: bytes("") }); @@ -559,11 +615,13 @@ contract ExperimentalBridgeTest is Test { bool isFreezable, bytes4[] memory mockSelectors, address mockInitAddress, - bytes memory mockInitCalldata - ) public { + bytes memory mockInitCalldata, + uint256 salt, + uint256 randomValue + ) public useRandomToken(randomValue) { address deployerAddress = makeAddr("DEPLOYER_ADDRESS"); admin = makeAddr("NEW_CHAIN_ADMIN"); - // Diamond.DiamondCutData memory dcData; + chainId = bound(chainId, 1, type(uint48).max); vm.prank(bridgeOwner); bridgeHub.setPendingAdmin(deployerAddress); @@ -583,13 +641,12 @@ contract ExperimentalBridgeTest is Test { _chainId: chainId, _stateTransitionManager: address(mockSTM), _baseToken: address(testToken), - _salt: uint256(123), + _salt: salt, _admin: admin, _initData: bytes("") }); } - chainId = bound(chainId, 1, type(uint48).max); vm.prank(mockSTM.owner()); bytes memory _newChainInitData = _createNewChainInitData( isFreezable, @@ -823,24 +880,22 @@ contract ExperimentalBridgeTest is Test { vm.clearMockedCalls(); } - function test_requestL2TransactionDirect_ETHCase( + function _prepareETHL2TransactionDirectRequest( uint256 mockChainId, uint256 mockMintValue, - uint256 msgValue, address mockL2Contract, uint256 mockL2Value, bytes memory mockL2Calldata, uint256 mockL2GasLimit, uint256 mockL2GasPerPubdataByteLimit, bytes[] memory mockFactoryDeps, - address mockRefundRecipient, - bytes[] memory mockRefundRecipientBH - ) public { + address randomCaller + ) internal returns (L2TransactionRequestDirect memory l2TxnReqDirect) { if (mockFactoryDeps.length > MAX_NEW_FACTORY_DEPS) { mockFactoryDeps = _restrictArraySize(mockFactoryDeps, MAX_NEW_FACTORY_DEPS); } - L2TransactionRequestDirect memory l2TxnReqDirect = _createMockL2TransactionRequestDirect({ + l2TxnReqDirect = _createMockL2TransactionRequestDirect({ mockChainId: mockChainId, mockMintValue: mockMintValue, mockL2Contract: mockL2Contract, @@ -849,24 +904,20 @@ contract ExperimentalBridgeTest is Test { mockL2GasLimit: mockL2GasLimit, mockL2GasPerPubdataByteLimit: mockL2GasPerPubdataByteLimit, mockFactoryDeps: mockFactoryDeps, - mockRefundRecipient: mockRefundRecipient + mockRefundRecipient: address(0) }); l2TxnReqDirect.chainId = _setUpHyperchainForChainId(l2TxnReqDirect.chainId); assertTrue(!(bridgeHub.baseToken(l2TxnReqDirect.chainId) == ETH_TOKEN_ADDRESS)); - _setUpBaseTokenForChainId(l2TxnReqDirect.chainId, true); + _setUpBaseTokenForChainId(l2TxnReqDirect.chainId, true, address(0)); assertTrue(bridgeHub.baseToken(l2TxnReqDirect.chainId) == ETH_TOKEN_ADDRESS); _setUpSharedBridge(); _setUpSharedBridgeL2(mockChainId); - address randomCaller = makeAddr("RANDOM_CALLER"); - assertTrue(bridgeHub.getHyperchain(l2TxnReqDirect.chainId) == address(mockChainContract)); bytes32 canonicalHash = keccak256(abi.encode("CANONICAL_TX_HASH")); - //BridgehubL2TransactionRequest memory bhL2TxnRequest = - _createBhL2TxnRequest(mockRefundRecipientBH); vm.mockCall( address(mockChainContract), @@ -878,17 +929,71 @@ contract ExperimentalBridgeTest is Test { mockChainContract.setBaseTokenGasMultiplierPrice(uint128(1), uint128(1)); mockChainContract.setBridgeHubAddress(address(bridgeHub)); assertTrue(mockChainContract.getBridgeHubAddress() == address(bridgeHub)); + } - if (msgValue != mockMintValue) { - vm.deal(randomCaller, msgValue); - vm.expectRevert("Bridgehub: msg.value mismatch 1"); - vm.prank(randomCaller); - bridgeHub.requestL2TransactionDirect{value: msgValue}(l2TxnReqDirect); - } + function test_requestL2TransactionDirect_RevertWhen_incorrectETHParams( + uint256 mockChainId, + uint256 mockMintValue, + address mockL2Contract, + uint256 mockL2Value, + uint256 msgValue, + bytes memory mockL2Calldata, + uint256 mockL2GasLimit, + uint256 mockL2GasPerPubdataByteLimit, + bytes[] memory mockFactoryDeps + ) public { + address randomCaller = makeAddr("RANDOM_CALLER"); + vm.assume(msgValue != mockMintValue); + + L2TransactionRequestDirect memory l2TxnReqDirect = _prepareETHL2TransactionDirectRequest({ + mockChainId: mockChainId, + mockMintValue: mockMintValue, + mockL2Contract: mockL2Contract, + mockL2Value: mockL2Value, + mockL2Calldata: mockL2Calldata, + mockL2GasLimit: mockL2GasLimit, + mockL2GasPerPubdataByteLimit: mockL2GasPerPubdataByteLimit, + mockFactoryDeps: mockFactoryDeps, + randomCaller: randomCaller + }); + + vm.deal(randomCaller, msgValue); + vm.expectRevert("Bridgehub: msg.value mismatch 1"); + vm.prank(randomCaller); + bridgeHub.requestL2TransactionDirect{value: msgValue}(l2TxnReqDirect); + } + + function test_requestL2TransactionDirect_ETHCase( + uint256 mockChainId, + uint256 mockMintValue, + address mockL2Contract, + uint256 mockL2Value, + bytes memory mockL2Calldata, + uint256 mockL2GasLimit, + uint256 mockL2GasPerPubdataByteLimit, + bytes[] memory mockFactoryDeps, + uint256 gasPrice + ) public { + address randomCaller = makeAddr("RANDOM_CALLER"); + mockChainId = bound(mockChainId, 1, type(uint48).max); + + L2TransactionRequestDirect memory l2TxnReqDirect = _prepareETHL2TransactionDirectRequest({ + mockChainId: mockChainId, + mockMintValue: mockMintValue, + mockL2Contract: mockL2Contract, + mockL2Value: mockL2Value, + mockL2Calldata: mockL2Calldata, + mockL2GasLimit: mockL2GasLimit, + mockL2GasPerPubdataByteLimit: mockL2GasPerPubdataByteLimit, + mockFactoryDeps: mockFactoryDeps, + randomCaller: randomCaller + }); vm.deal(randomCaller, l2TxnReqDirect.mintValue); - vm.txGasPrice(0.05 ether); + gasPrice = bound(gasPrice, 1_000, 50_000_000); + vm.txGasPrice(gasPrice * 1 gwei); vm.prank(randomCaller); + bytes32 canonicalHash = keccak256(abi.encode("CANONICAL_TX_HASH")); bytes32 resultantHash = bridgeHub.requestL2TransactionDirect{value: randomCaller.balance}(l2TxnReqDirect); assertTrue(resultantHash == canonicalHash); @@ -903,8 +1008,12 @@ contract ExperimentalBridgeTest is Test { uint256 mockL2GasLimit, uint256 mockL2GasPerPubdataByteLimit, bytes[] memory mockFactoryDeps, - address mockRefundRecipient - ) public { + uint256 gasPrice, + uint256 randomValue + ) public useRandomToken(randomValue) { + address randomCaller = makeAddr("RANDOM_CALLER"); + mockChainId = bound(mockChainId, 1, type(uint48).max); + if (mockFactoryDeps.length > MAX_NEW_FACTORY_DEPS) { mockFactoryDeps = _restrictArraySize(mockFactoryDeps, MAX_NEW_FACTORY_DEPS); } @@ -918,12 +1027,12 @@ contract ExperimentalBridgeTest is Test { mockL2GasLimit: mockL2GasLimit, mockL2GasPerPubdataByteLimit: mockL2GasPerPubdataByteLimit, mockFactoryDeps: mockFactoryDeps, - mockRefundRecipient: mockRefundRecipient + mockRefundRecipient: address(0) }); l2TxnReqDirect.chainId = _setUpHyperchainForChainId(l2TxnReqDirect.chainId); - _setUpBaseTokenForChainId(l2TxnReqDirect.chainId, false); + _setUpBaseTokenForChainId(l2TxnReqDirect.chainId, false, address(testToken)); _setUpSharedBridge(); _setUpSharedBridgeL2(mockChainId); @@ -941,11 +1050,10 @@ contract ExperimentalBridgeTest is Test { mockChainContract.setBridgeHubAddress(address(bridgeHub)); assertTrue(mockChainContract.getBridgeHubAddress() == address(bridgeHub)); - vm.txGasPrice(0.05 ether); + gasPrice = bound(gasPrice, 1_000, 50_000_000); + vm.txGasPrice(gasPrice * 1 gwei); - address randomCaller = makeAddr("RANDOM_CALLER"); vm.deal(randomCaller, 1 ether); - vm.prank(randomCaller); vm.expectRevert("Bridgehub: non-eth bridge with msg.value"); bytes32 resultantHash = bridgeHub.requestL2TransactionDirect{value: randomCaller.balance}(l2TxnReqDirect); @@ -975,6 +1083,8 @@ contract ExperimentalBridgeTest is Test { bytes memory secondBridgeCalldata, bytes32 magicValue ) public { + chainId = bound(chainId, 1, type(uint48).max); + L2TransactionRequestTwoBridgesOuter memory l2TxnReq2BridgeOut = _createMockL2TransactionRequestTwoBridgesOuter({ chainId: chainId, mintValue: mintValue, @@ -988,7 +1098,7 @@ contract ExperimentalBridgeTest is Test { l2TxnReq2BridgeOut.chainId = _setUpHyperchainForChainId(l2TxnReq2BridgeOut.chainId); - _setUpBaseTokenForChainId(l2TxnReq2BridgeOut.chainId, true); + _setUpBaseTokenForChainId(l2TxnReq2BridgeOut.chainId, true, address(0)); assertTrue(bridgeHub.baseToken(l2TxnReq2BridgeOut.chainId) == ETH_TOKEN_ADDRESS); _setUpSharedBridge(); @@ -1033,6 +1143,8 @@ contract ExperimentalBridgeTest is Test { uint160 secondBridgeAddressValue, bytes memory secondBridgeCalldata ) public { + chainId = bound(chainId, 1, type(uint48).max); + L2TransactionRequestTwoBridgesOuter memory l2TxnReq2BridgeOut = _createMockL2TransactionRequestTwoBridgesOuter({ chainId: chainId, mintValue: mintValue, @@ -1046,7 +1158,7 @@ contract ExperimentalBridgeTest is Test { l2TxnReq2BridgeOut.chainId = _setUpHyperchainForChainId(l2TxnReq2BridgeOut.chainId); - _setUpBaseTokenForChainId(l2TxnReq2BridgeOut.chainId, true); + _setUpBaseTokenForChainId(l2TxnReq2BridgeOut.chainId, true, address(0)); assertTrue(bridgeHub.baseToken(l2TxnReq2BridgeOut.chainId) == ETH_TOKEN_ADDRESS); _setUpSharedBridge(); @@ -1103,9 +1215,10 @@ contract ExperimentalBridgeTest is Test { uint256 l2Value, uint256 l2GasLimit, uint256 l2GasPerPubdataByteLimit, - address refundRecipient, - address l2Receiver - ) public { + address l2Receiver, + uint256 randomValue + ) public useRandomToken(randomValue) { + // create another token, to avoid base token TestnetERC20Token erc20Token = new TestnetERC20Token("ZKESTT", "ZkSync ERC Test Token", 18); address erc20TokenAddress = address(erc20Token); l2Value = bound(l2Value, 1, type(uint256).max); @@ -1119,25 +1232,22 @@ contract ExperimentalBridgeTest is Test { l2Value: 0, // not used l2GasLimit: l2GasLimit, l2GasPerPubdataByteLimit: l2GasPerPubdataByteLimit, - refundRecipient: refundRecipient, + refundRecipient: address(0), secondBridgeValue: 0, // not used cause we are using ERC20 secondBridgeCalldata: secondBridgeCalldata }); - _setUpBaseTokenForChainId(l2TxnReq2BridgeOut.chainId, false); - assertTrue(bridgeHub.baseToken(l2TxnReq2BridgeOut.chainId) == address(testToken)); + address randomCaller = makeAddr("RANDOM_CALLER"); + bytes32 canonicalHash = keccak256(abi.encode("CANONICAL_TX_HASH")); + _setUpBaseTokenForChainId(l2TxnReq2BridgeOut.chainId, false, address(testToken)); + assertTrue(bridgeHub.baseToken(l2TxnReq2BridgeOut.chainId) == address(testToken)); _setUpSharedBridge(); - _setUpSharedBridgeL2(chainId); + _setUpSharedBridgeL2(chainId); assertTrue(bridgeHub.getHyperchain(l2TxnReq2BridgeOut.chainId) == address(mockChainContract)); - - address randomCaller = makeAddr("RANDOM_CALLER"); - mockChainContract.setBridgeHubAddress(address(bridgeHub)); - bytes32 canonicalHash = keccak256(abi.encode("CANONICAL_TX_HASH")); - vm.mockCall( address(mockChainContract), abi.encodeWithSelector(mockChainContract.bridgehubRequestL2Transaction.selector), @@ -1181,8 +1291,9 @@ contract ExperimentalBridgeTest is Test { uint256 l2GasPerPubdataByteLimit, address refundRecipient, uint256 secondBridgeValue, - address l2Receiver - ) public { + address l2Receiver, + uint256 randomValue + ) public useRandomToken(randomValue) { secondBridgeValue = bound(secondBridgeValue, 1, type(uint256).max); bytes memory secondBridgeCalldata = abi.encode(ETH_TOKEN_ADDRESS, 0, l2Receiver); @@ -1199,7 +1310,7 @@ contract ExperimentalBridgeTest is Test { secondBridgeCalldata: secondBridgeCalldata }); - _setUpBaseTokenForChainId(l2TxnReq2BridgeOut.chainId, false); + _setUpBaseTokenForChainId(l2TxnReq2BridgeOut.chainId, false, address(testToken)); assertTrue(bridgeHub.baseToken(l2TxnReq2BridgeOut.chainId) == address(testToken)); _setUpSharedBridge(); @@ -1345,7 +1456,7 @@ contract ExperimentalBridgeTest is Test { } function _setUpHyperchainForChainId(uint256 mockChainId) internal returns (uint256 mockChainIdInRange) { - mockChainId = bound(mockChainId, 2, type(uint48).max); + mockChainId = bound(mockChainId, 1, type(uint48).max); mockChainIdInRange = mockChainId; vm.prank(bridgeOwner); bridgeHub.addStateTransitionManager(address(mockSTM)); @@ -1363,8 +1474,8 @@ contract ExperimentalBridgeTest is Test { mockSTM.setHyperchain(mockChainId, address(mockChainContract)); } - function _setUpBaseTokenForChainId(uint256 mockChainId, bool tokenIsETH) internal { - address baseToken = tokenIsETH ? ETH_TOKEN_ADDRESS : address(testToken); + function _setUpBaseTokenForChainId(uint256 mockChainId, bool tokenIsETH, address token) internal { + address baseToken = tokenIsETH ? ETH_TOKEN_ADDRESS : token; stdstore.target(address(bridgeHub)).sig("baseToken(uint256)").with_key(mockChainId).checked_write(baseToken); } @@ -1375,6 +1486,8 @@ contract ExperimentalBridgeTest is Test { } function _setUpSharedBridgeL2(uint256 _chainId) internal { + _chainId = bound(_chainId, 1, type(uint48).max); + vm.prank(bridgeOwner); sharedBridge.initializeChainGovernance(_chainId, mockL2Contract);