From 59f88b043d4909af3320b92a045d1ff67e7a1397 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 4 Feb 2026 10:31:36 -0800 Subject: [PATCH 1/9] feat: upgrade bridge to allow for transfer of all token assets --- contracts/src/Portal.sol | 51 ++++++- contracts/test/Portal.t.sol | 278 ++++++++++++++++++++++++++++++++++++ 2 files changed, 327 insertions(+), 2 deletions(-) create mode 100644 contracts/test/Portal.t.sol diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index 8fe07422..36b00841 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -90,16 +90,33 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// @param success Whether the withdrawal transaction was successful. event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); + /// @notice Emitted when an emergency withdrawal is executed. + /// @param recipient The address that received the funds. + /// @param token The token address (Constants.ETHER for native ETH). + /// @param amount The amount withdrawn. + event EmergencyWithdrawal(address indexed recipient, address indexed token, uint256 amount); + /// @notice Reverts when paused. modifier whenNotPaused() { if (paused()) revert CallPaused(); _; } + /// @notice Reverts if caller is not the proxy admin. + modifier onlyAdmin() { + address admin; + bytes32 adminSlot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + assembly { + admin := sload(adminSlot) + } + require(msg.sender == admin, "Portal: caller is not admin"); + _; + } + /// @notice Semantic version. - /// @custom:semver 1.0.0 + /// @custom:semver 1.1.0 function version() public pure virtual returns (string memory) { - return "1.0.0"; + return "1.1.0"; } /// @notice Constructs the OptimismPortal contract. @@ -489,6 +506,33 @@ contract Portal is Initializable, ResourceMetering, ISemver { ); } + /// @notice Emergency function to withdraw all tokens held by the portal. + /// This is intended for fund recovery in emergency situations. + /// Can be called regardless of pause state. + /// @param _recipient The address to receive the withdrawn funds. + function emergencyWithdraw(address _recipient) external onlyAdmin { + require(_recipient != address(0), "Portal: zero recipient"); + + (address token,) = gasPayingToken(); + uint256 amount; + + if (token == Constants.ETHER) { + amount = address(this).balance; + if (amount > 0) { + (bool success,) = _recipient.call{value: amount}(""); + require(success, "Portal: ETH transfer failed"); + } + } else { + amount = IERC20(token).balanceOf(address(this)); + if (amount > 0) { + _balance = 0; + IERC20(token).safeTransfer(_recipient, amount); + } + } + + emit EmergencyWithdrawal(_recipient, token, amount); + } + /// @notice Determine if a given output is finalized. /// Reverts if the call to l2Oracle.getL2Output reverts. /// Returns a boolean otherwise. @@ -505,4 +549,7 @@ contract Portal is Initializable, ResourceMetering, ISemver { function _isFinalizationPeriodElapsed(uint256 _timestamp) internal view returns (bool) { return block.timestamp > _timestamp; } + + /// @dev Reserved storage for future upgrades. + uint256[44] private __gap; } diff --git a/contracts/test/Portal.t.sol b/contracts/test/Portal.t.sol new file mode 100644 index 00000000..abe57321 --- /dev/null +++ b/contracts/test/Portal.t.sol @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import {Test} from "forge-std/Test.sol"; +import {ProxyAdmin} from "@eth-optimism-bedrock/src/universal/ProxyAdmin.sol"; +import {Proxy} from "@eth-optimism-bedrock/src/universal/Proxy.sol"; +import {ISuperchainConfig} from "@eth-optimism-bedrock/src/L1/interfaces/ISuperchainConfig.sol"; +import {ISystemConfig} from "@eth-optimism-bedrock/src/L1/interfaces/ISystemConfig.sol"; +import {IResourceMetering} from "@eth-optimism-bedrock/src/L1/interfaces/IResourceMetering.sol"; +import {Constants} from "@eth-optimism-bedrock/src/libraries/Constants.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +import {Portal} from "../src/Portal.sol"; +import {OutputOracle} from "../src/OutputOracle.sol"; + +/// @notice Mock ERC20 token for testing +contract MockERC20 is ERC20 { + constructor() ERC20("Mock Token", "MCK") {} + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } + + function decimals() public pure override returns (uint8) { + return 18; + } +} + +/// @notice Mock SuperchainConfig for testing +contract MockSuperchainConfig is ISuperchainConfig { + bool internal _paused; + address internal _guardian; + + function setPaused(bool paused_) external { + _paused = paused_; + } + + function paused() external view returns (bool) { + return _paused; + } + + function guardian() external view returns (address) { + return _guardian; + } + + function GUARDIAN_SLOT() external pure returns (bytes32) { + return bytes32(0); + } + + function PAUSED_SLOT() external pure returns (bytes32) { + return bytes32(0); + } + + function initialize(address, bool) external {} + + function pause(string memory) external {} + + function unpause() external {} + + function version() external pure returns (string memory) { + return "1.0.0"; + } +} + +/// @notice Mock SystemConfig for testing +contract MockSystemConfig { + address internal _gasPayingToken; + + function setGasPayingToken(address token) external { + _gasPayingToken = token; + } + + function gasPayingToken() external view returns (address, uint8) { + if (_gasPayingToken == address(0)) { + return (Constants.ETHER, 18); + } + return (_gasPayingToken, 18); + } + + function resourceConfig() external pure returns (IResourceMetering.ResourceConfig memory) { + return IResourceMetering.ResourceConfig({ + maxResourceLimit: 20_000_000, + elasticityMultiplier: 10, + baseFeeMaxChangeDenominator: 8, + minimumBaseFee: 1 gwei, + systemTxMaxGas: 1_000_000, + maximumBaseFee: type(uint128).max + }); + } +} + +contract PortalTest is Test { + Portal internal portalImpl; + Portal internal portal; + ProxyAdmin internal admin; + Proxy internal proxy; + MockSuperchainConfig internal superchainConfig; + MockSystemConfig internal systemConfig; + MockERC20 internal token; + + address internal recipient = makeAddr("recipient"); + address internal nonAdmin = makeAddr("nonAdmin"); + + /// @notice EIP-1967 admin slot + bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /// @notice Emitted when an emergency withdrawal is executed. + event EmergencyWithdrawal(address indexed recipient, address indexed token, uint256 amount); + + function setUp() public { + // Deploy mocks + superchainConfig = new MockSuperchainConfig(); + systemConfig = new MockSystemConfig(); + token = new MockERC20(); + + // Deploy Portal implementation + portalImpl = new Portal(); + + // Deploy proxy with admin + admin = new ProxyAdmin(address(this)); + proxy = new Proxy(address(admin)); + + // Upgrade proxy to Portal implementation + admin.upgrade(payable(address(proxy)), address(portalImpl)); + + // Get Portal interface on proxy + portal = Portal(payable(address(proxy))); + + // Initialize portal + portal.initialize({ + _l2Oracle: OutputOracle(address(0)), + _systemConfig: ISystemConfig(address(systemConfig)), + _superchainConfig: ISuperchainConfig(address(superchainConfig)) + }); + } + + function test_emergencyWithdraw_ETH_success() public { + // Fund the portal with ETH + uint256 amount = 10 ether; + vm.deal(address(portal), amount); + + uint256 recipientBalanceBefore = recipient.balance; + + // Admin withdraws (admin is ProxyAdmin, which is owned by address(this)) + // But the actual proxy admin is the ProxyAdmin contract, so we need to call from it + // Actually, the admin slot stores the ProxyAdmin address, so we need to prank as ProxyAdmin + vm.prank(address(admin)); + portal.emergencyWithdraw(recipient); + + assertEq(address(portal).balance, 0); + assertEq(recipient.balance, recipientBalanceBefore + amount); + } + + function test_emergencyWithdraw_ERC20_success() public { + // Set up custom gas token + systemConfig.setGasPayingToken(address(token)); + + // Fund the portal with ERC20 tokens + uint256 amount = 1000 ether; + token.mint(address(portal), amount); + + uint256 recipientBalanceBefore = token.balanceOf(recipient); + + // Admin withdraws + vm.prank(address(admin)); + portal.emergencyWithdraw(recipient); + + assertEq(token.balanceOf(address(portal)), 0); + assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount); + } + + function test_emergencyWithdraw_onlyAdmin_reverts() public { + // Fund the portal + vm.deal(address(portal), 10 ether); + + // Non-admin tries to withdraw + vm.prank(nonAdmin); + vm.expectRevert("Portal: caller is not admin"); + portal.emergencyWithdraw(recipient); + } + + function test_emergencyWithdraw_zeroRecipient_reverts() public { + // Fund the portal + vm.deal(address(portal), 10 ether); + + // Admin tries to withdraw to zero address + vm.prank(address(admin)); + vm.expectRevert("Portal: zero recipient"); + portal.emergencyWithdraw(address(0)); + } + + function test_emergencyWithdraw_worksWhenPaused() public { + // Fund the portal + uint256 amount = 10 ether; + vm.deal(address(portal), amount); + + // Pause the superchain config + superchainConfig.setPaused(true); + assertTrue(portal.paused()); + + uint256 recipientBalanceBefore = recipient.balance; + + // Admin can still withdraw even when paused + vm.prank(address(admin)); + portal.emergencyWithdraw(recipient); + + assertEq(address(portal).balance, 0); + assertEq(recipient.balance, recipientBalanceBefore + amount); + } + + function test_emergencyWithdraw_emitsEvent() public { + // Fund the portal + uint256 amount = 10 ether; + vm.deal(address(portal), amount); + + // Expect the EmergencyWithdrawal event + vm.expectEmit(true, true, false, true); + emit EmergencyWithdrawal(recipient, Constants.ETHER, amount); + + vm.prank(address(admin)); + portal.emergencyWithdraw(recipient); + } + + function test_emergencyWithdraw_emitsEvent_ERC20() public { + // Set up custom gas token + systemConfig.setGasPayingToken(address(token)); + + // Fund the portal + uint256 amount = 1000 ether; + token.mint(address(portal), amount); + + // Expect the EmergencyWithdrawal event + vm.expectEmit(true, true, false, true); + emit EmergencyWithdrawal(recipient, address(token), amount); + + vm.prank(address(admin)); + portal.emergencyWithdraw(recipient); + } + + function test_emergencyWithdraw_zeroBalance_ETH() public { + // Portal has no ETH + assertEq(address(portal).balance, 0); + + uint256 recipientBalanceBefore = recipient.balance; + + // Admin withdraws (should succeed with 0 amount) + vm.expectEmit(true, true, false, true); + emit EmergencyWithdrawal(recipient, Constants.ETHER, 0); + + vm.prank(address(admin)); + portal.emergencyWithdraw(recipient); + + assertEq(recipient.balance, recipientBalanceBefore); + } + + function test_emergencyWithdraw_zeroBalance_ERC20() public { + // Set up custom gas token + systemConfig.setGasPayingToken(address(token)); + + // Portal has no tokens + assertEq(token.balanceOf(address(portal)), 0); + + uint256 recipientBalanceBefore = token.balanceOf(recipient); + + // Admin withdraws (should succeed with 0 amount) + vm.expectEmit(true, true, false, true); + emit EmergencyWithdrawal(recipient, address(token), 0); + + vm.prank(address(admin)); + portal.emergencyWithdraw(recipient); + + assertEq(token.balanceOf(recipient), recipientBalanceBefore); + } + + function test_version() public view { + assertEq(portal.version(), "1.1.0"); + } +} From c3cfd5482c664811964b947f0442083d7adef712 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 10 Feb 2026 17:25:46 -0800 Subject: [PATCH 2/9] support other assets withdrawl and function overloading --- contracts/src/Portal.sol | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index 36b00841..96047088 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -506,16 +506,28 @@ contract Portal is Initializable, ResourceMetering, ISemver { ); } - /// @notice Emergency function to withdraw all tokens held by the portal. - /// This is intended for fund recovery in emergency situations. + /// @notice Allows owner to withdraw the gas paying token held by the portal. /// Can be called regardless of pause state. /// @param _recipient The address to receive the withdrawn funds. - function emergencyWithdraw(address _recipient) external onlyAdmin { + function chainOwnerExitPortal(address _recipient) external onlyAdmin { + chainOwnerExitPortal(address(0), _recipient); + } + + /// @notice Allows owner to withdraw all tokens held by the portal. + /// Can be called regardless of pause state. + /// @param _asset The token address to withdraw, or address(0) for the gas paying token. + /// @param _recipient The address to receive the withdrawn funds. + function chainOwnerExitPortal(address _asset, address _recipient) public onlyAdmin { require(_recipient != address(0), "Portal: zero recipient"); - (address token,) = gasPayingToken(); - uint256 amount; + address token; + if (_asset != address(0)) { + token = _asset; + } else { + (token,) = gasPayingToken(); + } + uint256 amount; if (token == Constants.ETHER) { amount = address(this).balance; if (amount > 0) { @@ -525,7 +537,10 @@ contract Portal is Initializable, ResourceMetering, ISemver { } else { amount = IERC20(token).balanceOf(address(this)); if (amount > 0) { - _balance = 0; + (address gasToken,) = gasPayingToken(); + if (token == gasToken) { + _balance = 0; + } IERC20(token).safeTransfer(_recipient, amount); } } @@ -548,8 +563,4 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// @return Whether or not the finalization period has elapsed. function _isFinalizationPeriodElapsed(uint256 _timestamp) internal view returns (bool) { return block.timestamp > _timestamp; - } - - /// @dev Reserved storage for future upgrades. - uint256[44] private __gap; -} + } \ No newline at end of file From 1ff1b41237b509b14b3845878c1d4426dec1a503 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 20 Feb 2026 18:34:17 -0800 Subject: [PATCH 3/9] update test and portal --- contracts/src/Portal.sol | 3 ++- contracts/test/Portal.t.sol | 36 ++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index 96047088..374dd796 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -563,4 +563,5 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// @return Whether or not the finalization period has elapsed. function _isFinalizationPeriodElapsed(uint256 _timestamp) internal view returns (bool) { return block.timestamp > _timestamp; - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/contracts/test/Portal.t.sol b/contracts/test/Portal.t.sol index abe57321..1aeff0d3 100644 --- a/contracts/test/Portal.t.sol +++ b/contracts/test/Portal.t.sol @@ -134,7 +134,7 @@ contract PortalTest is Test { }); } - function test_emergencyWithdraw_ETH_success() public { + function test_chainOwnerExitPortal_ETH_success() public { // Fund the portal with ETH uint256 amount = 10 ether; vm.deal(address(portal), amount); @@ -145,13 +145,13 @@ contract PortalTest is Test { // But the actual proxy admin is the ProxyAdmin contract, so we need to call from it // Actually, the admin slot stores the ProxyAdmin address, so we need to prank as ProxyAdmin vm.prank(address(admin)); - portal.emergencyWithdraw(recipient); + portal.chainOwnerExitPortal(recipient); assertEq(address(portal).balance, 0); assertEq(recipient.balance, recipientBalanceBefore + amount); } - function test_emergencyWithdraw_ERC20_success() public { + function test_chainOwnerExitPortal_ERC20_success() public { // Set up custom gas token systemConfig.setGasPayingToken(address(token)); @@ -163,33 +163,33 @@ contract PortalTest is Test { // Admin withdraws vm.prank(address(admin)); - portal.emergencyWithdraw(recipient); + portal.chainOwnerExitPortal(recipient); assertEq(token.balanceOf(address(portal)), 0); assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount); } - function test_emergencyWithdraw_onlyAdmin_reverts() public { + function test_chainOwnerExitPortal_onlyAdmin_reverts() public { // Fund the portal vm.deal(address(portal), 10 ether); // Non-admin tries to withdraw vm.prank(nonAdmin); vm.expectRevert("Portal: caller is not admin"); - portal.emergencyWithdraw(recipient); + portal.chainOwnerExitPortal(recipient); } - function test_emergencyWithdraw_zeroRecipient_reverts() public { + function test_chainOwnerExitPortal_zeroRecipient_reverts() public { // Fund the portal vm.deal(address(portal), 10 ether); // Admin tries to withdraw to zero address vm.prank(address(admin)); vm.expectRevert("Portal: zero recipient"); - portal.emergencyWithdraw(address(0)); + portal.chainOwnerExitPortal(address(0)); } - function test_emergencyWithdraw_worksWhenPaused() public { + function test_chainOwnerExitPortal_worksWhenPaused() public { // Fund the portal uint256 amount = 10 ether; vm.deal(address(portal), amount); @@ -202,13 +202,13 @@ contract PortalTest is Test { // Admin can still withdraw even when paused vm.prank(address(admin)); - portal.emergencyWithdraw(recipient); + portal.chainOwnerExitPortal(recipient); assertEq(address(portal).balance, 0); assertEq(recipient.balance, recipientBalanceBefore + amount); } - function test_emergencyWithdraw_emitsEvent() public { + function test_chainOwnerExitPortal_emitsEvent() public { // Fund the portal uint256 amount = 10 ether; vm.deal(address(portal), amount); @@ -218,10 +218,10 @@ contract PortalTest is Test { emit EmergencyWithdrawal(recipient, Constants.ETHER, amount); vm.prank(address(admin)); - portal.emergencyWithdraw(recipient); + portal.chainOwnerExitPortal(recipient); } - function test_emergencyWithdraw_emitsEvent_ERC20() public { + function test_chainOwnerExitPortal_emitsEvent_ERC20() public { // Set up custom gas token systemConfig.setGasPayingToken(address(token)); @@ -234,10 +234,10 @@ contract PortalTest is Test { emit EmergencyWithdrawal(recipient, address(token), amount); vm.prank(address(admin)); - portal.emergencyWithdraw(recipient); + portal.chainOwnerExitPortal(recipient); } - function test_emergencyWithdraw_zeroBalance_ETH() public { + function test_chainOwnerExitPortal_zeroBalance_ETH() public { // Portal has no ETH assertEq(address(portal).balance, 0); @@ -248,12 +248,12 @@ contract PortalTest is Test { emit EmergencyWithdrawal(recipient, Constants.ETHER, 0); vm.prank(address(admin)); - portal.emergencyWithdraw(recipient); + portal.chainOwnerExitPortal(recipient); assertEq(recipient.balance, recipientBalanceBefore); } - function test_emergencyWithdraw_zeroBalance_ERC20() public { + function test_chainOwnerExitPortal_zeroBalance_ERC20() public { // Set up custom gas token systemConfig.setGasPayingToken(address(token)); @@ -267,7 +267,7 @@ contract PortalTest is Test { emit EmergencyWithdrawal(recipient, address(token), 0); vm.prank(address(admin)); - portal.emergencyWithdraw(recipient); + portal.chainOwnerExitPortal(recipient); assertEq(token.balanceOf(recipient), recipientBalanceBefore); } From f004b734a7f3ee30ecf6302520e85afadb5ed412 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 23 Feb 2026 13:07:25 -0800 Subject: [PATCH 4/9] fix tests --- contracts/src/Portal.sol | 8 ++++---- contracts/test/Portal.t.sol | 41 +++++++++++++++---------------------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index 374dd796..820f3ad7 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -90,11 +90,11 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// @param success Whether the withdrawal transaction was successful. event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); - /// @notice Emitted when an emergency withdrawal is executed. + /// @notice Emitted when the chain owner executes a withdrawal. /// @param recipient The address that received the funds. /// @param token The token address (Constants.ETHER for native ETH). /// @param amount The amount withdrawn. - event EmergencyWithdrawal(address indexed recipient, address indexed token, uint256 amount); + event ChainOwnerExitWithdrawal(address indexed recipient, address indexed token, uint256 amount); /// @notice Reverts when paused. modifier whenNotPaused() { @@ -509,7 +509,7 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// @notice Allows owner to withdraw the gas paying token held by the portal. /// Can be called regardless of pause state. /// @param _recipient The address to receive the withdrawn funds. - function chainOwnerExitPortal(address _recipient) external onlyAdmin { + function chainOwnerExitPortalNetworkToken(address _recipient) external onlyAdmin { chainOwnerExitPortal(address(0), _recipient); } @@ -545,7 +545,7 @@ contract Portal is Initializable, ResourceMetering, ISemver { } } - emit EmergencyWithdrawal(_recipient, token, amount); + emit ChainOwnerExitWithdrawal(recipient, token, amount); } /// @notice Determine if a given output is finalized. diff --git a/contracts/test/Portal.t.sol b/contracts/test/Portal.t.sol index 1aeff0d3..5a02848d 100644 --- a/contracts/test/Portal.t.sol +++ b/contracts/test/Portal.t.sol @@ -134,7 +134,7 @@ contract PortalTest is Test { }); } - function test_chainOwnerExitPortal_ETH_success() public { + function test_chainOwnerExitPortalNetworkToken_ETH_success() public { // Fund the portal with ETH uint256 amount = 10 ether; vm.deal(address(portal), amount); @@ -145,51 +145,48 @@ contract PortalTest is Test { // But the actual proxy admin is the ProxyAdmin contract, so we need to call from it // Actually, the admin slot stores the ProxyAdmin address, so we need to prank as ProxyAdmin vm.prank(address(admin)); - portal.chainOwnerExitPortal(recipient); + portal.chainOwnerExitPortalNetworkToken(recipient); assertEq(address(portal).balance, 0); assertEq(recipient.balance, recipientBalanceBefore + amount); } function test_chainOwnerExitPortal_ERC20_success() public { - // Set up custom gas token - systemConfig.setGasPayingToken(address(token)); - // Fund the portal with ERC20 tokens uint256 amount = 1000 ether; token.mint(address(portal), amount); uint256 recipientBalanceBefore = token.balanceOf(recipient); - // Admin withdraws + // Admin withdraws specific ERC20 asset vm.prank(address(admin)); - portal.chainOwnerExitPortal(recipient); + portal.chainOwnerExitPortal(address(token), recipient); assertEq(token.balanceOf(address(portal)), 0); assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount); } - function test_chainOwnerExitPortal_onlyAdmin_reverts() public { + function test_chainOwnerExitPortalNetworkToken_onlyAdmin_reverts() public { // Fund the portal vm.deal(address(portal), 10 ether); // Non-admin tries to withdraw vm.prank(nonAdmin); vm.expectRevert("Portal: caller is not admin"); - portal.chainOwnerExitPortal(recipient); + portal.chainOwnerExitPortalNetworkToken(recipient); } - function test_chainOwnerExitPortal_zeroRecipient_reverts() public { + function test_chainOwnerExitPortalNetworkToken_zeroRecipient_reverts() public { // Fund the portal vm.deal(address(portal), 10 ether); // Admin tries to withdraw to zero address vm.prank(address(admin)); vm.expectRevert("Portal: zero recipient"); - portal.chainOwnerExitPortal(address(0)); + portal.chainOwnerExitPortalNetworkToken(address(0)); } - function test_chainOwnerExitPortal_worksWhenPaused() public { + function test_chainOwnerExitPortalNetworkToken_worksWhenPaused() public { // Fund the portal uint256 amount = 10 ether; vm.deal(address(portal), amount); @@ -202,13 +199,13 @@ contract PortalTest is Test { // Admin can still withdraw even when paused vm.prank(address(admin)); - portal.chainOwnerExitPortal(recipient); + portal.chainOwnerExitPortalNetworkToken(recipient); assertEq(address(portal).balance, 0); assertEq(recipient.balance, recipientBalanceBefore + amount); } - function test_chainOwnerExitPortal_emitsEvent() public { + function test_chainOwnerExitPortalNetworkToken_emitsEvent() public { // Fund the portal uint256 amount = 10 ether; vm.deal(address(portal), amount); @@ -218,13 +215,10 @@ contract PortalTest is Test { emit EmergencyWithdrawal(recipient, Constants.ETHER, amount); vm.prank(address(admin)); - portal.chainOwnerExitPortal(recipient); + portal.chainOwnerExitPortalNetworkToken(recipient); } function test_chainOwnerExitPortal_emitsEvent_ERC20() public { - // Set up custom gas token - systemConfig.setGasPayingToken(address(token)); - // Fund the portal uint256 amount = 1000 ether; token.mint(address(portal), amount); @@ -234,10 +228,10 @@ contract PortalTest is Test { emit EmergencyWithdrawal(recipient, address(token), amount); vm.prank(address(admin)); - portal.chainOwnerExitPortal(recipient); + portal.chainOwnerExitPortal(address(token), recipient); } - function test_chainOwnerExitPortal_zeroBalance_ETH() public { + function test_chainOwnerExitPortalNetworkToken_zeroBalance_ETH() public { // Portal has no ETH assertEq(address(portal).balance, 0); @@ -248,15 +242,12 @@ contract PortalTest is Test { emit EmergencyWithdrawal(recipient, Constants.ETHER, 0); vm.prank(address(admin)); - portal.chainOwnerExitPortal(recipient); + portal.chainOwnerExitPortalNetworkToken(recipient); assertEq(recipient.balance, recipientBalanceBefore); } function test_chainOwnerExitPortal_zeroBalance_ERC20() public { - // Set up custom gas token - systemConfig.setGasPayingToken(address(token)); - // Portal has no tokens assertEq(token.balanceOf(address(portal)), 0); @@ -267,7 +258,7 @@ contract PortalTest is Test { emit EmergencyWithdrawal(recipient, address(token), 0); vm.prank(address(admin)); - portal.chainOwnerExitPortal(recipient); + portal.chainOwnerExitPortal(address(token), recipient); assertEq(token.balanceOf(recipient), recipientBalanceBefore); } From 75e4ab095cb07a06c973b8614a5e34448635ce4b Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 2 Mar 2026 07:57:46 -0800 Subject: [PATCH 5/9] new chain owner exit script --- .../84532/run-1771885109540.json | 69 +++ .../84532/run-1772412607872.json | 69 +++ .../84532/run-latest.json | 69 +++ contracts/remappings.txt | 1 + .../script/DeployPortalWithChainExit.s.sol | 15 + contracts/src/Portal.sol | 77 +-- contracts/src/PortalWithChainExit.sol | 585 ++++++++++++++++++ ...l.t.sol => PortalWithChainOwnerExit.t.sol} | 64 +- 8 files changed, 845 insertions(+), 104 deletions(-) create mode 100644 contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-1771885109540.json create mode 100644 contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-1772412607872.json create mode 100644 contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-latest.json create mode 100644 contracts/script/DeployPortalWithChainExit.s.sol create mode 100644 contracts/src/PortalWithChainExit.sol rename contracts/test/{Portal.t.sol => PortalWithChainOwnerExit.t.sol} (80%) diff --git a/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-1771885109540.json b/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-1771885109540.json new file mode 100644 index 00000000..3d56fe54 --- /dev/null +++ b/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-1771885109540.json @@ -0,0 +1,69 @@ +{ + "transactions": [ + { + "hash": "0x0cd8ac2902bb3ed5269dacb3f9925c70e3cd8b00f61f2c2ff1ff8c2953677ace", + "transactionType": "CREATE", + "contractName": "PortalWithChainExit", + "contractAddress": "0x60fb133b3eebc3a62615700dee3c7d783fc5bd66", + "function": null, + "arguments": null, + "transaction": { + "from": "0xbc89eded043666121f491e2f311b1f4e96affd53", + "gas": "0x5ecdc0", + "value": "0x0", + "input": "0x60806040523480156200001157600080fd5b50620000206000808062000026565b62000282565b600054610100900460ff1615808015620000475750600054600160ff909116105b806200007757506200006430620001b460201b62001c161760201c565b15801562000077575060005460ff166001145b620000e05760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000104576000805461ff0019166101001790555b603580546001600160a01b038087166001600160a01b03199283161790925560368054868416908316179055603480548584169216919091179055603254166200015d57603280546001600160a01b03191661dead1790555b62000167620001c3565b8015620001ae576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b6001600160a01b03163b151590565b600054610100900460ff16620002305760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401620000d7565b600154600160c01b90046001600160401b0316600003620002805760408051606081018252633b9aca0080825260006020830152436001600160401b031691909201819052600160c01b02176001555b565b6154a980620002926000396000f3fe6080604052600436106101795760003560e01c806381119581116100cb578063a14238e71161007f578063c0c53b8b11610059578063c0c53b8b14610495578063cff0ab96146104b5578063e9e05c421461055657600080fd5b8063a14238e714610409578063a35d99df14610439578063b69ef8a81461047257600080fd5b80638c3152e9116100b05780638c3152e91461038f5780639b5f694a146103af5780639bf62d82146103dc57600080fd5b8063811195811461036f5780638b4c40b01461019e57600080fd5b806347f55db51161012d5780635c975abb116101075780635c975abb1461030a5780636dbffb781461032f57806371cfaa3f1461034f57600080fd5b806347f55db51461027e5780634870496f1461029e57806354fd4d50146102be57600080fd5b806333d7e2bd1161015e57806333d7e2bd146101e557806335e80ab31461023c578063452a93201461026957600080fd5b8063149f2f22146101a5578063326cec29146101c557600080fd5b366101a05761019e3334620186a0600060405180602001604052806000815250610564565b005b600080fd5b3480156101b157600080fd5b5061019e6101c036600461487e565b610609565b3480156101d157600080fd5b5061019e6101e0366004614903565b61084a565b3480156101f157600080fd5b506036546102129073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561024857600080fd5b506034546102129073ffffffffffffffffffffffffffffffffffffffff1681565b34801561027557600080fd5b50610212610902565b34801561028a57600080fd5b5061019e6102993660046149cf565b61099b565b3480156102aa57600080fd5b5061019e6102b93660046149cf565b61123b565b3480156102ca57600080fd5b50604080518082018252600581527f312e312e30000000000000000000000000000000000000000000000000000000602082015290516102339190614b21565b34801561031657600080fd5b5061031f61124f565b6040519015158152602001610233565b34801561033b57600080fd5b5061031f61034a366004614b34565b6112e3565b34801561035b57600080fd5b5061019e61036a366004614b5c565b61139e565b34801561037b57600080fd5b5061019e61038a366004614ba2565b61155e565b34801561039b57600080fd5b5061019e6103aa366004614bdb565b611937565b3480156103bb57600080fd5b506035546102129073ffffffffffffffffffffffffffffffffffffffff1681565b3480156103e857600080fd5b506032546102129073ffffffffffffffffffffffffffffffffffffffff1681565b34801561041557600080fd5b5061031f610424366004614b34565b60336020526000908152604090205460ff1681565b34801561044557600080fd5b50610459610454366004614c18565b611979565b60405167ffffffffffffffff9091168152602001610233565b34801561047e57600080fd5b50610487611992565b604051908152602001610233565b3480156104a157600080fd5b5061019e6104b0366004614c33565b6119ec565b3480156104c157600080fd5b5060015461051d906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff9283166020850152911690820152606001610233565b61019e610564366004614c7e565b8260005a90506000610574611c32565b50905073ffffffffffffffffffffffffffffffffffffffff811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015906105b057503415155b156105e7576040517ff2365b5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105f5883489898989611ccf565b506106008282611e7b565b50505050505050565b8260005a90506000610619611c32565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff82160161068b576040517f0eaf3c0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b876037600082825461069d9190614d2a565b90915550506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa15801561070f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107339190614d42565b905061075773ffffffffffffffffffffffffffffffffffffffff831633308c612148565b6107618982614d2a565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa1580156107cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ef9190614d42565b14610826576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108348a8a8a8a8a8a611ccf565b50506108408282611e7b565b5050505050505050565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61038054903373ffffffffffffffffffffffffffffffffffffffff8316146108f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f506f7274616c3a2063616c6c6572206973206e6f742061646d696e000000000060448201526064015b60405180910390fd5b6108fd60008461155e565b505050565b603454604080517f452a9320000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163452a93209160048083019260209291908290030181865afa158015610972573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109969190614d5b565b905090565b6109a361124f565b156109da576040517ff480973e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610a43576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6035546040517fa25ae5570000000000000000000000000000000000000000000000000000000081526004810186905260009173ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610ab3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad79190614d98565b519050610af1610aec36869003860186614dfd565b612224565b8114610b7f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f66000000000000000000000000000000000000000000000060648201526084016108e9565b6000610b8a87612280565b90506000816000604051602001610bab929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610c5e9101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610c548789614e63565b89604001356122b0565b610cea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f66000000000000000000000000000060648201526084016108e9565b876040015173ffffffffffffffffffffffffffffffffffffffff16886020015173ffffffffffffffffffffffffffffffffffffffff16837f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f6260405160405180910390a460325473ffffffffffffffffffffffffffffffffffffffff1661dead14610da0576040517f9396d15600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526033602052604090205460ff1615610e3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a6564000000000000000000000060648201526084016108e9565b6000828152603360209081526040822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558901516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff000000000000000000000000000000000000000090921691909117905580610eca611c32565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff821601610f2d57610f268a604001518b608001518c606001518d60a001516122d4565b9150611180565b8073ffffffffffffffffffffffffffffffffffffffff168a6040015173ffffffffffffffffffffffffffffffffffffffff1603610f96576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608a01511561115757896060015160376000828254610fb69190614ee7565b90915550506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611028573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104c9190614d42565b90506110818b604001518c606001518473ffffffffffffffffffffffffffffffffffffffff166123329092919063ffffffff16565b60608b01516110909082614ee7565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa1580156110fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061111e9190614d42565b14611155576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60a08a0151511561117b57610f268a604001518b6080015160008d60a001516122d4565b600191505b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405184907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b906111e290851515815260200190565b60405180910390a2811580156111f85750326001145b1561122f576040517feeae4ed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050505050505050565b611248858585858561099b565b5050505050565b603454604080517f5c975abb000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691635c975abb9160048083019260209291908290030181865afa1580156112bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109969190614efe565b6035546040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018390526000916113989173ffffffffffffffffffffffffffffffffffffffff9091169063a25ae55790602401606060405180830381865afa158015611359573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137d9190614d98565b602001516fffffffffffffffffffffffffffffffff16421190565b92915050565b60365473ffffffffffffffffffffffffffffffffffffffff1633146113ef576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113fb62030d40612388565b60405173ffffffffffffffffffffffffffffffffffffffff8516602482015260ff8416604482015260648101839052608481018290526000907342000000000000000000000000000000000000159073deaddeaddeaddeaddeaddeaddeaddeaddead0001907fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32908490819062030d4090829060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f71cfaa3f00000000000000000000000000000000000000000000000000000000179052905161151896959493929101614f1b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261155091614b21565b60405180910390a450505050565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61038054903373ffffffffffffffffffffffffffffffffffffffff831614611601576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f506f7274616c3a2063616c6c6572206973206e6f742061646d696e000000000060448201526064016108e9565b73ffffffffffffffffffffffffffffffffffffffff831661167e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f506f7274616c3a207a65726f20726563697069656e740000000000000000000060448201526064016108e9565b600073ffffffffffffffffffffffffffffffffffffffff8516156116a35750836116af565b6116ab611c32565b5090505b60007fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8316016117c757504780156117c25760008573ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114611750576040519150601f19603f3d011682016040523d82523d6000602084013e611755565b606091505b50509050806117c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f506f7274616c3a20455448207472616e73666572206661696c6564000000000060448201526064016108e9565b505b6118c6565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611831573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118559190614d42565b905080156118c6576000611867611c32565b5090508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036118a35760006037555b6118c473ffffffffffffffffffffffffffffffffffffffff84168784612332565b505b8173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f38fbe7c102c02d78250517e4e56bb32e9762f1346cf9713e5efff41d8fb5d9928360405161192591815260200190565b60405180910390a3505050505050565b565b61193f61124f565b15611976576040517ff480973e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6000611986826010614f80565b61139890615208614fb0565b60008061199d611c32565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8216016119e4574791505090565b505060375490565b600054610100900460ff1615808015611a0c5750600054600160ff909116105b80611a265750303b158015611a26575060005460ff166001145b611ab2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108e9565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611b1057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6035805473ffffffffffffffffffffffffffffffffffffffff8087167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556036805486841690831617905560348054858416921691909117905560325416611ba557603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790555b611bad6123ea565b8015611c1057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b603654604080517f4397dfef0000000000000000000000000000000000000000000000000000000081528151600093849373ffffffffffffffffffffffffffffffffffffffff90911692634397dfef92600480830193928290030181865afa158015611ca2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc69190614fdc565b90939092509050565b818015611cf1575073ffffffffffffffffffffffffffffffffffffffff861615155b15611d28576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d328151611979565b67ffffffffffffffff168367ffffffffffffffff161015611d7f576040517f4929b80800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6201d4c081511115611dbd576040517f73052b0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33328114611dde575033731111000000000000000000000000000000001111015b60008686868686604051602001611df9959493929190614f1b565b604051602081830303815290604052905060008873ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c3284604051611e699190614b21565b60405180910390a45050505050505050565b600154600090611eb1907801000000000000000000000000000000000000000000000000900467ffffffffffffffff1643614ee7565b90506000611ebd6124fd565b90506000816020015160ff16826000015163ffffffff16611ede919061503a565b9050821561201557600154600090611f15908390700100000000000000000000000000000000900467ffffffffffffffff166150a2565b90506000836040015160ff1683611f2c9190615116565b600154611f4c9084906fffffffffffffffffffffffffffffffff16615116565b611f56919061503a565b600154909150600090611fa790611f809084906fffffffffffffffffffffffffffffffff166151d2565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff1661263a565b90506001861115611fd657611fd3611f8082876040015160ff1660018a611fce9190614ee7565b612659565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090612048908490700100000000000000000000000000000000900467ffffffffffffffff16614fb0565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff1613156120d5576040517f77ebef4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154600090612101906fffffffffffffffffffffffffffffffff1667ffffffffffffffff8816615246565b9050600061211348633b9aca006126ae565b61211d9083615283565b905060005a61212c9088614ee7565b905080821115610840576108406121438284614ee7565b6126c5565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052611c109085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526126ee565b60008160000151826020015183604001518460600151604051602001612263949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097612263979096959101615297565b6000806122bc866127fa565b90506122ca8186868661282c565b9695505050505050565b60008060006122e486600061285c565b90508061231a576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080855160208701888b5af1979650505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526108fd9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016121a2565b6001805463ffffffff831691906010906123c1908490700100000000000000000000000000000000900467ffffffffffffffff16614fb0565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555050565b600054610100900460ff16612481576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108e9565b6001547801000000000000000000000000000000000000000000000000900467ffffffffffffffff166000036119355760408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152603654604080517fcc731b02000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163cc731b029160048083019260c09291908290030181865afa15801561259f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c39190615302565b90506040518060c00160405280826000015163ffffffff168152602001826020015160ff168152602001826040015160ff168152602001826060015163ffffffff168152602001826080015163ffffffff1681526020018260a001516fffffffffffffffffffffffffffffffff1681525091505090565b600061264f612649858561287a565b8361288a565b90505b9392505050565b6000670de0b6b3a764000061269a612671858361503a565b61268390670de0b6b3a76400006150a2565b61269585670de0b6b3a7640000615116565b612899565b6126a49086615116565b61264f919061503a565b6000818310156126be5781612652565b5090919050565b6000805a90505b825a6126d89083614ee7565b10156108fd576126e7826153a5565b91506126cc565b6000612750826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166128ca9092919063ffffffff16565b8051909150156108fd578080602001905181019061276e9190614efe565b6108fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016108e9565b6060818051906020012060405160200161281691815260200190565b6040516020818303038152906040529050919050565b60006128538461283d8786866128d9565b8051602091820120825192909101919091201490565b95945050505050565b600080603f83619c4001026040850201603f5a021015949350505050565b6000818312156126be5781612652565b60008183126126be5781612652565b6000612652670de0b6b3a7640000836128b186613357565b6128bb9190615116565b6128c5919061503a565b61359b565b606061264f84846000856137da565b60606000845111612946576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b6579000000000000000000000060448201526064016108e9565b600061295184613970565b9050600061295e86613a5c565b905060008460405160200161297591815260200190565b60405160208183030381529060405290506000805b84518110156132ce5760008582815181106129a7576129a76153dd565b602002602001015190508451831115612a42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e67746800000000000000000000000000000000000060648201526084016108e9565b82600003612afb5780518051602091820120604051612a9092612a6a92910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b612af6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f74206861736800000060448201526064016108e9565b612c52565b805151602011612bb15780518051602091820120604051612b2592612a6a92910190815260200190565b612af6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c20686173680000000000000000000000000000000000000000000000000060648201526084016108e9565b805184516020808701919091208251919092012014612c52576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f652068617368000000000000000000000000000000000000000000000000000060648201526084016108e9565b612c5e60106001614d2a565b81602001515103612e3a5784518303612dd257612c988160200151601081518110612c8b57612c8b6153dd565b6020026020010151613abf565b96506000875111612d2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e636829000000000060648201526084016108e9565b60018651612d399190614ee7565b8214612dc7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e63682900000000000060648201526084016108e9565b505050505050612652565b6000858481518110612de657612de66153dd565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612e1157612e116153dd565b60200260200101519050612e2481613b73565b9550612e31600186614d2a565b945050506132bb565b600281602001515103613233576000612e5282613b98565b9050600081600081518110612e6957612e696153dd565b016020015160f81c90506000612e8060028361540c565b612e8b90600261542e565b90506000612e9c848360ff16613bbc565b90506000612eaa8a89613bbc565b90506000612eb88383613bf2565b905080835114612f4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b657900000000000060648201526084016108e9565b60ff851660021480612f5f575060ff85166003145b1561314e5780825114612ff4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e64657200000060648201526084016108e9565b61300e8760200151600181518110612c8b57612c8b6153dd565b9c5060008d51116130a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c656166290000000000000060648201526084016108e9565b60018c516130af9190614ee7565b881461313d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c65616629000000000000000060648201526084016108e9565b505050505050505050505050612652565b60ff85161580613161575060ff85166001145b156131a05761318d8760200151600181518110613180576131806153dd565b6020026020010151613b73565b9950613199818a614d2a565b9850613228565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e20707265666978000000000000000000000000000060648201526084016108e9565b5050505050506132bb565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f646500000000000000000000000000000000000000000000000060648201526084016108e9565b50806132c6816153a5565b91505061298a565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e747300000000000000000000000000000000000000000000000000000060648201526084016108e9565b60008082136133c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e4544000000000000000000000000000000000000000000000060448201526064016108e9565b600060606133cf84613ca6565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136135cc57506000919050565b680755bf798b4a1bf1e5821261363e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f57000000000000000000000000000000000000000060448201526064016108e9565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b60608247101561386c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016108e9565b73ffffffffffffffffffffffffffffffffffffffff85163b6138ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108e9565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516139139190615451565b60006040518083038185875af1925050503d8060008114613950576040519150601f19603f3d011682016040523d82523d6000602084013e613955565b606091505b5091509150613965828286613d7c565b979650505050505050565b80516060908067ffffffffffffffff81111561398e5761398e614772565b6040519080825280602002602001820160405280156139d357816020015b60408051808201909152606080825260208201528152602001906001900390816139ac5790505b50915060005b81811015613a555760405180604001604052808583815181106139fe576139fe6153dd565b60200260200101518152602001613a2d868481518110613a2057613a206153dd565b6020026020010151613dcf565b815250838281518110613a4257613a426153dd565b60209081029190910101526001016139d9565b5050919050565b606080604051905082518060011b603f8101601f1916830160405280835250602084016020830160005b83811015613ab4578060011b82018184015160001a8060041c8253600f811660018301535050600101613a86565b509295945050505050565b60606000806000613acf85613de2565b919450925090506000816001811115613aea57613aea61546d565b14613b21576040517f1ff9b2e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613b2b8284614d2a565b855114613b64576040517f5c5537b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61285385602001518484614280565b60606020826000015110613b8f57613b8a82613abf565b611398565b61139882614314565b6060611398613bb78360200151600081518110612c8b57612c8b6153dd565b613a5c565b606082518210613bdb5750604080516020810190915260008152611398565b6126528383848651613bed9190614ee7565b61432a565b6000808251845110613c05578251613c08565b83515b90505b8082108015613c8f5750828281518110613c2757613c276153dd565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916848381518110613c6657613c666153dd565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b15613c9f57816001019150613c0b565b5092915050565b6000808211613d11576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e4544000000000000000000000000000000000000000000000060448201526064016108e9565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b60608315613d8b575081612652565b825115613d9b5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108e99190614b21565b6060611398613ddd83614502565b61456f565b60008060008360000151600003613e25576040517f5ab458fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020840151805160001a607f8111613e4a576000600160009450945094505050614279565b60b78111613f60576000613e5f608083614ee7565b905080876000015111613e9e576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082148015613f1657507f80000000000000000000000000000000000000000000000000000000000000007fff000000000000000000000000000000000000000000000000000000000000008216105b15613f4d576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060019550935060009250614279915050565b60bf81116140be576000613f7560b783614ee7565b905080876000015111613fb4576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003614016576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600184015160088302610100031c6037811161405e576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6140688184614d2a565b8951116140a1576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6140ac836001614d2a565b97509550600094506142799350505050565b60f781116141235760006140d360c083614ee7565b905080876000015111614112576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600195509350849250614279915050565b600061413060f783614ee7565b90508087600001511161416f576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301517fff000000000000000000000000000000000000000000000000000000000000001660008190036141d1576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600184015160088302610100031c60378111614219576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6142238184614d2a565b89511161425c576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b614267836001614d2a565b97509550600194506142799350505050565b9193909250565b60608167ffffffffffffffff81111561429b5761429b614772565b6040519080825280601f01601f1916602001820160405280156142c5576020820181803683370190505b50905081156126525760006142da8486614d2a565b90506020820160005b848110156142fb5782810151828201526020016142e3565b8481111561430a576000858301525b5050509392505050565b6060611398826020015160008460000151614280565b60608182601f011015614399576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f7700000000000000000000000000000000000060448201526064016108e9565b828284011015614405576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f7700000000000000000000000000000000000060448201526064016108e9565b81830184511015614472576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e647300000000000000000000000000000060448201526064016108e9565b60608215801561449157604051915060008252602082016040526144f9565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156144ca5780518352602092830192016144b2565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b60408051808201909152600080825260208201528151600003614551576040517f5ab458fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50604080518082019091528151815260209182019181019190915290565b6060600080600061457f85613de2565b91945092509050600181600181111561459a5761459a61546d565b146145d1576040517f4b9c6abe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84516145dd8385614d2a565b14614614576040517f5c5537b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020808252610420820190925290816020015b604080518082019091526000808252602082015281526020019060019003908161462b5790505093506000835b86518110156147195760008061469e6040518060400160405280858c600001516146829190614ee7565b8152602001858c602001516146979190614d2a565b9052613de2565b5091509150604051806040016040528083836146ba9190614d2a565b8152602001848b602001516146cf9190614d2a565b8152508885815181106146e4576146e46153dd565b60209081029190910101526146fa600185614d2a565b93506147068183614d2a565b6147109084614d2a565b92505050614658565b50845250919392505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461197657600080fd5b803567ffffffffffffffff8116811461475f57600080fd5b919050565b801515811461197657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156147e8576147e8614772565b604052919050565b600082601f83011261480157600080fd5b813567ffffffffffffffff81111561481b5761481b614772565b61484c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016147a1565b81815284602083860101111561486157600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c0878903121561489757600080fd5b86356148a281614725565b955060208701359450604087013593506148be60608801614747565b925060808701356148ce81614764565b915060a087013567ffffffffffffffff8111156148ea57600080fd5b6148f689828a016147f0565b9150509295509295509295565b60006020828403121561491557600080fd5b813561265281614725565b600060c0828403121561493257600080fd5b60405160c0810167ffffffffffffffff828210818311171561495657614956614772565b81604052829350843583526020850135915061497182614725565b8160208401526040850135915061498782614725565b816040840152606085013560608401526080850135608084015260a08501359150808211156149b557600080fd5b506149c2858286016147f0565b60a0830152505092915050565b600080600080600085870360e08112156149e857600080fd5b863567ffffffffffffffff80821115614a0057600080fd5b614a0c8a838b01614920565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614a4557600080fd5b60408901955060c0890135925080831115614a5f57600080fd5b828901925089601f840112614a7357600080fd5b8235915080821115614a8457600080fd5b508860208260051b8401011115614a9a57600080fd5b959894975092955050506020019190565b60005b83811015614ac6578181015183820152602001614aae565b83811115611c105750506000910152565b60008151808452614aef816020860160208601614aab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006126526020830184614ad7565b600060208284031215614b4657600080fd5b5035919050565b60ff8116811461197657600080fd5b60008060008060808587031215614b7257600080fd5b8435614b7d81614725565b93506020850135614b8d81614b4d565b93969395505050506040820135916060013590565b60008060408385031215614bb557600080fd5b8235614bc081614725565b91506020830135614bd081614725565b809150509250929050565b600060208284031215614bed57600080fd5b813567ffffffffffffffff811115614c0457600080fd5b614c1084828501614920565b949350505050565b600060208284031215614c2a57600080fd5b61265282614747565b600080600060608486031215614c4857600080fd5b8335614c5381614725565b92506020840135614c6381614725565b91506040840135614c7381614725565b809150509250925092565b600080600080600060a08688031215614c9657600080fd5b8535614ca181614725565b945060208601359350614cb660408701614747565b92506060860135614cc681614764565b9150608086013567ffffffffffffffff811115614ce257600080fd5b614cee888289016147f0565b9150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614d3d57614d3d614cfb565b500190565b600060208284031215614d5457600080fd5b5051919050565b600060208284031215614d6d57600080fd5b815161265281614725565b80516fffffffffffffffffffffffffffffffff8116811461475f57600080fd5b600060608284031215614daa57600080fd5b6040516060810181811067ffffffffffffffff82111715614dcd57614dcd614772565b60405282518152614de060208401614d78565b6020820152614df160408401614d78565b60408201529392505050565b600060808284031215614e0f57600080fd5b6040516080810181811067ffffffffffffffff82111715614e3257614e32614772565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115614e7e57614e7e614772565b8360051b6020614e8f8183016147a1565b868152918501918181019036841115614ea757600080fd5b865b84811015614edb57803586811115614ec15760008081fd5b614ecd36828b016147f0565b845250918301918301614ea9565b50979650505050505050565b600082821015614ef957614ef9614cfb565b500390565b600060208284031215614f1057600080fd5b815161265281614764565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251614f6f816049850160208701614aab565b919091016049019695505050505050565b600067ffffffffffffffff80831681851681830481118215151615614fa757614fa7614cfb565b02949350505050565b600067ffffffffffffffff808316818516808303821115614fd357614fd3614cfb565b01949350505050565b60008060408385031215614fef57600080fd5b8251614ffa81614725565b6020840151909250614bd081614b4d565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826150495761504961500b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561509d5761509d614cfb565b500590565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156150dc576150dc614cfb565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561511057615110614cfb565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561515757615157614cfb565b7f8000000000000000000000000000000000000000000000000000000000000000600087128682058812818416161561519257615192614cfb565b600087129250878205871284841616156151ae576151ae614cfb565b878505871281841616156151c4576151c4614cfb565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561520c5761520c614cfb565b827f800000000000000000000000000000000000000000000000000000000000000003841281161561524057615240614cfb565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561527e5761527e614cfb565b500290565b6000826152925761529261500b565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a08301526152e260c0830184614ad7565b98975050505050505050565b805163ffffffff8116811461475f57600080fd5b600060c0828403121561531457600080fd5b60405160c0810181811067ffffffffffffffff8211171561533757615337614772565b604052615343836152ee565b8152602083015161535381614b4d565b6020820152604083015161536681614b4d565b6040820152615377606084016152ee565b6060820152615388608084016152ee565b608082015261539960a08401614d78565b60a08201529392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036153d6576153d6614cfb565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060ff83168061541f5761541f61500b565b8060ff84160691505092915050565b600060ff821660ff84168082101561544857615448614cfb565b90039392505050565b60008251615463818460208701614aab565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a", + "nonce": "0x7b", + "chainId": "0x14a34" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x187e9c5", + "logs": [ + { + "address": "0x60fb133b3eebc3a62615700dee3c7d783fc5bd66", + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x244b9ab", + "blockTimestamp": "0x699cd236", + "transactionHash": "0x0cd8ac2902bb3ed5269dacb3f9925c70e3cd8b00f61f2c2ff1ff8c2953677ace", + "transactionIndex": "0x20", + "logIndex": "0x339", + "removed": false + } + ], + "logsBloom": "0x08000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x0cd8ac2902bb3ed5269dacb3f9925c70e3cd8b00f61f2c2ff1ff8c2953677ace", + "transactionIndex": "0x20", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x244b9ab", + "gasUsed": "0x48ed0a", + "effectiveGasPrice": "0x5b8d80", + "blobGasUsed": "0x13f0fe", + "from": "0xbc89eded043666121f491e2f311b1f4e96affd53", + "to": null, + "contractAddress": "0x60fb133b3eebc3a62615700dee3c7d783fc5bd66", + "daFootprintGasScalar": "0x8b", + "l1BaseFeeScalar": "0x44d", + "l1BlobBaseFee": "0x1", + "l1BlobBaseFeeScalar": "0xa118b", + "l1Fee": "0x5c", + "l1GasPrice": "0xf", + "l1GasUsed": "0x24ba5" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1771885109540, + "chain": 84532, + "commit": "f004b73" +} \ No newline at end of file diff --git a/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-1772412607872.json b/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-1772412607872.json new file mode 100644 index 00000000..d7b500e2 --- /dev/null +++ b/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-1772412607872.json @@ -0,0 +1,69 @@ +{ + "transactions": [ + { + "hash": "0x2018ac5a01105e6a5789505349652e6aacfe9c0a2d1e3953dcc71ca3fad9ab34", + "transactionType": "CREATE", + "contractName": "PortalWithChainExit", + "contractAddress": "0xdb872b0ef3fabd03c5b9f3fe76abebee6fc6c6fb", + "function": null, + "arguments": null, + "transaction": { + "from": "0xbc89eded043666121f491e2f311b1f4e96affd53", + "gas": "0x607467", + "value": "0x0", + "input": "0x60806040523480156200001157600080fd5b50620000206000808062000026565b62000282565b600054610100900460ff1615808015620000475750600054600160ff909116105b806200007757506200006430620001b460201b62001d981760201c565b15801562000077575060005460ff166001145b620000e05760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000104576000805461ff0019166101001790555b603580546001600160a01b038087166001600160a01b03199283161790925560368054868416908316179055603480548584169216919091179055603254166200015d57603280546001600160a01b03191661dead1790555b62000167620001c3565b8015620001ae576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b6001600160a01b03163b151590565b600054610100900460ff16620002305760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401620000d7565b600154600160c01b90046001600160401b0316600003620002805760408051606081018252633b9aca0080825260006020830152436001600160401b031691909201819052600160c01b02176001555b565b61563080620002926000396000f3fe60806040526004361061018f5760003560e01c806375b68f0f116100d6578063a14238e71161007f578063c0c53b8b11610059578063c0c53b8b146104f8578063cff0ab9614610518578063e9e05c42146105b957600080fd5b8063a14238e71461046c578063a35d99df1461049c578063b69ef8a8146104d557600080fd5b80638c3152e9116100b05780638c3152e9146103f25780639b5f694a146104125780639bf62d821461043f57600080fd5b806375b68f0f146103a557806381119581146103d25780638b4c40b0146101b457600080fd5b806347f55db5116101385780635c975abb116101125780635c975abb146103405780636dbffb781461036557806371cfaa3f1461038557600080fd5b806347f55db5146102b45780634870496f146102d457806354fd4d50146102f457600080fd5b806333d7e2bd1161016957806333d7e2bd1461021b57806335e80ab314610272578063452a93201461029f57600080fd5b8063149f2f22146101bb5780632590f623146101db578063326cec29146101fb57600080fd5b366101b6576101b43334620186a06000604051806020016040528060008152506105c7565b005b600080fd5b3480156101c757600080fd5b506101b46101d6366004614a05565b61066c565b3480156101e757600080fd5b506101b46101f6366004614a8a565b6108ad565b34801561020757600080fd5b506101b4610216366004614a8a565b6109d0565b34801561022757600080fd5b506036546102489073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561027e57600080fd5b506034546102489073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102ab57600080fd5b50610248610a85565b3480156102c057600080fd5b506101b46102cf366004614b56565b610b1e565b3480156102e057600080fd5b506101b46102ef366004614b56565b6113be565b34801561030057600080fd5b50604080518082018252600581527f312e312e30000000000000000000000000000000000000000000000000000000602082015290516102699190614ca8565b34801561034c57600080fd5b506103556113d2565b6040519015158152602001610269565b34801561037157600080fd5b50610355610380366004614cbb565b611466565b34801561039157600080fd5b506101b46103a0366004614ce3565b611521565b3480156103b157600080fd5b506038546102489073ffffffffffffffffffffffffffffffffffffffff1681565b3480156103de57600080fd5b506101b46103ed366004614d29565b6116e1565b3480156103fe57600080fd5b506101b461040d366004614d62565b611abc565b34801561041e57600080fd5b506035546102489073ffffffffffffffffffffffffffffffffffffffff1681565b34801561044b57600080fd5b506032546102489073ffffffffffffffffffffffffffffffffffffffff1681565b34801561047857600080fd5b50610355610487366004614cbb565b60336020526000908152604090205460ff1681565b3480156104a857600080fd5b506104bc6104b7366004614d9f565b611afb565b60405167ffffffffffffffff9091168152602001610269565b3480156104e157600080fd5b506104ea611b14565b604051908152602001610269565b34801561050457600080fd5b506101b4610513366004614dba565b611b6e565b34801561052457600080fd5b50600154610580906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff9283166020850152911690820152606001610269565b6101b46105c7366004614e05565b8260005a905060006105d7611db4565b50905073ffffffffffffffffffffffffffffffffffffffff811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480159061061357503415155b1561064a576040517ff2365b5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610658883489898989611e51565b506106638282611ffd565b50505050505050565b8260005a9050600061067c611db4565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8216016106ee576040517f0eaf3c0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87603760008282546107009190614eb1565b90915550506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610772573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107969190614ec9565b90506107ba73ffffffffffffffffffffffffffffffffffffffff831633308c6122ca565b6107c48982614eb1565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa15801561082e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108529190614ec9565b14610889576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108978a8a8a8a8a8a611e51565b50506108a38282611ffd565b5050505050505050565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61038054903373ffffffffffffffffffffffffffffffffffffffff831614610955576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f506f7274616c3a2063616c6c6572206973206e6f742061646d696e000000000060448201526064015b60405180910390fd5b603880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff85169081179091556040519081527f52b94172f9b7b650ba45a860fd7816a7e8da73b09832a2f2feddd248dd2d5b1d9060200160405180910390a1505050565b60385473ffffffffffffffffffffffffffffffffffffffff163314610a77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f506f7274616c3a2063616c6c6572206973206e6f7420636861696e206f776e6560448201527f7200000000000000000000000000000000000000000000000000000000000000606482015260840161094c565b610a826000826116e1565b50565b603454604080517f452a9320000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163452a93209160048083019260209291908290030181865afa158015610af5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b199190614ee2565b905090565b610b266113d2565b15610b5d576040517ff480973e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610bc6576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6035546040517fa25ae5570000000000000000000000000000000000000000000000000000000081526004810186905260009173ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610c36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5a9190614f1f565b519050610c74610c6f36869003860186614f84565b6123a6565b8114610d02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f660000000000000000000000000000000000000000000000606482015260840161094c565b6000610d0d87612402565b90506000816000604051602001610d2e929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610de19101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610dd78789614fea565b8960400135612432565b610e6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f660000000000000000000000000000606482015260840161094c565b876040015173ffffffffffffffffffffffffffffffffffffffff16886020015173ffffffffffffffffffffffffffffffffffffffff16837f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f6260405160405180910390a460325473ffffffffffffffffffffffffffffffffffffffff1661dead14610f23576040517f9396d15600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526033602052604090205460ff1615610fc2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a65640000000000000000000000606482015260840161094c565b6000828152603360209081526040822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558901516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558061104d611db4565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8216016110b0576110a98a604001518b608001518c606001518d60a00151612456565b9150611303565b8073ffffffffffffffffffffffffffffffffffffffff168a6040015173ffffffffffffffffffffffffffffffffffffffff1603611119576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608a0151156112da57896060015160376000828254611139919061506e565b90915550506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa1580156111ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111cf9190614ec9565b90506112048b604001518c606001518473ffffffffffffffffffffffffffffffffffffffff166124b49092919063ffffffff16565b60608b0151611213908261506e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa15801561127d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a19190614ec9565b146112d8576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60a08a015151156112fe576110a98a604001518b6080015160008d60a00151612456565b600191505b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405184907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b9061136590851515815260200190565b60405180910390a28115801561137b5750326001145b156113b2576040517feeae4ed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050505050505050565b6113cb8585858585610b1e565b5050505050565b603454604080517f5c975abb000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691635c975abb9160048083019260209291908290030181865afa158015611442573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b199190615085565b6035546040517fa25ae5570000000000000000000000000000000000000000000000000000000081526004810183905260009161151b9173ffffffffffffffffffffffffffffffffffffffff9091169063a25ae55790602401606060405180830381865afa1580156114dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115009190614f1f565b602001516fffffffffffffffffffffffffffffffff16421190565b92915050565b60365473ffffffffffffffffffffffffffffffffffffffff163314611572576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61157e62030d4061250f565b60405173ffffffffffffffffffffffffffffffffffffffff8516602482015260ff8416604482015260648101839052608481018290526000907342000000000000000000000000000000000000159073deaddeaddeaddeaddeaddeaddeaddeaddead0001907fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32908490819062030d4090829060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f71cfaa3f00000000000000000000000000000000000000000000000000000000179052905161169b969594939291016150a2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526116d391614ca8565b60405180910390a450505050565b60385473ffffffffffffffffffffffffffffffffffffffff163314611788576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f506f7274616c3a2063616c6c6572206973206e6f7420636861696e206f776e6560448201527f7200000000000000000000000000000000000000000000000000000000000000606482015260840161094c565b73ffffffffffffffffffffffffffffffffffffffff8116611805576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f506f7274616c3a207a65726f20726563697069656e7400000000000000000000604482015260640161094c565b600073ffffffffffffffffffffffffffffffffffffffff83161561182a575081611836565b611832611db4565b5090505b60007fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff83160161194e57504780156119495760008373ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d80600081146118d7576040519150601f19603f3d011682016040523d82523d6000602084013e6118dc565b606091505b5050905080611947576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f506f7274616c3a20455448207472616e73666572206661696c65640000000000604482015260640161094c565b505b611a4d565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa1580156119b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119dc9190614ec9565b90508015611a4d5760006119ee611db4565b5090508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611a2a5760006037555b611a4b73ffffffffffffffffffffffffffffffffffffffff841685846124b4565b505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f38fbe7c102c02d78250517e4e56bb32e9762f1346cf9713e5efff41d8fb5d99283604051611aac91815260200190565b60405180910390a350505050565b565b611ac46113d2565b15610a82576040517ff480973e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611b08826010615107565b61151b90615208615137565b600080611b1f611db4565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff821601611b66574791505090565b505060375490565b600054610100900460ff1615808015611b8e5750600054600160ff909116105b80611ba85750303b158015611ba8575060005460ff166001145b611c34576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161094c565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611c9257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6035805473ffffffffffffffffffffffffffffffffffffffff8087167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556036805486841690831617905560348054858416921691909117905560325416611d2757603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790555b611d2f612571565b8015611d9257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b603654604080517f4397dfef0000000000000000000000000000000000000000000000000000000081528151600093849373ffffffffffffffffffffffffffffffffffffffff90911692634397dfef92600480830193928290030181865afa158015611e24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e489190615163565b90939092509050565b818015611e73575073ffffffffffffffffffffffffffffffffffffffff861615155b15611eaa576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611eb48151611afb565b67ffffffffffffffff168367ffffffffffffffff161015611f01576040517f4929b80800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6201d4c081511115611f3f576040517f73052b0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33328114611f60575033731111000000000000000000000000000000001111015b60008686868686604051602001611f7b9594939291906150a2565b604051602081830303815290604052905060008873ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c3284604051611feb9190614ca8565b60405180910390a45050505050505050565b600154600090612033907801000000000000000000000000000000000000000000000000900467ffffffffffffffff164361506e565b9050600061203f612684565b90506000816020015160ff16826000015163ffffffff1661206091906151c1565b9050821561219757600154600090612097908390700100000000000000000000000000000000900467ffffffffffffffff16615229565b90506000836040015160ff16836120ae919061529d565b6001546120ce9084906fffffffffffffffffffffffffffffffff1661529d565b6120d891906151c1565b600154909150600090612129906121029084906fffffffffffffffffffffffffffffffff16615359565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff166127c1565b905060018611156121585761215561210282876040015160ff1660018a612150919061506e565b6127e0565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b600180548691906010906121ca908490700100000000000000000000000000000000900467ffffffffffffffff16615137565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315612257576040517f77ebef4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154600090612283906fffffffffffffffffffffffffffffffff1667ffffffffffffffff88166153cd565b9050600061229548633b9aca00612835565b61229f908361540a565b905060005a6122ae908861506e565b9050808211156108a3576108a36122c5828461506e565b61284c565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052611d929085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612875565b600081600001518260200151836040015184606001516040516020016123e5949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a088015193516000976123e597909695910161541e565b60008061243e86612981565b905061244c818686866129b3565b9695505050505050565b60008060006124668660006129e3565b90508061249c576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080855160208701888b5af1979650505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905261250a9084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401612324565b505050565b6001805463ffffffff83169190601090612548908490700100000000000000000000000000000000900467ffffffffffffffff16615137565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555050565b600054610100900460ff16612608576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161094c565b6001547801000000000000000000000000000000000000000000000000900467ffffffffffffffff16600003611aba5760408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152603654604080517fcc731b02000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163cc731b029160048083019260c09291908290030181865afa158015612726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061274a9190615489565b90506040518060c00160405280826000015163ffffffff168152602001826020015160ff168152602001826040015160ff168152602001826060015163ffffffff168152602001826080015163ffffffff1681526020018260a001516fffffffffffffffffffffffffffffffff1681525091505090565b60006127d66127d08585612a01565b83612a11565b90505b9392505050565b6000670de0b6b3a76400006128216127f885836151c1565b61280a90670de0b6b3a7640000615229565b61281c85670de0b6b3a764000061529d565b612a20565b61282b908661529d565b6127d691906151c1565b60008183101561284557816127d9565b5090919050565b6000805a90505b825a61285f908361506e565b101561250a5761286e8261552c565b9150612853565b60006128d7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612a519092919063ffffffff16565b80519091501561250a57808060200190518101906128f59190615085565b61250a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161094c565b6060818051906020012060405160200161299d91815260200190565b6040516020818303038152906040529050919050565b60006129da846129c4878686612a60565b8051602091820120825192909101919091201490565b95945050505050565b600080603f83619c4001026040850201603f5a021015949350505050565b60008183121561284557816127d9565b600081831261284557816127d9565b60006127d9670de0b6b3a764000083612a38866134de565b612a42919061529d565b612a4c91906151c1565b613722565b60606127d68484600085613961565b60606000845111612acd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b65790000000000000000000000604482015260640161094c565b6000612ad884613af7565b90506000612ae586613be3565b9050600084604051602001612afc91815260200190565b60405160208183030381529060405290506000805b8451811015613455576000858281518110612b2e57612b2e615564565b602002602001015190508451831115612bc9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e677468000000000000000000000000000000000000606482015260840161094c565b82600003612c825780518051602091820120604051612c1792612bf192910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b612c7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f742068617368000000604482015260640161094c565b612dd9565b805151602011612d385780518051602091820120604051612cac92612bf192910190815260200190565b612c7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c206861736800000000000000000000000000000000000000000000000000606482015260840161094c565b805184516020808701919091208251919092012014612dd9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f6520686173680000000000000000000000000000000000000000000000000000606482015260840161094c565b612de560106001614eb1565b81602001515103612fc15784518303612f5957612e1f8160200151601081518110612e1257612e12615564565b6020026020010151613c46565b96506000875111612eb2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e6368290000000000606482015260840161094c565b60018651612ec0919061506e565b8214612f4e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e636829000000000000606482015260840161094c565b5050505050506127d9565b6000858481518110612f6d57612f6d615564565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612f9857612f98615564565b60200260200101519050612fab81613cfa565b9550612fb8600186614eb1565b94505050613442565b6002816020015151036133ba576000612fd982613d1f565b9050600081600081518110612ff057612ff0615564565b016020015160f81c90506000613007600283615593565b6130129060026155b5565b90506000613023848360ff16613d43565b905060006130318a89613d43565b9050600061303f8383613d79565b9050808351146130d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b6579000000000000606482015260840161094c565b60ff8516600214806130e6575060ff85166003145b156132d5578082511461317b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e646572000000606482015260840161094c565b6131958760200151600181518110612e1257612e12615564565b9c5060008d5111613228576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c6561662900000000000000606482015260840161094c565b60018c51613236919061506e565b88146132c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c656166290000000000000000606482015260840161094c565b5050505050505050505050506127d9565b60ff851615806132e8575060ff85166001145b1561332757613314876020015160018151811061330757613307615564565b6020026020010151613cfa565b9950613320818a614eb1565b98506133af565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e207072656669780000000000000000000000000000606482015260840161094c565b505050505050613442565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f6465000000000000000000000000000000000000000000000000606482015260840161094c565b508061344d8161552c565b915050612b11565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e7473000000000000000000000000000000000000000000000000000000606482015260840161094c565b6000808213613549576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e45440000000000000000000000000000000000000000000000604482015260640161094c565b6000606061355684613e2d565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c1821361375357506000919050565b680755bf798b4a1bf1e582126137c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f570000000000000000000000000000000000000000604482015260640161094c565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b6060824710156139f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161094c565b73ffffffffffffffffffffffffffffffffffffffff85163b613a71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161094c565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051613a9a91906155d8565b60006040518083038185875af1925050503d8060008114613ad7576040519150601f19603f3d011682016040523d82523d6000602084013e613adc565b606091505b5091509150613aec828286613f03565b979650505050505050565b80516060908067ffffffffffffffff811115613b1557613b156148f9565b604051908082528060200260200182016040528015613b5a57816020015b6040805180820190915260608082526020820152815260200190600190039081613b335790505b50915060005b81811015613bdc576040518060400160405280858381518110613b8557613b85615564565b60200260200101518152602001613bb4868481518110613ba757613ba7615564565b6020026020010151613f56565b815250838281518110613bc957613bc9615564565b6020908102919091010152600101613b60565b5050919050565b606080604051905082518060011b603f8101601f1916830160405280835250602084016020830160005b83811015613c3b578060011b82018184015160001a8060041c8253600f811660018301535050600101613c0d565b509295945050505050565b60606000806000613c5685613f69565b919450925090506000816001811115613c7157613c716155f4565b14613ca8576040517f1ff9b2e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613cb28284614eb1565b855114613ceb576040517f5c5537b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6129da85602001518484614407565b60606020826000015110613d1657613d1182613c46565b61151b565b61151b8261449b565b606061151b613d3e8360200151600081518110612e1257612e12615564565b613be3565b606082518210613d62575060408051602081019091526000815261151b565b6127d98383848651613d74919061506e565b6144b1565b6000808251845110613d8c578251613d8f565b83515b90505b8082108015613e165750828281518110613dae57613dae615564565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916848381518110613ded57613ded615564565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b15613e2657816001019150613d92565b5092915050565b6000808211613e98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e45440000000000000000000000000000000000000000000000604482015260640161094c565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b60608315613f125750816127d9565b825115613f225782518084602001fd5b816040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094c9190614ca8565b606061151b613f6483614689565b6146f6565b60008060008360000151600003613fac576040517f5ab458fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020840151805160001a607f8111613fd1576000600160009450945094505050614400565b60b781116140e7576000613fe660808361506e565b905080876000015111614025576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001838101517fff0000000000000000000000000000000000000000000000000000000000000016908214801561409d57507f80000000000000000000000000000000000000000000000000000000000000007fff000000000000000000000000000000000000000000000000000000000000008216105b156140d4576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060019550935060009250614400915050565b60bf81116142455760006140fc60b78361506e565b90508087600001511161413b576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301517fff0000000000000000000000000000000000000000000000000000000000000016600081900361419d576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600184015160088302610100031c603781116141e5576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6141ef8184614eb1565b895111614228576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b614233836001614eb1565b97509550600094506144009350505050565b60f781116142aa57600061425a60c08361506e565b905080876000015111614299576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600195509350849250614400915050565b60006142b760f78361506e565b9050808760000151116142f6576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003614358576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600184015160088302610100031c603781116143a0576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6143aa8184614eb1565b8951116143e3576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6143ee836001614eb1565b97509550600194506144009350505050565b9193909250565b60608167ffffffffffffffff811115614422576144226148f9565b6040519080825280601f01601f19166020018201604052801561444c576020820181803683370190505b50905081156127d95760006144618486614eb1565b90506020820160005b8481101561448257828101518282015260200161446a565b84811115614491576000858301525b5050509392505050565b606061151b826020015160008460000151614407565b60608182601f011015614520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161094c565b82828401101561458c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161094c565b818301845110156145f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604482015260640161094c565b6060821580156146185760405191506000825260208201604052614680565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015614651578051835260209283019201614639565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b604080518082019091526000808252602082015281516000036146d8576040517f5ab458fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50604080518082019091528151815260209182019181019190915290565b6060600080600061470685613f69565b919450925090506001816001811115614721576147216155f4565b14614758576040517f4b9c6abe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84516147648385614eb1565b1461479b576040517f5c5537b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020808252610420820190925290816020015b60408051808201909152600080825260208201528152602001906001900390816147b25790505093506000835b86518110156148a0576000806148256040518060400160405280858c60000151614809919061506e565b8152602001858c6020015161481e9190614eb1565b9052613f69565b5091509150604051806040016040528083836148419190614eb1565b8152602001848b602001516148569190614eb1565b81525088858151811061486b5761486b615564565b6020908102919091010152614881600185614eb1565b935061488d8183614eb1565b6148979084614eb1565b925050506147df565b50845250919392505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610a8257600080fd5b803567ffffffffffffffff811681146148e657600080fd5b919050565b8015158114610a8257600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561496f5761496f6148f9565b604052919050565b600082601f83011261498857600080fd5b813567ffffffffffffffff8111156149a2576149a26148f9565b6149d360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614928565b8181528460208386010111156149e857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c08789031215614a1e57600080fd5b8635614a29816148ac565b95506020870135945060408701359350614a45606088016148ce565b92506080870135614a55816148eb565b915060a087013567ffffffffffffffff811115614a7157600080fd5b614a7d89828a01614977565b9150509295509295509295565b600060208284031215614a9c57600080fd5b81356127d9816148ac565b600060c08284031215614ab957600080fd5b60405160c0810167ffffffffffffffff8282108183111715614add57614add6148f9565b816040528293508435835260208501359150614af8826148ac565b81602084015260408501359150614b0e826148ac565b816040840152606085013560608401526080850135608084015260a0850135915080821115614b3c57600080fd5b50614b4985828601614977565b60a0830152505092915050565b600080600080600085870360e0811215614b6f57600080fd5b863567ffffffffffffffff80821115614b8757600080fd5b614b938a838b01614aa7565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614bcc57600080fd5b60408901955060c0890135925080831115614be657600080fd5b828901925089601f840112614bfa57600080fd5b8235915080821115614c0b57600080fd5b508860208260051b8401011115614c2157600080fd5b959894975092955050506020019190565b60005b83811015614c4d578181015183820152602001614c35565b83811115611d925750506000910152565b60008151808452614c76816020860160208601614c32565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006127d96020830184614c5e565b600060208284031215614ccd57600080fd5b5035919050565b60ff81168114610a8257600080fd5b60008060008060808587031215614cf957600080fd5b8435614d04816148ac565b93506020850135614d1481614cd4565b93969395505050506040820135916060013590565b60008060408385031215614d3c57600080fd5b8235614d47816148ac565b91506020830135614d57816148ac565b809150509250929050565b600060208284031215614d7457600080fd5b813567ffffffffffffffff811115614d8b57600080fd5b614d9784828501614aa7565b949350505050565b600060208284031215614db157600080fd5b6127d9826148ce565b600080600060608486031215614dcf57600080fd5b8335614dda816148ac565b92506020840135614dea816148ac565b91506040840135614dfa816148ac565b809150509250925092565b600080600080600060a08688031215614e1d57600080fd5b8535614e28816148ac565b945060208601359350614e3d604087016148ce565b92506060860135614e4d816148eb565b9150608086013567ffffffffffffffff811115614e6957600080fd5b614e7588828901614977565b9150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614ec457614ec4614e82565b500190565b600060208284031215614edb57600080fd5b5051919050565b600060208284031215614ef457600080fd5b81516127d9816148ac565b80516fffffffffffffffffffffffffffffffff811681146148e657600080fd5b600060608284031215614f3157600080fd5b6040516060810181811067ffffffffffffffff82111715614f5457614f546148f9565b60405282518152614f6760208401614eff565b6020820152614f7860408401614eff565b60408201529392505050565b600060808284031215614f9657600080fd5b6040516080810181811067ffffffffffffffff82111715614fb957614fb96148f9565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115615005576150056148f9565b8360051b6020615016818301614928565b86815291850191818101903684111561502e57600080fd5b865b84811015615062578035868111156150485760008081fd5b61505436828b01614977565b845250918301918301615030565b50979650505050505050565b60008282101561508057615080614e82565b500390565b60006020828403121561509757600080fd5b81516127d9816148eb565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b6048820152600082516150f6816049850160208701614c32565b919091016049019695505050505050565b600067ffffffffffffffff8083168185168183048111821515161561512e5761512e614e82565b02949350505050565b600067ffffffffffffffff80831681851680830382111561515a5761515a614e82565b01949350505050565b6000806040838503121561517657600080fd5b8251615181816148ac565b6020840151909250614d5781614cd4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826151d0576151d0615192565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561522457615224614e82565b500590565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561526357615263614e82565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561529757615297614e82565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000841360008413858304851182821616156152de576152de614e82565b7f8000000000000000000000000000000000000000000000000000000000000000600087128682058812818416161561531957615319614e82565b6000871292508782058712848416161561533557615335614e82565b8785058712818416161561534b5761534b614e82565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561539357615393614e82565b827f80000000000000000000000000000000000000000000000000000000000000000384128116156153c7576153c7614e82565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561540557615405614e82565b500290565b60008261541957615419615192565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261546960c0830184614c5e565b98975050505050505050565b805163ffffffff811681146148e657600080fd5b600060c0828403121561549b57600080fd5b60405160c0810181811067ffffffffffffffff821117156154be576154be6148f9565b6040526154ca83615475565b815260208301516154da81614cd4565b602082015260408301516154ed81614cd4565b60408201526154fe60608401615475565b606082015261550f60808401615475565b608082015261552060a08401614eff565b60a08201529392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361555d5761555d614e82565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060ff8316806155a6576155a6615192565b8060ff84160691505092915050565b600060ff821660ff8416808210156155cf576155cf614e82565b90039392505050565b600082516155ea818460208701614c32565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a", + "nonce": "0x7c", + "chainId": "0x14a34" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xb31ce5", + "logs": [ + { + "address": "0xdb872b0ef3fabd03c5b9f3fe76abebee6fc6c6fb", + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x248bff0", + "blockTimestamp": "0x69a4dec0", + "transactionHash": "0x2018ac5a01105e6a5789505349652e6aacfe9c0a2d1e3953dcc71ca3fad9ab34", + "transactionIndex": "0x31", + "logIndex": "0xb6", + "removed": false + } + ], + "logsBloom": "0x00000000000000000020000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000", + "type": "0x2", + "transactionHash": "0x2018ac5a01105e6a5789505349652e6aacfe9c0a2d1e3953dcc71ca3fad9ab34", + "transactionIndex": "0x31", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x248bff0", + "gasUsed": "0x4a3228", + "effectiveGasPrice": "0x5b8d80", + "blobGasUsed": "0x143d02", + "from": "0xbc89eded043666121f491e2f311b1f4e96affd53", + "to": null, + "contractAddress": "0xdb872b0ef3fabd03c5b9f3fe76abebee6fc6c6fb", + "daFootprintGasScalar": "0x8b", + "l1BaseFeeScalar": "0x44d", + "l1BlobBaseFee": "0x1", + "l1BlobBaseFeeScalar": "0xa118b", + "l1Fee": "0x53", + "l1GasPrice": "0xa", + "l1GasUsed": "0x25460" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1772412607872, + "chain": 84532, + "commit": "f004b73" +} \ No newline at end of file diff --git a/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-latest.json b/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-latest.json new file mode 100644 index 00000000..d7b500e2 --- /dev/null +++ b/contracts/broadcast/DeployPortalWithChainExit.s.sol/84532/run-latest.json @@ -0,0 +1,69 @@ +{ + "transactions": [ + { + "hash": "0x2018ac5a01105e6a5789505349652e6aacfe9c0a2d1e3953dcc71ca3fad9ab34", + "transactionType": "CREATE", + "contractName": "PortalWithChainExit", + "contractAddress": "0xdb872b0ef3fabd03c5b9f3fe76abebee6fc6c6fb", + "function": null, + "arguments": null, + "transaction": { + "from": "0xbc89eded043666121f491e2f311b1f4e96affd53", + "gas": "0x607467", + "value": "0x0", + "input": "0x60806040523480156200001157600080fd5b50620000206000808062000026565b62000282565b600054610100900460ff1615808015620000475750600054600160ff909116105b806200007757506200006430620001b460201b62001d981760201c565b15801562000077575060005460ff166001145b620000e05760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000104576000805461ff0019166101001790555b603580546001600160a01b038087166001600160a01b03199283161790925560368054868416908316179055603480548584169216919091179055603254166200015d57603280546001600160a01b03191661dead1790555b62000167620001c3565b8015620001ae576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b6001600160a01b03163b151590565b600054610100900460ff16620002305760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401620000d7565b600154600160c01b90046001600160401b0316600003620002805760408051606081018252633b9aca0080825260006020830152436001600160401b031691909201819052600160c01b02176001555b565b61563080620002926000396000f3fe60806040526004361061018f5760003560e01c806375b68f0f116100d6578063a14238e71161007f578063c0c53b8b11610059578063c0c53b8b146104f8578063cff0ab9614610518578063e9e05c42146105b957600080fd5b8063a14238e71461046c578063a35d99df1461049c578063b69ef8a8146104d557600080fd5b80638c3152e9116100b05780638c3152e9146103f25780639b5f694a146104125780639bf62d821461043f57600080fd5b806375b68f0f146103a557806381119581146103d25780638b4c40b0146101b457600080fd5b806347f55db5116101385780635c975abb116101125780635c975abb146103405780636dbffb781461036557806371cfaa3f1461038557600080fd5b806347f55db5146102b45780634870496f146102d457806354fd4d50146102f457600080fd5b806333d7e2bd1161016957806333d7e2bd1461021b57806335e80ab314610272578063452a93201461029f57600080fd5b8063149f2f22146101bb5780632590f623146101db578063326cec29146101fb57600080fd5b366101b6576101b43334620186a06000604051806020016040528060008152506105c7565b005b600080fd5b3480156101c757600080fd5b506101b46101d6366004614a05565b61066c565b3480156101e757600080fd5b506101b46101f6366004614a8a565b6108ad565b34801561020757600080fd5b506101b4610216366004614a8a565b6109d0565b34801561022757600080fd5b506036546102489073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561027e57600080fd5b506034546102489073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102ab57600080fd5b50610248610a85565b3480156102c057600080fd5b506101b46102cf366004614b56565b610b1e565b3480156102e057600080fd5b506101b46102ef366004614b56565b6113be565b34801561030057600080fd5b50604080518082018252600581527f312e312e30000000000000000000000000000000000000000000000000000000602082015290516102699190614ca8565b34801561034c57600080fd5b506103556113d2565b6040519015158152602001610269565b34801561037157600080fd5b50610355610380366004614cbb565b611466565b34801561039157600080fd5b506101b46103a0366004614ce3565b611521565b3480156103b157600080fd5b506038546102489073ffffffffffffffffffffffffffffffffffffffff1681565b3480156103de57600080fd5b506101b46103ed366004614d29565b6116e1565b3480156103fe57600080fd5b506101b461040d366004614d62565b611abc565b34801561041e57600080fd5b506035546102489073ffffffffffffffffffffffffffffffffffffffff1681565b34801561044b57600080fd5b506032546102489073ffffffffffffffffffffffffffffffffffffffff1681565b34801561047857600080fd5b50610355610487366004614cbb565b60336020526000908152604090205460ff1681565b3480156104a857600080fd5b506104bc6104b7366004614d9f565b611afb565b60405167ffffffffffffffff9091168152602001610269565b3480156104e157600080fd5b506104ea611b14565b604051908152602001610269565b34801561050457600080fd5b506101b4610513366004614dba565b611b6e565b34801561052457600080fd5b50600154610580906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff9283166020850152911690820152606001610269565b6101b46105c7366004614e05565b8260005a905060006105d7611db4565b50905073ffffffffffffffffffffffffffffffffffffffff811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480159061061357503415155b1561064a576040517ff2365b5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610658883489898989611e51565b506106638282611ffd565b50505050505050565b8260005a9050600061067c611db4565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8216016106ee576040517f0eaf3c0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87603760008282546107009190614eb1565b90915550506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610772573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107969190614ec9565b90506107ba73ffffffffffffffffffffffffffffffffffffffff831633308c6122ca565b6107c48982614eb1565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa15801561082e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108529190614ec9565b14610889576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108978a8a8a8a8a8a611e51565b50506108a38282611ffd565b5050505050505050565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61038054903373ffffffffffffffffffffffffffffffffffffffff831614610955576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f506f7274616c3a2063616c6c6572206973206e6f742061646d696e000000000060448201526064015b60405180910390fd5b603880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff85169081179091556040519081527f52b94172f9b7b650ba45a860fd7816a7e8da73b09832a2f2feddd248dd2d5b1d9060200160405180910390a1505050565b60385473ffffffffffffffffffffffffffffffffffffffff163314610a77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f506f7274616c3a2063616c6c6572206973206e6f7420636861696e206f776e6560448201527f7200000000000000000000000000000000000000000000000000000000000000606482015260840161094c565b610a826000826116e1565b50565b603454604080517f452a9320000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163452a93209160048083019260209291908290030181865afa158015610af5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b199190614ee2565b905090565b610b266113d2565b15610b5d576040517ff480973e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610bc6576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6035546040517fa25ae5570000000000000000000000000000000000000000000000000000000081526004810186905260009173ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610c36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5a9190614f1f565b519050610c74610c6f36869003860186614f84565b6123a6565b8114610d02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f660000000000000000000000000000000000000000000000606482015260840161094c565b6000610d0d87612402565b90506000816000604051602001610d2e929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610de19101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610dd78789614fea565b8960400135612432565b610e6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f660000000000000000000000000000606482015260840161094c565b876040015173ffffffffffffffffffffffffffffffffffffffff16886020015173ffffffffffffffffffffffffffffffffffffffff16837f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f6260405160405180910390a460325473ffffffffffffffffffffffffffffffffffffffff1661dead14610f23576040517f9396d15600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526033602052604090205460ff1615610fc2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a65640000000000000000000000606482015260840161094c565b6000828152603360209081526040822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558901516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558061104d611db4565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8216016110b0576110a98a604001518b608001518c606001518d60a00151612456565b9150611303565b8073ffffffffffffffffffffffffffffffffffffffff168a6040015173ffffffffffffffffffffffffffffffffffffffff1603611119576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608a0151156112da57896060015160376000828254611139919061506e565b90915550506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa1580156111ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111cf9190614ec9565b90506112048b604001518c606001518473ffffffffffffffffffffffffffffffffffffffff166124b49092919063ffffffff16565b60608b0151611213908261506e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa15801561127d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a19190614ec9565b146112d8576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60a08a015151156112fe576110a98a604001518b6080015160008d60a00151612456565b600191505b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405184907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b9061136590851515815260200190565b60405180910390a28115801561137b5750326001145b156113b2576040517feeae4ed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050505050505050565b6113cb8585858585610b1e565b5050505050565b603454604080517f5c975abb000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff1691635c975abb9160048083019260209291908290030181865afa158015611442573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b199190615085565b6035546040517fa25ae5570000000000000000000000000000000000000000000000000000000081526004810183905260009161151b9173ffffffffffffffffffffffffffffffffffffffff9091169063a25ae55790602401606060405180830381865afa1580156114dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115009190614f1f565b602001516fffffffffffffffffffffffffffffffff16421190565b92915050565b60365473ffffffffffffffffffffffffffffffffffffffff163314611572576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61157e62030d4061250f565b60405173ffffffffffffffffffffffffffffffffffffffff8516602482015260ff8416604482015260648101839052608481018290526000907342000000000000000000000000000000000000159073deaddeaddeaddeaddeaddeaddeaddeaddead0001907fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32908490819062030d4090829060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f71cfaa3f00000000000000000000000000000000000000000000000000000000179052905161169b969594939291016150a2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526116d391614ca8565b60405180910390a450505050565b60385473ffffffffffffffffffffffffffffffffffffffff163314611788576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f506f7274616c3a2063616c6c6572206973206e6f7420636861696e206f776e6560448201527f7200000000000000000000000000000000000000000000000000000000000000606482015260840161094c565b73ffffffffffffffffffffffffffffffffffffffff8116611805576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f506f7274616c3a207a65726f20726563697069656e7400000000000000000000604482015260640161094c565b600073ffffffffffffffffffffffffffffffffffffffff83161561182a575081611836565b611832611db4565b5090505b60007fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff83160161194e57504780156119495760008373ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d80600081146118d7576040519150601f19603f3d011682016040523d82523d6000602084013e6118dc565b606091505b5050905080611947576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f506f7274616c3a20455448207472616e73666572206661696c65640000000000604482015260640161094c565b505b611a4d565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa1580156119b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119dc9190614ec9565b90508015611a4d5760006119ee611db4565b5090508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611a2a5760006037555b611a4b73ffffffffffffffffffffffffffffffffffffffff841685846124b4565b505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f38fbe7c102c02d78250517e4e56bb32e9762f1346cf9713e5efff41d8fb5d99283604051611aac91815260200190565b60405180910390a350505050565b565b611ac46113d2565b15610a82576040517ff480973e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611b08826010615107565b61151b90615208615137565b600080611b1f611db4565b5090507fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff821601611b66574791505090565b505060375490565b600054610100900460ff1615808015611b8e5750600054600160ff909116105b80611ba85750303b158015611ba8575060005460ff166001145b611c34576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161094c565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611c9257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6035805473ffffffffffffffffffffffffffffffffffffffff8087167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556036805486841690831617905560348054858416921691909117905560325416611d2757603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790555b611d2f612571565b8015611d9257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b603654604080517f4397dfef0000000000000000000000000000000000000000000000000000000081528151600093849373ffffffffffffffffffffffffffffffffffffffff90911692634397dfef92600480830193928290030181865afa158015611e24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e489190615163565b90939092509050565b818015611e73575073ffffffffffffffffffffffffffffffffffffffff861615155b15611eaa576040517f13496fda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611eb48151611afb565b67ffffffffffffffff168367ffffffffffffffff161015611f01576040517f4929b80800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6201d4c081511115611f3f576040517f73052b0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33328114611f60575033731111000000000000000000000000000000001111015b60008686868686604051602001611f7b9594939291906150a2565b604051602081830303815290604052905060008873ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c3284604051611feb9190614ca8565b60405180910390a45050505050505050565b600154600090612033907801000000000000000000000000000000000000000000000000900467ffffffffffffffff164361506e565b9050600061203f612684565b90506000816020015160ff16826000015163ffffffff1661206091906151c1565b9050821561219757600154600090612097908390700100000000000000000000000000000000900467ffffffffffffffff16615229565b90506000836040015160ff16836120ae919061529d565b6001546120ce9084906fffffffffffffffffffffffffffffffff1661529d565b6120d891906151c1565b600154909150600090612129906121029084906fffffffffffffffffffffffffffffffff16615359565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff166127c1565b905060018611156121585761215561210282876040015160ff1660018a612150919061506e565b6127e0565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b600180548691906010906121ca908490700100000000000000000000000000000000900467ffffffffffffffff16615137565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315612257576040517f77ebef4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154600090612283906fffffffffffffffffffffffffffffffff1667ffffffffffffffff88166153cd565b9050600061229548633b9aca00612835565b61229f908361540a565b905060005a6122ae908861506e565b9050808211156108a3576108a36122c5828461506e565b61284c565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052611d929085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612875565b600081600001518260200151836040015184606001516040516020016123e5949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a088015193516000976123e597909695910161541e565b60008061243e86612981565b905061244c818686866129b3565b9695505050505050565b60008060006124668660006129e3565b90508061249c576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080855160208701888b5af1979650505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905261250a9084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401612324565b505050565b6001805463ffffffff83169190601090612548908490700100000000000000000000000000000000900467ffffffffffffffff16615137565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555050565b600054610100900460ff16612608576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161094c565b6001547801000000000000000000000000000000000000000000000000900467ffffffffffffffff16600003611aba5760408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152603654604080517fcc731b02000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163cc731b029160048083019260c09291908290030181865afa158015612726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061274a9190615489565b90506040518060c00160405280826000015163ffffffff168152602001826020015160ff168152602001826040015160ff168152602001826060015163ffffffff168152602001826080015163ffffffff1681526020018260a001516fffffffffffffffffffffffffffffffff1681525091505090565b60006127d66127d08585612a01565b83612a11565b90505b9392505050565b6000670de0b6b3a76400006128216127f885836151c1565b61280a90670de0b6b3a7640000615229565b61281c85670de0b6b3a764000061529d565b612a20565b61282b908661529d565b6127d691906151c1565b60008183101561284557816127d9565b5090919050565b6000805a90505b825a61285f908361506e565b101561250a5761286e8261552c565b9150612853565b60006128d7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612a519092919063ffffffff16565b80519091501561250a57808060200190518101906128f59190615085565b61250a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161094c565b6060818051906020012060405160200161299d91815260200190565b6040516020818303038152906040529050919050565b60006129da846129c4878686612a60565b8051602091820120825192909101919091201490565b95945050505050565b600080603f83619c4001026040850201603f5a021015949350505050565b60008183121561284557816127d9565b600081831261284557816127d9565b60006127d9670de0b6b3a764000083612a38866134de565b612a42919061529d565b612a4c91906151c1565b613722565b60606127d68484600085613961565b60606000845111612acd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b65790000000000000000000000604482015260640161094c565b6000612ad884613af7565b90506000612ae586613be3565b9050600084604051602001612afc91815260200190565b60405160208183030381529060405290506000805b8451811015613455576000858281518110612b2e57612b2e615564565b602002602001015190508451831115612bc9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e677468000000000000000000000000000000000000606482015260840161094c565b82600003612c825780518051602091820120604051612c1792612bf192910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b612c7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f742068617368000000604482015260640161094c565b612dd9565b805151602011612d385780518051602091820120604051612cac92612bf192910190815260200190565b612c7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c206861736800000000000000000000000000000000000000000000000000606482015260840161094c565b805184516020808701919091208251919092012014612dd9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f6520686173680000000000000000000000000000000000000000000000000000606482015260840161094c565b612de560106001614eb1565b81602001515103612fc15784518303612f5957612e1f8160200151601081518110612e1257612e12615564565b6020026020010151613c46565b96506000875111612eb2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e6368290000000000606482015260840161094c565b60018651612ec0919061506e565b8214612f4e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e636829000000000000606482015260840161094c565b5050505050506127d9565b6000858481518110612f6d57612f6d615564565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612f9857612f98615564565b60200260200101519050612fab81613cfa565b9550612fb8600186614eb1565b94505050613442565b6002816020015151036133ba576000612fd982613d1f565b9050600081600081518110612ff057612ff0615564565b016020015160f81c90506000613007600283615593565b6130129060026155b5565b90506000613023848360ff16613d43565b905060006130318a89613d43565b9050600061303f8383613d79565b9050808351146130d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b6579000000000000606482015260840161094c565b60ff8516600214806130e6575060ff85166003145b156132d5578082511461317b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e646572000000606482015260840161094c565b6131958760200151600181518110612e1257612e12615564565b9c5060008d5111613228576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c6561662900000000000000606482015260840161094c565b60018c51613236919061506e565b88146132c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c656166290000000000000000606482015260840161094c565b5050505050505050505050506127d9565b60ff851615806132e8575060ff85166001145b1561332757613314876020015160018151811061330757613307615564565b6020026020010151613cfa565b9950613320818a614eb1565b98506133af565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e207072656669780000000000000000000000000000606482015260840161094c565b505050505050613442565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f6465000000000000000000000000000000000000000000000000606482015260840161094c565b508061344d8161552c565b915050612b11565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e7473000000000000000000000000000000000000000000000000000000606482015260840161094c565b6000808213613549576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e45440000000000000000000000000000000000000000000000604482015260640161094c565b6000606061355684613e2d565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c1821361375357506000919050565b680755bf798b4a1bf1e582126137c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f570000000000000000000000000000000000000000604482015260640161094c565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b6060824710156139f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161094c565b73ffffffffffffffffffffffffffffffffffffffff85163b613a71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161094c565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051613a9a91906155d8565b60006040518083038185875af1925050503d8060008114613ad7576040519150601f19603f3d011682016040523d82523d6000602084013e613adc565b606091505b5091509150613aec828286613f03565b979650505050505050565b80516060908067ffffffffffffffff811115613b1557613b156148f9565b604051908082528060200260200182016040528015613b5a57816020015b6040805180820190915260608082526020820152815260200190600190039081613b335790505b50915060005b81811015613bdc576040518060400160405280858381518110613b8557613b85615564565b60200260200101518152602001613bb4868481518110613ba757613ba7615564565b6020026020010151613f56565b815250838281518110613bc957613bc9615564565b6020908102919091010152600101613b60565b5050919050565b606080604051905082518060011b603f8101601f1916830160405280835250602084016020830160005b83811015613c3b578060011b82018184015160001a8060041c8253600f811660018301535050600101613c0d565b509295945050505050565b60606000806000613c5685613f69565b919450925090506000816001811115613c7157613c716155f4565b14613ca8576040517f1ff9b2e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613cb28284614eb1565b855114613ceb576040517f5c5537b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6129da85602001518484614407565b60606020826000015110613d1657613d1182613c46565b61151b565b61151b8261449b565b606061151b613d3e8360200151600081518110612e1257612e12615564565b613be3565b606082518210613d62575060408051602081019091526000815261151b565b6127d98383848651613d74919061506e565b6144b1565b6000808251845110613d8c578251613d8f565b83515b90505b8082108015613e165750828281518110613dae57613dae615564565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916848381518110613ded57613ded615564565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b15613e2657816001019150613d92565b5092915050565b6000808211613e98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e45440000000000000000000000000000000000000000000000604482015260640161094c565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b60608315613f125750816127d9565b825115613f225782518084602001fd5b816040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094c9190614ca8565b606061151b613f6483614689565b6146f6565b60008060008360000151600003613fac576040517f5ab458fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020840151805160001a607f8111613fd1576000600160009450945094505050614400565b60b781116140e7576000613fe660808361506e565b905080876000015111614025576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001838101517fff0000000000000000000000000000000000000000000000000000000000000016908214801561409d57507f80000000000000000000000000000000000000000000000000000000000000007fff000000000000000000000000000000000000000000000000000000000000008216105b156140d4576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060019550935060009250614400915050565b60bf81116142455760006140fc60b78361506e565b90508087600001511161413b576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301517fff0000000000000000000000000000000000000000000000000000000000000016600081900361419d576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600184015160088302610100031c603781116141e5576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6141ef8184614eb1565b895111614228576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b614233836001614eb1565b97509550600094506144009350505050565b60f781116142aa57600061425a60c08361506e565b905080876000015111614299576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600195509350849250614400915050565b60006142b760f78361506e565b9050808760000151116142f6576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003614358576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600184015160088302610100031c603781116143a0576040517fbabb01dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6143aa8184614eb1565b8951116143e3576040517f66c9448500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6143ee836001614eb1565b97509550600194506144009350505050565b9193909250565b60608167ffffffffffffffff811115614422576144226148f9565b6040519080825280601f01601f19166020018201604052801561444c576020820181803683370190505b50905081156127d95760006144618486614eb1565b90506020820160005b8481101561448257828101518282015260200161446a565b84811115614491576000858301525b5050509392505050565b606061151b826020015160008460000151614407565b60608182601f011015614520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161094c565b82828401101561458c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161094c565b818301845110156145f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604482015260640161094c565b6060821580156146185760405191506000825260208201604052614680565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015614651578051835260209283019201614639565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b604080518082019091526000808252602082015281516000036146d8576040517f5ab458fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50604080518082019091528151815260209182019181019190915290565b6060600080600061470685613f69565b919450925090506001816001811115614721576147216155f4565b14614758576040517f4b9c6abe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84516147648385614eb1565b1461479b576040517f5c5537b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020808252610420820190925290816020015b60408051808201909152600080825260208201528152602001906001900390816147b25790505093506000835b86518110156148a0576000806148256040518060400160405280858c60000151614809919061506e565b8152602001858c6020015161481e9190614eb1565b9052613f69565b5091509150604051806040016040528083836148419190614eb1565b8152602001848b602001516148569190614eb1565b81525088858151811061486b5761486b615564565b6020908102919091010152614881600185614eb1565b935061488d8183614eb1565b6148979084614eb1565b925050506147df565b50845250919392505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610a8257600080fd5b803567ffffffffffffffff811681146148e657600080fd5b919050565b8015158114610a8257600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561496f5761496f6148f9565b604052919050565b600082601f83011261498857600080fd5b813567ffffffffffffffff8111156149a2576149a26148f9565b6149d360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614928565b8181528460208386010111156149e857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c08789031215614a1e57600080fd5b8635614a29816148ac565b95506020870135945060408701359350614a45606088016148ce565b92506080870135614a55816148eb565b915060a087013567ffffffffffffffff811115614a7157600080fd5b614a7d89828a01614977565b9150509295509295509295565b600060208284031215614a9c57600080fd5b81356127d9816148ac565b600060c08284031215614ab957600080fd5b60405160c0810167ffffffffffffffff8282108183111715614add57614add6148f9565b816040528293508435835260208501359150614af8826148ac565b81602084015260408501359150614b0e826148ac565b816040840152606085013560608401526080850135608084015260a0850135915080821115614b3c57600080fd5b50614b4985828601614977565b60a0830152505092915050565b600080600080600085870360e0811215614b6f57600080fd5b863567ffffffffffffffff80821115614b8757600080fd5b614b938a838b01614aa7565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614bcc57600080fd5b60408901955060c0890135925080831115614be657600080fd5b828901925089601f840112614bfa57600080fd5b8235915080821115614c0b57600080fd5b508860208260051b8401011115614c2157600080fd5b959894975092955050506020019190565b60005b83811015614c4d578181015183820152602001614c35565b83811115611d925750506000910152565b60008151808452614c76816020860160208601614c32565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006127d96020830184614c5e565b600060208284031215614ccd57600080fd5b5035919050565b60ff81168114610a8257600080fd5b60008060008060808587031215614cf957600080fd5b8435614d04816148ac565b93506020850135614d1481614cd4565b93969395505050506040820135916060013590565b60008060408385031215614d3c57600080fd5b8235614d47816148ac565b91506020830135614d57816148ac565b809150509250929050565b600060208284031215614d7457600080fd5b813567ffffffffffffffff811115614d8b57600080fd5b614d9784828501614aa7565b949350505050565b600060208284031215614db157600080fd5b6127d9826148ce565b600080600060608486031215614dcf57600080fd5b8335614dda816148ac565b92506020840135614dea816148ac565b91506040840135614dfa816148ac565b809150509250925092565b600080600080600060a08688031215614e1d57600080fd5b8535614e28816148ac565b945060208601359350614e3d604087016148ce565b92506060860135614e4d816148eb565b9150608086013567ffffffffffffffff811115614e6957600080fd5b614e7588828901614977565b9150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614ec457614ec4614e82565b500190565b600060208284031215614edb57600080fd5b5051919050565b600060208284031215614ef457600080fd5b81516127d9816148ac565b80516fffffffffffffffffffffffffffffffff811681146148e657600080fd5b600060608284031215614f3157600080fd5b6040516060810181811067ffffffffffffffff82111715614f5457614f546148f9565b60405282518152614f6760208401614eff565b6020820152614f7860408401614eff565b60408201529392505050565b600060808284031215614f9657600080fd5b6040516080810181811067ffffffffffffffff82111715614fb957614fb96148f9565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115615005576150056148f9565b8360051b6020615016818301614928565b86815291850191818101903684111561502e57600080fd5b865b84811015615062578035868111156150485760008081fd5b61505436828b01614977565b845250918301918301615030565b50979650505050505050565b60008282101561508057615080614e82565b500390565b60006020828403121561509757600080fd5b81516127d9816148eb565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b6048820152600082516150f6816049850160208701614c32565b919091016049019695505050505050565b600067ffffffffffffffff8083168185168183048111821515161561512e5761512e614e82565b02949350505050565b600067ffffffffffffffff80831681851680830382111561515a5761515a614e82565b01949350505050565b6000806040838503121561517657600080fd5b8251615181816148ac565b6020840151909250614d5781614cd4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826151d0576151d0615192565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561522457615224614e82565b500590565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561526357615263614e82565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561529757615297614e82565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000841360008413858304851182821616156152de576152de614e82565b7f8000000000000000000000000000000000000000000000000000000000000000600087128682058812818416161561531957615319614e82565b6000871292508782058712848416161561533557615335614e82565b8785058712818416161561534b5761534b614e82565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561539357615393614e82565b827f80000000000000000000000000000000000000000000000000000000000000000384128116156153c7576153c7614e82565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561540557615405614e82565b500290565b60008261541957615419615192565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261546960c0830184614c5e565b98975050505050505050565b805163ffffffff811681146148e657600080fd5b600060c0828403121561549b57600080fd5b60405160c0810181811067ffffffffffffffff821117156154be576154be6148f9565b6040526154ca83615475565b815260208301516154da81614cd4565b602082015260408301516154ed81614cd4565b60408201526154fe60608401615475565b606082015261550f60808401615475565b608082015261552060a08401614eff565b60a08201529392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361555d5761555d614e82565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060ff8316806155a6576155a6615192565b8060ff84160691505092915050565b600060ff821660ff8416808210156155cf576155cf614e82565b90039392505050565b600082516155ea818460208701614c32565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a", + "nonce": "0x7c", + "chainId": "0x14a34" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xb31ce5", + "logs": [ + { + "address": "0xdb872b0ef3fabd03c5b9f3fe76abebee6fc6c6fb", + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x248bff0", + "blockTimestamp": "0x69a4dec0", + "transactionHash": "0x2018ac5a01105e6a5789505349652e6aacfe9c0a2d1e3953dcc71ca3fad9ab34", + "transactionIndex": "0x31", + "logIndex": "0xb6", + "removed": false + } + ], + "logsBloom": "0x00000000000000000020000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000", + "type": "0x2", + "transactionHash": "0x2018ac5a01105e6a5789505349652e6aacfe9c0a2d1e3953dcc71ca3fad9ab34", + "transactionIndex": "0x31", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x248bff0", + "gasUsed": "0x4a3228", + "effectiveGasPrice": "0x5b8d80", + "blobGasUsed": "0x143d02", + "from": "0xbc89eded043666121f491e2f311b1f4e96affd53", + "to": null, + "contractAddress": "0xdb872b0ef3fabd03c5b9f3fe76abebee6fc6c6fb", + "daFootprintGasScalar": "0x8b", + "l1BaseFeeScalar": "0x44d", + "l1BlobBaseFee": "0x1", + "l1BlobBaseFeeScalar": "0xa118b", + "l1Fee": "0x53", + "l1GasPrice": "0xa", + "l1GasUsed": "0x25460" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1772412607872, + "chain": 84532, + "commit": "f004b73" +} \ No newline at end of file diff --git a/contracts/remappings.txt b/contracts/remappings.txt index 6ba539ea..0927fc0e 100644 --- a/contracts/remappings.txt +++ b/contracts/remappings.txt @@ -12,3 +12,4 @@ src/universal/=lib/optimism/packages/contracts-bedrock/src/universal/ src/vendor/=lib/optimism/packages/contracts-bedrock/src/vendor/ scripts/=lib/optimism/packages/contracts-bedrock/scripts/ @nitro-validator/=lib/nitro-validator/src/ +src/legacy/=lib/optimism/packages/contracts-bedrock/src/legacy/ \ No newline at end of file diff --git a/contracts/script/DeployPortalWithChainExit.s.sol b/contracts/script/DeployPortalWithChainExit.s.sol new file mode 100644 index 00000000..1f438abb --- /dev/null +++ b/contracts/script/DeployPortalWithChainExit.s.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Script} from "forge-std/Script.sol"; +import {PortalWithChainExit} from "../src/PortalWithChainExit.sol"; +import {console2 as console} from "forge-std/console2.sol"; + +contract DeployPortalWithChainExit is Script { + function run() public { + vm.startBroadcast(); + PortalWithChainExit portalWithChainExit = new PortalWithChainExit(); + vm.stopBroadcast(); + console.log("Deploying PortalWithChainOwnerExit at:", address(portalWithChainExit)); + } +} diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index 820f3ad7..b1594be8 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -90,33 +90,16 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// @param success Whether the withdrawal transaction was successful. event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); - /// @notice Emitted when the chain owner executes a withdrawal. - /// @param recipient The address that received the funds. - /// @param token The token address (Constants.ETHER for native ETH). - /// @param amount The amount withdrawn. - event ChainOwnerExitWithdrawal(address indexed recipient, address indexed token, uint256 amount); - /// @notice Reverts when paused. modifier whenNotPaused() { if (paused()) revert CallPaused(); _; } - /// @notice Reverts if caller is not the proxy admin. - modifier onlyAdmin() { - address admin; - bytes32 adminSlot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - assembly { - admin := sload(adminSlot) - } - require(msg.sender == admin, "Portal: caller is not admin"); - _; - } - /// @notice Semantic version. - /// @custom:semver 1.1.0 + /// @custom:semver 1.0.0 function version() public pure virtual returns (string memory) { - return "1.1.0"; + return "1.0.0"; } /// @notice Constructs the OptimismPortal contract. @@ -400,12 +383,7 @@ contract Portal is Initializable, ResourceMetering, ISemver { } _depositTransaction({ - _to: _to, - _mint: _mint, - _value: _value, - _gasLimit: _gasLimit, - _isCreation: _isCreation, - _data: _data + _to: _to, _mint: _mint, _value: _value, _gasLimit: _gasLimit, _isCreation: _isCreation, _data: _data }); } @@ -427,12 +405,7 @@ contract Portal is Initializable, ResourceMetering, ISemver { if (token != Constants.ETHER && msg.value != 0) revert NoValue(); _depositTransaction({ - _to: _to, - _mint: msg.value, - _value: _value, - _gasLimit: _gasLimit, - _isCreation: _isCreation, - _data: _data + _to: _to, _mint: msg.value, _value: _value, _gasLimit: _gasLimit, _isCreation: _isCreation, _data: _data }); } @@ -506,48 +479,6 @@ contract Portal is Initializable, ResourceMetering, ISemver { ); } - /// @notice Allows owner to withdraw the gas paying token held by the portal. - /// Can be called regardless of pause state. - /// @param _recipient The address to receive the withdrawn funds. - function chainOwnerExitPortalNetworkToken(address _recipient) external onlyAdmin { - chainOwnerExitPortal(address(0), _recipient); - } - - /// @notice Allows owner to withdraw all tokens held by the portal. - /// Can be called regardless of pause state. - /// @param _asset The token address to withdraw, or address(0) for the gas paying token. - /// @param _recipient The address to receive the withdrawn funds. - function chainOwnerExitPortal(address _asset, address _recipient) public onlyAdmin { - require(_recipient != address(0), "Portal: zero recipient"); - - address token; - if (_asset != address(0)) { - token = _asset; - } else { - (token,) = gasPayingToken(); - } - - uint256 amount; - if (token == Constants.ETHER) { - amount = address(this).balance; - if (amount > 0) { - (bool success,) = _recipient.call{value: amount}(""); - require(success, "Portal: ETH transfer failed"); - } - } else { - amount = IERC20(token).balanceOf(address(this)); - if (amount > 0) { - (address gasToken,) = gasPayingToken(); - if (token == gasToken) { - _balance = 0; - } - IERC20(token).safeTransfer(_recipient, amount); - } - } - - emit ChainOwnerExitWithdrawal(recipient, token, amount); - } - /// @notice Determine if a given output is finalized. /// Reverts if the call to l2Oracle.getL2Output reverts. /// Returns a boolean otherwise. diff --git a/contracts/src/PortalWithChainExit.sol b/contracts/src/PortalWithChainExit.sol new file mode 100644 index 00000000..f631a5d0 --- /dev/null +++ b/contracts/src/PortalWithChainExit.sol @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Contracts +import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; +import {ResourceMetering} from "@eth-optimism-bedrock/src/L1/ResourceMetering.sol"; +import {L1Block} from "@eth-optimism-bedrock/src/L2/L1Block.sol"; +import {OutputOracle} from "./OutputOracle.sol"; + +// Libraries +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {SafeCall} from "@eth-optimism-bedrock/src/libraries/SafeCall.sol"; +import {Constants} from "@eth-optimism-bedrock/src/libraries/Constants.sol"; +import {Types} from "@eth-optimism-bedrock/src/libraries/Types.sol"; +import {Hashing} from "@eth-optimism-bedrock/src/libraries/Hashing.sol"; +import {SecureMerkleTrie} from "@eth-optimism-bedrock/src/libraries/trie/SecureMerkleTrie.sol"; +import {Predeploys} from "@eth-optimism-bedrock/src/libraries/Predeploys.sol"; +import {AddressAliasHelper} from "@eth-optimism-bedrock/src/vendor/AddressAliasHelper.sol"; +import "@eth-optimism-bedrock/src/libraries/PortalErrors.sol"; + +// Interfaces +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IL2OutputOracle} from "@eth-optimism-bedrock/src/L1/interfaces/IL2OutputOracle.sol"; +import {ISystemConfig} from "@eth-optimism-bedrock/src/L1/interfaces/ISystemConfig.sol"; +import {IResourceMetering} from "@eth-optimism-bedrock/src/L1/interfaces/IResourceMetering.sol"; +import {ISuperchainConfig} from "@eth-optimism-bedrock/src/L1/interfaces/ISuperchainConfig.sol"; +import {ISemver} from "@eth-optimism-bedrock/src/universal/interfaces/ISemver.sol"; + +/// @custom:proxied true +/// @title OptimismPortal +/// @notice The OptimismPortal is a low-level contract responsible for passing messages between L1 +/// and L2. Messages sent directly to the OptimismPortal have no form of replayability. +/// Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface. +contract PortalWithChainExit is Initializable, ResourceMetering, ISemver { + /// @notice Allows for interactions with non standard ERC20 tokens. + using SafeERC20 for IERC20; + + /// @notice Version of the deposit event. + uint256 internal constant DEPOSIT_VERSION = 0; + + /// @notice The L2 gas limit set when eth is deposited using the receive() function. + uint64 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000; + + /// @notice The L2 gas limit for system deposit transactions that are initiated from L1. + uint32 internal constant SYSTEM_DEPOSIT_GAS_LIMIT = 200_000; + + /// @notice Address of the L2 account which initiated a withdrawal in this transaction. + /// If the of this variable is the default L2 sender address, then we are NOT inside of + /// a call to finalizeWithdrawalTransaction. + address public l2Sender; + + /// @notice A list of withdrawal hashes which have been successfully finalized. + mapping(bytes32 => bool) public finalizedWithdrawals; + + /// @notice Contract of the Superchain Config. + ISuperchainConfig public superchainConfig; + + /// @notice Contract of the L2OutputOracle. + /// @custom:network-specific + OutputOracle public l2Oracle; + + /// @notice Contract of the SystemConfig. + /// @custom:network-specific + ISystemConfig public systemConfig; + + /// @notice Represents the amount of native asset minted in L2. This may not + /// be 100% accurate due to the ability to send ether to the contract + /// without triggering a deposit transaction. It also is used to prevent + /// overflows for L2 account balances when custom gas tokens are used. + /// It is not safe to trust `ERC20.balanceOf` as it may lie. + uint256 internal _balance; + + // Owner of the current chain + address public chainOwner; + + + /// @notice Emitted when a transaction is deposited from L1 to L2. + /// The parameters of this event are read by the rollup node and used to derive deposit + /// transactions on L2. + /// @param from Address that triggered the deposit transaction. + /// @param to Address that the deposit transaction is directed to. + /// @param version Version of this deposit transaction event. + /// @param opaqueData ABI encoded deposit data to be parsed off-chain. + event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); + + /// @notice Emitted when a withdrawal transaction is proven. + /// @param withdrawalHash Hash of the withdrawal transaction. + /// @param from Address that triggered the withdrawal transaction. + /// @param to Address that the withdrawal transaction is directed to. + event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); + + /// @notice Emitted when a withdrawal transaction is finalized. + /// @param withdrawalHash Hash of the withdrawal transaction. + /// @param success Whether the withdrawal transaction was successful. + event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); + + /// @notice Emitted when a chain owner is set + /// @param chainOwner address of the chain owner + event ChainOwnerSet(address chainOwner); + + /// @notice Emitted when the chain owner executes a withdrawal. + /// @param recipient The address that received the funds. + /// @param token The token address (Constants.ETHER for native ETH). + /// @param amount The amount withdrawn. + event ChainOwnerExitWithdrawal(address indexed recipient, address indexed token, uint256 amount); + + /// @notice Reverts when paused. + modifier whenNotPaused() { + if (paused()) revert CallPaused(); + _; + } + + /// @notice Reverts if caller is not the proxy admin. + modifier onlyAdmin() { + address admin; + bytes32 adminSlot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + assembly { + admin := sload(adminSlot) + } + require(msg.sender == admin, "Portal: caller is not admin"); + _; + } + + modifier onlyChainOwner() { + require(msg.sender == chainOwner, "Portal: caller is not chain owner"); + _; + } + + /// @notice Semantic version. + /// @custom:semver 1.1.0 + function version() public pure virtual returns (string memory) { + return "1.1.0"; + } + + /// @notice Constructs the OptimismPortal contract. + constructor() { + initialize({ + _l2Oracle: OutputOracle(address(0)), + _systemConfig: ISystemConfig(address(0)), + _superchainConfig: ISuperchainConfig(address(0)) + }); + } + + /// @notice Initializer. + /// @param _l2Oracle Contract of the L2OutputOracle. + /// @param _systemConfig Contract of the SystemConfig. + /// @param _superchainConfig Contract of the SuperchainConfig. + function initialize(OutputOracle _l2Oracle, ISystemConfig _systemConfig, ISuperchainConfig _superchainConfig) + public + initializer + { + l2Oracle = _l2Oracle; + systemConfig = _systemConfig; + superchainConfig = _superchainConfig; + if (l2Sender == address(0)) { + l2Sender = Constants.DEFAULT_L2_SENDER; + } + __ResourceMetering_init(); + } + + /// @notice Getter for the balance of the contract. + function balance() public view returns (uint256) { + (address token,) = gasPayingToken(); + if (token == Constants.ETHER) { + return address(this).balance; + } else { + return _balance; + } + } + + /// @notice Getter function for the address of the guardian. + /// Public getter is legacy and will be removed in the future. Use `SuperchainConfig.guardian()` instead. + /// @return Address of the guardian. + /// @custom:legacy + function guardian() public view returns (address) { + return superchainConfig.guardian(); + } + + /// @notice Getter for the current paused status. + /// @return paused_ Whether or not the contract is paused. + function paused() public view returns (bool paused_) { + paused_ = superchainConfig.paused(); + } + + /// @notice Computes the minimum gas limit for a deposit. + /// The minimum gas limit linearly increases based on the size of the calldata. + /// This is to prevent users from creating L2 resource usage without paying for it. + /// This function can be used when interacting with the portal to ensure forwards + /// compatibility. + /// @param _byteCount Number of bytes in the calldata. + /// @return The minimum gas limit for a deposit. + function minimumGasLimit(uint64 _byteCount) public pure returns (uint64) { + return _byteCount * 16 + 21000; + } + + /// @notice Accepts value so that users can send ETH directly to this contract and have the + /// funds be deposited to their address on L2. This is intended as a convenience + /// function for EOAs. Contracts should call the depositTransaction() function directly + /// otherwise any deposited funds will be lost due to address aliasing. + receive() external payable { + depositTransaction(msg.sender, msg.value, RECEIVE_DEFAULT_GAS_LIMIT, false, bytes("")); + } + + /// @notice Accepts ETH value without triggering a deposit to L2. + /// This function mainly exists for the sake of the migration between the legacy + /// Optimism system and Bedrock. + function donateETH() external payable { + // Intentionally empty. + } + + /// @notice Returns the gas paying token and its decimals. + function gasPayingToken() internal view returns (address addr_, uint8 decimals_) { + (addr_, decimals_) = systemConfig.gasPayingToken(); + } + + /// @notice Getter for the resource config. + /// Used internally by the ResourceMetering contract. + /// The SystemConfig is the source of truth for the resource config. + /// @return ResourceMetering ResourceConfig + function _resourceConfig() internal view override returns (ResourceMetering.ResourceConfig memory) { + IResourceMetering.ResourceConfig memory config = systemConfig.resourceConfig(); + return ResourceConfig({ + maxResourceLimit: config.maxResourceLimit, + elasticityMultiplier: config.elasticityMultiplier, + baseFeeMaxChangeDenominator: config.baseFeeMaxChangeDenominator, + minimumBaseFee: config.minimumBaseFee, + systemTxMaxGas: config.systemTxMaxGas, + maximumBaseFee: config.maximumBaseFee + }); + } + + /// @notice Proves a withdrawal transaction. + /// @param _tx Withdrawal transaction to finalize. + /// @param _l2OutputIndex L2 output index to prove against. + /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root. + /// @param _withdrawalProof Inclusion proof of the withdrawal in L2ToL1MessagePasser contract. + function proveWithdrawalTransaction( + Types.WithdrawalTransaction memory _tx, + uint256 _l2OutputIndex, + Types.OutputRootProof calldata _outputRootProof, + bytes[] calldata _withdrawalProof + ) external { + proveAndFinalizeWithdrawalTransaction(_tx, _l2OutputIndex, _outputRootProof, _withdrawalProof); + } + + /// @notice Finalizes a withdrawal transaction. + /// @param _tx Withdrawal transaction to finalize. + function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external whenNotPaused { + // do nothing + } + + /// @notice Proves and finalizes a withdrawal transaction. + /// @param _tx Withdrawal transaction to finalize. + /// @param _l2OutputIndex L2 output index to prove against. + /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root. + /// @param _withdrawalProof Inclusion proof of the withdrawal in L2ToL1MessagePasser contract. + function proveAndFinalizeWithdrawalTransaction( + Types.WithdrawalTransaction memory _tx, + uint256 _l2OutputIndex, + Types.OutputRootProof calldata _outputRootProof, + bytes[] calldata _withdrawalProof + ) public whenNotPaused { + // Prevent users from creating a deposit transaction where this address is the message + // sender on L2. + if (_tx.target == address(this)) revert BadTarget(); + + // Get the output root and load onto the stack to prevent multiple mloads. This will + // revert if there is no output root for the given block number. + bytes32 outputRoot = l2Oracle.getL2Output(_l2OutputIndex).outputRoot; + + // Verify that the output root can be generated with the elements in the proof. + require( + outputRoot == Hashing.hashOutputRootProof(_outputRootProof), "OptimismPortal: invalid output root proof" + ); + + // Compute the storage slot of the withdrawal hash in the L2ToL1MessagePasser contract. + // Refer to the Solidity documentation for more information on how storage layouts are + // computed for mappings. + bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx); + bytes32 storageKey = keccak256( + abi.encode( + withdrawalHash, + uint256(0) // The withdrawals mapping is at the first slot in the layout. + ) + ); + + // Verify that the hash of this withdrawal was stored in the L2toL1MessagePasser contract + // on L2. If this is true, under the assumption that the SecureMerkleTrie does not have + // bugs, then we know that this withdrawal was actually triggered on L2 and can therefore + // be relayed on L1. + require( + SecureMerkleTrie.verifyInclusionProof({ + _key: abi.encode(storageKey), + _value: hex"01", + _proof: _withdrawalProof, + _root: _outputRootProof.messagePasserStorageRoot + }), + "OptimismPortal: invalid withdrawal inclusion proof" + ); + + // Emit a `WithdrawalProven` event. + emit WithdrawalProven(withdrawalHash, _tx.sender, _tx.target); + + // Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other + // than the default value when a withdrawal transaction is being finalized. This check is + // a defacto reentrancy guard. + if (l2Sender != Constants.DEFAULT_L2_SENDER) revert NonReentrant(); + + // Check that this withdrawal has not already been finalized, this is replay protection. + require(finalizedWithdrawals[withdrawalHash] == false, "OptimismPortal: withdrawal has already been finalized"); + + // Mark the withdrawal as finalized so it can't be replayed. + finalizedWithdrawals[withdrawalHash] = true; + + // Set the l2Sender so contracts know who triggered this withdrawal on L2. + // This acts as a reentrancy guard. + l2Sender = _tx.sender; + + bool success; + (address token,) = gasPayingToken(); + if (token == Constants.ETHER) { + // Trigger the call to the target contract. We use a custom low level method + // SafeCall.callWithMinGas to ensure two key properties + // 1. Target contracts cannot force this call to run out of gas by returning a very large + // amount of data (and this is OK because we don't care about the returndata here). + // 2. The amount of gas provided to the execution context of the target is at least the + // gas limit specified by the user. If there is not enough gas in the current context + // to accomplish this, `callWithMinGas` will revert. + success = SafeCall.callWithMinGas(_tx.target, _tx.gasLimit, _tx.value, _tx.data); + } else { + // Cannot call the token contract directly from the portal. This would allow an attacker + // to call approve from a withdrawal and drain the balance of the portal. + if (_tx.target == token) revert BadTarget(); + + // Only transfer value when a non zero value is specified. This saves gas in the case of + // using the standard bridge or arbitrary message passing. + if (_tx.value != 0) { + // Update the contracts internal accounting of the amount of native asset in L2. + _balance -= _tx.value; + + // Read the balance of the target contract before the transfer so the consistency + // of the transfer can be checked afterwards. + uint256 startBalance = IERC20(token).balanceOf(address(this)); + + // Transfer the ERC20 balance to the target, accounting for non standard ERC20 + // implementations that may not return a boolean. This reverts if the low level + // call is not successful. + IERC20(token).safeTransfer({to: _tx.target, value: _tx.value}); + + // The balance must be transferred exactly. + if (IERC20(token).balanceOf(address(this)) != startBalance - _tx.value) { + revert TransferFailed(); + } + } + + // Make a call to the target contract only if there is calldata. + if (_tx.data.length != 0) { + success = SafeCall.callWithMinGas(_tx.target, _tx.gasLimit, 0, _tx.data); + } else { + success = true; + } + } + + // Reset the l2Sender back to the default value. + l2Sender = Constants.DEFAULT_L2_SENDER; + + // All withdrawals are immediately finalized. Replayability can + // be achieved through contracts built on top of this contract + emit WithdrawalFinalized(withdrawalHash, success); + + // Reverting here is useful for determining the exact gas cost to successfully execute the + // sub call to the target contract if the minimum gas limit specified by the user would not + // be sufficient to execute the sub call. + if (success == false && tx.origin == Constants.ESTIMATION_ADDRESS) { + revert GasEstimation(); + } + } + + /// @notice Entrypoint to depositing an ERC20 token as a custom gas token. + /// This function depends on a well formed ERC20 token. There are only + /// so many checks that can be done on chain for this so it is assumed + /// that chain operators will deploy chains with well formed ERC20 tokens. + /// @param _to Target address on L2. + /// @param _mint Units of ERC20 token to deposit into L2. + /// @param _value Units of ERC20 token to send on L2 to the recipient. + /// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1. + /// @param _isCreation Whether or not the transaction is a contract creation. + /// @param _data Data to trigger the recipient with. + function depositERC20Transaction( + address _to, + uint256 _mint, + uint256 _value, + uint64 _gasLimit, + bool _isCreation, + bytes memory _data + ) public metered(_gasLimit) { + // Can only be called if an ERC20 token is used for gas paying on L2 + (address token,) = gasPayingToken(); + if (token == Constants.ETHER) revert OnlyCustomGasToken(); + + // Gives overflow protection for L2 account balances. + _balance += _mint; + + // Get the balance of the portal before the transfer. + uint256 startBalance = IERC20(token).balanceOf(address(this)); + + // Take ownership of the token. It is assumed that the user has given the portal an approval. + IERC20(token).safeTransferFrom({from: msg.sender, to: address(this), value: _mint}); + + // Double check that the portal now has the exact amount of token. + if (IERC20(token).balanceOf(address(this)) != startBalance + _mint) { + revert TransferFailed(); + } + + _depositTransaction({ + _to: _to, + _mint: _mint, + _value: _value, + _gasLimit: _gasLimit, + _isCreation: _isCreation, + _data: _data + }); + } + + /// @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in + /// deriving deposit transactions. Note that if a deposit is made by a contract, its + /// address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider + /// using the CrossDomainMessenger contracts for a simpler developer experience. + /// @param _to Target address on L2. + /// @param _value ETH value to send to the recipient. + /// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1. + /// @param _isCreation Whether or not the transaction is a contract creation. + /// @param _data Data to trigger the recipient with. + function depositTransaction(address _to, uint256 _value, uint64 _gasLimit, bool _isCreation, bytes memory _data) + public + payable + metered(_gasLimit) + { + (address token,) = gasPayingToken(); + if (token != Constants.ETHER && msg.value != 0) revert NoValue(); + + _depositTransaction({ + _to: _to, + _mint: msg.value, + _value: _value, + _gasLimit: _gasLimit, + _isCreation: _isCreation, + _data: _data + }); + } + + /// @notice Common logic for creating deposit transactions. + /// @param _to Target address on L2. + /// @param _mint Units of asset to deposit into L2. + /// @param _value Units of asset to send on L2 to the recipient. + /// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1. + /// @param _isCreation Whether or not the transaction is a contract creation. + /// @param _data Data to trigger the recipient with. + function _depositTransaction( + address _to, + uint256 _mint, + uint256 _value, + uint64 _gasLimit, + bool _isCreation, + bytes memory _data + ) internal { + // Just to be safe, make sure that people specify address(0) as the target when doing + // contract creations. + if (_isCreation && _to != address(0)) revert BadTarget(); + + // Prevent depositing transactions that have too small of a gas limit. Users should pay + // more for more resource usage. + if (_gasLimit < minimumGasLimit(uint64(_data.length))) revert SmallGasLimit(); + + // Prevent the creation of deposit transactions that have too much calldata. This gives an + // upper limit on the size of unsafe blocks over the p2p network. 120kb is chosen to ensure + // that the transaction can fit into the p2p network policy of 128kb even though deposit + // transactions are not gossipped over the p2p network. + if (_data.length > 120_000) revert LargeCalldata(); + + // Transform the from-address to its alias if the caller is a contract. + address from = msg.sender; + if (msg.sender != tx.origin) { + from = AddressAliasHelper.applyL1ToL2Alias(msg.sender); + } + + // Compute the opaque data that will be emitted as part of the TransactionDeposited event. + // We use opaque data so that we can update the TransactionDeposited event in the future + // without breaking the current interface. + bytes memory opaqueData = abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data); + + // Emit a TransactionDeposited event so that the rollup node can derive a deposit + // transaction for this deposit. + emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); + } + + /// @notice Sets the gas paying token for the L2 system. This token is used as the + /// L2 native asset. Only the SystemConfig contract can call this function. + function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external { + if (msg.sender != address(systemConfig)) revert Unauthorized(); + + // Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas. + // This value must be large enough to cover the cost of calling `L1Block.setGasPayingToken`. + useGas(SYSTEM_DEPOSIT_GAS_LIMIT); + + // Emit the special deposit transaction directly that sets the gas paying + // token in the L1Block predeploy contract. + emit TransactionDeposited( + Constants.DEPOSITOR_ACCOUNT, + Predeploys.L1_BLOCK_ATTRIBUTES, + DEPOSIT_VERSION, + abi.encodePacked( + uint256(0), // mint + uint256(0), // value + uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit + false, // isCreation, + abi.encodeCall(L1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) + ) + ); + } + + function setChainOwner(address _chainOwner) external onlyAdmin { + chainOwner = _chainOwner; + emit ChainOwnerSet(chainOwner); + } + + /// @notice Allows owner to withdraw the gas paying token held by the portal. + /// Can be called regardless of pause state. + /// @param _recipient The address to receive the withdrawn funds. + function chainOwnerExitPortalNetworkToken(address _recipient) external onlyChainOwner { + chainOwnerExitPortal(address(0), _recipient); + } + + /// @notice Allows owner to withdraw all tokens held by the portal. + /// Can be called regardless of pause state. + /// @param _asset The token address to withdraw, or address(0) for the gas paying token. + /// @param _recipient The address to receive the withdrawn funds. + function chainOwnerExitPortal(address _asset, address _recipient) public onlyChainOwner { + require(_recipient != address(0), "Portal: zero recipient"); + + address token; + if (_asset != address(0)) { + token = _asset; + } else { + (token,) = gasPayingToken(); + } + + uint256 amount; + if (token == Constants.ETHER) { + amount = address(this).balance; + if (amount > 0) { + (bool success,) = _recipient.call{value: amount}(""); + require(success, "Portal: ETH transfer failed"); + } + } else { + amount = IERC20(token).balanceOf(address(this)); + if (amount > 0) { + (address gasToken,) = gasPayingToken(); + if (token == gasToken) { + _balance = 0; + } + IERC20(token).safeTransfer(_recipient, amount); + } + } + + emit ChainOwnerExitWithdrawal(_recipient, token, amount); + } + + /// @notice Determine if a given output is finalized. + /// Reverts if the call to l2Oracle.getL2Output reverts. + /// Returns a boolean otherwise. + /// @param _l2OutputIndex Index of the L2 output to check. + /// @return Whether or not the output is finalized. + function isOutputFinalized(uint256 _l2OutputIndex) external view returns (bool) { + return _isFinalizationPeriodElapsed(l2Oracle.getL2Output(_l2OutputIndex).timestamp); + } + + /// @notice Determines whether the finalization period has elapsed with respect to + /// the provided block timestamp. + /// @param _timestamp Timestamp to check. + /// @return Whether or not the finalization period has elapsed. + function _isFinalizationPeriodElapsed(uint256 _timestamp) internal view returns (bool) { + return block.timestamp > _timestamp; + } +} \ No newline at end of file diff --git a/contracts/test/Portal.t.sol b/contracts/test/PortalWithChainOwnerExit.t.sol similarity index 80% rename from contracts/test/Portal.t.sol rename to contracts/test/PortalWithChainOwnerExit.t.sol index 5a02848d..51040bdd 100644 --- a/contracts/test/Portal.t.sol +++ b/contracts/test/PortalWithChainOwnerExit.t.sol @@ -10,7 +10,7 @@ import {IResourceMetering} from "@eth-optimism-bedrock/src/L1/interfaces/IResour import {Constants} from "@eth-optimism-bedrock/src/libraries/Constants.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import {Portal} from "../src/Portal.sol"; +import {PortalWithChainExit} from "../src/PortalWithChainExit.sol"; import {OutputOracle} from "../src/OutputOracle.sol"; /// @notice Mock ERC20 token for testing @@ -90,8 +90,8 @@ contract MockSystemConfig { } contract PortalTest is Test { - Portal internal portalImpl; - Portal internal portal; + PortalWithChainExit internal portalImpl; + PortalWithChainExit internal portal; ProxyAdmin internal admin; Proxy internal proxy; MockSuperchainConfig internal superchainConfig; @@ -99,13 +99,14 @@ contract PortalTest is Test { MockERC20 internal token; address internal recipient = makeAddr("recipient"); - address internal nonAdmin = makeAddr("nonAdmin"); + address internal nonOwner = makeAddr("nonOwner"); + address internal chainOwner = makeAddr("chainOwner"); /// @notice EIP-1967 admin slot bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /// @notice Emitted when an emergency withdrawal is executed. - event EmergencyWithdrawal(address indexed recipient, address indexed token, uint256 amount); + event ChainOwnerExitWithdrawal(address indexed recipient, address indexed token, uint256 amount); function setUp() public { // Deploy mocks @@ -114,17 +115,18 @@ contract PortalTest is Test { token = new MockERC20(); // Deploy Portal implementation - portalImpl = new Portal(); + portalImpl = new PortalWithChainExit(); // Deploy proxy with admin admin = new ProxyAdmin(address(this)); proxy = new Proxy(address(admin)); - // Upgrade proxy to Portal implementation - admin.upgrade(payable(address(proxy)), address(portalImpl)); + // Upgrade proxy to Portal implementation, setting chain owner in the same call + bytes memory _data = abi.encodeCall(PortalWithChainExit.setChainOwner, chainOwner); + admin.upgradeAndCall(payable(address(proxy)), address(portalImpl), _data); // Get Portal interface on proxy - portal = Portal(payable(address(proxy))); + portal = PortalWithChainExit(payable(address(proxy))); // Initialize portal portal.initialize({ @@ -144,7 +146,7 @@ contract PortalTest is Test { // Admin withdraws (admin is ProxyAdmin, which is owned by address(this)) // But the actual proxy admin is the ProxyAdmin contract, so we need to call from it // Actually, the admin slot stores the ProxyAdmin address, so we need to prank as ProxyAdmin - vm.prank(address(admin)); + vm.prank(address(chainOwner)); portal.chainOwnerExitPortalNetworkToken(recipient); assertEq(address(portal).balance, 0); @@ -159,20 +161,20 @@ contract PortalTest is Test { uint256 recipientBalanceBefore = token.balanceOf(recipient); // Admin withdraws specific ERC20 asset - vm.prank(address(admin)); + vm.prank(address(chainOwner)); portal.chainOwnerExitPortal(address(token), recipient); assertEq(token.balanceOf(address(portal)), 0); assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount); } - function test_chainOwnerExitPortalNetworkToken_onlyAdmin_reverts() public { + function test_chainOwnerExitPortalNetworkToken_onlyOwner_reverts() public { // Fund the portal vm.deal(address(portal), 10 ether); - // Non-admin tries to withdraw - vm.prank(nonAdmin); - vm.expectRevert("Portal: caller is not admin"); + // Non-owner tries to withdraw + vm.prank(nonOwner); + vm.expectRevert("Portal: caller is not chain owner"); portal.chainOwnerExitPortalNetworkToken(recipient); } @@ -181,7 +183,7 @@ contract PortalTest is Test { vm.deal(address(portal), 10 ether); // Admin tries to withdraw to zero address - vm.prank(address(admin)); + vm.prank(address(chainOwner)); vm.expectRevert("Portal: zero recipient"); portal.chainOwnerExitPortalNetworkToken(address(0)); } @@ -197,8 +199,8 @@ contract PortalTest is Test { uint256 recipientBalanceBefore = recipient.balance; - // Admin can still withdraw even when paused - vm.prank(address(admin)); + // Chain owner can still withdraw even when paused + vm.prank(address(chainOwner)); portal.chainOwnerExitPortalNetworkToken(recipient); assertEq(address(portal).balance, 0); @@ -210,11 +212,11 @@ contract PortalTest is Test { uint256 amount = 10 ether; vm.deal(address(portal), amount); - // Expect the EmergencyWithdrawal event + // Expect the ChainOwnerExitWithdrawal event vm.expectEmit(true, true, false, true); - emit EmergencyWithdrawal(recipient, Constants.ETHER, amount); + emit ChainOwnerExitWithdrawal(recipient, Constants.ETHER, amount); - vm.prank(address(admin)); + vm.prank(address(chainOwner)); portal.chainOwnerExitPortalNetworkToken(recipient); } @@ -223,11 +225,11 @@ contract PortalTest is Test { uint256 amount = 1000 ether; token.mint(address(portal), amount); - // Expect the EmergencyWithdrawal event + // Expect the ChainOwnerExitWithdrawal event vm.expectEmit(true, true, false, true); - emit EmergencyWithdrawal(recipient, address(token), amount); + emit ChainOwnerExitWithdrawal(recipient, address(token), amount); - vm.prank(address(admin)); + vm.prank(address(chainOwner)); portal.chainOwnerExitPortal(address(token), recipient); } @@ -239,9 +241,9 @@ contract PortalTest is Test { // Admin withdraws (should succeed with 0 amount) vm.expectEmit(true, true, false, true); - emit EmergencyWithdrawal(recipient, Constants.ETHER, 0); + emit ChainOwnerExitWithdrawal(recipient, Constants.ETHER, 0); - vm.prank(address(admin)); + vm.prank(address(chainOwner)); portal.chainOwnerExitPortalNetworkToken(recipient); assertEq(recipient.balance, recipientBalanceBefore); @@ -255,15 +257,15 @@ contract PortalTest is Test { // Admin withdraws (should succeed with 0 amount) vm.expectEmit(true, true, false, true); - emit EmergencyWithdrawal(recipient, address(token), 0); + emit ChainOwnerExitWithdrawal(recipient, address(token), 0); - vm.prank(address(admin)); + vm.prank(address(chainOwner)); portal.chainOwnerExitPortal(address(token), recipient); assertEq(token.balanceOf(recipient), recipientBalanceBefore); } - function test_version() public view { - assertEq(portal.version(), "1.1.0"); - } + // function test_version() public view { + // assertEq(portal.version(), "1.1.0"); + // } } From 11b66b1a64d48fe24a913e17c8943cd5453a59ae Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 3 Mar 2026 08:56:25 -0800 Subject: [PATCH 6/9] ,revert back to same contract --- contracts/src/Portal.sol | 95 ++++- contracts/src/PortalWithChainExit.sol | 585 -------------------------- 2 files changed, 91 insertions(+), 589 deletions(-) delete mode 100644 contracts/src/PortalWithChainExit.sol diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index b1594be8..6faa7977 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -70,6 +70,10 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// It is not safe to trust `ERC20.balanceOf` as it may lie. uint256 internal _balance; + // Owner of the current chain + address public chainOwner; + + /// @notice Emitted when a transaction is deposited from L1 to L2. /// The parameters of this event are read by the rollup node and used to derive deposit /// transactions on L2. @@ -90,16 +94,42 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// @param success Whether the withdrawal transaction was successful. event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); + /// @notice Emitted when a chain owner is set + /// @param chainOwner address of the chain owner + event ChainOwnerSet(address chainOwner); + + /// @notice Emitted when the chain owner executes a withdrawal. + /// @param recipient The address that received the funds. + /// @param token The token address (Constants.ETHER for native ETH). + /// @param amount The amount withdrawn. + event ChainOwnerExitWithdrawal(address indexed recipient, address indexed token, uint256 amount); + /// @notice Reverts when paused. modifier whenNotPaused() { if (paused()) revert CallPaused(); _; } + /// @notice Reverts if caller is not the proxy admin. + modifier onlyAdmin() { + address admin; + bytes32 adminSlot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + assembly { + admin := sload(adminSlot) + } + require(msg.sender == admin, "Portal: caller is not admin"); + _; + } + + modifier onlyChainOwner() { + require(msg.sender == chainOwner, "Portal: caller is not chain owner"); + _; + } + /// @notice Semantic version. - /// @custom:semver 1.0.0 + /// @custom:semver 1.1.0 function version() public pure virtual returns (string memory) { - return "1.0.0"; + return "1.1.0"; } /// @notice Constructs the OptimismPortal contract. @@ -383,7 +413,12 @@ contract Portal is Initializable, ResourceMetering, ISemver { } _depositTransaction({ - _to: _to, _mint: _mint, _value: _value, _gasLimit: _gasLimit, _isCreation: _isCreation, _data: _data + _to: _to, + _mint: _mint, + _value: _value, + _gasLimit: _gasLimit, + _isCreation: _isCreation, + _data: _data }); } @@ -405,7 +440,12 @@ contract Portal is Initializable, ResourceMetering, ISemver { if (token != Constants.ETHER && msg.value != 0) revert NoValue(); _depositTransaction({ - _to: _to, _mint: msg.value, _value: _value, _gasLimit: _gasLimit, _isCreation: _isCreation, _data: _data + _to: _to, + _mint: msg.value, + _value: _value, + _gasLimit: _gasLimit, + _isCreation: _isCreation, + _data: _data }); } @@ -479,6 +519,53 @@ contract Portal is Initializable, ResourceMetering, ISemver { ); } + function setChainOwner(address _chainOwner) external onlyAdmin { + chainOwner = _chainOwner;` + emit ChainOwnerSet(chainOwner); + } + + /// @notice Allows owner to withdraw the gas paying token held by the portal. + /// Can be called regardless of pause state. + /// @param _recipient The address to receive the withdrawn funds. + function chainOwnerExitPortalNetworkToken(address _recipient) external onlyChainOwner { + chainOwnerExitPortal(address(0), _recipient); + } + + /// @notice Allows owner to withdraw all tokens held by the portal. + /// Can be called regardless of pause state. + /// @param _asset The token address to withdraw, or address(0) for the gas paying token. + /// @param _recipient The address to receive the withdrawn funds. + function chainOwnerExitPortal(address _asset, address _recipient) public onlyChainOwner { + require(_recipient != address(0), "Portal: zero recipient"); + + address token; + if (_asset != address(0)) { + token = _asset; + } else { + (token,) = gasPayingToken(); + } + + uint256 amount; + if (token == Constants.ETHER) { + amount = address(this).balance; + if (amount > 0) { + (bool success,) = _recipient.call{value: amount}(""); + require(success, "Portal: ETH transfer failed"); + } + } else { + amount = IERC20(token).balanceOf(address(this)); + if (amount > 0) { + (address gasToken,) = gasPayingToken(); + if (token == gasToken) { + _balance = 0; + } + IERC20(token).safeTransfer(_recipient, amount); + } + } + + emit ChainOwnerExitWithdrawal(_recipient, token, amount); + } + /// @notice Determine if a given output is finalized. /// Reverts if the call to l2Oracle.getL2Output reverts. /// Returns a boolean otherwise. diff --git a/contracts/src/PortalWithChainExit.sol b/contracts/src/PortalWithChainExit.sol deleted file mode 100644 index f631a5d0..00000000 --- a/contracts/src/PortalWithChainExit.sol +++ /dev/null @@ -1,585 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Contracts -import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import {ResourceMetering} from "@eth-optimism-bedrock/src/L1/ResourceMetering.sol"; -import {L1Block} from "@eth-optimism-bedrock/src/L2/L1Block.sol"; -import {OutputOracle} from "./OutputOracle.sol"; - -// Libraries -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {SafeCall} from "@eth-optimism-bedrock/src/libraries/SafeCall.sol"; -import {Constants} from "@eth-optimism-bedrock/src/libraries/Constants.sol"; -import {Types} from "@eth-optimism-bedrock/src/libraries/Types.sol"; -import {Hashing} from "@eth-optimism-bedrock/src/libraries/Hashing.sol"; -import {SecureMerkleTrie} from "@eth-optimism-bedrock/src/libraries/trie/SecureMerkleTrie.sol"; -import {Predeploys} from "@eth-optimism-bedrock/src/libraries/Predeploys.sol"; -import {AddressAliasHelper} from "@eth-optimism-bedrock/src/vendor/AddressAliasHelper.sol"; -import "@eth-optimism-bedrock/src/libraries/PortalErrors.sol"; - -// Interfaces -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IL2OutputOracle} from "@eth-optimism-bedrock/src/L1/interfaces/IL2OutputOracle.sol"; -import {ISystemConfig} from "@eth-optimism-bedrock/src/L1/interfaces/ISystemConfig.sol"; -import {IResourceMetering} from "@eth-optimism-bedrock/src/L1/interfaces/IResourceMetering.sol"; -import {ISuperchainConfig} from "@eth-optimism-bedrock/src/L1/interfaces/ISuperchainConfig.sol"; -import {ISemver} from "@eth-optimism-bedrock/src/universal/interfaces/ISemver.sol"; - -/// @custom:proxied true -/// @title OptimismPortal -/// @notice The OptimismPortal is a low-level contract responsible for passing messages between L1 -/// and L2. Messages sent directly to the OptimismPortal have no form of replayability. -/// Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface. -contract PortalWithChainExit is Initializable, ResourceMetering, ISemver { - /// @notice Allows for interactions with non standard ERC20 tokens. - using SafeERC20 for IERC20; - - /// @notice Version of the deposit event. - uint256 internal constant DEPOSIT_VERSION = 0; - - /// @notice The L2 gas limit set when eth is deposited using the receive() function. - uint64 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000; - - /// @notice The L2 gas limit for system deposit transactions that are initiated from L1. - uint32 internal constant SYSTEM_DEPOSIT_GAS_LIMIT = 200_000; - - /// @notice Address of the L2 account which initiated a withdrawal in this transaction. - /// If the of this variable is the default L2 sender address, then we are NOT inside of - /// a call to finalizeWithdrawalTransaction. - address public l2Sender; - - /// @notice A list of withdrawal hashes which have been successfully finalized. - mapping(bytes32 => bool) public finalizedWithdrawals; - - /// @notice Contract of the Superchain Config. - ISuperchainConfig public superchainConfig; - - /// @notice Contract of the L2OutputOracle. - /// @custom:network-specific - OutputOracle public l2Oracle; - - /// @notice Contract of the SystemConfig. - /// @custom:network-specific - ISystemConfig public systemConfig; - - /// @notice Represents the amount of native asset minted in L2. This may not - /// be 100% accurate due to the ability to send ether to the contract - /// without triggering a deposit transaction. It also is used to prevent - /// overflows for L2 account balances when custom gas tokens are used. - /// It is not safe to trust `ERC20.balanceOf` as it may lie. - uint256 internal _balance; - - // Owner of the current chain - address public chainOwner; - - - /// @notice Emitted when a transaction is deposited from L1 to L2. - /// The parameters of this event are read by the rollup node and used to derive deposit - /// transactions on L2. - /// @param from Address that triggered the deposit transaction. - /// @param to Address that the deposit transaction is directed to. - /// @param version Version of this deposit transaction event. - /// @param opaqueData ABI encoded deposit data to be parsed off-chain. - event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); - - /// @notice Emitted when a withdrawal transaction is proven. - /// @param withdrawalHash Hash of the withdrawal transaction. - /// @param from Address that triggered the withdrawal transaction. - /// @param to Address that the withdrawal transaction is directed to. - event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); - - /// @notice Emitted when a withdrawal transaction is finalized. - /// @param withdrawalHash Hash of the withdrawal transaction. - /// @param success Whether the withdrawal transaction was successful. - event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); - - /// @notice Emitted when a chain owner is set - /// @param chainOwner address of the chain owner - event ChainOwnerSet(address chainOwner); - - /// @notice Emitted when the chain owner executes a withdrawal. - /// @param recipient The address that received the funds. - /// @param token The token address (Constants.ETHER for native ETH). - /// @param amount The amount withdrawn. - event ChainOwnerExitWithdrawal(address indexed recipient, address indexed token, uint256 amount); - - /// @notice Reverts when paused. - modifier whenNotPaused() { - if (paused()) revert CallPaused(); - _; - } - - /// @notice Reverts if caller is not the proxy admin. - modifier onlyAdmin() { - address admin; - bytes32 adminSlot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - assembly { - admin := sload(adminSlot) - } - require(msg.sender == admin, "Portal: caller is not admin"); - _; - } - - modifier onlyChainOwner() { - require(msg.sender == chainOwner, "Portal: caller is not chain owner"); - _; - } - - /// @notice Semantic version. - /// @custom:semver 1.1.0 - function version() public pure virtual returns (string memory) { - return "1.1.0"; - } - - /// @notice Constructs the OptimismPortal contract. - constructor() { - initialize({ - _l2Oracle: OutputOracle(address(0)), - _systemConfig: ISystemConfig(address(0)), - _superchainConfig: ISuperchainConfig(address(0)) - }); - } - - /// @notice Initializer. - /// @param _l2Oracle Contract of the L2OutputOracle. - /// @param _systemConfig Contract of the SystemConfig. - /// @param _superchainConfig Contract of the SuperchainConfig. - function initialize(OutputOracle _l2Oracle, ISystemConfig _systemConfig, ISuperchainConfig _superchainConfig) - public - initializer - { - l2Oracle = _l2Oracle; - systemConfig = _systemConfig; - superchainConfig = _superchainConfig; - if (l2Sender == address(0)) { - l2Sender = Constants.DEFAULT_L2_SENDER; - } - __ResourceMetering_init(); - } - - /// @notice Getter for the balance of the contract. - function balance() public view returns (uint256) { - (address token,) = gasPayingToken(); - if (token == Constants.ETHER) { - return address(this).balance; - } else { - return _balance; - } - } - - /// @notice Getter function for the address of the guardian. - /// Public getter is legacy and will be removed in the future. Use `SuperchainConfig.guardian()` instead. - /// @return Address of the guardian. - /// @custom:legacy - function guardian() public view returns (address) { - return superchainConfig.guardian(); - } - - /// @notice Getter for the current paused status. - /// @return paused_ Whether or not the contract is paused. - function paused() public view returns (bool paused_) { - paused_ = superchainConfig.paused(); - } - - /// @notice Computes the minimum gas limit for a deposit. - /// The minimum gas limit linearly increases based on the size of the calldata. - /// This is to prevent users from creating L2 resource usage without paying for it. - /// This function can be used when interacting with the portal to ensure forwards - /// compatibility. - /// @param _byteCount Number of bytes in the calldata. - /// @return The minimum gas limit for a deposit. - function minimumGasLimit(uint64 _byteCount) public pure returns (uint64) { - return _byteCount * 16 + 21000; - } - - /// @notice Accepts value so that users can send ETH directly to this contract and have the - /// funds be deposited to their address on L2. This is intended as a convenience - /// function for EOAs. Contracts should call the depositTransaction() function directly - /// otherwise any deposited funds will be lost due to address aliasing. - receive() external payable { - depositTransaction(msg.sender, msg.value, RECEIVE_DEFAULT_GAS_LIMIT, false, bytes("")); - } - - /// @notice Accepts ETH value without triggering a deposit to L2. - /// This function mainly exists for the sake of the migration between the legacy - /// Optimism system and Bedrock. - function donateETH() external payable { - // Intentionally empty. - } - - /// @notice Returns the gas paying token and its decimals. - function gasPayingToken() internal view returns (address addr_, uint8 decimals_) { - (addr_, decimals_) = systemConfig.gasPayingToken(); - } - - /// @notice Getter for the resource config. - /// Used internally by the ResourceMetering contract. - /// The SystemConfig is the source of truth for the resource config. - /// @return ResourceMetering ResourceConfig - function _resourceConfig() internal view override returns (ResourceMetering.ResourceConfig memory) { - IResourceMetering.ResourceConfig memory config = systemConfig.resourceConfig(); - return ResourceConfig({ - maxResourceLimit: config.maxResourceLimit, - elasticityMultiplier: config.elasticityMultiplier, - baseFeeMaxChangeDenominator: config.baseFeeMaxChangeDenominator, - minimumBaseFee: config.minimumBaseFee, - systemTxMaxGas: config.systemTxMaxGas, - maximumBaseFee: config.maximumBaseFee - }); - } - - /// @notice Proves a withdrawal transaction. - /// @param _tx Withdrawal transaction to finalize. - /// @param _l2OutputIndex L2 output index to prove against. - /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root. - /// @param _withdrawalProof Inclusion proof of the withdrawal in L2ToL1MessagePasser contract. - function proveWithdrawalTransaction( - Types.WithdrawalTransaction memory _tx, - uint256 _l2OutputIndex, - Types.OutputRootProof calldata _outputRootProof, - bytes[] calldata _withdrawalProof - ) external { - proveAndFinalizeWithdrawalTransaction(_tx, _l2OutputIndex, _outputRootProof, _withdrawalProof); - } - - /// @notice Finalizes a withdrawal transaction. - /// @param _tx Withdrawal transaction to finalize. - function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external whenNotPaused { - // do nothing - } - - /// @notice Proves and finalizes a withdrawal transaction. - /// @param _tx Withdrawal transaction to finalize. - /// @param _l2OutputIndex L2 output index to prove against. - /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root. - /// @param _withdrawalProof Inclusion proof of the withdrawal in L2ToL1MessagePasser contract. - function proveAndFinalizeWithdrawalTransaction( - Types.WithdrawalTransaction memory _tx, - uint256 _l2OutputIndex, - Types.OutputRootProof calldata _outputRootProof, - bytes[] calldata _withdrawalProof - ) public whenNotPaused { - // Prevent users from creating a deposit transaction where this address is the message - // sender on L2. - if (_tx.target == address(this)) revert BadTarget(); - - // Get the output root and load onto the stack to prevent multiple mloads. This will - // revert if there is no output root for the given block number. - bytes32 outputRoot = l2Oracle.getL2Output(_l2OutputIndex).outputRoot; - - // Verify that the output root can be generated with the elements in the proof. - require( - outputRoot == Hashing.hashOutputRootProof(_outputRootProof), "OptimismPortal: invalid output root proof" - ); - - // Compute the storage slot of the withdrawal hash in the L2ToL1MessagePasser contract. - // Refer to the Solidity documentation for more information on how storage layouts are - // computed for mappings. - bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx); - bytes32 storageKey = keccak256( - abi.encode( - withdrawalHash, - uint256(0) // The withdrawals mapping is at the first slot in the layout. - ) - ); - - // Verify that the hash of this withdrawal was stored in the L2toL1MessagePasser contract - // on L2. If this is true, under the assumption that the SecureMerkleTrie does not have - // bugs, then we know that this withdrawal was actually triggered on L2 and can therefore - // be relayed on L1. - require( - SecureMerkleTrie.verifyInclusionProof({ - _key: abi.encode(storageKey), - _value: hex"01", - _proof: _withdrawalProof, - _root: _outputRootProof.messagePasserStorageRoot - }), - "OptimismPortal: invalid withdrawal inclusion proof" - ); - - // Emit a `WithdrawalProven` event. - emit WithdrawalProven(withdrawalHash, _tx.sender, _tx.target); - - // Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other - // than the default value when a withdrawal transaction is being finalized. This check is - // a defacto reentrancy guard. - if (l2Sender != Constants.DEFAULT_L2_SENDER) revert NonReentrant(); - - // Check that this withdrawal has not already been finalized, this is replay protection. - require(finalizedWithdrawals[withdrawalHash] == false, "OptimismPortal: withdrawal has already been finalized"); - - // Mark the withdrawal as finalized so it can't be replayed. - finalizedWithdrawals[withdrawalHash] = true; - - // Set the l2Sender so contracts know who triggered this withdrawal on L2. - // This acts as a reentrancy guard. - l2Sender = _tx.sender; - - bool success; - (address token,) = gasPayingToken(); - if (token == Constants.ETHER) { - // Trigger the call to the target contract. We use a custom low level method - // SafeCall.callWithMinGas to ensure two key properties - // 1. Target contracts cannot force this call to run out of gas by returning a very large - // amount of data (and this is OK because we don't care about the returndata here). - // 2. The amount of gas provided to the execution context of the target is at least the - // gas limit specified by the user. If there is not enough gas in the current context - // to accomplish this, `callWithMinGas` will revert. - success = SafeCall.callWithMinGas(_tx.target, _tx.gasLimit, _tx.value, _tx.data); - } else { - // Cannot call the token contract directly from the portal. This would allow an attacker - // to call approve from a withdrawal and drain the balance of the portal. - if (_tx.target == token) revert BadTarget(); - - // Only transfer value when a non zero value is specified. This saves gas in the case of - // using the standard bridge or arbitrary message passing. - if (_tx.value != 0) { - // Update the contracts internal accounting of the amount of native asset in L2. - _balance -= _tx.value; - - // Read the balance of the target contract before the transfer so the consistency - // of the transfer can be checked afterwards. - uint256 startBalance = IERC20(token).balanceOf(address(this)); - - // Transfer the ERC20 balance to the target, accounting for non standard ERC20 - // implementations that may not return a boolean. This reverts if the low level - // call is not successful. - IERC20(token).safeTransfer({to: _tx.target, value: _tx.value}); - - // The balance must be transferred exactly. - if (IERC20(token).balanceOf(address(this)) != startBalance - _tx.value) { - revert TransferFailed(); - } - } - - // Make a call to the target contract only if there is calldata. - if (_tx.data.length != 0) { - success = SafeCall.callWithMinGas(_tx.target, _tx.gasLimit, 0, _tx.data); - } else { - success = true; - } - } - - // Reset the l2Sender back to the default value. - l2Sender = Constants.DEFAULT_L2_SENDER; - - // All withdrawals are immediately finalized. Replayability can - // be achieved through contracts built on top of this contract - emit WithdrawalFinalized(withdrawalHash, success); - - // Reverting here is useful for determining the exact gas cost to successfully execute the - // sub call to the target contract if the minimum gas limit specified by the user would not - // be sufficient to execute the sub call. - if (success == false && tx.origin == Constants.ESTIMATION_ADDRESS) { - revert GasEstimation(); - } - } - - /// @notice Entrypoint to depositing an ERC20 token as a custom gas token. - /// This function depends on a well formed ERC20 token. There are only - /// so many checks that can be done on chain for this so it is assumed - /// that chain operators will deploy chains with well formed ERC20 tokens. - /// @param _to Target address on L2. - /// @param _mint Units of ERC20 token to deposit into L2. - /// @param _value Units of ERC20 token to send on L2 to the recipient. - /// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1. - /// @param _isCreation Whether or not the transaction is a contract creation. - /// @param _data Data to trigger the recipient with. - function depositERC20Transaction( - address _to, - uint256 _mint, - uint256 _value, - uint64 _gasLimit, - bool _isCreation, - bytes memory _data - ) public metered(_gasLimit) { - // Can only be called if an ERC20 token is used for gas paying on L2 - (address token,) = gasPayingToken(); - if (token == Constants.ETHER) revert OnlyCustomGasToken(); - - // Gives overflow protection for L2 account balances. - _balance += _mint; - - // Get the balance of the portal before the transfer. - uint256 startBalance = IERC20(token).balanceOf(address(this)); - - // Take ownership of the token. It is assumed that the user has given the portal an approval. - IERC20(token).safeTransferFrom({from: msg.sender, to: address(this), value: _mint}); - - // Double check that the portal now has the exact amount of token. - if (IERC20(token).balanceOf(address(this)) != startBalance + _mint) { - revert TransferFailed(); - } - - _depositTransaction({ - _to: _to, - _mint: _mint, - _value: _value, - _gasLimit: _gasLimit, - _isCreation: _isCreation, - _data: _data - }); - } - - /// @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in - /// deriving deposit transactions. Note that if a deposit is made by a contract, its - /// address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider - /// using the CrossDomainMessenger contracts for a simpler developer experience. - /// @param _to Target address on L2. - /// @param _value ETH value to send to the recipient. - /// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1. - /// @param _isCreation Whether or not the transaction is a contract creation. - /// @param _data Data to trigger the recipient with. - function depositTransaction(address _to, uint256 _value, uint64 _gasLimit, bool _isCreation, bytes memory _data) - public - payable - metered(_gasLimit) - { - (address token,) = gasPayingToken(); - if (token != Constants.ETHER && msg.value != 0) revert NoValue(); - - _depositTransaction({ - _to: _to, - _mint: msg.value, - _value: _value, - _gasLimit: _gasLimit, - _isCreation: _isCreation, - _data: _data - }); - } - - /// @notice Common logic for creating deposit transactions. - /// @param _to Target address on L2. - /// @param _mint Units of asset to deposit into L2. - /// @param _value Units of asset to send on L2 to the recipient. - /// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1. - /// @param _isCreation Whether or not the transaction is a contract creation. - /// @param _data Data to trigger the recipient with. - function _depositTransaction( - address _to, - uint256 _mint, - uint256 _value, - uint64 _gasLimit, - bool _isCreation, - bytes memory _data - ) internal { - // Just to be safe, make sure that people specify address(0) as the target when doing - // contract creations. - if (_isCreation && _to != address(0)) revert BadTarget(); - - // Prevent depositing transactions that have too small of a gas limit. Users should pay - // more for more resource usage. - if (_gasLimit < minimumGasLimit(uint64(_data.length))) revert SmallGasLimit(); - - // Prevent the creation of deposit transactions that have too much calldata. This gives an - // upper limit on the size of unsafe blocks over the p2p network. 120kb is chosen to ensure - // that the transaction can fit into the p2p network policy of 128kb even though deposit - // transactions are not gossipped over the p2p network. - if (_data.length > 120_000) revert LargeCalldata(); - - // Transform the from-address to its alias if the caller is a contract. - address from = msg.sender; - if (msg.sender != tx.origin) { - from = AddressAliasHelper.applyL1ToL2Alias(msg.sender); - } - - // Compute the opaque data that will be emitted as part of the TransactionDeposited event. - // We use opaque data so that we can update the TransactionDeposited event in the future - // without breaking the current interface. - bytes memory opaqueData = abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data); - - // Emit a TransactionDeposited event so that the rollup node can derive a deposit - // transaction for this deposit. - emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); - } - - /// @notice Sets the gas paying token for the L2 system. This token is used as the - /// L2 native asset. Only the SystemConfig contract can call this function. - function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external { - if (msg.sender != address(systemConfig)) revert Unauthorized(); - - // Set L2 deposit gas as used without paying burning gas. Ensures that deposits cannot use too much L2 gas. - // This value must be large enough to cover the cost of calling `L1Block.setGasPayingToken`. - useGas(SYSTEM_DEPOSIT_GAS_LIMIT); - - // Emit the special deposit transaction directly that sets the gas paying - // token in the L1Block predeploy contract. - emit TransactionDeposited( - Constants.DEPOSITOR_ACCOUNT, - Predeploys.L1_BLOCK_ATTRIBUTES, - DEPOSIT_VERSION, - abi.encodePacked( - uint256(0), // mint - uint256(0), // value - uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit - false, // isCreation, - abi.encodeCall(L1Block.setGasPayingToken, (_token, _decimals, _name, _symbol)) - ) - ); - } - - function setChainOwner(address _chainOwner) external onlyAdmin { - chainOwner = _chainOwner; - emit ChainOwnerSet(chainOwner); - } - - /// @notice Allows owner to withdraw the gas paying token held by the portal. - /// Can be called regardless of pause state. - /// @param _recipient The address to receive the withdrawn funds. - function chainOwnerExitPortalNetworkToken(address _recipient) external onlyChainOwner { - chainOwnerExitPortal(address(0), _recipient); - } - - /// @notice Allows owner to withdraw all tokens held by the portal. - /// Can be called regardless of pause state. - /// @param _asset The token address to withdraw, or address(0) for the gas paying token. - /// @param _recipient The address to receive the withdrawn funds. - function chainOwnerExitPortal(address _asset, address _recipient) public onlyChainOwner { - require(_recipient != address(0), "Portal: zero recipient"); - - address token; - if (_asset != address(0)) { - token = _asset; - } else { - (token,) = gasPayingToken(); - } - - uint256 amount; - if (token == Constants.ETHER) { - amount = address(this).balance; - if (amount > 0) { - (bool success,) = _recipient.call{value: amount}(""); - require(success, "Portal: ETH transfer failed"); - } - } else { - amount = IERC20(token).balanceOf(address(this)); - if (amount > 0) { - (address gasToken,) = gasPayingToken(); - if (token == gasToken) { - _balance = 0; - } - IERC20(token).safeTransfer(_recipient, amount); - } - } - - emit ChainOwnerExitWithdrawal(_recipient, token, amount); - } - - /// @notice Determine if a given output is finalized. - /// Reverts if the call to l2Oracle.getL2Output reverts. - /// Returns a boolean otherwise. - /// @param _l2OutputIndex Index of the L2 output to check. - /// @return Whether or not the output is finalized. - function isOutputFinalized(uint256 _l2OutputIndex) external view returns (bool) { - return _isFinalizationPeriodElapsed(l2Oracle.getL2Output(_l2OutputIndex).timestamp); - } - - /// @notice Determines whether the finalization period has elapsed with respect to - /// the provided block timestamp. - /// @param _timestamp Timestamp to check. - /// @return Whether or not the finalization period has elapsed. - function _isFinalizationPeriodElapsed(uint256 _timestamp) internal view returns (bool) { - return block.timestamp > _timestamp; - } -} \ No newline at end of file From 97452bd948fc5f5348733e66a9c45aca61add3ab Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 3 Mar 2026 13:18:54 -0800 Subject: [PATCH 7/9] ,update modifer for admin and owner --- contracts/src/Portal.sol | 15 ++++--- contracts/test/PortalWithChainOwnerExit.t.sol | 44 ++++++++++++++++--- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index 6faa7977..aa99aff2 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -121,8 +121,13 @@ contract Portal is Initializable, ResourceMetering, ISemver { _; } - modifier onlyChainOwner() { - require(msg.sender == chainOwner, "Portal: caller is not chain owner"); + modifier onlyChainOwnerAndAdmin() { + address admin; + bytes32 adminSlot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + assembly { + admin := sload(adminSlot) + } + require(msg.sender == chainOwner || msg.sender == admin, "Portal: caller is not chain owner or admin"); _; } @@ -520,14 +525,14 @@ contract Portal is Initializable, ResourceMetering, ISemver { } function setChainOwner(address _chainOwner) external onlyAdmin { - chainOwner = _chainOwner;` + chainOwner = _chainOwner; emit ChainOwnerSet(chainOwner); } /// @notice Allows owner to withdraw the gas paying token held by the portal. /// Can be called regardless of pause state. /// @param _recipient The address to receive the withdrawn funds. - function chainOwnerExitPortalNetworkToken(address _recipient) external onlyChainOwner { + function chainOwnerExitPortalNetworkToken(address _recipient) external onlyChainOwnerAndAdmin { chainOwnerExitPortal(address(0), _recipient); } @@ -535,7 +540,7 @@ contract Portal is Initializable, ResourceMetering, ISemver { /// Can be called regardless of pause state. /// @param _asset The token address to withdraw, or address(0) for the gas paying token. /// @param _recipient The address to receive the withdrawn funds. - function chainOwnerExitPortal(address _asset, address _recipient) public onlyChainOwner { + function chainOwnerExitPortal(address _asset, address _recipient) public onlyChainOwnerAndAdmin { require(_recipient != address(0), "Portal: zero recipient"); address token; diff --git a/contracts/test/PortalWithChainOwnerExit.t.sol b/contracts/test/PortalWithChainOwnerExit.t.sol index 51040bdd..5abff910 100644 --- a/contracts/test/PortalWithChainOwnerExit.t.sol +++ b/contracts/test/PortalWithChainOwnerExit.t.sol @@ -10,7 +10,7 @@ import {IResourceMetering} from "@eth-optimism-bedrock/src/L1/interfaces/IResour import {Constants} from "@eth-optimism-bedrock/src/libraries/Constants.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import {PortalWithChainExit} from "../src/PortalWithChainExit.sol"; +import {Portal} from "../src/Portal.sol"; import {OutputOracle} from "../src/OutputOracle.sol"; /// @notice Mock ERC20 token for testing @@ -90,8 +90,8 @@ contract MockSystemConfig { } contract PortalTest is Test { - PortalWithChainExit internal portalImpl; - PortalWithChainExit internal portal; + Portal internal portalImpl; + Portal internal portal; ProxyAdmin internal admin; Proxy internal proxy; MockSuperchainConfig internal superchainConfig; @@ -115,18 +115,18 @@ contract PortalTest is Test { token = new MockERC20(); // Deploy Portal implementation - portalImpl = new PortalWithChainExit(); + portalImpl = new Portal(); // Deploy proxy with admin admin = new ProxyAdmin(address(this)); proxy = new Proxy(address(admin)); // Upgrade proxy to Portal implementation, setting chain owner in the same call - bytes memory _data = abi.encodeCall(PortalWithChainExit.setChainOwner, chainOwner); + bytes memory _data = abi.encodeCall(Portal.setChainOwner, chainOwner); admin.upgradeAndCall(payable(address(proxy)), address(portalImpl), _data); // Get Portal interface on proxy - portal = PortalWithChainExit(payable(address(proxy))); + portal = Portal(payable(address(proxy))); // Initialize portal portal.initialize({ @@ -174,7 +174,7 @@ contract PortalTest is Test { // Non-owner tries to withdraw vm.prank(nonOwner); - vm.expectRevert("Portal: caller is not chain owner"); + vm.expectRevert("Portal: caller is not chain owner or admin"); portal.chainOwnerExitPortalNetworkToken(recipient); } @@ -265,6 +265,36 @@ contract PortalTest is Test { assertEq(token.balanceOf(recipient), recipientBalanceBefore); } + function test_adminCanCallChainOwnerExitPortalNetworkToken_ETH() public { + // Fund the portal with ETH + uint256 amount = 10 ether; + vm.deal(address(portal), amount); + + uint256 recipientBalanceBefore = recipient.balance; + + // Proxy admin (ProxyAdmin contract) calls the exit function + vm.prank(address(admin)); + portal.chainOwnerExitPortalNetworkToken(recipient); + + assertEq(address(portal).balance, 0); + assertEq(recipient.balance, recipientBalanceBefore + amount); + } + + function test_adminCanCallChainOwnerExitPortal_ERC20() public { + // Fund the portal with ERC20 tokens + uint256 amount = 1000 ether; + token.mint(address(portal), amount); + + uint256 recipientBalanceBefore = token.balanceOf(recipient); + + // Proxy admin calls the exit function + vm.prank(address(admin)); + portal.chainOwnerExitPortal(address(token), recipient); + + assertEq(token.balanceOf(address(portal)), 0); + assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount); + } + // function test_version() public view { // assertEq(portal.version(), "1.1.0"); // } From 798076752dc1fbf3cd5f07b3e3bdf81da23de4b3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 4 Mar 2026 18:50:02 -0800 Subject: [PATCH 8/9] update test with new gas token balance drain --- contracts/src/Portal.sol | 2 +- contracts/test/PortalWithChainOwnerExit.t.sol | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index aa99aff2..d2b10777 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -524,7 +524,7 @@ contract Portal is Initializable, ResourceMetering, ISemver { ); } - function setChainOwner(address _chainOwner) external onlyAdmin { + function setChainOwner(address _chainOwner) external onlyChainOwnerAndAdmin { chainOwner = _chainOwner; emit ChainOwnerSet(chainOwner); } diff --git a/contracts/test/PortalWithChainOwnerExit.t.sol b/contracts/test/PortalWithChainOwnerExit.t.sol index 5abff910..3d4e5f99 100644 --- a/contracts/test/PortalWithChainOwnerExit.t.sol +++ b/contracts/test/PortalWithChainOwnerExit.t.sol @@ -295,6 +295,50 @@ contract PortalTest is Test { assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount); } + function test_chainOwnerExitPortalNetworkToken_customGasToken_resetsBalance() public { + // Configure the chain to use a custom gas token (not ETH) + systemConfig.setGasPayingToken(address(token)); + + // Fund the portal with the custom gas token + uint256 amount = 1000 ether; + token.mint(address(portal), amount); + + // Simulate _balance being set (as would happen via depositERC20Transaction). + // Portal storage layout (behind proxy): + // slot 0: Initializable (_initialized + _initializing, packed) + // slot 1: ResourceMetering.params (ResourceParams struct, 256 bits) + // slots 2-49: ResourceMetering.__gap[48] + // slot 50: l2Sender + // slot 51: finalizedWithdrawals (mapping) + // slot 52: superchainConfig + // slot 53: l2Oracle + // slot 54: systemConfig + // slot 55: _balance + // slot 56: chainOwner + uint256 balanceSlot = 55; + + // Confirm balance() returns 0 before we write (custom gas token path returns _balance) + assertEq(portal.balance(), 0); + + vm.store(address(portal), bytes32(balanceSlot), bytes32(amount)); + assertEq(portal.balance(), amount, "_balance should be set via vm.store"); + + uint256 recipientBalanceBefore = token.balanceOf(recipient); + + // Chain owner exits with the network token (custom gas token) + vm.expectEmit(true, true, false, true); + emit ChainOwnerExitWithdrawal(recipient, address(token), amount); + + vm.prank(chainOwner); + portal.chainOwnerExitPortalNetworkToken(recipient); + + // Verify _balance was reset to 0 + assertEq(portal.balance(), 0, "_balance should be reset to 0"); + // Verify tokens were transferred + assertEq(token.balanceOf(address(portal)), 0, "portal should have no tokens"); + assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount, "recipient should receive tokens"); + } + // function test_version() public view { // assertEq(portal.version(), "1.1.0"); // } From d3a9285d022767213d8d61d761c4a8b2f8a50b3c Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 4 Mar 2026 19:02:32 -0800 Subject: [PATCH 9/9] update test to test chainOwnerSetter and require not 0 address --- contracts/src/Portal.sol | 1 + contracts/test/PortalWithChainOwnerExit.t.sol | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/contracts/src/Portal.sol b/contracts/src/Portal.sol index d2b10777..db266b52 100644 --- a/contracts/src/Portal.sol +++ b/contracts/src/Portal.sol @@ -525,6 +525,7 @@ contract Portal is Initializable, ResourceMetering, ISemver { } function setChainOwner(address _chainOwner) external onlyChainOwnerAndAdmin { + require(_chainOwner != address(0), "chain owner must not be 0 address"); chainOwner = _chainOwner; emit ChainOwnerSet(chainOwner); } diff --git a/contracts/test/PortalWithChainOwnerExit.t.sol b/contracts/test/PortalWithChainOwnerExit.t.sol index 3d4e5f99..fbfcab9f 100644 --- a/contracts/test/PortalWithChainOwnerExit.t.sol +++ b/contracts/test/PortalWithChainOwnerExit.t.sol @@ -108,6 +108,9 @@ contract PortalTest is Test { /// @notice Emitted when an emergency withdrawal is executed. event ChainOwnerExitWithdrawal(address indexed recipient, address indexed token, uint256 amount); + /// @notice Emitted when a chain owner is set. + event ChainOwnerSet(address chainOwner); + function setUp() public { // Deploy mocks superchainConfig = new MockSuperchainConfig(); @@ -295,6 +298,84 @@ contract PortalTest is Test { assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount); } + // ────────────────────────────────────────────── + // setChainOwner tests + // ────────────────────────────────────────────── + + function test_setChainOwner_adminCanSet() public { + address newOwner = makeAddr("newOwner"); + + vm.prank(address(admin)); + portal.setChainOwner(newOwner); + + assertEq(portal.chainOwner(), newOwner); + } + + function test_setChainOwner_chainOwnerCanSet() public { + address newOwner = makeAddr("newOwner"); + + vm.prank(chainOwner); + portal.setChainOwner(newOwner); + + assertEq(portal.chainOwner(), newOwner); + } + + function test_setChainOwner_nonOwnerReverts() public { + vm.prank(nonOwner); + vm.expectRevert("Portal: caller is not chain owner or admin"); + portal.setChainOwner(makeAddr("newOwner")); + } + + function test_setChainOwner_emitsEvent() public { + address newOwner = makeAddr("newOwner"); + + vm.expectEmit(false, false, false, true); + emit ChainOwnerSet(newOwner); + + vm.prank(chainOwner); + portal.setChainOwner(newOwner); + } + + function test_setChainOwner_oldOwnerLosesAccess() public { + address newOwner = makeAddr("newOwner"); + + // Transfer ownership + vm.prank(chainOwner); + portal.setChainOwner(newOwner); + + // Old chain owner can no longer call owner-restricted functions + vm.prank(chainOwner); + vm.expectRevert("Portal: caller is not chain owner or admin"); + portal.chainOwnerExitPortalNetworkToken(recipient); + } + + function test_setChainOwner_newOwnerHasAccess() public { + address newOwner = makeAddr("newOwner"); + + // Transfer ownership + vm.prank(chainOwner); + portal.setChainOwner(newOwner); + + // Fund portal so exit has something to transfer + vm.deal(address(portal), 1 ether); + + // New owner can call owner-restricted functions + vm.prank(newOwner); + portal.chainOwnerExitPortalNetworkToken(recipient); + + assertEq(address(portal).balance, 0); + } + + function test_setChainOwner_toZeroAddress() public { + vm.prank(chainOwner); + vm.expectRevert("chain owner must not be 0 address"); + portal.setChainOwner(address(0)); + } + + // ────────────────────────────────────────────── + // chainOwnerExit – custom gas token tests + // ────────────────────────────────────────────── + function test_chainOwnerExitPortalNetworkToken_customGasToken_resetsBalance() public { // Configure the chain to use a custom gas token (not ETH) systemConfig.setGasPayingToken(address(token));