diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index 1db89059..29053734 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -57,7 +57,7 @@ contract ForkableBridge is address to ) external onlyAfterForking { require(_hardAssetManager == msg.sender, "Not authorized"); - require(to == children[0] || to == children[1], "Invalid to address"); + require(to == children[0] || to == children[1], "Invalid to"); IERC20(token).transfer(to, amount); } @@ -79,9 +79,8 @@ contract ForkableBridge is if (parentContract != address(0)) { // Also check the parent contract for claims if it is set return ForkableBridge(parentContract).isClaimed(index); - } else { - return false; } + return false; } /** @@ -116,7 +115,7 @@ contract ForkableBridge is bytes calldata metadata, address destinationAddress ) external onlyParent { - require(originNetwork != networkID, "Token is from this network"); + require(originNetwork != networkID, "wrong Token"); _issueBridgedTokens( originNetwork, token, @@ -127,16 +126,18 @@ contract ForkableBridge is } // @inheritdoc IForkableBridge - function splitTokenIntoChildTokens( + function splitTokenIntoChildToken( address token, - uint256 amount - ) external onlyAfterForking { - BridgeAssetOperations.splitTokenIntoChildTokens( + uint256 amount, + bool mintSecondChildAsWell + ) public onlyAfterForking { + BridgeAssetOperations.splitTokenIntoChildToken( token, amount, - wrappedTokenToTokenInfo[token], children[0], - children[1] + // If the second token should not be minted - to safe gas or since its broken - we pass address(0) + mintSecondChildAsWell ? children[1] : address(0), + wrappedTokenToTokenInfo[token] ); } @@ -171,17 +172,20 @@ contract ForkableBridge is * @dev Allows the forkmanager to take out the forkonomic tokens * and send them to the children-bridge contracts * Notice that forkonomic tokens are special, as they their main contract - * is on L1, but they are still forkable tokens as all the tokens from L2. + * is on L1, but they are still forkable tokens as all the tokens from + * @param useFirstChild boolean indicating for which child the operation should be run */ - function sendForkonomicTokensToChildren() - public - onlyForkManger - onlyAfterForking - { - BridgeAssetOperations.sendForkonomicTokensToChildren( + function sendForkonomicTokensToChild( + uint256 amount, + bool useFirstChild, + bool useChildTokenAllowance + ) public onlyForkManger onlyAfterForking { + BridgeAssetOperations.sendForkonomicTokensToChild( gasTokenAddress, - children[0], - children[1] + amount, + useFirstChild ? children[0] : children[1], + useFirstChild, + useChildTokenAllowance ); } diff --git a/contracts/ForkingManager.sol b/contracts/ForkingManager.sol index 99e319de..21b2fe55 100644 --- a/contracts/ForkingManager.sol +++ b/contracts/ForkingManager.sol @@ -102,38 +102,32 @@ contract ForkingManager is IForkingManager, ForkableStructure { /** * @dev function that executes a fork proposal */ - function executeFork() external onlyBeforeForking { + function executeFork1() external onlyBeforeForking { require( executionTimeForProposal != 0 && // solhint-disable-next-line not-rely-on-time executionTimeForProposal <= block.timestamp, "ForkingManager: fork not ready" ); + NewImplementations memory newImplementations = proposedImplementations; // Create the children of each contract NewInstances memory newInstances; - ( - newInstances.forkingManager.one, - newInstances.forkingManager.two - ) = _createChildren(newImplementations.forkingManagerImplementation); - (newInstances.bridge.one, newInstances.bridge.two) = IForkableBridge( - bridge - ).createChildren(newImplementations.bridgeImplementation); - (newInstances.zkEVM.one, newInstances.zkEVM.two) = IForkableZkEVM(zkEVM) - .createChildren(newImplementations.zkEVMImplementation); - ( - newInstances.forkonomicToken.one, - newInstances.forkonomicToken.two - ) = IForkonomicToken(forkonomicToken).createChildren( - newImplementations.forkonomicTokenImplementation + (newInstances.forkingManager.one, ) = _createChildren( + newImplementations.forkingManagerImplementation ); - ( - newInstances.globalExitRoot.one, - newInstances.globalExitRoot.two - ) = IForkableGlobalExitRoot(globalExitRoot).createChildren( - newImplementations.globalExitRootImplementation + (newInstances.bridge.one, ) = IForkableBridge(bridge).createChildren( + newImplementations.bridgeImplementation ); + (newInstances.zkEVM.one, ) = IForkableZkEVM(zkEVM).createChildren( + newImplementations.zkEVMImplementation + ); + (newInstances.forkonomicToken.one, ) = IForkonomicToken(forkonomicToken) + .createChildren(newImplementations.forkonomicTokenImplementation); + (newInstances.globalExitRoot.one, ) = IForkableGlobalExitRoot( + globalExitRoot + ).createChildren(newImplementations.globalExitRootImplementation); // Initialize the zkEVM contracts IPolygonZkEVM.InitializePackedParameters @@ -175,11 +169,106 @@ contract ForkingManager is IForkingManager, ForkableStructure { IForkableZkEVM(zkEVM).rollupVerifier(), IPolygonZkEVMBridge(newInstances.bridge.one) ); - initializePackedParameters.chainID = ChainIdManager(chainIdManager) - .getNextUsableChainId(); - initializePackedParameters.forkID = newImplementations.forkID > 0 - ? newImplementations.forkID - : IPolygonZkEVM(zkEVM).forkID(); + } + + // Initialize the tokens + IForkonomicToken(newInstances.forkonomicToken.one).initialize( + newInstances.forkingManager.one, + forkonomicToken, + address(this), + string.concat(IERC20Metadata(forkonomicToken).name(), "0"), + IERC20Metadata(forkonomicToken).symbol() + ); + + bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] + memory depositBranch = IForkableBridge(bridge).getBranch(); + + //Initialize the bridge contracts + IForkableBridge(newInstances.bridge.one).initialize( + newInstances.forkingManager.one, + bridge, + 0, // network identifiers will always be 0 on mainnet and 1 on L2 + IBasePolygonZkEVMGlobalExitRoot(newInstances.globalExitRoot.one), + address(newInstances.zkEVM.one), + address(newInstances.forkonomicToken.one), + false, + IForkableBridge(bridge).getHardAssetManager(), + IForkableBridge(bridge).getLastUpdatedDepositCount(), + depositBranch + ); + + //Initialize the forking manager contracts + IForkingManager(newInstances.forkingManager.one).initialize( + newInstances.zkEVM.one, + newInstances.bridge.one, + newInstances.forkonomicToken.one, + address(this), + newInstances.globalExitRoot.one, + arbitrationFee, + chainIdManager + ); + + //Initialize the global exit root contracts + IForkableGlobalExitRoot(newInstances.globalExitRoot.one).initialize( + newInstances.forkingManager.one, + globalExitRoot, + newInstances.zkEVM.one, + newInstances.bridge.one + ); + } + + /** + * @dev function that creates the second fork for the fork proposal + */ + function executeFork2() external onlyBeforeCreatingChild2 { + require( + executionTimeForProposal != 0 && + // solhint-disable-next-line not-rely-on-time + executionTimeForProposal <= block.timestamp, + "ForkingManager: fork not ready" + ); + NewImplementations memory newImplementations = proposedImplementations; + + // Create the children of each contract + NewInstances memory newInstances; + (, newInstances.forkingManager.two) = getChildren(); + (, newInstances.bridge.two) = IForkableBridge(bridge).getChildren(); + (, newInstances.zkEVM.two) = IForkableZkEVM(zkEVM).getChildren(); + (, newInstances.forkonomicToken.two) = IForkonomicToken(forkonomicToken) + .getChildren(); + (, newInstances.globalExitRoot.two) = IForkableGlobalExitRoot( + globalExitRoot + ).getChildren(); + + // Initialize the zkEVM contracts + IPolygonZkEVM.InitializePackedParameters + memory initializePackedParameters; + + { + // retrieve some information from the zkEVM contract + bytes32 genesisRoot = IPolygonZkEVM(zkEVM).batchNumToStateRoot( + IPolygonZkEVM(zkEVM).lastVerifiedBatch() + ); + // the following variables could be used to save gas, but it requires via-ir in the compiler settings + string memory trustedSequencerURL = IPolygonZkEVM(zkEVM) + .trustedSequencerURL(); + string memory networkName = IPolygonZkEVM(zkEVM).networkName(); + // string memory version = "0.1.0"; // Todo: get version from zkEVM, currently only emitted as event + initializePackedParameters = IPolygonZkEVM + .InitializePackedParameters({ + admin: IPolygonZkEVM(zkEVM).admin(), + trustedSequencer: IPolygonZkEVM(zkEVM).trustedSequencer(), + pendingStateTimeout: IPolygonZkEVM(zkEVM) + .pendingStateTimeout(), + trustedAggregator: IPolygonZkEVM(zkEVM).trustedAggregator(), + trustedAggregatorTimeout: IPolygonZkEVM(zkEVM) + .trustedAggregatorTimeout(), + chainID: ChainIdManager(chainIdManager) + .getNextUsableChainId(), + forkID: newImplementations.forkID > 0 + ? newImplementations.forkID + : IPolygonZkEVM(zkEVM).forkID() + }); IForkableZkEVM(newInstances.zkEVM.two).initialize( newInstances.forkingManager.two, zkEVM, @@ -196,13 +285,6 @@ contract ForkingManager is IForkingManager, ForkableStructure { } // Initialize the tokens - IForkonomicToken(newInstances.forkonomicToken.one).initialize( - newInstances.forkingManager.one, - forkonomicToken, - address(this), - string.concat(IERC20Metadata(forkonomicToken).name(), "0"), - IERC20Metadata(forkonomicToken).symbol() - ); IForkonomicToken(newInstances.forkonomicToken.two).initialize( newInstances.forkingManager.two, forkonomicToken, @@ -215,18 +297,6 @@ contract ForkingManager is IForkingManager, ForkableStructure { memory depositBranch = IForkableBridge(bridge).getBranch(); //Initialize the bridge contracts - IForkableBridge(newInstances.bridge.one).initialize( - newInstances.forkingManager.one, - bridge, - 0, // network identifiers will always be 0 on mainnet and 1 on L2 - IBasePolygonZkEVMGlobalExitRoot(newInstances.globalExitRoot.one), - address(newInstances.zkEVM.one), - address(newInstances.forkonomicToken.one), - false, - IForkableBridge(bridge).getHardAssetManager(), - IForkableBridge(bridge).getLastUpdatedDepositCount(), - depositBranch - ); IForkableBridge(newInstances.bridge.two).initialize( newInstances.forkingManager.two, bridge, @@ -241,15 +311,6 @@ contract ForkingManager is IForkingManager, ForkableStructure { ); //Initialize the forking manager contracts - IForkingManager(newInstances.forkingManager.one).initialize( - newInstances.zkEVM.one, - newInstances.bridge.one, - newInstances.forkonomicToken.one, - address(this), - newInstances.globalExitRoot.one, - arbitrationFee, - chainIdManager - ); IForkingManager(newInstances.forkingManager.two).initialize( newInstances.zkEVM.two, newInstances.bridge.two, @@ -261,12 +322,6 @@ contract ForkingManager is IForkingManager, ForkableStructure { ); //Initialize the global exit root contracts - IForkableGlobalExitRoot(newInstances.globalExitRoot.one).initialize( - newInstances.forkingManager.one, - globalExitRoot, - newInstances.zkEVM.one, - newInstances.bridge.one - ); IForkableGlobalExitRoot(newInstances.globalExitRoot.two).initialize( newInstances.forkingManager.two, globalExitRoot, diff --git a/contracts/ForkonomicToken.sol b/contracts/ForkonomicToken.sol index 349e4c4a..4f9832cb 100644 --- a/contracts/ForkonomicToken.sol +++ b/contracts/ForkonomicToken.sol @@ -16,6 +16,12 @@ contract ForkonomicToken is /// @dev The role that allows minting new tokens bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + /// @dev Mapping that stores burned amounts + /// address The address of the token owner + /// bool indicating whether the first or second child was burnt + /// uint256 The amount of burned tokens + mapping(address => mapping(bool => uint256)) public childTokenAllowances; + /// @inheritdoc IForkonomicToken function initialize( address _forkmanager, @@ -46,12 +52,42 @@ contract ForkonomicToken is return _createChildren(implementation); } + function splitTokenAndMintOneChild( + uint256 amount, + bool firstChild, + bool useChildTokenAllowance + ) public onlyAfterForking { + require(children[0] != address(0), "Children not created yet"); + if (useChildTokenAllowance) { + require( + childTokenAllowances[msg.sender][firstChild] >= amount, + "Not enough allowance" + ); + childTokenAllowances[msg.sender][firstChild] -= amount; + } else { + _burn(msg.sender, amount); + childTokenAllowances[msg.sender][!firstChild] += amount; + } + IForkonomicToken(firstChild ? children[0] : children[1]).mint( + msg.sender, + amount + ); + } + + /// @dev Allows anyone to prepare the splitting of tokens + /// by burning them + /// @param amount The amount of tokens to burn + function prepareSplittingTokens(uint256 amount) public { + require(children[0] != address(0), "Children not created yet"); + _burn(msg.sender, amount); + childTokenAllowances[msg.sender][false] += amount; + childTokenAllowances[msg.sender][true] += amount; + } + /// @dev Allows anyone to split the tokens from the parent contract into the tokens of the children /// @param amount The amount of tokens to split function splitTokensIntoChildTokens(uint256 amount) external { - require(children[0] != address(0), "Children not created yet"); - _burn(msg.sender, amount); - IForkonomicToken(children[0]).mint(msg.sender, amount); - IForkonomicToken(children[1]).mint(msg.sender, amount); + splitTokenAndMintOneChild(amount, true, false); + splitTokenAndMintOneChild(amount, false, true); } } diff --git a/contracts/interfaces/IForkableBridge.sol b/contracts/interfaces/IForkableBridge.sol index 4057ffe0..e5a05888 100644 --- a/contracts/interfaces/IForkableBridge.sol +++ b/contracts/interfaces/IForkableBridge.sol @@ -4,8 +4,9 @@ pragma solidity ^0.8.20; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IPolygonZkEVMBridge} from "@RealityETH/zkevm-contracts/contracts/interfaces/IPolygonZkEVMBridge.sol"; import {IBasePolygonZkEVMGlobalExitRoot} from "@RealityETH/zkevm-contracts/contracts/interfaces/IBasePolygonZkEVMGlobalExitRoot.sol"; +import {IForkableStructure} from "./IForkableStructure.sol"; -interface IForkableBridge is IPolygonZkEVMBridge { +interface IForkableBridge is IForkableStructure, IPolygonZkEVMBridge { /** * @dev Function to initialize the contract * @param _forkmanager: address of the forkmanager contract @@ -49,7 +50,11 @@ interface IForkableBridge is IPolygonZkEVMBridge { * @param token token that should be split * @param amount amount of tokens to be split */ - function splitTokenIntoChildTokens(address token, uint256 amount) external; + function splitTokenIntoChildToken( + address token, + uint256 amount, + bool mintSecondChild + ) external; /** * @dev Function to mint the forkable token by the parent contract diff --git a/contracts/interfaces/IForkonomicToken.sol b/contracts/interfaces/IForkonomicToken.sol index f3a81331..0bde2307 100644 --- a/contracts/interfaces/IForkonomicToken.sol +++ b/contracts/interfaces/IForkonomicToken.sol @@ -34,4 +34,14 @@ interface IForkonomicToken is IForkableStructure, IERC20Upgradeable { /// @dev Allows anyone to split the tokens from the parent contract into the tokens of the children /// @param amount The amount of tokens to split function splitTokensIntoChildTokens(uint256 amount) external; + + /// @dev Allows anyone to split the tokens from the parent contract into the tokens of the children + /// @param amount The amount of tokens to split + /// @param firstChild Whether to mint the first or second child + /// @param useChildTokenAllowance Whether to use the child token allowance or the parent token balance + function splitTokenAndMintOneChild( + uint256 amount, + bool firstChild, + bool useChildTokenAllowance + ) external; } diff --git a/contracts/lib/BridgeAssetOperations.sol b/contracts/lib/BridgeAssetOperations.sol index 3f5b28e4..5403ff0a 100644 --- a/contracts/lib/BridgeAssetOperations.sol +++ b/contracts/lib/BridgeAssetOperations.sol @@ -45,35 +45,25 @@ library BridgeAssetOperations { } /** - * @notice Function to split tokens into children-bridge contracts + * @notice Function to create child tokens after splitting operation * @param token Address of the token * @param amount Amount of tokens to transfer * @param tokenInfo Information about the token - * @param child0 Address of the first child-bridge contract - * @param child1 Address of the second child-bridge contract + * @param child Address of the first child-bridge contract */ - function splitTokenIntoChildTokens( + function createChildToken( address token, uint256 amount, PolygonZkEVMBridge.TokenInformation memory tokenInfo, - address child0, - address child1 - ) external { + address child + ) public { require(tokenInfo.originNetwork != 0, "Token not forkable"); - TokenWrapped(token).burn(msg.sender, amount); bytes memory metadata = abi.encode( IERC20Metadata(token).name(), IERC20Metadata(token).symbol(), IERC20Metadata(token).decimals() ); - ForkableBridge(child0).mintForkableToken( - tokenInfo.originTokenAddress, - tokenInfo.originNetwork, - amount, - metadata, - msg.sender - ); - ForkableBridge(child1).mintForkableToken( + ForkableBridge(child).mintForkableToken( tokenInfo.originTokenAddress, tokenInfo.originNetwork, amount, @@ -82,30 +72,46 @@ library BridgeAssetOperations { ); } + // @inheritdoc IForkableBridge + function splitTokenIntoChildToken( + address token, + uint256 amount, + address child1, + address child2, + PolygonZkEVMBridge.TokenInformation memory tokenInfo + ) public { + TokenWrapped(token).burn(msg.sender, amount); + createChildToken(token, amount, tokenInfo, child1); + if (child2 != address(0)) { + createChildToken(token, amount, tokenInfo, child2); + } + } + /** - * @notice Function to send tokens into children-bridge contracts by the admin + * @notice Function to send tokens into children-bridge contract by the admin * @param gasTokenAddress Address of the token - * @param child0 Address of the first child-bridge contract - * @param child1 Address of the second child-bridge contract + * @param child Address of the first child-bridge contract + * @param useFirstChild Boolean to indicate which child to send tokens to */ - function sendForkonomicTokensToChildren( + function sendForkonomicTokensToChild( address gasTokenAddress, - address child0, - address child1 + uint256 amount, + address child, + bool useFirstChild, + bool useChildTokenAllowance ) public { - IForkonomicToken(gasTokenAddress).splitTokensIntoChildTokens( - IERC20(gasTokenAddress).balanceOf(address(this)) + IForkonomicToken(gasTokenAddress).splitTokenAndMintOneChild( + amount, + useFirstChild, + useChildTokenAllowance ); (address forkonomicToken1, address forkonomicToken2) = IForkonomicToken( gasTokenAddress ).getChildren(); - IERC20(forkonomicToken1).transfer( - child0, - IERC20(forkonomicToken1).balanceOf(address(this)) - ); - IERC20(forkonomicToken2).transfer( - child1, - IERC20(forkonomicToken2).balanceOf(address(this)) - ); + if (useFirstChild) { + IERC20(forkonomicToken1).transfer(child, amount); + } else { + IERC20(forkonomicToken2).transfer(child, amount); + } } } diff --git a/contracts/lib/CreateChildren.sol b/contracts/lib/CreateChildren.sol index 472e9f53..42fb9436 100644 --- a/contracts/lib/CreateChildren.sol +++ b/contracts/lib/CreateChildren.sol @@ -36,11 +36,7 @@ library CreateChildren { } /// @dev Internal function to create the children contracts. - /// - /// @param implementation Allows to pass a different implementation contract for the second proxied child. - function createChildren( - address implementation - ) public returns (address forkingManager1, address forkingManager2) { + function createChild1() public returns (address forkingManager1) { // Fork 1 will always keep the original implementation forkingManager1 = address( new TransparentUpgradeableProxy( @@ -49,6 +45,14 @@ library CreateChildren { "" ) ); + } + + /// @dev Internal function to create the child 2 + /// + /// @param implementation Allows to pass a different implementation contract for the second proxied child. + function createChild2( + address implementation + ) public returns (address forkingManager2) { if (address(implementation) == address(0)) { implementation = _getImplementation(); } @@ -58,4 +62,14 @@ library CreateChildren { new TransparentUpgradeableProxy(implementation, _getAdmin(), "") ); } + + /// @dev Internal function to create the children contracts. + /// + /// @param implementation Allows to pass a different implementation contract for the second proxied child. + function createChildren( + address implementation + ) public returns (address forkingManager1, address forkingManager2) { + forkingManager1 = createChild1(); + forkingManager2 = createChild2(implementation); + } } diff --git a/contracts/mixin/ForkableStructure.sol b/contracts/mixin/ForkableStructure.sol index 1bd10f4f..6d3853d0 100644 --- a/contracts/mixin/ForkableStructure.sol +++ b/contracts/mixin/ForkableStructure.sol @@ -22,17 +22,24 @@ contract ForkableStructure is IForkableStructure, Initializable { _; } + modifier onlyBeforeCreatingChild2() { + require(children[2] == address(0x0), "No changes after forking"); + _; + } + modifier onlyAfterForking() { require(children[0] != address(0x0), "onlyAfterForking"); + require(children[1] != address(0x0), "onlyAfterForking"); _; } + modifier onlyParent() { - require(msg.sender == parentContract, "Only available for parent"); + require(msg.sender == parentContract, "Not parent"); _; } modifier onlyForkManger() { - require(msg.sender == forkmanager, "Only forkManager is allowed"); + require(msg.sender == forkmanager, "Not forkManager"); _; } @@ -56,14 +63,13 @@ contract ForkableStructure is IForkableStructure, Initializable { function _createChildren( address implementation ) internal returns (address forkingManager1, address forkingManager2) { - (forkingManager1, forkingManager2) = CreateChildren.createChildren( - implementation - ); + forkingManager1 = CreateChildren.createChild1(); children[0] = forkingManager1; + forkingManager2 = CreateChildren.createChild2(implementation); children[1] = forkingManager2; } - function getChildren() external view returns (address, address) { + function getChildren() public view returns (address, address) { return (children[0], children[1]); } } diff --git a/test/ForkableBridge.t.sol b/test/ForkableBridge.t.sol index 6c06d8b0..03fbc842 100644 --- a/test/ForkableBridge.t.sol +++ b/test/ForkableBridge.t.sol @@ -72,7 +72,7 @@ contract ForkableBridgeTest is Test { address secondBridgeImplementation = address( new ForkableBridgeWrapper() ); - vm.expectRevert(bytes("Only forkManager is allowed")); + vm.expectRevert(bytes("Not forkManager")); forkableBridge.createChildren(secondBridgeImplementation); ForkableGlobalExitRoot exitRoot = new ForkableGlobalExitRoot(); vm.mockCall( @@ -101,7 +101,7 @@ contract ForkableBridgeTest is Test { abi.encodePacked(originNetwork, token) ); - vm.expectRevert(bytes("Only available for parent")); + vm.expectRevert(bytes("Not parent")); forkableBridge.mintForkableToken( address(token), originNetwork, @@ -111,7 +111,7 @@ contract ForkableBridgeTest is Test { ); vm.prank(forkableBridge.parentContract()); - vm.expectRevert(bytes("Token is from this network")); + vm.expectRevert(bytes("wrong Token")); forkableBridge.mintForkableToken( address(token), networkID, // <-- this line is changed @@ -178,7 +178,7 @@ contract ForkableBridgeTest is Test { amount ); - vm.expectRevert(bytes("Only available for parent")); + vm.expectRevert(bytes("Not parent")); forkableBridge.burnForkableTokens( destinationAddress, originTokenAddress, @@ -219,7 +219,7 @@ contract ForkableBridgeTest is Test { // Testing revert if children are not yet created vm.expectRevert(bytes("onlyAfterForking")); - forkableBridge.splitTokenIntoChildTokens(address(token), amount); + forkableBridge.splitTokenIntoChildToken(address(token), amount, true); address secondBridgeImplementation = address( new ForkableBridgeWrapper() @@ -228,9 +228,6 @@ contract ForkableBridgeTest is Test { (address child1, address child2) = forkableBridge.createChildren( secondBridgeImplementation ); - // Testing revert if token was not bridged before (i.e. is not forkable) - vm.expectRevert(bytes("Token not forkable")); - forkableBridge.splitTokenIntoChildTokens(address(token), amount); // Create forkable token vm.prank(forkableBridge.parentContract()); @@ -275,10 +272,10 @@ contract ForkableBridgeTest is Test { // splitting fails, if sender does not have the funds vm.prank(address(0x234234)); vm.expectRevert(bytes("ERC20: burn amount exceeds balance")); - forkableBridge.splitTokenIntoChildTokens(forkableToken, amount); + forkableBridge.splitTokenIntoChildToken(forkableToken, amount, true); // Split the token - forkableBridge.splitTokenIntoChildTokens(forkableToken, amount); + forkableBridge.splitTokenIntoChildToken(forkableToken, amount, true); // Assert token balances address forkableTokenChild1 = ForkableBridge(child1) @@ -360,7 +357,7 @@ contract ForkableBridgeTest is Test { ); // Split the token - forkableBridge.splitTokenIntoChildTokens(forkableToken, amount); + forkableBridge.splitTokenIntoChildToken(forkableToken, amount, true); // Only parent can merge vm.expectRevert(bytes("onlyAfterForking")); @@ -524,7 +521,7 @@ contract ForkableBridgeTest is Test { to ); - vm.expectRevert("Invalid to address"); + vm.expectRevert("Invalid to"); vm.prank(hardAssetManger); forkableBridge.transferHardAssetsToChild( address(erc20Token), @@ -587,7 +584,10 @@ contract ForkableBridgeTest is Test { // Now, call the function as the forkmanager vm.expectRevert(bytes("onlyAfterForking")); vm.prank(forkmanager); - forkableBridge2.sendForkonomicTokensToChildren(); + forkableBridge2.sendForkonomicTokensToChild(10, true, false); + vm.expectRevert(bytes("onlyAfterForking")); + vm.prank(forkmanager); + forkableBridge2.sendForkonomicTokensToChild(10, true, false); // Create initiate the forking process and create children address secondBridgeImplementation = address( @@ -635,8 +635,11 @@ contract ForkableBridgeTest is Test { "FTK" ); // Now, call the function as the forkmanager + uint256 amount = erc20GasToken.balanceOf(address(forkableBridge2)); + vm.prank(forkmanager); + forkableBridge2.sendForkonomicTokensToChild(amount, true, false); vm.prank(forkmanager); - forkableBridge2.sendForkonomicTokensToChildren(); + forkableBridge2.sendForkonomicTokensToChild(amount, false, true); // Check that the tokens were transferred correctly assertEq( diff --git a/test/ForkableGlobalExitRoot.t.sol b/test/ForkableGlobalExitRoot.t.sol index 0dfb2dc1..435db0c8 100644 --- a/test/ForkableGlobalExitRoot.t.sol +++ b/test/ForkableGlobalExitRoot.t.sol @@ -74,7 +74,7 @@ contract ForkableGlobalExitRootTest is Test { new ForkableGlobalExitRoot() ); - vm.expectRevert("Only forkManager is allowed"); + vm.expectRevert("Not forkManager"); forkableGlobalExitRoot.createChildren( secondForkableGlobalExitRootImplementation ); diff --git a/test/ForkableZkEVM.t.sol b/test/ForkableZkEVM.t.sol index 72db4a47..debd269b 100644 --- a/test/ForkableZkEVM.t.sol +++ b/test/ForkableZkEVM.t.sol @@ -97,7 +97,7 @@ contract ForkableZkEVMTest is Test { new ForkableZkEVM() ); - vm.expectRevert("Only forkManager is allowed"); + vm.expectRevert("Not forkManager"); forkableZkEVM.createChildren(secondForkableZkEVMImplementation); vm.prank(forkableZkEVM.forkmanager()); diff --git a/test/ForkingManager.t.sol b/test/ForkingManager.t.sol index b369f5d0..5bf34045 100644 --- a/test/ForkingManager.t.sol +++ b/test/ForkingManager.t.sol @@ -236,7 +236,8 @@ contract ForkingManagerTest is Test { assertFalse(forkmanager.canFork()); vm.warp(block.timestamp + forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); + forkmanager.executeFork2(); assertTrue(forkmanager.isForkingInitiated()); assertTrue(forkmanager.isForkingExecuted()); @@ -313,7 +314,8 @@ contract ForkingManagerTest is Test { }) ); vm.warp(block.timestamp + forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); + forkmanager.executeFork2(); // Fetch the children from the ForkingManager (address childForkmanager1, address childForkmanager2) = forkmanager @@ -462,7 +464,8 @@ contract ForkingManagerTest is Test { // Call the initiateFork function to create a new fork forkmanager.initiateFork(disputeData, noNewImplementations); vm.warp(block.timestamp + forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); + forkmanager.executeFork2(); // Fetch the children from the ForkingManager (address childForkmanager1, address childForkmanager2) = forkmanager @@ -624,7 +627,9 @@ contract ForkingManagerTest is Test { }) ); skip(forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); + forkmanager.executeFork2(); + ( bool receivedIsL1, address receivedDisputeContract, @@ -646,7 +651,7 @@ contract ForkingManagerTest is Test { function testExecuteForkRespectsTime() public { // reverts on empty proposal list vm.expectRevert("ForkingManager: fork not ready"); - forkmanager.executeFork(); + forkmanager.executeFork1(); // Mint and approve the arbitration fee for the test contract forkonomicToken.approve(address(forkmanager), arbitrationFee); @@ -670,9 +675,9 @@ contract ForkingManagerTest is Test { ); vm.expectRevert("ForkingManager: fork not ready"); - forkmanager.executeFork(); + forkmanager.executeFork1(); vm.warp(testTimestamp + forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); } function testExecuteForkCanOnlyExecutedOnce() public { @@ -697,9 +702,10 @@ contract ForkingManagerTest is Test { }) ); skip(forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); + forkmanager.executeFork2(); vm.expectRevert("No changes after forking"); - forkmanager.executeFork(); + forkmanager.executeFork1(); } function testRevertsSecondProposal() public { diff --git a/test/ForkonomicToken.t.sol b/test/ForkonomicToken.t.sol index 5bcc4f9c..03144275 100644 --- a/test/ForkonomicToken.t.sol +++ b/test/ForkonomicToken.t.sol @@ -4,6 +4,7 @@ import {Test} from "forge-std/Test.sol"; import {ForkonomicToken} from "../contracts/ForkonomicToken.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC20Upgradeable.sol"; import {IForkonomicToken} from "../contracts/interfaces/IForkonomicToken.sol"; +import {ForkableBridge} from "../contracts/ForkableBridge.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Util} from "./utils/Util.sol"; diff --git a/test/L1GlobalChainInfoPublisher.t.sol b/test/L1GlobalChainInfoPublisher.t.sol index e2ac212a..67a8a48f 100644 --- a/test/L1GlobalChainInfoPublisher.t.sol +++ b/test/L1GlobalChainInfoPublisher.t.sol @@ -292,7 +292,8 @@ contract L1GlobalChainInfoPublisherTest is Test { }) ); skip(forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); + forkmanager.executeFork2(); // The current bridge should no longer work vm.expectRevert("No changes after forking"); @@ -357,7 +358,8 @@ contract L1GlobalChainInfoPublisherTest is Test { // Call the initiateFork function to create a new fork forkmanager2.initiateFork(disputeData2, newImplementations2); skip(forkmanager.forkPreparationTime() + 1); - forkmanager2.executeFork(); + forkmanager2.executeFork1(); + forkmanager2.executeFork2(); vm.expectRevert("No changes after forking"); l1GlobalChainInfoPublisher.updateL2ChainInfo( diff --git a/test/L1GlobalForkRequester.t.sol b/test/L1GlobalForkRequester.t.sol index 749f1f1a..e487aeff 100644 --- a/test/L1GlobalForkRequester.t.sol +++ b/test/L1GlobalForkRequester.t.sol @@ -363,7 +363,8 @@ contract L1GlobalForkRequesterTest is Test { // Execute the other guy's fork skip(forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); + forkmanager.executeFork2(); { uint256 balBeforeSplit = forkonomicToken.balanceOf(