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

Hyperchains Integration #439

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
4ba46a0
create new chain
tommysr Apr 30, 2024
b163342
spawn multiple hyperchains
tommysr May 1, 2024
a14d5f3
test calls to bridgehub changes shared bridge balances
tommysr May 1, 2024
519992d
feat: Code refactor
tejks May 1, 2024
608fb72
feat: Hyperchain direct deposit test with ETH token
tejks May 1, 2024
47b2374
two bridges tx with non base token
tommysr May 2, 2024
9e68caa
chore: Compile yul files in parallel within a single folder (#271)
popzxc Mar 22, 2024
5fdbbfb
ci: Make build release manual triggered only (#287)
onyxet Mar 26, 2024
5bcb5f3
feat: Use scripts deploy
tejks May 6, 2024
4cc0216
hyperchains register script
tommysr May 7, 2024
c441521
fix: Broadcast with bridgehub owner
tejks May 7, 2024
d3e9089
generate config for hyperchains deployment
tommysr May 7, 2024
c48fe6d
feat: Implement DeployErc20 and DeployL1 scripts to tests
tejks May 7, 2024
b5e0d20
deploy hyperchain success
tommysr May 7, 2024
3daa43f
test hyperchainconfig save
tommysr May 7, 2024
fb5ce41
feat: New files structure
tejks May 7, 2024
629eddb
feat: Adding new hyperchains
tejks May 7, 2024
f1c798c
feat: Integration of the new deployer with tests
tejks May 7, 2024
8d24a6c
cleanup
tommysr May 8, 2024
bfc9040
remove unused imports
tommysr May 8, 2024
60be529
feat: Bump forge-std to v1.8.2
tejks May 9, 2024
81626a6
fix: Move the deployment scripts to the test folder
tejks May 9, 2024
98bad3f
fix: Unused imports
tejks May 9, 2024
588dc4d
fix: Overwriting the token address
tejks May 6, 2024
b8780ed
feat: Add SharedBridge balance setter
tejks May 9, 2024
fd9892a
feat: Withdraw test
tejks May 9, 2024
f5fc00b
structure for invariant testing
tommysr May 8, 2024
d1b4d4e
add base token invariant test
tommysr May 9, 2024
87d6410
use valid requestl2transaction
tommysr May 9, 2024
9e7b466
get priority tx data from logs and read into struct
tommysr May 9, 2024
cb28827
mocked l2 contract, send eth on l2 log
tommysr May 9, 2024
d1f8303
add token address to mock
tommysr May 10, 2024
c0aab8e
base token deposit with appropriate transfer
tommysr May 10, 2024
121dfb1
feat: Move invariant tests to the separate file
tejks May 10, 2024
722e0af
two bridges deposit with eliminated mocking
tommysr May 10, 2024
8d16a8b
add verifier implementation to config
tommysr May 13, 2024
caa1608
deposit two bridges to erc20 chain
tommysr May 14, 2024
ba819c8
feat: Add IsWithdrawalFinalized setter for SharedBridge
tejks May 11, 2024
3d1aa58
feat: Use ERC20 withdraw to invariant tests
tejks May 11, 2024
589b612
feat: Withdrawal invariant test of the ETH token
tejks May 13, 2024
c7f6d2c
fixed lint errors
tommysr May 15, 2024
0834d38
unused vars removed
tommysr May 15, 2024
272376e
revert changes
neotheprogramist May 16, 2024
aaa587f
Add comments and fixes
tejks May 20, 2024
d37a354
fix: Suggestions from comments
tejks May 20, 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
2 changes: 1 addition & 1 deletion l1-contracts-foundry/script/DeployErc20.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ contract DeployErc20Script is Script {
mint: token.mint
});
console.log("Token deployed at:", tokenAddress);
token.addr = tokenAddress;
config.tokens[i].addr = tokenAddress;
neotheprogramist marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
9 changes: 9 additions & 0 deletions l1-contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,13 @@ ignored_warnings_from = [
"contracts/dev-contracts"
]

fs_permissions = [
{ access = "read", path = "./test/foundry/integration/deploy-scripts/script-config/" },
{ access = "read-write", path = "./test/foundry/integration/deploy-scripts/script-out/" },
{ access = "read", path = "./out" }
]

# See more config options https://github.com/foundry-rs/foundry/tree/master/crates/config

[invariant] #invariant section
fail_on_revert = false
313 changes: 313 additions & 0 deletions l1-contracts/test/foundry/integration/BaseIntegrationTests.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol";
import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol";

import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol";

import {L1ContractDeployer} from "./_SharedL1ContractDeployer.t.sol";
import {TokenDeployer} from "./_SharedTokenDeployer.t.sol";
import {HyperchainDeployer} from "./_SharedHyperchainDeployer.t.sol";
import {L2TxMocker} from "./_SharedL2TxMocker.t.sol";

import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol";
import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddresses.sol";
import {L2Message} from "contracts/common/Messaging.sol";
import {IMailbox} from "contracts/state-transition/chain-interfaces/IMailbox.sol";
import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol";

contract BaseIntegrationTests is L1ContractDeployer, HyperchainDeployer, TokenDeployer, L2TxMocker {
address alice;
address bob;

TestnetERC20Token baseToken;

function setUp() public {
_deployL1Contracts();
_deployTokens();

_registerNewTokens(tokens);

_addNewHyperchainToDeploy("hyperchain1", ETH_TOKEN_ADDRESS);
_addNewHyperchainToDeploy("hyperchain2", ETH_TOKEN_ADDRESS);
_addNewHyperchainToDeploy("hyperchain3", tokens[0]);
_addNewHyperchainToDeploy("hyperchain4", tokens[0]);
_deployHyperchains();

alice = makeAddr("alice");
bob = makeAddr("bob");
baseToken = TestnetERC20Token(tokens[0]);
}

function test_hyperchainTokenDirectDeposit_Eth() public {
vm.txGasPrice(0.05 ether);
vm.deal(alice, 1 ether);
vm.deal(bob, 1 ether);
Comment on lines +44 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These numbers can be fuzzed.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are fuzzed in invariant tests. If invariant tests are enough, we can get rid of these instead of fuzzing them.


uint256 firstChainId = hyperchainIds[0];
uint256 secondChainId = hyperchainIds[1];

assertTrue(getHyperchainBaseToken(firstChainId) == ETH_TOKEN_ADDRESS);
assertTrue(getHyperchainBaseToken(secondChainId) == ETH_TOKEN_ADDRESS);

L2TransactionRequestDirect memory aliceRequest = _createMockL2TransactionRequestDirect(
firstChainId,
1 ether,
0.1 ether
);
L2TransactionRequestDirect memory bobRequest = _createMockL2TransactionRequestDirect(
secondChainId,
1 ether,
0.1 ether
);

bytes32 canonicalHash = keccak256(abi.encode("CANONICAL_TX_HASH"));
address firstHyperChainAddress = getHyperchainAddress(firstChainId);
address secondHyperChainAddress = getHyperchainAddress(secondChainId);

vm.mockCall(
firstHyperChainAddress,
abi.encodeWithSelector(MailboxFacet.bridgehubRequestL2Transaction.selector),
abi.encode(canonicalHash)
);

vm.mockCall(
neotheprogramist marked this conversation as resolved.
Show resolved Hide resolved
secondHyperChainAddress,
abi.encodeWithSelector(MailboxFacet.bridgehubRequestL2Transaction.selector),
abi.encode(canonicalHash)
);

vm.prank(alice);
bytes32 resultantHash = bridgeHub.requestL2TransactionDirect{value: alice.balance}(aliceRequest);
assertEq(canonicalHash, resultantHash);

vm.prank(bob);
bytes32 resultantHash2 = bridgeHub.requestL2TransactionDirect{value: bob.balance}(bobRequest);
assertEq(canonicalHash, resultantHash2);

assertEq(alice.balance, 0);
assertEq(bob.balance, 0);

assertEq(address(sharedBridge).balance, 2 ether);
assertEq(sharedBridge.chainBalance(firstChainId, ETH_TOKEN_ADDRESS), 1 ether);
assertEq(sharedBridge.chainBalance(secondChainId, ETH_TOKEN_ADDRESS), 1 ether);
}

function test_hyperchainTokenDirectDeposit_NonEth() public {
uint256 mockMintValue = 1 ether;

vm.txGasPrice(0.05 ether);
vm.deal(alice, 1 ether);
vm.deal(bob, 1 ether);

baseToken.mint(alice, mockMintValue);
baseToken.mint(bob, mockMintValue);

assertEq(baseToken.balanceOf(alice), mockMintValue);
assertEq(baseToken.balanceOf(bob), mockMintValue);

uint256 firstChainId = hyperchainIds[2];
uint256 secondChainId = hyperchainIds[3];

assertTrue(getHyperchainBaseToken(firstChainId) == address(baseToken));
assertTrue(getHyperchainBaseToken(secondChainId) == address(baseToken));

L2TransactionRequestDirect memory aliceRequest = _createMockL2TransactionRequestDirect(
firstChainId,
1 ether,
0.1 ether
);
L2TransactionRequestDirect memory bobRequest = _createMockL2TransactionRequestDirect(
secondChainId,
1 ether,
0.1 ether
);

bytes32 canonicalHash = keccak256(abi.encode("CANONICAL_TX_HASH"));
address firstHyperChainAddress = getHyperchainAddress(firstChainId);
address secondHyperChainAddress = getHyperchainAddress(secondChainId);

vm.startPrank(alice);
assertEq(baseToken.balanceOf(alice), mockMintValue);
baseToken.approve(address(sharedBridge), mockMintValue);
vm.stopPrank();

vm.startPrank(bob);
assertEq(baseToken.balanceOf(bob), mockMintValue);
baseToken.approve(address(sharedBridge), mockMintValue);
vm.stopPrank();

vm.mockCall(
firstHyperChainAddress,
abi.encodeWithSelector(MailboxFacet.bridgehubRequestL2Transaction.selector),
abi.encode(canonicalHash)
);

vm.mockCall(
secondHyperChainAddress,
abi.encodeWithSelector(MailboxFacet.bridgehubRequestL2Transaction.selector),
abi.encode(canonicalHash)
);

vm.prank(alice);
bytes32 resultantHash = bridgeHub.requestL2TransactionDirect(aliceRequest);
assertEq(canonicalHash, resultantHash);

vm.prank(bob);
bytes32 resultantHash2 = bridgeHub.requestL2TransactionDirect(bobRequest);
assertEq(canonicalHash, resultantHash2);

// check if the balances of alice and bob are 0
assertEq(baseToken.balanceOf(alice), 0);
assertEq(baseToken.balanceOf(bob), 0);

// check if the shared bridge has the correct balances
assertEq(baseToken.balanceOf(address(sharedBridge)), 2 ether);

// check if the shared bridge has the correct balances for each chain
assertEq(sharedBridge.chainBalance(firstChainId, address(baseToken)), mockMintValue);
assertEq(sharedBridge.chainBalance(secondChainId, address(baseToken)), mockMintValue);
}

function test_hyperchainDepositNonBaseWithBaseETH() public {
uint256 aliceDepositAmount = 1 ether;
uint256 bobDepositAmount = 1.5 ether;

uint256 mintValue = 2 ether;
uint256 l2Value = 10000;
address l2Receiver = makeAddr("receiver");
address tokenAddress = address(baseToken);

uint256 firstChainId = hyperchainIds[0];
uint256 secondChainId = hyperchainIds[1];

address firstHyperChainAddress = getHyperchainAddress(firstChainId);
address secondHyperChainAddress = getHyperchainAddress(secondChainId);

assertTrue(getHyperchainBaseToken(firstChainId) == ETH_TOKEN_ADDRESS);
assertTrue(getHyperchainBaseToken(secondChainId) == ETH_TOKEN_ADDRESS);

_registerL2SharedBridge(firstChainId, mockL2SharedBridge);
_registerL2SharedBridge(secondChainId, mockL2SharedBridge);

vm.txGasPrice(0.05 ether);
vm.deal(alice, mintValue);
vm.deal(bob, mintValue);

assertEq(alice.balance, mintValue);
assertEq(bob.balance, mintValue);

baseToken.mint(alice, aliceDepositAmount);
baseToken.mint(bob, bobDepositAmount);

assertEq(baseToken.balanceOf(alice), aliceDepositAmount);
assertEq(baseToken.balanceOf(bob), bobDepositAmount);

vm.prank(alice);
baseToken.approve(address(sharedBridge), aliceDepositAmount);

vm.prank(bob);
baseToken.approve(address(sharedBridge), bobDepositAmount);

bytes32 canonicalHash = keccak256(abi.encode("CANONICAL_TX_HASH"));
{
bytes memory aliceSecondBridgeCalldata = abi.encode(tokenAddress, aliceDepositAmount, l2Receiver);
L2TransactionRequestTwoBridgesOuter memory aliceRequest = _createMockL2TransactionRequestTwoBridges({
_chainId: firstChainId,
_mintValue: mintValue,
_secondBridgeValue: 0,
_l2Value: l2Value,
_secondBridgeAddress: address(sharedBridge),
_secondBridgeCalldata: aliceSecondBridgeCalldata
});

vm.mockCall(
firstHyperChainAddress,
abi.encodeWithSelector(MailboxFacet.bridgehubRequestL2Transaction.selector),
abi.encode(canonicalHash)
);

vm.prank(alice);
bytes32 resultantHash = bridgeHub.requestL2TransactionTwoBridges{value: mintValue}(aliceRequest);
assertEq(canonicalHash, resultantHash);
}

{
bytes memory bobSecondBridgeCalldata = abi.encode(tokenAddress, bobDepositAmount, l2Receiver);
L2TransactionRequestTwoBridgesOuter memory bobRequest = _createMockL2TransactionRequestTwoBridges({
_chainId: secondChainId,
_mintValue: mintValue,
_secondBridgeValue: 0,
_l2Value: l2Value,
_secondBridgeAddress: address(sharedBridge),
_secondBridgeCalldata: bobSecondBridgeCalldata
});

vm.mockCall(
secondHyperChainAddress,
abi.encodeWithSelector(MailboxFacet.bridgehubRequestL2Transaction.selector),
abi.encode(canonicalHash)
);

vm.prank(bob);
bytes32 resultantHash2 = bridgeHub.requestL2TransactionTwoBridges{value: mintValue}(bobRequest);
assertEq(canonicalHash, resultantHash2);
}

assertEq(alice.balance, 0);
assertEq(bob.balance, 0);
assertEq(address(sharedBridge).balance, 2 * mintValue);
assertEq(sharedBridge.chainBalance(firstChainId, ETH_TOKEN_ADDRESS), mintValue);
assertEq(sharedBridge.chainBalance(secondChainId, ETH_TOKEN_ADDRESS), mintValue);
assertEq(sharedBridge.chainBalance(firstChainId, tokenAddress), aliceDepositAmount);
assertEq(sharedBridge.chainBalance(secondChainId, tokenAddress), bobDepositAmount);
assertEq(baseToken.balanceOf(address(sharedBridge)), aliceDepositAmount + bobDepositAmount);
}

function test_hyperchainFinalizeWithdrawal() public {
uint256 amountToWithdraw = 1 ether;
_setSharedBridgeChainBalance(10, ETH_TOKEN_ADDRESS, amountToWithdraw);

uint256 l2BatchNumber = uint256(uint160(makeAddr("l2BatchNumber")));
uint256 l2MessageIndex = uint256(uint160(makeAddr("l2MessageIndex")));
uint16 l2TxNumberInBatch = uint16(uint160(makeAddr("l2TxNumberInBatch")));
bytes32[] memory merkleProof = new bytes32[](1);
uint256 chainId = 10;

vm.deal(address(sharedBridge), amountToWithdraw);

bytes memory message = abi.encodePacked(IMailbox.finalizeEthWithdrawal.selector, alice, amountToWithdraw);
L2Message memory l2ToL1Message = L2Message({
txNumberInBatch: l2TxNumberInBatch,
sender: L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR,
data: message
});

vm.mockCall(
bridgehubProxyAddress,
// solhint-disable-next-line func-named-parameters
abi.encodeWithSelector(
IBridgehub.proveL2MessageInclusion.selector,
chainId,
l2BatchNumber,
l2MessageIndex,
l2ToL1Message,
merkleProof
),
abi.encode(true)
);

sharedBridge.finalizeWithdrawal({
_chainId: chainId,
_l2BatchNumber: l2BatchNumber,
_l2MessageIndex: l2MessageIndex,
_l2TxNumberInBatch: l2TxNumberInBatch,
_message: message,
_merkleProof: merkleProof
});

assertEq(alice.balance, amountToWithdraw);
assertEq(address(sharedBridge).balance, 0);
}
}
Loading
Loading