From 21365e350bc8ada0841f18a8d72d482440e392bd Mon Sep 17 00:00:00 2001 From: josojo Date: Mon, 6 Nov 2023 20:18:41 +0100 Subject: [PATCH 1/8] very first draft(to be worked on) --- contracts/ForkableBridge.sol | 24 ++- contracts/ForkableGlobalExitRoot.sol | 15 ++ contracts/ForkableZkEVM.sol | 24 ++- contracts/ForkingManager.sol | 177 ++++++++++++------ contracts/ForkonomicToken.sol | 12 ++ contracts/interfaces/IForkableBridge.sol | 11 ++ .../interfaces/IForkableGlobalExitRoot.sol | 4 + contracts/interfaces/IForkableZkEVM.sol | 11 ++ contracts/interfaces/IForkonomicToken.sol | 9 + contracts/lib/CreateChildren.sol | 24 ++- contracts/mixin/ForkableStructure.sol | 35 +++- test/ChainIdManager.t.sol | 2 +- test/ForkingManager.t.sol | 18 +- 13 files changed, 275 insertions(+), 91 deletions(-) diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index 1db89059..9b4d506c 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -99,6 +99,22 @@ contract ForkableBridge is PolygonZkEVMBridge._setAndCheckClaimed(index); } + // @inheritdoc IForkableBridge + function createChild1() external onlyForkManger returns (address) { + // process all pending deposits/messages before coping over the state root. + updateGlobalExitRoot(); + return _createChild1(); + } + + // @inheritdoc IForkableBridge + function createChild2( + address implementation + ) external onlyForkManger returns (address) { + // process all pending deposits/messages before coping over the state root. + updateGlobalExitRoot(); + return _createChild2(implementation); + } + // @inheritdoc IForkableBridge function createChildren( address implementation @@ -200,7 +216,7 @@ contract ForkableBridge is public payable override(PolygonZkEVMBridge, IPolygonZkEVMBridge) - onlyBeforeForking + onlyBeforeCreatingChild1 { PolygonZkEVMBridge.bridgeAsset( destinationNetwork, @@ -222,7 +238,7 @@ contract ForkableBridge is payable virtual override(PolygonZkEVMBridge, IPolygonZkEVMBridge) - onlyBeforeForking + onlyBeforeCreatingChild1 { PolygonZkEVMBridge.bridgeMessage( destinationNetwork, @@ -246,7 +262,7 @@ contract ForkableBridge is ) public override(IPolygonZkEVMBridge, PolygonZkEVMBridge) - onlyBeforeForking + onlyBeforeCreatingChild1 { PolygonZkEVMBridge.claimMessage( smtProof, @@ -276,7 +292,7 @@ contract ForkableBridge is ) public override(IPolygonZkEVMBridge, PolygonZkEVMBridge) - onlyBeforeForking + onlyBeforeCreatingChild1 { PolygonZkEVMBridge.claimAsset( smtProof, diff --git a/contracts/ForkableGlobalExitRoot.sol b/contracts/ForkableGlobalExitRoot.sol index 125ca5d3..903204f3 100644 --- a/contracts/ForkableGlobalExitRoot.sol +++ b/contracts/ForkableGlobalExitRoot.sol @@ -52,6 +52,21 @@ contract ForkableGlobalExitRoot is ); } + /// @dev Public interface to create children. This can only be done by the forkmanager + /// @return The addresses of the two children + function createChild1() external onlyForkManger returns (address) { + return _createChild1(); + } + + /// @dev Public interface to create children. This can only be done by the forkmanager + /// @param implementation Allows to pass a different implementation contract for the second proxied child. + /// @return The addresses of the two children + function createChild2( + address implementation + ) external onlyForkManger returns (address) { + return _createChild2(implementation); + } + /// @dev Public interface to create children. This can only be done by the forkmanager /// @param implementation Allows to pass a different implementation contract for the second proxied child. /// @return The addresses of the two children diff --git a/contracts/ForkableZkEVM.sol b/contracts/ForkableZkEVM.sol index f2f0eb02..0397e486 100644 --- a/contracts/ForkableZkEVM.sol +++ b/contracts/ForkableZkEVM.sol @@ -40,6 +40,18 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { ); } + // @inheritdoc IForkableZkEVM + function createChild1() external onlyForkManger returns (address) { + return _createChild1(); + } + + // @inheritdoc IForkableZkEVM + function createChild2( + address implementation + ) external onlyForkManger returns (address) { + return _createChild2(implementation); + } + // @inheritdoc IForkableZkEVM function createChildren( address implementation @@ -48,7 +60,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { } //////////////////////////////////////////////////////////////////////////// - // For the following functions a modifier called: onlyBeforeForking is added. + // For the following functions a modifier called: onlyBeforeCreatingChild1 is added. // This ensure that the functions do not change the consolidated state after forking. /////////////////////////////////////////////////////////////////////////// @@ -59,7 +71,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof - ) public override onlyBeforeForking { + ) public override onlyBeforeCreatingChild1 { PolygonZkEVM.verifyBatches( pendingStateNum, initNumBatch, @@ -77,7 +89,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof - ) public override onlyBeforeForking { + ) public override onlyBeforeCreatingChild1 { PolygonZkEVM.verifyBatchesTrustedAggregator( pendingStateNum, initNumBatch, @@ -90,7 +102,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { function consolidatePendingState( uint64 pendingStateNum - ) public override onlyBeforeForking { + ) public override onlyBeforeCreatingChild1 { PolygonZkEVM.consolidatePendingState(pendingStateNum); } @@ -102,7 +114,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof - ) public override onlyBeforeForking { + ) public override onlyBeforeCreatingChild1 { PolygonZkEVM.overridePendingState( initPendingStateNum, finalPendingStateNum, @@ -119,7 +131,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { function sequenceBatches( BatchData[] calldata batches, address l2Coinbase - ) public override onlyBeforeForking { + ) public override onlyBeforeCreatingChild1 { PolygonZkEVM.sequenceBatches(batches, l2Coinbase); } diff --git a/contracts/ForkingManager.sol b/contracts/ForkingManager.sol index 99e319de..3eaf4b59 100644 --- a/contracts/ForkingManager.sol +++ b/contracts/ForkingManager.sol @@ -84,7 +84,7 @@ contract ForkingManager is IForkingManager, ForkableStructure { function initiateFork( DisputeData memory _disputeData, NewImplementations calldata _newImplementations - ) external onlyBeforeForking { + ) external onlyBeforeCreatingChild1 { require(executionTimeForProposal == 0, "ForkingManager: fork pending"); // Charge the forking fee IERC20(forkonomicToken).safeTransferFrom( @@ -102,38 +102,24 @@ contract ForkingManager is IForkingManager, ForkableStructure { /** * @dev function that executes a fork proposal */ - function executeFork() external onlyBeforeForking { + function executeFork1() external onlyBeforeCreatingChild1 { 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.globalExitRoot.one, - newInstances.globalExitRoot.two - ) = IForkableGlobalExitRoot(globalExitRoot).createChildren( - newImplementations.globalExitRootImplementation - ); + newInstances.forkingManager.one = _createChild1(); + (newInstances.bridge.one) = IForkableBridge(bridge).createChild1(); + (newInstances.zkEVM.one) = IForkableZkEVM(zkEVM).createChild1(); + (newInstances.forkonomicToken.one) = IForkonomicToken(forkonomicToken) + .createChild1(); + (newInstances.globalExitRoot.one) = IForkableGlobalExitRoot( + globalExitRoot + ).createChild1(); // Initialize the zkEVM contracts IPolygonZkEVM.InitializePackedParameters @@ -175,11 +161,112 @@ contract ForkingManager is IForkingManager, ForkableStructure { IForkableZkEVM(zkEVM).rollupVerifier(), IPolygonZkEVMBridge(newInstances.bridge.one) ); - initializePackedParameters.chainID = ChainIdManager(chainIdManager) - .getNextUsableChainId(); - initializePackedParameters.forkID = newImplementations.forkID > 0 + } + + // 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 = _createChild2( + newImplementations.forkingManagerImplementation + ); + newInstances.bridge.two = IForkableBridge(bridge).createChild2( + newImplementations.bridgeImplementation + ); + newInstances.zkEVM.two = IForkableZkEVM(zkEVM).createChild2( + newImplementations.zkEVMImplementation + ); + newInstances.forkonomicToken.two = IForkonomicToken(forkonomicToken) + .createChild2(newImplementations.forkonomicTokenImplementation); + (newInstances.globalExitRoot.two) = IForkableGlobalExitRoot( + globalExitRoot + ).createChild2(newImplementations.globalExitRootImplementation); + + // 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(); + : IPolygonZkEVM(zkEVM).forkID() + }); IForkableZkEVM(newInstances.zkEVM.two).initialize( newInstances.forkingManager.two, zkEVM, @@ -196,13 +283,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 +295,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 +309,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 +320,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..92b3f660 100644 --- a/contracts/ForkonomicToken.sol +++ b/contracts/ForkonomicToken.sol @@ -39,6 +39,18 @@ contract ForkonomicToken is _mint(to, amount); } + /// @inheritdoc IForkonomicToken + function createChild1() external onlyForkManger returns (address) { + return _createChild1(); + } + + /// @inheritdoc IForkonomicToken + function createChild2( + address implementation + ) external onlyForkManger returns (address) { + return _createChild2(implementation); + } + /// @inheritdoc IForkonomicToken function createChildren( address implementation diff --git a/contracts/interfaces/IForkableBridge.sol b/contracts/interfaces/IForkableBridge.sol index 4057ffe0..60d52f4e 100644 --- a/contracts/interfaces/IForkableBridge.sol +++ b/contracts/interfaces/IForkableBridge.sol @@ -44,6 +44,17 @@ interface IForkableBridge is IPolygonZkEVMBridge { address implementation ) external returns (address, address); + /** + * @dev Internal function to create the children contracts. + */ + function createChild1() external returns (address); + + /** + * @dev Internal function to create the children contracts. + * @param implementation Allows to pass a different implementation contract for the second proxied child. + */ + function createChild2(address implementation) external returns (address); + /** * @dev Anyone can use their tokens to split the bridged tokens into the two corresponding children tokens * @param token token that should be split diff --git a/contracts/interfaces/IForkableGlobalExitRoot.sol b/contracts/interfaces/IForkableGlobalExitRoot.sol index d4b485c7..b58e4573 100644 --- a/contracts/interfaces/IForkableGlobalExitRoot.sol +++ b/contracts/interfaces/IForkableGlobalExitRoot.sol @@ -15,6 +15,10 @@ interface IForkableGlobalExitRoot is address _bridgeAddress ) external; + function createChild2(address implementation) external returns (address); + + function createChild1() external returns (address); + function createChildren( address implementation ) external returns (address, address); diff --git a/contracts/interfaces/IForkableZkEVM.sol b/contracts/interfaces/IForkableZkEVM.sol index 2dfec15d..f2600907 100644 --- a/contracts/interfaces/IForkableZkEVM.sol +++ b/contracts/interfaces/IForkableZkEVM.sol @@ -35,6 +35,17 @@ interface IForkableZkEVM is IForkableStructure, IPolygonZkEVM { IPolygonZkEVMBridge _bridgeAddress ) external; + /** + * @dev Internal function to create the children contracts. + */ + function createChild1() external returns (address); + + /** + * @dev Internal function to create the children contracts. + * @param implementation Allows to pass a different implementation contract for the second proxied child. + */ + function createChild2(address implementation) external returns (address); + // @dev: This function is used to create the children contracts. // The initialization of the children contracts should be called in the same transaction // as the creation of the children contracts. diff --git a/contracts/interfaces/IForkonomicToken.sol b/contracts/interfaces/IForkonomicToken.sol index f3a81331..905315d9 100644 --- a/contracts/interfaces/IForkonomicToken.sol +++ b/contracts/interfaces/IForkonomicToken.sol @@ -24,6 +24,15 @@ interface IForkonomicToken is IForkableStructure, IERC20Upgradeable { /// @param amount The amount of tokens to mint function mint(address to, uint256 amount) external; + /// @dev Interface for the forkManger to create children + /// @return The addresses of the two children + function createChild1() external returns (address); + + /// @dev Interface for the forkManger to create children + /// @param implementation Allows to pass a different implementation contract for the second proxied child. + /// @return The addresses of the two children + function createChild2(address implementation) external returns (address); + /// @dev Interface for the forkManger to create children /// @param implementation Allows to pass a different implementation contract for the second proxied child. /// @return The addresses of the two children 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..901bf692 100644 --- a/contracts/mixin/ForkableStructure.sol +++ b/contracts/mixin/ForkableStructure.sol @@ -17,15 +17,22 @@ contract ForkableStructure is IForkableStructure, Initializable { // but this would make the initialization more complex due to proxy construction. mapping(uint256 => address) public children; - modifier onlyBeforeForking() { + modifier onlyBeforeCreatingChild1() { require(children[0] == address(0x0), "No changes after forking"); _; } + 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"); _; @@ -49,6 +56,25 @@ contract ForkableStructure is IForkableStructure, Initializable { parentContract = _parentContract; } + /** + * @dev Internal function to create the children contracts. + */ + function _createChild1() internal returns (address forkingManager1) { + forkingManager1 = CreateChildren.createChild1(); + children[0] = forkingManager1; + } + + /** + * @dev Internal function to create the children contracts. + * @param implementation Allows to pass a different implementation contract for the second proxied child. + */ + function _createChild2( + address implementation + ) internal returns (address forkingManager2) { + forkingManager2 = CreateChildren.createChild2(implementation); + children[1] = forkingManager2; + } + /** * @dev Internal function to create the children contracts. * @param implementation Allows to pass a different implementation contract for the second proxied child. @@ -56,11 +82,8 @@ contract ForkableStructure is IForkableStructure, Initializable { function _createChildren( address implementation ) internal returns (address forkingManager1, address forkingManager2) { - (forkingManager1, forkingManager2) = CreateChildren.createChildren( - implementation - ); - children[0] = forkingManager1; - children[1] = forkingManager2; + forkingManager1 = _createChild1(); + forkingManager2 = _createChild2(implementation); } function getChildren() external view returns (address, address) { diff --git a/test/ChainIdManager.t.sol b/test/ChainIdManager.t.sol index f629770c..b42933fd 100644 --- a/test/ChainIdManager.t.sol +++ b/test/ChainIdManager.t.sol @@ -75,7 +75,7 @@ contract ChainIdManagerTest is Test { ); } - function testCheckGasBurn() public view { + function testCheckGasBurn() view public { uint256 initialGasLeft = gasleft(); chainIdManager.burnGas(); uint256 finalGasLeft = gasleft(); diff --git a/test/ForkingManager.t.sol b/test/ForkingManager.t.sol index b369f5d0..9ab11d58 100644 --- a/test/ForkingManager.t.sol +++ b/test/ForkingManager.t.sol @@ -313,7 +313,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 @@ -624,7 +625,9 @@ contract ForkingManagerTest is Test { }) ); skip(forkmanager.forkPreparationTime() + 1); - forkmanager.executeFork(); + forkmanager.executeFork1(); + forkmanager.executeFork2(); + ( bool receivedIsL1, address receivedDisputeContract, @@ -646,7 +649,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 +673,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 +700,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 { From 5b44a6345195fe204d962945984e319eaf9f96c0 Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 28 Nov 2023 11:45:01 +0100 Subject: [PATCH 2/8] draft for splitting tokens --- contracts/ForkableBridge.sol | 53 ++++++++++++++++----- contracts/ForkonomicToken.sol | 33 +++++++++++-- contracts/interfaces/IForkonomicToken.sol | 10 ++++ contracts/lib/BridgeAssetOperations.sol | 56 ++++++++++------------- test/ForkableBridge.t.sol | 12 +++-- test/ForkonomicToken.t.sol | 1 + 6 files changed, 114 insertions(+), 51 deletions(-) diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index 9b4d506c..5e226c9b 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -19,6 +19,12 @@ contract ForkableBridge is // tokens to the children-bridge contracts address internal _hardAssetManager; + // @dev Mapping to keep track of the allowances for the child tokens minting process + // @param token Address of the token + // @param isChild0 Boolean to indicate if the allowance is for the first or second child-bridge contract + // @param amount Amount of tokens allowed to be minted + mapping(address => mapping(bool => uint256)) public childTokenAllowances; + // @inheritdoc IForkableBridge function initialize( address _forkmanager, @@ -142,17 +148,41 @@ contract ForkableBridge is ); } + function prepareSplittinTokens( + address token, + uint256 amount + ) public onlyAfterForking { + TokenWrapped(token).burn(msg.sender, amount); + childTokenAllowances[token][true] += amount; + childTokenAllowances[token][false] += amount; + } + // @inheritdoc IForkableBridge function splitTokenIntoChildTokens( address token, uint256 amount ) external onlyAfterForking { - BridgeAssetOperations.splitTokenIntoChildTokens( + prepareSplittinTokens(token, amount); + createChildToken(token, amount, true); + createChildToken(token, amount, false); + } + + // @inheritdoc IForkableBridge + function createChildToken( + address token, + uint256 amount, + bool firstChild + ) public onlyAfterForking { + require( + childTokenAllowances[token][firstChild] >= amount, + "Not enough allowance" + ); + childTokenAllowances[token][firstChild] -= amount; + BridgeAssetOperations.createChildToken( token, amount, wrappedTokenToTokenInfo[token], - children[0], - children[1] + firstChild ? children[0] : children[1] ); } @@ -187,17 +217,16 @@ 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 firstChild boolean indicating for which child the operation should be run */ - function sendForkonomicTokensToChildren() - public - onlyForkManger - onlyAfterForking - { - BridgeAssetOperations.sendForkonomicTokensToChildren( + function sendForkonomicTokensToChild( + bool firstChild + ) public onlyForkManger onlyAfterForking { + BridgeAssetOperations.sendForkonomicTokensToChild( gasTokenAddress, - children[0], - children[1] + firstChild ? children[0] : children[1], + firstChild ); } diff --git a/contracts/ForkonomicToken.sol b/contracts/ForkonomicToken.sol index 92b3f660..aaf0ef3f 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, @@ -58,12 +64,33 @@ contract ForkonomicToken is return _createChildren(implementation); } + /// @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 finish their splitting of tokens + /// by minting them + /// @param firstChild Whether to mint the tokens of the first or second child + function finishSplittingTokens(bool firstChild) public { + IForkonomicToken(firstChild ? children[0] : children[1]).mint( + msg.sender, + childTokenAllowances[msg.sender][firstChild] + ); + childTokenAllowances[msg.sender][firstChild] = 0; + } + /// @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); + prepareSplittingTokens(amount); + finishSplittingTokens(true); + finishSplittingTokens(false); } } diff --git a/contracts/interfaces/IForkonomicToken.sol b/contracts/interfaces/IForkonomicToken.sol index 905315d9..e8a400d7 100644 --- a/contracts/interfaces/IForkonomicToken.sol +++ b/contracts/interfaces/IForkonomicToken.sol @@ -43,4 +43,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 prepare the splitting of tokens + /// by burning them + /// @param amount The amount of tokens to burn + function prepareSplittingTokens(uint256 amount) external; + + /// @dev Allows anyone to finish their splitting of tokens + /// by minting them + /// @param firstChild Whether to mint the tokens of the first or second child + function finishSplittingTokens(bool firstChild) external; } diff --git a/contracts/lib/BridgeAssetOperations.sol b/contracts/lib/BridgeAssetOperations.sol index 3f5b28e4..623e126c 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, @@ -83,29 +73,33 @@ library BridgeAssetOperations { } /** - * @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 firstChild Boolean to indicate which child to send tokens to */ - function sendForkonomicTokensToChildren( + function sendForkonomicTokensToChild( address gasTokenAddress, - address child0, - address child1 + address child, + bool firstChild ) public { - IForkonomicToken(gasTokenAddress).splitTokensIntoChildTokens( + IForkonomicToken(gasTokenAddress).prepareSplittingTokens( IERC20(gasTokenAddress).balanceOf(address(this)) ); + IForkonomicToken(gasTokenAddress).finishSplittingTokens(firstChild); (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 (firstChild) { + IERC20(forkonomicToken1).transfer( + child, + IERC20(forkonomicToken1).balanceOf(address(this)) + ); + } else { + IERC20(forkonomicToken2).transfer( + child, + IERC20(forkonomicToken2).balanceOf(address(this)) + ); + } } } diff --git a/test/ForkableBridge.t.sol b/test/ForkableBridge.t.sol index 6c06d8b0..22cb3f5b 100644 --- a/test/ForkableBridge.t.sol +++ b/test/ForkableBridge.t.sol @@ -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()); @@ -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(true); + vm.expectRevert(bytes("onlyAfterForking")); + vm.prank(forkmanager); + forkableBridge2.sendForkonomicTokensToChild(false); // Create initiate the forking process and create children address secondBridgeImplementation = address( @@ -636,7 +636,9 @@ contract ForkableBridgeTest is Test { ); // Now, call the function as the forkmanager vm.prank(forkmanager); - forkableBridge2.sendForkonomicTokensToChildren(); + forkableBridge2.sendForkonomicTokensToChild(true); + vm.prank(forkmanager); + forkableBridge2.sendForkonomicTokensToChild(false); // Check that the tokens were transferred correctly assertEq( 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"; From 5a5aad39ec0649a34a25c3a5adf149b9ca1e9eb0 Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 28 Nov 2023 12:30:10 +0100 Subject: [PATCH 3/8] store tokenAllowance per user --- contracts/ForkableBridge.sol | 12 +++++++----- contracts/ForkingManager.sol | 4 ++-- test/ChainIdManager.t.sol | 2 +- test/ForkingManager.t.sol | 6 ++++-- test/L1GlobalChainInfoPublisher.t.sol | 6 ++++-- test/L1GlobalForkRequester.t.sol | 3 ++- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index 5e226c9b..623bc69b 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -20,10 +20,12 @@ contract ForkableBridge is address internal _hardAssetManager; // @dev Mapping to keep track of the allowances for the child tokens minting process + // @param user Address of the token // @param token Address of the token // @param isChild0 Boolean to indicate if the allowance is for the first or second child-bridge contract // @param amount Amount of tokens allowed to be minted - mapping(address => mapping(bool => uint256)) public childTokenAllowances; + mapping(address => mapping(address => mapping(bool => uint256))) + public childTokenAllowances; // @inheritdoc IForkableBridge function initialize( @@ -153,8 +155,8 @@ contract ForkableBridge is uint256 amount ) public onlyAfterForking { TokenWrapped(token).burn(msg.sender, amount); - childTokenAllowances[token][true] += amount; - childTokenAllowances[token][false] += amount; + childTokenAllowances[msg.sender][token][true] += amount; + childTokenAllowances[msg.sender][token][false] += amount; } // @inheritdoc IForkableBridge @@ -174,10 +176,10 @@ contract ForkableBridge is bool firstChild ) public onlyAfterForking { require( - childTokenAllowances[token][firstChild] >= amount, + childTokenAllowances[msg.sender][token][firstChild] >= amount, "Not enough allowance" ); - childTokenAllowances[token][firstChild] -= amount; + childTokenAllowances[msg.sender][token][firstChild] -= amount; BridgeAssetOperations.createChildToken( token, amount, diff --git a/contracts/ForkingManager.sol b/contracts/ForkingManager.sol index 3eaf4b59..3b592793 100644 --- a/contracts/ForkingManager.sol +++ b/contracts/ForkingManager.sol @@ -264,8 +264,8 @@ contract ForkingManager is IForkingManager, ForkableStructure { chainID: ChainIdManager(chainIdManager) .getNextUsableChainId(), forkID: newImplementations.forkID > 0 - ? newImplementations.forkID - : IPolygonZkEVM(zkEVM).forkID() + ? newImplementations.forkID + : IPolygonZkEVM(zkEVM).forkID() }); IForkableZkEVM(newInstances.zkEVM.two).initialize( newInstances.forkingManager.two, diff --git a/test/ChainIdManager.t.sol b/test/ChainIdManager.t.sol index b42933fd..f629770c 100644 --- a/test/ChainIdManager.t.sol +++ b/test/ChainIdManager.t.sol @@ -75,7 +75,7 @@ contract ChainIdManagerTest is Test { ); } - function testCheckGasBurn() view public { + function testCheckGasBurn() public view { uint256 initialGasLeft = gasleft(); chainIdManager.burnGas(); uint256 finalGasLeft = gasleft(); diff --git a/test/ForkingManager.t.sol b/test/ForkingManager.t.sol index 9ab11d58..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()); @@ -463,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 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( From 1914f4484ca343c2c6dbdc4bae2ee19951f36f28 Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 28 Nov 2023 12:45:47 +0100 Subject: [PATCH 4/8] ed's other suggestion --- contracts/ForkableBridge.sol | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index 623bc69b..175444ea 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -150,36 +150,32 @@ contract ForkableBridge is ); } - function prepareSplittinTokens( - address token, - uint256 amount - ) public onlyAfterForking { - TokenWrapped(token).burn(msg.sender, amount); - childTokenAllowances[msg.sender][token][true] += amount; - childTokenAllowances[msg.sender][token][false] += amount; - } - // @inheritdoc IForkableBridge function splitTokenIntoChildTokens( address token, uint256 amount ) external onlyAfterForking { - prepareSplittinTokens(token, amount); - createChildToken(token, amount, true); - createChildToken(token, amount, false); + splitTokenIntoChildToken(token, amount, true, false); + splitTokenIntoChildToken(token, amount, false, true); } // @inheritdoc IForkableBridge - function createChildToken( + function splitTokenIntoChildToken( address token, uint256 amount, - bool firstChild + bool firstChild, + bool useChildTokenAllowances ) public onlyAfterForking { - require( - childTokenAllowances[msg.sender][token][firstChild] >= amount, - "Not enough allowance" - ); - childTokenAllowances[msg.sender][token][firstChild] -= amount; + if (useChildTokenAllowances) { + require( + childTokenAllowances[msg.sender][token][firstChild] >= amount, + "Not enough allowance" + ); + childTokenAllowances[msg.sender][token][firstChild] -= amount; + } else { + TokenWrapped(token).burn(msg.sender, amount); + childTokenAllowances[msg.sender][token][!firstChild] += amount; + } BridgeAssetOperations.createChildToken( token, amount, From 1b1010f9bf8f9b4ab408cdd4f92c41a96ab17c68 Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 28 Nov 2023 15:20:52 +0100 Subject: [PATCH 5/8] desperately trying to reduce code size, but I managed ;) --- contracts/ForkableBridge.sol | 59 +++---------------- contracts/ForkableGlobalExitRoot.sol | 15 ----- contracts/ForkableZkEVM.sol | 12 ---- contracts/ForkingManager.sol | 35 +++++------ contracts/ForkonomicToken.sol | 12 ---- contracts/interfaces/IForkableBridge.sol | 20 +++---- .../interfaces/IForkableGlobalExitRoot.sol | 4 -- contracts/interfaces/IForkableZkEVM.sol | 11 ---- contracts/interfaces/IForkonomicToken.sol | 9 --- contracts/lib/BridgeAssetOperations.sol | 25 ++++++++ contracts/mixin/ForkableStructure.sol | 31 +++------- test/ForkableBridge.t.sol | 20 ++++--- test/ForkableGlobalExitRoot.t.sol | 2 +- test/ForkableZkEVM.t.sol | 2 +- 14 files changed, 76 insertions(+), 181 deletions(-) diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index 175444ea..99d79b0b 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -19,13 +19,6 @@ contract ForkableBridge is // tokens to the children-bridge contracts address internal _hardAssetManager; - // @dev Mapping to keep track of the allowances for the child tokens minting process - // @param user Address of the token - // @param token Address of the token - // @param isChild0 Boolean to indicate if the allowance is for the first or second child-bridge contract - // @param amount Amount of tokens allowed to be minted - mapping(address => mapping(address => mapping(bool => uint256))) - public childTokenAllowances; // @inheritdoc IForkableBridge function initialize( @@ -65,7 +58,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); } @@ -87,9 +80,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; } /** @@ -107,22 +99,6 @@ contract ForkableBridge is PolygonZkEVMBridge._setAndCheckClaimed(index); } - // @inheritdoc IForkableBridge - function createChild1() external onlyForkManger returns (address) { - // process all pending deposits/messages before coping over the state root. - updateGlobalExitRoot(); - return _createChild1(); - } - - // @inheritdoc IForkableBridge - function createChild2( - address implementation - ) external onlyForkManger returns (address) { - // process all pending deposits/messages before coping over the state root. - updateGlobalExitRoot(); - return _createChild2(implementation); - } - // @inheritdoc IForkableBridge function createChildren( address implementation @@ -140,7 +116,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, @@ -150,37 +126,18 @@ contract ForkableBridge is ); } - // @inheritdoc IForkableBridge - function splitTokenIntoChildTokens( - address token, - uint256 amount - ) external onlyAfterForking { - splitTokenIntoChildToken(token, amount, true, false); - splitTokenIntoChildToken(token, amount, false, true); - } - // @inheritdoc IForkableBridge function splitTokenIntoChildToken( address token, uint256 amount, - bool firstChild, - bool useChildTokenAllowances + bool mintSecondChildAsWell ) public onlyAfterForking { - if (useChildTokenAllowances) { - require( - childTokenAllowances[msg.sender][token][firstChild] >= amount, - "Not enough allowance" - ); - childTokenAllowances[msg.sender][token][firstChild] -= amount; - } else { - TokenWrapped(token).burn(msg.sender, amount); - childTokenAllowances[msg.sender][token][!firstChild] += amount; - } - BridgeAssetOperations.createChildToken( + BridgeAssetOperations.splitTokenIntoChildToken( token, amount, - wrappedTokenToTokenInfo[token], - firstChild ? children[0] : children[1] + children[0], + mintSecondChildAsWell ? children[1] : address(0), + wrappedTokenToTokenInfo[token] ); } diff --git a/contracts/ForkableGlobalExitRoot.sol b/contracts/ForkableGlobalExitRoot.sol index 903204f3..125ca5d3 100644 --- a/contracts/ForkableGlobalExitRoot.sol +++ b/contracts/ForkableGlobalExitRoot.sol @@ -52,21 +52,6 @@ contract ForkableGlobalExitRoot is ); } - /// @dev Public interface to create children. This can only be done by the forkmanager - /// @return The addresses of the two children - function createChild1() external onlyForkManger returns (address) { - return _createChild1(); - } - - /// @dev Public interface to create children. This can only be done by the forkmanager - /// @param implementation Allows to pass a different implementation contract for the second proxied child. - /// @return The addresses of the two children - function createChild2( - address implementation - ) external onlyForkManger returns (address) { - return _createChild2(implementation); - } - /// @dev Public interface to create children. This can only be done by the forkmanager /// @param implementation Allows to pass a different implementation contract for the second proxied child. /// @return The addresses of the two children diff --git a/contracts/ForkableZkEVM.sol b/contracts/ForkableZkEVM.sol index 0397e486..0370f7ca 100644 --- a/contracts/ForkableZkEVM.sol +++ b/contracts/ForkableZkEVM.sol @@ -40,18 +40,6 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { ); } - // @inheritdoc IForkableZkEVM - function createChild1() external onlyForkManger returns (address) { - return _createChild1(); - } - - // @inheritdoc IForkableZkEVM - function createChild2( - address implementation - ) external onlyForkManger returns (address) { - return _createChild2(implementation); - } - // @inheritdoc IForkableZkEVM function createChildren( address implementation diff --git a/contracts/ForkingManager.sol b/contracts/ForkingManager.sol index 3b592793..c5d03b62 100644 --- a/contracts/ForkingManager.sol +++ b/contracts/ForkingManager.sol @@ -110,16 +110,18 @@ contract ForkingManager is IForkingManager, ForkableStructure { "ForkingManager: fork not ready" ); + NewImplementations memory newImplementations = proposedImplementations; + // Create the children of each contract NewInstances memory newInstances; - newInstances.forkingManager.one = _createChild1(); - (newInstances.bridge.one) = IForkableBridge(bridge).createChild1(); - (newInstances.zkEVM.one) = IForkableZkEVM(zkEVM).createChild1(); - (newInstances.forkonomicToken.one) = IForkonomicToken(forkonomicToken) - .createChild1(); - (newInstances.globalExitRoot.one) = IForkableGlobalExitRoot( + (newInstances.forkingManager.one, ) = _createChildren(newImplementations.forkingManagerImplementation); + (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 - ).createChild1(); + ).createChildren(newImplementations.globalExitRootImplementation); // Initialize the zkEVM contracts IPolygonZkEVM.InitializePackedParameters @@ -223,20 +225,15 @@ contract ForkingManager is IForkingManager, ForkableStructure { // Create the children of each contract NewInstances memory newInstances; - newInstances.forkingManager.two = _createChild2( - newImplementations.forkingManagerImplementation - ); - newInstances.bridge.two = IForkableBridge(bridge).createChild2( - newImplementations.bridgeImplementation - ); - newInstances.zkEVM.two = IForkableZkEVM(zkEVM).createChild2( - newImplementations.zkEVMImplementation + (,newInstances.forkingManager.two) = getChildren(); + (,newInstances.bridge.two) = IForkableBridge(bridge).getChildren(); + (,newInstances.zkEVM.two) = IForkableZkEVM(zkEVM).getChildren( ); - newInstances.forkonomicToken.two = IForkonomicToken(forkonomicToken) - .createChild2(newImplementations.forkonomicTokenImplementation); - (newInstances.globalExitRoot.two) = IForkableGlobalExitRoot( + (,newInstances.forkonomicToken.two) = IForkonomicToken(forkonomicToken) + .getChildren(); + (,newInstances.globalExitRoot.two) = IForkableGlobalExitRoot( globalExitRoot - ).createChild2(newImplementations.globalExitRootImplementation); + ).getChildren(); // Initialize the zkEVM contracts IPolygonZkEVM.InitializePackedParameters diff --git a/contracts/ForkonomicToken.sol b/contracts/ForkonomicToken.sol index aaf0ef3f..a84c647c 100644 --- a/contracts/ForkonomicToken.sol +++ b/contracts/ForkonomicToken.sol @@ -45,18 +45,6 @@ contract ForkonomicToken is _mint(to, amount); } - /// @inheritdoc IForkonomicToken - function createChild1() external onlyForkManger returns (address) { - return _createChild1(); - } - - /// @inheritdoc IForkonomicToken - function createChild2( - address implementation - ) external onlyForkManger returns (address) { - return _createChild2(implementation); - } - /// @inheritdoc IForkonomicToken function createChildren( address implementation diff --git a/contracts/interfaces/IForkableBridge.sol b/contracts/interfaces/IForkableBridge.sol index 60d52f4e..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 @@ -44,23 +45,16 @@ interface IForkableBridge is IPolygonZkEVMBridge { address implementation ) external returns (address, address); - /** - * @dev Internal function to create the children contracts. - */ - function createChild1() external returns (address); - - /** - * @dev Internal function to create the children contracts. - * @param implementation Allows to pass a different implementation contract for the second proxied child. - */ - function createChild2(address implementation) external returns (address); - /** * @dev Anyone can use their tokens to split the bridged tokens into the two corresponding children tokens * @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/IForkableGlobalExitRoot.sol b/contracts/interfaces/IForkableGlobalExitRoot.sol index b58e4573..d4b485c7 100644 --- a/contracts/interfaces/IForkableGlobalExitRoot.sol +++ b/contracts/interfaces/IForkableGlobalExitRoot.sol @@ -15,10 +15,6 @@ interface IForkableGlobalExitRoot is address _bridgeAddress ) external; - function createChild2(address implementation) external returns (address); - - function createChild1() external returns (address); - function createChildren( address implementation ) external returns (address, address); diff --git a/contracts/interfaces/IForkableZkEVM.sol b/contracts/interfaces/IForkableZkEVM.sol index f2600907..2dfec15d 100644 --- a/contracts/interfaces/IForkableZkEVM.sol +++ b/contracts/interfaces/IForkableZkEVM.sol @@ -35,17 +35,6 @@ interface IForkableZkEVM is IForkableStructure, IPolygonZkEVM { IPolygonZkEVMBridge _bridgeAddress ) external; - /** - * @dev Internal function to create the children contracts. - */ - function createChild1() external returns (address); - - /** - * @dev Internal function to create the children contracts. - * @param implementation Allows to pass a different implementation contract for the second proxied child. - */ - function createChild2(address implementation) external returns (address); - // @dev: This function is used to create the children contracts. // The initialization of the children contracts should be called in the same transaction // as the creation of the children contracts. diff --git a/contracts/interfaces/IForkonomicToken.sol b/contracts/interfaces/IForkonomicToken.sol index e8a400d7..6d38a14b 100644 --- a/contracts/interfaces/IForkonomicToken.sol +++ b/contracts/interfaces/IForkonomicToken.sol @@ -24,15 +24,6 @@ interface IForkonomicToken is IForkableStructure, IERC20Upgradeable { /// @param amount The amount of tokens to mint function mint(address to, uint256 amount) external; - /// @dev Interface for the forkManger to create children - /// @return The addresses of the two children - function createChild1() external returns (address); - - /// @dev Interface for the forkManger to create children - /// @param implementation Allows to pass a different implementation contract for the second proxied child. - /// @return The addresses of the two children - function createChild2(address implementation) external returns (address); - /// @dev Interface for the forkManger to create children /// @param implementation Allows to pass a different implementation contract for the second proxied child. /// @return The addresses of the two children diff --git a/contracts/lib/BridgeAssetOperations.sol b/contracts/lib/BridgeAssetOperations.sol index 623e126c..aeb26de2 100644 --- a/contracts/lib/BridgeAssetOperations.sol +++ b/contracts/lib/BridgeAssetOperations.sol @@ -72,6 +72,31 @@ 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 contract by the admin * @param gasTokenAddress Address of the token diff --git a/contracts/mixin/ForkableStructure.sol b/contracts/mixin/ForkableStructure.sol index 901bf692..8dc1091e 100644 --- a/contracts/mixin/ForkableStructure.sol +++ b/contracts/mixin/ForkableStructure.sol @@ -34,12 +34,12 @@ contract ForkableStructure is IForkableStructure, Initializable { } 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,25 +56,6 @@ contract ForkableStructure is IForkableStructure, Initializable { parentContract = _parentContract; } - /** - * @dev Internal function to create the children contracts. - */ - function _createChild1() internal returns (address forkingManager1) { - forkingManager1 = CreateChildren.createChild1(); - children[0] = forkingManager1; - } - - /** - * @dev Internal function to create the children contracts. - * @param implementation Allows to pass a different implementation contract for the second proxied child. - */ - function _createChild2( - address implementation - ) internal returns (address forkingManager2) { - forkingManager2 = CreateChildren.createChild2(implementation); - children[1] = forkingManager2; - } - /** * @dev Internal function to create the children contracts. * @param implementation Allows to pass a different implementation contract for the second proxied child. @@ -82,11 +63,13 @@ contract ForkableStructure is IForkableStructure, Initializable { function _createChildren( address implementation ) internal returns (address forkingManager1, address forkingManager2) { - forkingManager1 = _createChild1(); - forkingManager2 = _createChild2(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 22cb3f5b..e15651d8 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() @@ -272,10 +272,11 @@ 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) @@ -357,7 +358,8 @@ contract ForkableBridgeTest is Test { ); // Split the token - forkableBridge.splitTokenIntoChildTokens(forkableToken, amount); + forkableBridge.splitTokenIntoChildToken(forkableToken, amount, true); + // Only parent can merge vm.expectRevert(bytes("onlyAfterForking")); @@ -521,7 +523,7 @@ contract ForkableBridgeTest is Test { to ); - vm.expectRevert("Invalid to address"); + vm.expectRevert("Invalid to"); vm.prank(hardAssetManger); forkableBridge.transferHardAssetsToChild( address(erc20Token), 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()); From eb9567365269497321a4cbed624312da5e922538 Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 28 Nov 2023 15:21:32 +0100 Subject: [PATCH 6/8] linting --- contracts/ForkableBridge.sol | 3 +-- contracts/ForkingManager.sol | 23 ++++++++++++++--------- contracts/lib/BridgeAssetOperations.sol | 18 ++++-------------- test/ForkableBridge.t.sol | 2 -- 4 files changed, 19 insertions(+), 27 deletions(-) diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index 99d79b0b..16849419 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -19,7 +19,6 @@ contract ForkableBridge is // tokens to the children-bridge contracts address internal _hardAssetManager; - // @inheritdoc IForkableBridge function initialize( address _forkmanager, @@ -132,7 +131,7 @@ contract ForkableBridge is uint256 amount, bool mintSecondChildAsWell ) public onlyAfterForking { - BridgeAssetOperations.splitTokenIntoChildToken( + BridgeAssetOperations.splitTokenIntoChildToken( token, amount, children[0], diff --git a/contracts/ForkingManager.sol b/contracts/ForkingManager.sol index c5d03b62..769f50e9 100644 --- a/contracts/ForkingManager.sol +++ b/contracts/ForkingManager.sol @@ -114,9 +114,15 @@ contract ForkingManager is IForkingManager, ForkableStructure { // Create the children of each contract NewInstances memory newInstances; - (newInstances.forkingManager.one, ) = _createChildren(newImplementations.forkingManagerImplementation); - (newInstances.bridge.one, ) = IForkableBridge(bridge).createChildren(newImplementations.bridgeImplementation); - (newInstances.zkEVM.one, ) = IForkableZkEVM(zkEVM).createChildren(newImplementations.zkEVMImplementation); + (newInstances.forkingManager.one, ) = _createChildren( + newImplementations.forkingManagerImplementation + ); + (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( @@ -225,13 +231,12 @@ contract ForkingManager is IForkingManager, ForkableStructure { // 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) + (, 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( + (, newInstances.globalExitRoot.two) = IForkableGlobalExitRoot( globalExitRoot ).getChildren(); diff --git a/contracts/lib/BridgeAssetOperations.sol b/contracts/lib/BridgeAssetOperations.sol index aeb26de2..8cee5eb5 100644 --- a/contracts/lib/BridgeAssetOperations.sol +++ b/contracts/lib/BridgeAssetOperations.sol @@ -72,7 +72,7 @@ library BridgeAssetOperations { ); } - // @inheritdoc IForkableBridge + // @inheritdoc IForkableBridge function splitTokenIntoChildToken( address token, uint256 amount, @@ -80,20 +80,10 @@ library BridgeAssetOperations { address child2, PolygonZkEVMBridge.TokenInformation memory tokenInfo ) public { - TokenWrapped(token).burn(msg.sender, amount); - createChildToken( - token, - amount, - tokenInfo, - child1 - ); + TokenWrapped(token).burn(msg.sender, amount); + createChildToken(token, amount, tokenInfo, child1); if (child2 != address(0)) { - createChildToken( - token, - amount, - tokenInfo, - child2 - ); + createChildToken(token, amount, tokenInfo, child2); } } diff --git a/test/ForkableBridge.t.sol b/test/ForkableBridge.t.sol index e15651d8..aac23260 100644 --- a/test/ForkableBridge.t.sol +++ b/test/ForkableBridge.t.sol @@ -277,7 +277,6 @@ contract ForkableBridgeTest is Test { // Split the token forkableBridge.splitTokenIntoChildToken(forkableToken, amount, true); - // Assert token balances address forkableTokenChild1 = ForkableBridge(child1) .tokenInfoToWrappedToken(tokenInfoHash); @@ -360,7 +359,6 @@ contract ForkableBridgeTest is Test { // Split the token forkableBridge.splitTokenIntoChildToken(forkableToken, amount, true); - // Only parent can merge vm.expectRevert(bytes("onlyAfterForking")); ForkableBridge(child1).mergeChildTokens(forkableToken, amount + 1); From f50a5759acbe10b5b2c51c557524fbf99b329baf Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 28 Nov 2023 15:34:27 +0100 Subject: [PATCH 7/8] reintroduce onlyBeforeForking --- contracts/ForkableBridge.sol | 8 ++++---- contracts/ForkableZkEVM.sol | 12 ++++++------ contracts/ForkingManager.sol | 4 ++-- contracts/mixin/ForkableStructure.sol | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index 16849419..cf60ec67 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -199,7 +199,7 @@ contract ForkableBridge is public payable override(PolygonZkEVMBridge, IPolygonZkEVMBridge) - onlyBeforeCreatingChild1 + onlyBeforeForking { PolygonZkEVMBridge.bridgeAsset( destinationNetwork, @@ -221,7 +221,7 @@ contract ForkableBridge is payable virtual override(PolygonZkEVMBridge, IPolygonZkEVMBridge) - onlyBeforeCreatingChild1 + onlyBeforeForking { PolygonZkEVMBridge.bridgeMessage( destinationNetwork, @@ -245,7 +245,7 @@ contract ForkableBridge is ) public override(IPolygonZkEVMBridge, PolygonZkEVMBridge) - onlyBeforeCreatingChild1 + onlyBeforeForking { PolygonZkEVMBridge.claimMessage( smtProof, @@ -275,7 +275,7 @@ contract ForkableBridge is ) public override(IPolygonZkEVMBridge, PolygonZkEVMBridge) - onlyBeforeCreatingChild1 + onlyBeforeForking { PolygonZkEVMBridge.claimAsset( smtProof, diff --git a/contracts/ForkableZkEVM.sol b/contracts/ForkableZkEVM.sol index 0370f7ca..f2f0eb02 100644 --- a/contracts/ForkableZkEVM.sol +++ b/contracts/ForkableZkEVM.sol @@ -48,7 +48,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { } //////////////////////////////////////////////////////////////////////////// - // For the following functions a modifier called: onlyBeforeCreatingChild1 is added. + // For the following functions a modifier called: onlyBeforeForking is added. // This ensure that the functions do not change the consolidated state after forking. /////////////////////////////////////////////////////////////////////////// @@ -59,7 +59,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof - ) public override onlyBeforeCreatingChild1 { + ) public override onlyBeforeForking { PolygonZkEVM.verifyBatches( pendingStateNum, initNumBatch, @@ -77,7 +77,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof - ) public override onlyBeforeCreatingChild1 { + ) public override onlyBeforeForking { PolygonZkEVM.verifyBatchesTrustedAggregator( pendingStateNum, initNumBatch, @@ -90,7 +90,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { function consolidatePendingState( uint64 pendingStateNum - ) public override onlyBeforeCreatingChild1 { + ) public override onlyBeforeForking { PolygonZkEVM.consolidatePendingState(pendingStateNum); } @@ -102,7 +102,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes32[24] calldata proof - ) public override onlyBeforeCreatingChild1 { + ) public override onlyBeforeForking { PolygonZkEVM.overridePendingState( initPendingStateNum, finalPendingStateNum, @@ -119,7 +119,7 @@ contract ForkableZkEVM is ForkableStructure, IForkableZkEVM, PolygonZkEVM { function sequenceBatches( BatchData[] calldata batches, address l2Coinbase - ) public override onlyBeforeCreatingChild1 { + ) public override onlyBeforeForking { PolygonZkEVM.sequenceBatches(batches, l2Coinbase); } diff --git a/contracts/ForkingManager.sol b/contracts/ForkingManager.sol index 769f50e9..21b2fe55 100644 --- a/contracts/ForkingManager.sol +++ b/contracts/ForkingManager.sol @@ -84,7 +84,7 @@ contract ForkingManager is IForkingManager, ForkableStructure { function initiateFork( DisputeData memory _disputeData, NewImplementations calldata _newImplementations - ) external onlyBeforeCreatingChild1 { + ) external onlyBeforeForking { require(executionTimeForProposal == 0, "ForkingManager: fork pending"); // Charge the forking fee IERC20(forkonomicToken).safeTransferFrom( @@ -102,7 +102,7 @@ contract ForkingManager is IForkingManager, ForkableStructure { /** * @dev function that executes a fork proposal */ - function executeFork1() external onlyBeforeCreatingChild1 { + function executeFork1() external onlyBeforeForking { require( executionTimeForProposal != 0 && // solhint-disable-next-line not-rely-on-time diff --git a/contracts/mixin/ForkableStructure.sol b/contracts/mixin/ForkableStructure.sol index 8dc1091e..6d3853d0 100644 --- a/contracts/mixin/ForkableStructure.sol +++ b/contracts/mixin/ForkableStructure.sol @@ -17,7 +17,7 @@ contract ForkableStructure is IForkableStructure, Initializable { // but this would make the initialization more complex due to proxy construction. mapping(uint256 => address) public children; - modifier onlyBeforeCreatingChild1() { + modifier onlyBeforeForking() { require(children[0] == address(0x0), "No changes after forking"); _; } From 7a4cf0f1dd2a761b816b86cf7b405498adaac98d Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 28 Nov 2023 18:02:55 +0100 Subject: [PATCH 8/8] use more efficient storage for forkonomicToken --- contracts/ForkableBridge.sol | 13 +++++--- contracts/ForkonomicToken.sol | 39 ++++++++++++++--------- contracts/interfaces/IForkonomicToken.sol | 18 +++++------ contracts/lib/BridgeAssetOperations.sol | 25 +++++++-------- test/ForkableBridge.t.sol | 9 +++--- 5 files changed, 58 insertions(+), 46 deletions(-) diff --git a/contracts/ForkableBridge.sol b/contracts/ForkableBridge.sol index cf60ec67..29053734 100644 --- a/contracts/ForkableBridge.sol +++ b/contracts/ForkableBridge.sol @@ -135,6 +135,7 @@ contract ForkableBridge is token, amount, children[0], + // 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] ); @@ -172,15 +173,19 @@ contract ForkableBridge is * 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 - * @param firstChild boolean indicating for which child the operation should be run + * @param useFirstChild boolean indicating for which child the operation should be run */ function sendForkonomicTokensToChild( - bool firstChild + uint256 amount, + bool useFirstChild, + bool useChildTokenAllowance ) public onlyForkManger onlyAfterForking { BridgeAssetOperations.sendForkonomicTokensToChild( gasTokenAddress, - firstChild ? children[0] : children[1], - firstChild + amount, + useFirstChild ? children[0] : children[1], + useFirstChild, + useChildTokenAllowance ); } diff --git a/contracts/ForkonomicToken.sol b/contracts/ForkonomicToken.sol index a84c647c..4f9832cb 100644 --- a/contracts/ForkonomicToken.sol +++ b/contracts/ForkonomicToken.sol @@ -52,6 +52,28 @@ 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 @@ -62,23 +84,10 @@ contract ForkonomicToken is childTokenAllowances[msg.sender][true] += amount; } - /// @dev Allows anyone to finish their splitting of tokens - /// by minting them - /// @param firstChild Whether to mint the tokens of the first or second child - function finishSplittingTokens(bool firstChild) public { - IForkonomicToken(firstChild ? children[0] : children[1]).mint( - msg.sender, - childTokenAllowances[msg.sender][firstChild] - ); - childTokenAllowances[msg.sender][firstChild] = 0; - } - /// @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"); - prepareSplittingTokens(amount); - finishSplittingTokens(true); - finishSplittingTokens(false); + splitTokenAndMintOneChild(amount, true, false); + splitTokenAndMintOneChild(amount, false, true); } } diff --git a/contracts/interfaces/IForkonomicToken.sol b/contracts/interfaces/IForkonomicToken.sol index 6d38a14b..0bde2307 100644 --- a/contracts/interfaces/IForkonomicToken.sol +++ b/contracts/interfaces/IForkonomicToken.sol @@ -35,13 +35,13 @@ interface IForkonomicToken is IForkableStructure, IERC20Upgradeable { /// @param amount The amount of tokens to split function splitTokensIntoChildTokens(uint256 amount) external; - /// @dev Allows anyone to prepare the splitting of tokens - /// by burning them - /// @param amount The amount of tokens to burn - function prepareSplittingTokens(uint256 amount) external; - - /// @dev Allows anyone to finish their splitting of tokens - /// by minting them - /// @param firstChild Whether to mint the tokens of the first or second child - function finishSplittingTokens(bool firstChild) 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 8cee5eb5..5403ff0a 100644 --- a/contracts/lib/BridgeAssetOperations.sol +++ b/contracts/lib/BridgeAssetOperations.sol @@ -91,30 +91,27 @@ library BridgeAssetOperations { * @notice Function to send tokens into children-bridge contract by the admin * @param gasTokenAddress Address of the token * @param child Address of the first child-bridge contract - * @param firstChild Boolean to indicate which child to send tokens to + * @param useFirstChild Boolean to indicate which child to send tokens to */ function sendForkonomicTokensToChild( address gasTokenAddress, + uint256 amount, address child, - bool firstChild + bool useFirstChild, + bool useChildTokenAllowance ) public { - IForkonomicToken(gasTokenAddress).prepareSplittingTokens( - IERC20(gasTokenAddress).balanceOf(address(this)) + IForkonomicToken(gasTokenAddress).splitTokenAndMintOneChild( + amount, + useFirstChild, + useChildTokenAllowance ); - IForkonomicToken(gasTokenAddress).finishSplittingTokens(firstChild); (address forkonomicToken1, address forkonomicToken2) = IForkonomicToken( gasTokenAddress ).getChildren(); - if (firstChild) { - IERC20(forkonomicToken1).transfer( - child, - IERC20(forkonomicToken1).balanceOf(address(this)) - ); + if (useFirstChild) { + IERC20(forkonomicToken1).transfer(child, amount); } else { - IERC20(forkonomicToken2).transfer( - child, - IERC20(forkonomicToken2).balanceOf(address(this)) - ); + IERC20(forkonomicToken2).transfer(child, amount); } } } diff --git a/test/ForkableBridge.t.sol b/test/ForkableBridge.t.sol index aac23260..03fbc842 100644 --- a/test/ForkableBridge.t.sol +++ b/test/ForkableBridge.t.sol @@ -584,10 +584,10 @@ contract ForkableBridgeTest is Test { // Now, call the function as the forkmanager vm.expectRevert(bytes("onlyAfterForking")); vm.prank(forkmanager); - forkableBridge2.sendForkonomicTokensToChild(true); + forkableBridge2.sendForkonomicTokensToChild(10, true, false); vm.expectRevert(bytes("onlyAfterForking")); vm.prank(forkmanager); - forkableBridge2.sendForkonomicTokensToChild(false); + forkableBridge2.sendForkonomicTokensToChild(10, true, false); // Create initiate the forking process and create children address secondBridgeImplementation = address( @@ -635,10 +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(true); + forkableBridge2.sendForkonomicTokensToChild(amount, true, false); vm.prank(forkmanager); - forkableBridge2.sendForkonomicTokensToChild(false); + forkableBridge2.sendForkonomicTokensToChild(amount, false, true); // Check that the tokens were transferred correctly assertEq(