diff --git a/op-geth b/op-geth index bd2d545f04afe..1e552a0441d4b 160000 --- a/op-geth +++ b/op-geth @@ -1 +1 @@ -Subproject commit bd2d545f04afe16589ce70fe3d51086e4e5b4203 +Subproject commit 1e552a0441d4b105d12a5d10d36ffa0721ac991b diff --git a/test/tokenmanager/README.md b/test/tokenmanager/README.md new file mode 100644 index 0000000000000..b9eb9df787a44 --- /dev/null +++ b/test/tokenmanager/README.md @@ -0,0 +1,341 @@ +# Token Manager Scripts Usage Guide + +## ๐Ÿ“‹ Overview + +This directory contains deployment and testing scripts for the Token Manager system, providing complete contract deployment, configuration, and functionality verification processes. + +## ๐Ÿš€ Quick Start + +```bash +# 0. Install dependencies for contracts +cd tokenmanager/contracts +npm install + +# 1. Transfer from Genesis deploy account to 0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15 +cast send 0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15 --value 10ether --private-key 0x815405dddb0e2a99b12af775fd2929e526704e1d1aea6a0b4e74dc33e2f7fcd2 --rpc-url http://127.0.0.1:8123 + +# 2. Deploy contracts +# Where +#PROXY_ADMIN="0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15" # Proxy admin, private key is 0x9935c242a0b0ee41edcbd2d963f5bc7f142fdc803eb24f0df396a6fdb16c6af9 +#OWNER_ADDRESS="0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15" # Contract Owner, same private key +#ADMIN_ADDRESS="0x8f8E2d6cF621f30e9a11309D6A56A876281Fd534" # Business Admin, private key is 0x815405dddb0e2a99b12af775fd2929e526704e1d1aea6a0b4e74dc33e2f7fcd2 +./deploy_tokenmanager.sh + +# 3. Local testing +./test_tokenmanager.sh + +# This enables complete testing. +``` + +### Prerequisites + +1. **OP Devnet Running**: Ensure X Layer OP Devnet is running +2. **RPC Access**: Ensure access to node RPC endpoint (default: `http://localhost:8123`) +3. **Private Key Preparation**: Prepare deployment account private key +4. **Cast Tool**: Ensure Foundry's `cast` command line tool is installed +5. **Solidity Compiler**: Ensure `solc` command line tool is installed +6. **Node Environment**: Ensure `npm` is installed + +```bash +# Install Foundry (if not already installed) +curl -L https://foundry.paradigm.xyz | bash +foundryup +``` + +--- + +## ๐Ÿ› ๏ธ Deployment Scripts + +### `deploy_tokenmanager.sh` + +Complete Token Manager system deployment script, including implementation contract and proxy contract. + +#### Basic Usage + +```bash +cd scripts +./deploy_tokenmanager.sh +``` + +#### Environment Variable Configuration + +```bash +# Basic configuration +export PRIVATE_KEY="0x9935c242a0b0ee41edcbd2d963f5bc7f142fdc803eb24f0df396a6fdb16c6af9" +export RPC_URL="http://localhost:8123" +export GAS_PRICE="1000000000" +export GAS_LIMIT="5000000" + +# Permission separation configuration +export PROXY_ADMIN="0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15" # Proxy admin +export OWNER_ADDRESS="0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15" # Contract Owner +export ADMIN_ADDRESS="0x8f8E2d6cF621f30e9a11309D6A56A876281Fd534" # Business Admin + +# Activation configuration +export ACTIVATION_BLOCK="0" # Activate immediately + +# Run deployment +./deploy_tokenmanager.sh +``` + +#### Deployment Process + +1. **Compile Contracts** - Compile TokenManagerV1 and TokenManagerProxy +2. **Deploy Implementation Contract** - Deploy TokenManagerV1 implementation +3. **Deploy Proxy Contract** - Deploy TransparentUpgradeableProxy +4. **Initialize Contract** - Set Owner, Admin, and activation block +5. **Verify Deployment** - Check contract status and permissions + +#### Output Information + +``` +๐ŸŽ‰ Token Manager System Deployed Successfully! +====================================== + +๐Ÿ“Š Deployment Summary: + Implementation: 0x1234...5678 + Proxy Address: 0x1FdC273F90e3Eba11D2b20561F233B11424Fcfab + Owner: 0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15 + Admin: 0x8f8E2d6cF621f30e9a11309D6A56A876281Fd534 + Status: Active (Block: 0) +``` + +--- + +## ๐Ÿงช Testing Scripts + +### `test_tokenmanager.sh` + +Comprehensive Token Manager functionality testing script, covering all interfaces and boundary conditions. + +#### Basic Usage + +```bash +cd scripts +./test_tokenmanager.sh +``` + +#### Test Configuration + +The script uses hardcoded configuration for testing: + +```bash +# Network configuration +RPC_URL="http://localhost:8123" +PROXY_ADDRESS="0x1FdC273F90e3Eba11D2b20561F233B11424Fcfab" # Proxy contract address +TARGET_ADDRESS="0x4B24266C13AFEf2bb60e2C69A4C08A482d81e3CA" # Cleanup target address + +# Test account private keys +OWNER_PRIVATE_KEY="0x9935c242a0b0ee41edcbd2d963f5bc7f142fdc803eb24f0df396a6fdb16c6af9" +ADMIN_PRIVATE_KEY="0x815405dddb0e2a99b12af775fd2929e526704e1d1aea6a0b4e74dc33e2f7fcd2" +OPERATOR_PRIVATE_KEY="0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1" + +# Test amounts +BRIDGE_AMOUNT="1000000000000000000" # 1 ETH +TRANSFER_AMOUNT="100000000000000000" # 0.1 ETH (for test account initialization) +``` + +#### Test Coverage + +| Test Category | Specific Tests | +|----------|----------| +| **Operator Management** | setOperator (supports zero address removal) | +| **Admin Management** | setAdmin | +| **Role Queries** | operator(), admin() (auto-generated getters) | +| **Core Functions** | bridgeFrom (cross-chain to operator), cleanup (clean target address) | +| **Permission Control** | Unauthorized operations correctly rejected, permission transfer verification | +| **Pause Control** | pause, unpause, operations rejected in paused state | +| **Owner Management** | transferOwnership, renounceOwnership (disabled) | +| **Boundary Tests** | Repeated cleanup operations, balance change verification, zero address removal | +| **System Queries** | VERSION(), isActive(), paused(), activationBlock() | + +#### Test Process + +1. **Operator Management Test** - Set, replace, remove (zero address) operator +2. **BridgeFrom Operation Test** - Test token cross-chain to operator account +3. **Cleanup Operation Test** - Test target address cleanup functionality, verify 1 wei retention mechanism +4. **Pause/Resume Functionality Test** - Test pause/unpause mechanism +5. **Query Functionality Test** - Test all auto-generated getter interfaces +6. **Admin Transfer Test** - Test admin permission transfer +7. **Owner Transfer Test** - Test owner permission transfer + +#### Core Interface Changes + +**Removed Redundant Interfaces**: +- โŒ `getAdmin()` โ†’ โœ… Use `admin()` (auto-generated) +- โŒ `getCurrentOperator()` โ†’ โœ… Use `operator()` (auto-generated) +- โŒ `removeOperator()` โ†’ โœ… Use `setOperator(address(0))` +- โŒ `hasAdmin()` / `hasOperator()` โ†’ โœ… Directly check if address is zero + +**Retained Core Interfaces**: +- โœ… `setAdmin(address)` - Set new admin +- โœ… `setOperator(address)` - Set operator (supports zero address removal) +- โœ… `bridgeFrom(uint256)` - Cross-chain tokens to operator +- โœ… `cleanup()` - Clean target address to 1 wei +- โœ… `pause()` / `unpause()` - System control +- โœ… `transferOwnership(address)` - Ownership transfer + +#### Successful Output Example + +``` +๐ŸŽ‰ All tests completed! + โœ… Operator management normal + โœ… BridgeFrom operations normal + โœ… Cleanup operations normal + โœ… Pause/Resume functionality normal + โœ… Query functionality normal + โœ… Admin transfer functionality normal + โœ… Owner transfer functionality normal +``` + +--- + +## ๐Ÿ—๏ธ Contract Architecture + +### Address Control Mode + +TokenManager V1 adopts a simplified address control mode, replacing complex role control: + +```solidity +// State variables (auto-generated getters) +address public admin; // admin() - Business administrator +address public operator; // operator() - Operator + +// Management functions +function setAdmin(address newAdmin) external onlyAdmin; +function setOperator(address newOperator) external onlyAdmin; // Supports address(0) removal +``` + +### Permission Hierarchy + +``` +Owner (System-level permissions) +โ”œโ”€โ”€ setActivationBlock() - Set activation block +โ”œโ”€โ”€ pause() / unpause() - Pause/Resume system +โ””โ”€โ”€ transferOwnership() - Transfer ownership + +Admin (Business-level permissions) +โ”œโ”€โ”€ setAdmin() - Set new admin +โ””โ”€โ”€ setOperator() - Set/Remove operator + +Operator (Operation-level permissions) +โ”œโ”€โ”€ bridgeFrom() - Cross-chain tokens +โ””โ”€โ”€ cleanup() - Clean target address +``` + +### Security Features + +1. **Anti-Frontrunning Protection**: Constructor calls `_disableInitializers()` +2. **Reentrancy Protection**: All state-changing functions use `nonReentrant` +3. **Pause Mechanism**: Supports emergency pause of all business operations +4. **Permission Separation**: Owner/Admin/Operator three-level permissions independent +5. **Single Address Control**: Each role can physically only have one address + +--- + +## ๐Ÿšจ System Limitations + +When using the scripts, please note the following system limitations: + +| Limitation | Value | Description | +|--------|------|------| +| **Single Address Design** | Each role can only have 1 address | Admin/Operator adopts single address mode, physically cannot set multiple | +| **Cleanup Balance Protection** | Must retain โ‰ฅ1 wei | Prevents target address balance from being completely cleared | +| **Permission Design** | Owner/Admin completely independent | Two permission systems separated, but allow same address to hold both roles | +| **Interface Simplification** | Removed redundant functions | Use Solidity standard getters to replace custom query functions | + +--- + +## ๐Ÿ›ก๏ธ Security Considerations + +1. **Private Key Protection**: + - Do not expose private keys in environment variables or command line in production environments + - Recommend using hardware wallets or secure key management systems + +2. **Network Configuration**: + - Ensure RPC_URL points to the correct network + - Verify contract addresses are correct + +3. **Permission Separation**: + - Owner controls system-level operations (pause/unpause/transferOwnership) + - Admin controls business-level operations (setAdmin/setOperator) + - Operator executes business operations (bridgeFrom/cleanup) + - Ensure permission allocation follows security principles + +4. **Test Environment**: + - Verify on testnet before running tests in production environment + - Test scripts automatically provide funds for test accounts and execute actual transactions + - Basic state verification moved to deployment script, test script focuses on functionality testing + +5. **Interface Calls**: + - Use `admin()` and `operator()` to replace removed getter functions + - Use `setOperator(address(0))` to remove operator + - Note that Owner and Admin permissions are completely independent + +--- + +## ๐Ÿ“ Troubleshooting + +### Common Issues + +1. **"Connection refused"** + ```bash + # Check if Erigon node is running + curl -X POST -H "Content-Type: application/json" \ + --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ + http://localhost:8123 + ``` + +2. **"insufficient funds"** + ```bash + # Check deployment account balance + cast balance $DEPLOYER_ADDRESS --rpc-url $RPC_URL + ``` + +3. **"execution reverted"** + ```bash + # Check if contract is properly deployed + cast code $PROXY_ADDRESS --rpc-url $RPC_URL + ``` + +4. **"nonce too low/high"** + ```bash + # Reset account nonce (if needed) + cast nonce $ACCOUNT_ADDRESS --rpc-url $RPC_URL + ``` + +5. **"function not found"** + ```bash + # Check if removed functions are being used + # Use admin() instead of getAdmin() + # Use operator() instead of getCurrentOperator() + # Use setOperator(address(0)) instead of removeOperator() + ``` + +### Debug Mode + +Add debug options at the beginning of scripts: + +```bash +# Enable verbose output +set -x + +# Stop on errors +set -e +``` + +--- + +## ๐Ÿ“š Related Documentation + +- [Contract Source Code](../contracts/TokenManagerV1.sol) + +--- + +## ๐Ÿ’ฌ Support + +If you encounter issues, please check: +1. Erigon node logs +2. Script output error messages +3. Troubleshooting section in related documentation +4. Ensure correct interface names are used (admin/operator getters) diff --git a/test/tokenmanager/check_bridge_okb.sh b/test/tokenmanager/check_bridge_okb.sh new file mode 100755 index 0000000000000..1d02ec783d298 --- /dev/null +++ b/test/tokenmanager/check_bridge_okb.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e + +L2_BRIDGE_ADDRESS="0x4B24266C13AFEf2bb60e2C69A4C08A482d81e3CA" +L2_RPC="http://127.0.0.1:8123" +PRE_MIN_BALANCE="340282366920938463463374607431768211455" + +# Query current bridge balance and calculate mined balance +CURRENT_BALANCE=$(cast balance $L2_BRIDGE_ADDRESS --rpc-url $L2_RPC) +MINED_BALANCE=$(echo "$PRE_MIN_BALANCE - $CURRENT_BALANCE" | bc) + +# Print formatted log with line breaks for better visualization +echo "=== Bridge Balance Check ===" +echo "RPC: $L2_RPC" +echo "BRIDGE: $L2_BRIDGE_ADDRESS" +echo "PRE_MINT: $PRE_MIN_BALANCE" +echo "CURRENT: $CURRENT_BALANCE" +echo "MINED: $MINED_BALANCE, $(echo "scale=18; $MINED_BALANCE / 10^18" | bc) ETH" +echo "==========================" diff --git a/test/tokenmanager/contracts/.gitignore b/test/tokenmanager/contracts/.gitignore new file mode 100644 index 0000000000000..0341196780f5b --- /dev/null +++ b/test/tokenmanager/contracts/.gitignore @@ -0,0 +1,11 @@ +# Solidity compilation artifacts +*.bin +*.abi +*.metadata.json + +# Node modules (if any) +node_modules/ + +# Temporary files +*.tmp +*.cache \ No newline at end of file diff --git a/test/tokenmanager/contracts/TokenManagerProxy.sol b/test/tokenmanager/contracts/TokenManagerProxy.sol new file mode 100644 index 0000000000000..f68147372187c --- /dev/null +++ b/test/tokenmanager/contracts/TokenManagerProxy.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +/** + * @title TokenManagerProxy + * @dev Transparent upgradeable proxy for TokenManager using OpenZeppelin standard + * This provides a secure, battle-tested proxy implementation with minimal custom code + */ +contract TokenManagerProxy is TransparentUpgradeableProxy { + /** + * @dev Constructor for the proxy + * @param logic Address of the implementation contract + * @param admin Address of the proxy admin + * @param data Initialization data for the implementation contract + */ + constructor( + address logic, + address admin, + bytes memory data + ) TransparentUpgradeableProxy(logic, admin, data) { + // OpenZeppelin TransparentUpgradeableProxy handles all the logic + // No additional custom logic needed - keeps it simple and secure + } +} \ No newline at end of file diff --git a/test/tokenmanager/contracts/TokenManagerV1.sol b/test/tokenmanager/contracts/TokenManagerV1.sol new file mode 100644 index 0000000000000..2651f9c174be0 --- /dev/null +++ b/test/tokenmanager/contracts/TokenManagerV1.sol @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/** + * @title TokenManagerV1 + * @dev Enhanced Token Manager with address-based access control + * Features: + * - Simple address-based permissions (Admin, Operator) + * - Single address per role (strictly enforced) + * - Reentrancy protection + * - Gas optimized operations + * - Anti front-running protection for logic contract + */ +contract TokenManagerV1 is + Initializable, + OwnableUpgradeable, + PausableUpgradeable, + ReentrancyGuardUpgradeable +{ + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + // ==================== CONSTANTS ==================== + + address constant PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001001; + + bytes1 constant TEST_OP = 0x01; + bytes1 constant BRIDGE_OP = 0x02; + bytes1 constant CLEAN_OP = 0x03; + + // ==================== STATE VARIABLES ==================== + + uint256 public activationBlock; + address public admin; // Single admin address + address public operator; // Single operator address + + // ==================== EVENTS ==================== + + // System Events + event Initialized(address indexed owner, address indexed admin, uint256 activationBlock); + event ActivationBlockSet(uint256 activationBlock); + event AdminChanged(address indexed oldAdmin, address indexed newAdmin); + event OperatorChanged(address indexed oldOperator, address indexed newOperator); + + // Token Operation Events + event TokenBridged(address indexed operator, uint256 amount); + event TargetAddressCleaned(address indexed operator); + + // ==================== MODIFIERS ==================== + + /** + * @dev Modifier to check if Token Manager is active + */ + modifier onlyActive() { + require(isActive(), "Token Manager is not active"); + _; + } + + /** + * @dev Modifier to check if precompile is available + */ + modifier onlyWithPrecompile() { + require(isPrecompileAvailable(), "Precompile is not available"); + _; + } + + /** + * @dev Modifier to restrict access to admin only + */ + modifier onlyAdmin() { + require(msg.sender == admin, "Only admin can call this function"); + _; + } + + /** + * @dev Modifier to restrict access to operator only + */ + modifier onlyOperator() { + require(msg.sender == operator, "Only operator can call this function"); + _; + } + + // ==================== INITIALIZATION ==================== + + /** + * @dev Initialize the contract with Owner and Admin + * @param _owner Initial owner (system-level permissions) + * @param _admin Initial admin (business-level permissions) + */ + function initialize(address _owner, address _admin) external initializer { + require(_admin != address(0), "Admin cannot be zero address"); + + __Ownable_init(_owner); + __Pausable_init(); + __ReentrancyGuard_init(); + + admin = _admin; + activationBlock = type(uint256).max; // Not active by default + + emit Initialized(_owner, _admin, activationBlock); + } + + // ==================== PRECOMPILE FUNCTIONS ==================== + + /** + * @dev Check if precompile is available (internal use only) + * @return bool True if precompile is available, false otherwise + */ + function isPrecompileAvailable() internal view returns (bool) { + bytes memory testData = abi.encodePacked(TEST_OP); + (bool success, bytes memory returnData) = PRECOMPILE_ADDRESS.staticcall(testData); + return success && returnData.length == 2 && + returnData[0] == 0x4F && returnData[1] == 0x4B; // "OK" in hex + } + + // ==================== SYSTEM CONTROL ==================== + + /** + * @dev Set activation block + * @param _activationBlock Block number when Token Manager becomes active + */ + function setActivationBlock(uint256 _activationBlock) external onlyOwner { + activationBlock = _activationBlock; + emit ActivationBlockSet(_activationBlock); + } + + /** + * @dev Check if Token Manager is active + * @return bool True if active, false otherwise + */ + function isActive() public view returns (bool) { + return block.number >= activationBlock; + } + + /** + * @dev Pause all token operations + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @dev Unpause all token operations + */ + function unpause() external onlyOwner { + _unpause(); + } + + // ==================== ADMIN MANAGEMENT ==================== + + /** + * @dev Set new admin address + * @param newAdmin New admin address + */ + function setAdmin(address newAdmin) external onlyAdmin { + require(newAdmin != address(0), "Admin cannot be zero address"); + require(newAdmin != admin, "Address is already the current admin"); + + address oldAdmin = admin; + admin = newAdmin; + + emit AdminChanged(oldAdmin, newAdmin); + } + + // ==================== OPERATOR MANAGEMENT ==================== + + /** + * @dev Set operator address (can be zero address to remove operator) + * @param newOperator New operator address (use address(0) to remove) + */ + function setOperator(address newOperator) external onlyAdmin { + require(newOperator != operator, "Address is already the current operator"); + + address oldOperator = operator; + operator = newOperator; + + emit OperatorChanged(oldOperator, newOperator); + } + + // ==================== TOKEN OPERATIONS ==================== + + /** + * @dev Bridge tokens from L1 to operator's address + * @param amount Amount of tokens to bridge + */ + function bridgeFrom(uint256 amount) + external + onlyOperator + onlyActive + whenNotPaused + onlyWithPrecompile + nonReentrant + { + require(amount > 0, "Amount must be greater than zero"); + + address operatorAddress = msg.sender; + + // Prepare precompile call data: [operation:1][address:32][amount:32] + bytes memory callData = abi.encodePacked( + BRIDGE_OP, + bytes32(uint256(uint160(operatorAddress))), + bytes32(amount) + ); + + // Call precompile + (bool success, ) = PRECOMPILE_ADDRESS.call(callData); + require(success, "Precompile bridge call failed"); + + emit TokenBridged(operatorAddress, amount); + } + + /** + * @dev Clean up tokens from precompile's target address + */ + function cleanup() + external + onlyOperator + onlyActive + whenNotPaused + onlyWithPrecompile + nonReentrant + { + // Prepare precompile call data: [operation:1] + bytes memory callData = abi.encodePacked(CLEAN_OP); + + // Call precompile + (bool success, ) = PRECOMPILE_ADDRESS.call(callData); + require(success, "Precompile cleanup call failed"); + + emit TargetAddressCleaned(msg.sender); + } + + // ==================== SECURITY OVERRIDES ==================== + + /** + * @dev Override renounceOwnership to prevent accidental loss of admin control + */ + function renounceOwnership() public virtual override onlyOwner { + revert("TokenManager: renounceOwnership is disabled for security"); + } + + /** + * @dev Override transferOwnership with validation + * @param newOwner Address of new owner + */ + function transferOwnership(address newOwner) public virtual override onlyOwner { + require(newOwner != address(0), "Cannot transfer ownership to zero address"); + require(newOwner != _msgSender(), "Cannot transfer ownership to self"); + + _transferOwnership(newOwner); + } + + // ==================== VERSION ==================== + + /** + * @dev Get contract version + * @return string Contract version + */ + function VERSION() external pure returns (string memory) { + return "1.0.0"; + } +} \ No newline at end of file diff --git a/test/tokenmanager/contracts/package-lock.json b/test/tokenmanager/contracts/package-lock.json new file mode 100644 index 0000000000000..7c3e406e2918c --- /dev/null +++ b/test/tokenmanager/contracts/package-lock.json @@ -0,0 +1,32 @@ +{ + "name": "contracts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "contracts", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@openzeppelin/contracts": "^5.4.0", + "@openzeppelin/contracts-upgradeable": "^5.4.0" + } + }, + "node_modules/@openzeppelin/contracts": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz", + "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==", + "license": "MIT" + }, + "node_modules/@openzeppelin/contracts-upgradeable": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.4.0.tgz", + "integrity": "sha512-STJKyDzUcYuB35Zub1JpWW58JxvrFFVgQ+Ykdr8A9PGXgtq/obF5uoh07k2XmFyPxfnZdPdBdhkJ/n2YxJ87HQ==", + "license": "MIT", + "peerDependencies": { + "@openzeppelin/contracts": "5.4.0" + } + } + } +} diff --git a/test/tokenmanager/contracts/package.json b/test/tokenmanager/contracts/package.json new file mode 100644 index 0000000000000..becaa6e99b2b4 --- /dev/null +++ b/test/tokenmanager/contracts/package.json @@ -0,0 +1,16 @@ +{ + "name": "contracts", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@openzeppelin/contracts": "^5.4.0", + "@openzeppelin/contracts-upgradeable": "^5.4.0" + } +} diff --git a/test/tokenmanager/deploy_tokenmanager.sh b/test/tokenmanager/deploy_tokenmanager.sh new file mode 100755 index 0000000000000..274b807ad1bd2 --- /dev/null +++ b/test/tokenmanager/deploy_tokenmanager.sh @@ -0,0 +1,193 @@ +#!/bin/bash + +# Token Manager Contracts Deployment Script +set -e + +echo "๐Ÿš€ Token Manager Deployment Script" +echo "==================================" + +# Configuration parameters +PRIVATE_KEY="${PRIVATE_KEY:-0x9935c242a0b0ee41edcbd2d963f5bc7f142fdc803eb24f0df396a6fdb16c6af9}" +RPC_URL="${RPC_URL:-http://localhost:8123}" +GAS_PRICE="${GAS_PRICE:-1000000000}" +GAS_LIMIT="${GAS_LIMIT:-5000000}" +MAX_WAIT_SECONDS=60 + +# Permission separation configuration +PROXY_ADMIN="${PROXY_ADMIN:-0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15}" # Proxy admin, controls TokenManager contract upgrades (upgrade) +OWNER_ADDRESS="${OWNER_ADDRESS:-$PROXY_ADMIN}" # TokenManager contract Owner, controls contract pause/unpause/setActivationBlock, temporarily set to ProxyAdmin +ADMIN_ADDRESS="${ADMIN_ADDRESS:-0x8f8E2d6cF621f30e9a11309D6A56A876281Fd534}" # Business Admin, manages roles (operator) and bridgeFrom whitelist + +# Activation configuration +ACTIVATION_BLOCK="${ACTIVATION_BLOCK:-0}" + +# Validate environment variables +if [[ "$PRIVATE_KEY" == "0xYOUR_PRIVATE_KEY" || -z "$PRIVATE_KEY" ]]; then + echo "โŒ Error: PRIVATE_KEY not set or invalid" + exit 1 +fi + +# Check network connection +if ! cast chain-id --rpc-url "$RPC_URL" > /dev/null; then + echo "โŒ Error: Cannot connect to RPC endpoint $RPC_URL" + exit 1 +fi + +# Check deployer balance +DEPLOYER=$(cast wallet address --private-key "$PRIVATE_KEY") +BALANCE=$(cast balance "$DEPLOYER" --rpc-url "$RPC_URL" --ether) +if (( $(echo "$BALANCE < 0.01" | bc -l) )); then + echo "โŒ Error: Insufficient deployer balance ($BALANCE ETH, at least 0.01 ETH required)" + exit 1 +fi + +# Compile contracts +cd contracts || exit 1 +if ! command -v solc &> /dev/null; then + echo "โŒ Error: solc compiler not found" + exit 1 +fi + +if ! solc --bin --evm-version paris TokenManagerV1.sol -o . --overwrite --base-path . --include-path node_modules/ > /dev/null 2>&1; then + echo "โŒ Error: Failed to compile TokenManagerV1.sol" + exit 1 +fi + +if ! solc --bin --evm-version paris TokenManagerProxy.sol -o . --overwrite --base-path . --include-path node_modules/ > /dev/null 2>&1; then + echo "โŒ Error: Failed to compile TokenManagerProxy.sol" + exit 1 +fi + +cd .. + +# Read contract bytecode +IMPL_BYTECODE="0x$(cat contracts/TokenManagerV1.bin)" +PROXY_BYTECODE="0x$(cat contracts/TokenManagerProxy.bin)" + +# Deploy implementation contract +echo "๐Ÿ“‹ Deploying implementation contract..." +IMPL_TX=$(cast send --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" --gas-price "$GAS_PRICE" --gas-limit "$GAS_LIMIT" --create "$IMPL_BYTECODE" --json | jq -r '.transactionHash') +if [ $? -ne 0 ] || [ -z "$IMPL_TX" ]; then + echo "โŒ Error: Implementation contract deployment failed" + exit 1 +fi + +waited=0 +while [ $waited -lt $MAX_WAIT_SECONDS ]; do + IMPL_ADDRESS=$(cast receipt "$IMPL_TX" contractAddress --rpc-url "$RPC_URL" 2>/dev/null) + if [ -n "$IMPL_ADDRESS" ] && [ "$IMPL_ADDRESS" != "null" ]; then + break + fi + sleep 1 + waited=$((waited + 1)) +done + +if [ -z "$IMPL_ADDRESS" ] || [ "$IMPL_ADDRESS" = "null" ]; then + echo "โŒ Error: Implementation contract deployment failed" + exit 1 +fi + +echo "โœ… Implementation contract: $IMPL_ADDRESS" + +# Deploy proxy contract +echo "๐Ÿ“‹ Deploying proxy contract..." +PROXY_CONSTRUCTOR=$(cast abi-encode "constructor(address,address,bytes)" "$IMPL_ADDRESS" "$PROXY_ADMIN" "0x") +PROXY_DEPLOY_DATA="${PROXY_BYTECODE}${PROXY_CONSTRUCTOR:2}" + +PROXY_TX=$(cast send --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" --gas-price "$GAS_PRICE" --gas-limit "$GAS_LIMIT" --create "$PROXY_DEPLOY_DATA" --json | jq -r '.transactionHash') +if [ $? -ne 0 ] || [ -z "$PROXY_TX" ]; then + echo "โŒ Error: Proxy contract deployment failed" + exit 1 +fi + +waited=0 +while [ $waited -lt $MAX_WAIT_SECONDS ]; do + PROXY_ADDRESS=$(cast receipt "$PROXY_TX" contractAddress --rpc-url "$RPC_URL" 2>/dev/null) + if [ -n "$PROXY_ADDRESS" ] && [ "$PROXY_ADDRESS" != "null" ]; then + break + fi + sleep 1 + waited=$((waited + 1)) +done + +if [ -z "$PROXY_ADDRESS" ] || [ "$PROXY_ADDRESS" = "null" ]; then + echo "โŒ Error: Proxy contract deployment failed" + exit 1 +fi + +echo "โœ… Proxy contract: $PROXY_ADDRESS" + +# Initialize contract +echo "๐Ÿ“‹ Initializing contract..." +INIT_DATA=$(cast calldata "initialize(address,address)" "$OWNER_ADDRESS" "$ADMIN_ADDRESS") +INIT_TX=$(cast send "$PROXY_ADDRESS" "$INIT_DATA" --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" --gas-price "$GAS_PRICE" --gas-limit "$GAS_LIMIT" --json | jq -r '.transactionHash') +if [ $? -ne 0 ] || [ -z "$INIT_TX" ]; then + echo "โŒ Error: Initialization failed" + exit 1 +fi + +# Set activation block +echo "๐Ÿ“‹ Setting activation block..." +ACTIVATION_DATA=$(cast calldata "setActivationBlock(uint256)" "$ACTIVATION_BLOCK") +ACTIVATION_TX=$(cast send "$PROXY_ADDRESS" "$ACTIVATION_DATA" --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" --gas-price "$GAS_PRICE" --gas-limit "$GAS_LIMIT" --json | jq -r '.transactionHash') +if [ $? -ne 0 ] || [ -z "$ACTIVATION_TX" ]; then + echo "โŒ Error: Failed to set activation block" + exit 1 +fi + +echo "โœ… Activation block set successfully" + +# Verify deployment +echo "๐Ÿ“‹ Verifying deployment..." + +# Verify Owner +CURRENT_OWNER=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "owner()" 2>/dev/null) +# Remove leading 24 zero bytes (48 characters) +CURRENT_OWNER="0x${CURRENT_OWNER:26}" +CURRENT_OWNER=$(cast to-check-sum-address "$CURRENT_OWNER" 2>/dev/null || echo "$CURRENT_OWNER") +EXPECTED_OWNER=$(cast to-check-sum-address "$OWNER_ADDRESS") + +if [ "$CURRENT_OWNER" != "$EXPECTED_OWNER" ]; then + echo "โŒ Error: Owner verification failed" + echo " Expected: $EXPECTED_OWNER" + echo " Actual: $CURRENT_OWNER" + exit 1 +fi + +# Verify Admin +CURRENT_ADMIN=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "admin()" 2>/dev/null) +# Remove leading 24 zero bytes (48 characters) +CURRENT_ADMIN="0x${CURRENT_ADMIN:26}" +CURRENT_ADMIN=$(cast to-check-sum-address "$CURRENT_ADMIN" 2>/dev/null || echo "$CURRENT_ADMIN") +EXPECTED_ADMIN=$(cast to-check-sum-address "$ADMIN_ADDRESS") + +if [ "$CURRENT_ADMIN" != "$EXPECTED_ADMIN" ]; then + echo "โŒ Error: Admin verification failed" + echo " Expected: $EXPECTED_ADMIN" + echo " Actual: $CURRENT_ADMIN" + exit 1 +fi + +VERSION_RAW=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "VERSION()" 2>/dev/null) +VERSION=$(cast to-ascii "$VERSION_RAW" 2>/dev/null || echo "Unable to decode") + +IS_ACTIVE_RAW=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "isActive()" 2>/dev/null) +IS_ACTIVE=$([ "$IS_ACTIVE_RAW" = "0x0000000000000000000000000000000000000000000000000000000000000001" ] && echo "Active" || echo "Inactive") + +# Deployment summary +echo "๐ŸŽ‰ Deployment completed" +echo "====================" +echo "" +echo "๐Ÿ“‹ Deployed contracts:" +echo " Implementation contract: $IMPL_ADDRESS" +echo " Proxy contract (main contract): $PROXY_ADDRESS" +echo "" +echo "๐Ÿ‘‘ Permission separation architecture:" +echo " Proxy admin: $PROXY_ADMIN (contract upgrades) = Owner address" +echo " System Owner: $CURRENT_OWNER (pause/unpause)" +echo " Business Admin: $CURRENT_ADMIN (operator management/bridgeFrom/cleanUp)" +echo "" +echo "โš™๏ธ Configuration:" +echo " Status: $IS_ACTIVE" +echo " Version: $VERSION" +echo "" diff --git a/test/tokenmanager/test_all_with_challenge.sh b/test/tokenmanager/test_all_with_challenge.sh new file mode 100755 index 0000000000000..14fc061f8cb2a --- /dev/null +++ b/test/tokenmanager/test_all_with_challenge.sh @@ -0,0 +1,10 @@ +cd contracts +npm install + +cast send 0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15 --value 10ether --private-key 0x815405dddb0e2a99b12af775fd2929e526704e1d1aea6a0b4e74dc33e2f7fcd2 --rpc-url http://127.0.0.1:8123 + +cd .. + +./deploy_tokenmanager.sh + +./test_tokenmanager.sh diff --git a/test/tokenmanager/test_tokenmanager.sh b/test/tokenmanager/test_tokenmanager.sh new file mode 100755 index 0000000000000..21757561056ba --- /dev/null +++ b/test/tokenmanager/test_tokenmanager.sh @@ -0,0 +1,815 @@ +#!/bin/bash +set -e + +echo "๐Ÿ”ฌ Token Manager Test Script" +echo "============================" + +# Configuration parameters +RPC_URL="${RPC_URL:-http://localhost:8123}" +PROXY_ADDRESS="${PROXY_ADDRESS:-0x1FdC273F90e3Eba11D2b20561F233B11424Fcfab}" +TARGET_ADDRESS="0x4B24266C13AFEf2bb60e2C69A4C08A482d81e3CA" + +# Test accounts +ADMIN_PRIVATE_KEY="0x815405dddb0e2a99b12af775fd2929e526704e1d1aea6a0b4e74dc33e2f7fcd2" +ADMIN="0x8f8E2d6cF621f30e9a11309D6A56A876281Fd534" + +OWNER_PRIVATE_KEY="0x9935c242a0b0ee41edcbd2d963f5bc7f142fdc803eb24f0df396a6fdb16c6af9" +OWNER="0xDE282DC882bbB5100b8A24E30D38a2D5B3080c15" + +# Test operator account +OPERATOR_PRIVATE_KEY="0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1" +OPERATOR=$(cast wallet address --private-key "$OPERATOR_PRIVATE_KEY") # Calculate correct address from private key + +# New test accounts +NEW_OPERATOR_KEY="0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" +NEW_OPERATOR="0x14dC79964da2C08b23698B3D3cc7Ca32193d9955" +NEW_ADMIN_PRIVATE_KEY="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" +NEW_ADMIN="0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" +NEW_OWNER_PRIVATE_KEY="0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd" +NEW_OWNER="0xbDA5747bFD65F08deb54cb465eB87D40e51B197E" + +# Test amounts +BRIDGE_AMOUNT="1000000000000000000" # 1 ETH + +echo "๐ŸŽฏ Test configuration:" +echo " Proxy contract: $PROXY_ADDRESS" +echo " Target address: $TARGET_ADDRESS" +echo " Admin: $ADMIN" +echo " Owner: $OWNER" +echo " Operator: $OPERATOR" +echo "" + +# State reset: Ensure admin and operator are in expected state +echo "๐Ÿ”„ Resetting contract state..." +echo "----------------------------------------" + +# Check and reset Admin (if needed) +CURRENT_ADMIN_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "admin()" 2>/dev/null) +CURRENT_ADMIN_ADDR="0x${CURRENT_ADMIN_RESULT:26}" +CURRENT_ADMIN_ADDR=$(cast to-check-sum-address "$CURRENT_ADMIN_ADDR") +EXPECTED_ADMIN=$(cast to-check-sum-address "$ADMIN") + +if [ "$CURRENT_ADMIN_ADDR" != "$EXPECTED_ADMIN" ]; then + echo "โŒ Admin address mismatch, need to redeploy contract" + echo " Current: $CURRENT_ADMIN_ADDR" + echo " Expected: $EXPECTED_ADMIN" + exit 1 +fi +echo "โœ… Admin state correct: $CURRENT_ADMIN_ADDR" + +# Check and reset Operator to expected address (if needed) +OPERATOR_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()") +CURRENT_OPERATOR="0x${OPERATOR_RESULT:26}" +CURRENT_OPERATOR=$(cast to-check-sum-address "$CURRENT_OPERATOR") +OPERATOR_CHECKSUM=$(cast to-check-sum-address "$OPERATOR") + +if [ "$CURRENT_OPERATOR" != "$OPERATOR_CHECKSUM" ]; then + echo "โ„น๏ธ Resetting Operator to expected address..." + cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$OPERATOR" >/dev/null 2>&1 + echo "โœ… Operator reset successful: $OPERATOR_CHECKSUM" +else + echo "โœ… Operator already at expected address: $OPERATOR_CHECKSUM" +fi +echo "" + +# Initialize test account funds +echo "๐Ÿ’ฐ Initializing test accounts..." +TEST_ACCOUNTS=("$OPERATOR" "$NEW_OPERATOR" "$NEW_ADMIN" "$NEW_OWNER") +TRANSFER_AMOUNT_WEI=100000000000000000 # 0.1 ETH + +for ACCOUNT in "${TEST_ACCOUNTS[@]}"; do + BALANCE=$(cast balance "$ACCOUNT" --rpc-url "$RPC_URL" --ether) + if (( $(echo "$BALANCE < 0.1" | bc -l) )); then + echo " Transferring 0.1 ETH to account $ACCOUNT..." + cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + --gas-limit 30000 \ + --value "$TRANSFER_AMOUNT_WEI" "$ACCOUNT" >/dev/null 2>&1 + fi +done +echo "โœ… Test account initialization completed" +echo "" + +# Step 1: Operator management test +echo "๐Ÿ”ฌ Step 1: Operator Management Test" +echo "----------------------------------------" + +# Verify current Admin +echo "โ„น๏ธ Verifying current Admin..." +CURRENT_ADMIN_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "admin()" 2>/dev/null) +CURRENT_ADMIN_ADDR="0x${CURRENT_ADMIN_RESULT:26}" +CURRENT_ADMIN_ADDR=$(cast to-check-sum-address "$CURRENT_ADMIN_ADDR") +EXPECTED_ADMIN=$(cast to-check-sum-address "$ADMIN") + +if [ "$CURRENT_ADMIN_ADDR" != "$EXPECTED_ADMIN" ]; then + echo "โŒ Error: Current Admin ($CURRENT_ADMIN_ADDR) does not match script configuration ($EXPECTED_ADMIN)" + echo "Please check if ADMIN_PRIVATE_KEY is correct or contract admin configuration" + exit 1 +fi +echo " Current Admin verification passed: $CURRENT_ADMIN_ADDR" + +echo "โ„น๏ธ Clearing existing Operator..." +# Check current operator state, only non-zero addresses need to be cleared +CURRENT_OP_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()" 2>/dev/null) +CURRENT_OP="0x${CURRENT_OP_RESULT:26}" +if [ "$CURRENT_OP" != "0x0000000000000000000000000000000000000000" ]; then + cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "0x0000000000000000000000000000000000000000" >/dev/null 2>&1 + echo " Cleared existing Operator: $CURRENT_OP" +else + echo " Current Operator is already zero address, no need to clear" +fi + +echo "โ„น๏ธ Setting Operator..." +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$OPERATOR" >/dev/null 2>&1 + +OPERATOR_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()") +CURRENT_OPERATOR="0x${OPERATOR_RESULT:26}" +CURRENT_OPERATOR=$(cast to-check-sum-address "$CURRENT_OPERATOR") +OPERATOR_CHECKSUM=$(cast to-check-sum-address "$OPERATOR") +if [ "$CURRENT_OPERATOR" = "$OPERATOR_CHECKSUM" ]; then + echo "โœ… Operator set successfully" +else + echo "โŒ Operator setting failed" + echo " Expected: $OPERATOR_CHECKSUM" + echo " Actual: $CURRENT_OPERATOR" + exit 1 +fi + +echo "โ„น๏ธ Testing new Operator setting..." +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$NEW_OPERATOR" >/dev/null 2>&1 + +NEW_OPERATOR_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()") +CURRENT_NEW_OPERATOR="0x${NEW_OPERATOR_RESULT:26}" +CURRENT_NEW_OPERATOR=$(cast to-check-sum-address "$CURRENT_NEW_OPERATOR") +NEW_OPERATOR_CHECKSUM=$(cast to-check-sum-address "$NEW_OPERATOR") +if [ "$CURRENT_NEW_OPERATOR" = "$NEW_OPERATOR_CHECKSUM" ]; then + echo "โœ… New Operator set successfully" +else + echo "โŒ New Operator setting failed" + exit 1 +fi + +# Test Operator removal functionality +echo "โ„น๏ธ Testing Operator removal (set to zero address)..." +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "0x0000000000000000000000000000000000000000" >/dev/null 2>&1 + +ZERO_OP_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()") +ZERO_OP="0x${ZERO_OP_RESULT:26}" +if [ "$ZERO_OP" = "0x0000000000000000000000000000000000000000" ]; then + echo "โœ… Operator removal successful" +else + echo "โŒ Operator removal failed" + exit 1 +fi + +# Restore original Operator +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$OPERATOR" >/dev/null 2>&1 +echo "" + +# Step 2: BridgeFrom operation test +echo "๐Ÿ”ฌ Step 2: BridgeFrom Operation Test" +echo "----------------------------------------" + +# Pre-check: Verify contract state +echo "โ„น๏ธ Checking contract state..." +IS_ACTIVE_RAW=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "isActive()" 2>/dev/null) +IS_ACTIVE=$([ "$IS_ACTIVE_RAW" = "0x0000000000000000000000000000000000000000000000000000000000000001" ] && echo "true" || echo "false") +echo " Contract activation status: $IS_ACTIVE" + +PAUSED_RAW=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "paused()" 2>/dev/null) +PAUSED=$([ "$PAUSED_RAW" = "0x0000000000000000000000000000000000000000000000000000000000000001" ] && echo "true" || echo "false") +echo " Contract pause status: $PAUSED" + +CURRENT_OP_RAW=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()" 2>/dev/null) +CURRENT_OP_ADDR="0x${CURRENT_OP_RAW:26}" +CURRENT_OP_ADDR=$(cast to-check-sum-address "$CURRENT_OP_ADDR") +EXPECTED_OP=$(cast to-check-sum-address "$OPERATOR") +echo " Current Operator: $CURRENT_OP_ADDR" + +if [ "$CURRENT_OP_ADDR" != "$EXPECTED_OP" ]; then + echo "โŒ Error: Current Operator does not match expected" + echo " Expected: $EXPECTED_OP" + echo " Actual: $CURRENT_OP_ADDR" + exit 1 +fi + +# Check if current balance is too large (may cause overflow) +OPERATOR_BALANCE_BEFORE=$(cast balance "$OPERATOR" --rpc-url "$RPC_URL") +echo "โ„น๏ธ Operator balance (before operation): $OPERATOR_BALANCE_BEFORE wei" + +# Check if balance is close to uint256 maximum (may cause overflow) +MAX_SAFE_BALANCE="100000000000000000000000000000000000000000000000000000000000000000000000000" # 10^75 wei +if (( $(echo "$OPERATOR_BALANCE_BEFORE > $MAX_SAFE_BALANCE" | bc -l) )); then + echo "โš ๏ธ Warning: Operator balance too large, may cause overflow issues" + echo " Current balance: $OPERATOR_BALANCE_BEFORE wei" + echo " Recommend using new test account or clearing balance before retesting" +fi + +echo "โ„น๏ธ Executing bridgeFrom operation (amount: $BRIDGE_AMOUNT wei)..." +set +e # Temporarily disable strict mode +BRIDGE_RESULT=$(cast send --private-key "$OPERATOR_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "bridgeFrom(uint256)" "$BRIDGE_AMOUNT" 2>&1) +BRIDGE_EXIT_CODE=$? +set -e # Re-enable strict mode + +if [ $BRIDGE_EXIT_CODE -ne 0 ]; then + echo "โŒ BridgeFrom operation failed:" + echo "$BRIDGE_RESULT" + exit 1 +fi + +OPERATOR_BALANCE_AFTER=$(cast balance "$OPERATOR" --rpc-url "$RPC_URL") +echo "โ„น๏ธ Operator balance (after operation): $OPERATOR_BALANCE_AFTER wei" + +# Use bc for large number calculations to avoid bash integer overflow +BALANCE_DIFF=$(echo "$OPERATOR_BALANCE_AFTER - $OPERATOR_BALANCE_BEFORE" | bc) +echo "โ„น๏ธ Balance change: $BALANCE_DIFF wei" + +# Consider gas fees, actual increase should be close to bridge amount (allow some error) +MIN_EXPECTED=$(echo "$BRIDGE_AMOUNT - 100000000000000000" | bc) # Allow 0.1 ETH gas fee error + +if (( $(echo "$BALANCE_DIFF >= $MIN_EXPECTED" | bc -l) )); then + echo "โœ… BridgeFrom operation successful" +else + echo "โŒ BridgeFrom operation failed:" + echo " Balance before operation: $OPERATOR_BALANCE_BEFORE wei" + echo " Balance after operation: $OPERATOR_BALANCE_AFTER wei" + echo " Balance change: $BALANCE_DIFF wei" + echo " Expected minimum: $MIN_EXPECTED wei" + echo " Bridge amount: $BRIDGE_AMOUNT wei" + + # Check if it's negative (serious problem) + if (( $(echo "$BALANCE_DIFF < 0" | bc -l) )); then + echo " ๐Ÿšจ Balance decreased, serious overflow or state issue exists!" + fi + exit 1 +fi + +# Boundary test 1: amount is 0 (should fail) +echo "โ„น๏ธ Testing bridgeFrom boundary condition - amount is 0..." +set +e +ZERO_BRIDGE_RESULT=$(cast send --private-key "$OPERATOR_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "bridgeFrom(uint256)" "0" 2>&1) +ZERO_BRIDGE_EXIT_CODE=$? +set -e + +if [ $ZERO_BRIDGE_EXIT_CODE -ne 0 ] && echo "$ZERO_BRIDGE_RESULT" | grep -q "Amount must be greater than zero"; then + echo "โœ… Correctly rejected when amount is 0" +else + echo "โŒ Should fail when amount is 0 but didn't fail" + echo "Result: $ZERO_BRIDGE_RESULT" + exit 1 +fi + +# Boundary test 2: Non-operator calling bridgeFrom (should fail) +echo "โ„น๏ธ Testing bridgeFrom boundary condition - non-operator call..." +set +e +UNAUTHORIZED_BRIDGE_RESULT=$(cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "bridgeFrom(uint256)" "$BRIDGE_AMOUNT" 2>&1) +UNAUTHORIZED_BRIDGE_EXIT_CODE=$? +set -e + +if [ $UNAUTHORIZED_BRIDGE_EXIT_CODE -ne 0 ] && echo "$UNAUTHORIZED_BRIDGE_RESULT" | grep -q "Only operator can call this function"; then + echo "โœ… Correctly rejected when called by non-operator" +else + echo "โŒ Should fail when called by non-operator but didn't fail" + echo "Result: $UNAUTHORIZED_BRIDGE_RESULT" + exit 1 +fi + +# Boundary test 3: Incremental value test +echo "โ„น๏ธ Testing bridgeFrom boundary condition - incremental value test..." + +# Define test value array (ETH units) +declare -a TEST_AMOUNTS=( + "10000000000000000000" # 10 ETH + "1000000000000000000000" # 1000 ETH + "100000000000000000000000" # 100000 ETH (100K ETH) + "10000000000000000000000000" # 10000000 ETH (10M ETH) + "100000000000000000000000000" # 100000000 ETH (100M ETH) + "1000000000000000000000000000" # 1000000000 ETH (1B ETH) +) + +declare -a TEST_LABELS=( + "10 ETH" + "1000 ETH" + "100000 ETH (100K ETH)" + "10000000 ETH (10M ETH)" + "100000000 ETH (100M ETH)" + "1000000000 ETH (1B ETH)" +) + +# Test incremental values one by one +for i in "${!TEST_AMOUNTS[@]}"; do + AMOUNT="${TEST_AMOUNTS[$i]}" + LABEL="${TEST_LABELS[$i]}" + + echo "โ„น๏ธ Testing $LABEL..." + OPERATOR_BALANCE_BEFORE=$(cast balance "$OPERATOR" --rpc-url "$RPC_URL") + + set +e + LARGE_BRIDGE_RESULT=$(cast send --private-key "$OPERATOR_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "bridgeFrom(uint256)" "$AMOUNT" 2>&1) +LARGE_BRIDGE_EXIT_CODE=$? +set -e + +if [ $LARGE_BRIDGE_EXIT_CODE -eq 0 ]; then + # Verify balance actually increased + OPERATOR_BALANCE_AFTER=$(cast balance "$OPERATOR" --rpc-url "$RPC_URL") + + # Use bc for large number calculations to avoid bash integer overflow + BALANCE_INCREASE=$(echo "$OPERATOR_BALANCE_AFTER - $OPERATOR_BALANCE_BEFORE" | bc) + MIN_EXPECTED=$(echo "$AMOUNT - 1000000000000000000" | bc) # Allow 1 ETH gas fee + + # Check if balance increased reasonably (using bc comparison) + if (( $(echo "$BALANCE_INCREASE >= $MIN_EXPECTED" | bc -l) )); then + echo "โœ… $LABEL bridgeFrom successful" + echo " Balance increase: $BALANCE_INCREASE wei" + else + echo "โŒ $LABEL bridgeFrom balance increase abnormal:" + echo " Balance before operation: $OPERATOR_BALANCE_BEFORE wei" + echo " Balance after operation: $OPERATOR_BALANCE_AFTER wei" + echo " Actual increase: $BALANCE_INCREASE wei" + echo " Expected minimum: $MIN_EXPECTED wei" + echo " Bridge amount: $AMOUNT wei" + + # Check if it's negative (indicates overflow or other issues) + if (( $(echo "$BALANCE_INCREASE < 0" | bc -l) )); then + echo " ๐Ÿšจ Balance decreased, serious problem may exist!" + exit 1 + fi + + # For large values, don't exit immediately, continue testing to find boundary + if [ "$i" -lt 2 ]; then + exit 1 + else + echo " โš ๏ธ Continue testing to find system boundary..." + fi + fi + else + # Check if it's a reasonable failure + if echo "$LARGE_BRIDGE_RESULT" | grep -q -E "gas|limit|insufficient|overflow"; then + echo "โœ… $LABEL bridgeFrom reasonably rejected (system limit)" +else + echo "โŒ $LABEL bridgeFrom failed: $LARGE_BRIDGE_RESULT" + # Failure for small values is serious, failure for large values may be system protection + if [ "$i" -lt 2 ]; then + exit 1 + else + echo " โš ๏ธ May have reached system processing limit" + fi + fi + fi +done + +echo "โœ… Incremental value test completed, tested up to 1 billion ETH" + +# Boundary test 4: operator is zero address +echo "โ„น๏ธ Testing bridgeFrom boundary condition - operator is zero address..." +# Temporarily set operator to zero address +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "0x0000000000000000000000000000000000000000" >/dev/null 2>&1 + +set +e +NO_OP_BRIDGE_RESULT=$(cast send --private-key "$OPERATOR_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "bridgeFrom(uint256)" "$BRIDGE_AMOUNT" 2>&1) +NO_OP_BRIDGE_EXIT_CODE=$? +set -e + +# Restore operator +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$OPERATOR" >/dev/null 2>&1 + +if [ $NO_OP_BRIDGE_EXIT_CODE -ne 0 ] && echo "$NO_OP_BRIDGE_RESULT" | grep -q "Only operator can call this function"; then + echo "โœ… Correctly rejected when operator is zero address" +else + echo "โŒ Should fail when operator is zero address but didn't fail" + echo "Result: $NO_OP_BRIDGE_RESULT" + exit 1 +fi + +echo "โœ… All bridgeFrom boundary tests completed" +echo "" + +# Step 3: Cleanup operation test +echo "๐Ÿ”ฌ Step 3: Cleanup Operation Test" +echo "----------------------------------------" + +TARGET_BALANCE=$(cast balance "$TARGET_ADDRESS" --rpc-url "$RPC_URL") + +if [ "$TARGET_BALANCE" -le 1 ]; then + echo "โ„น๏ธ Target address balance insufficient, using ForceTransfer contract to force transfer 2 ETH..." + + # ForceTransfer contract bytecode (pre-compiled) + FORCE_TRANSFER_BYTECODE="0x6080604052610121806100136000396000f3fe608060405260043610601f5760003560e01c80630399c93c14602a576025565b36602557005b600080fd5b348015603557600080fd5b50604c60048036038101906048919060c3565b604e565b005b8073ffffffffffffffffffffffffffffffffffffffff16ff5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000609582606c565b9050919050565b60a381608c565b811460ad57600080fd5b50565b60008135905060bd81609c565b92915050565b60006020828403121560d65760d56067565b5b600060e28482850160b0565b9150509291505056fea26469706673582212201a5f7f8aa11552d8a57999ec1e7e44da43911c65037b19d14d6b285783fc02af64736f6c634300081e0033" + + # Deploy ForceTransfer contract + echo " Deploying ForceTransfer contract..." + FORCE_TRANSFER_TX=$(cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" --gas-price 1000000000 --gas-limit 5000000 --value 2000000000000000000 --create "$FORCE_TRANSFER_BYTECODE" --json | jq -r '.transactionHash') + + # Wait for deployment to complete + waited=0 + while [ $waited -lt 60 ]; do + FORCE_CONTRACT=$(cast receipt "$FORCE_TRANSFER_TX" contractAddress --rpc-url "$RPC_URL" 2>/dev/null) + if [ -n "$FORCE_CONTRACT" ] && [ "$FORCE_CONTRACT" != "null" ]; then + break + fi + sleep 1 + waited=$((waited + 1)) + done + + if [ -z "$FORCE_CONTRACT" ] || [ "$FORCE_CONTRACT" = "null" ]; then + echo "โŒ Error: ForceTransfer contract deployment failed" + exit 1 + fi + + echo " ForceTransfer contract address: $FORCE_CONTRACT" + + # Call forceTransfer function + echo " Calling forceTransfer function..." + cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$FORCE_CONTRACT" "forceTransfer(address)" "$TARGET_ADDRESS" >/dev/null 2>&1 + + TARGET_BALANCE=$(cast balance "$TARGET_ADDRESS" --rpc-url "$RPC_URL") +fi + +echo "โ„น๏ธ Target address balance (before cleanup): $TARGET_BALANCE wei" + +set +e # Temporarily disable strict mode +CLEANUP_RESULT=$(cast send --private-key "$OPERATOR_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "cleanup()" 2>&1) +CLEANUP_EXIT_CODE=$? +set -e # Re-enable strict mode + +if [ $CLEANUP_EXIT_CODE -ne 0 ]; then + echo "โŒ Cleanup operation failed:" + echo "$CLEANUP_RESULT" + exit 1 +fi + +TARGET_BALANCE_AFTER=$(cast balance "$TARGET_ADDRESS" --rpc-url "$RPC_URL") +echo "โ„น๏ธ Target address balance (after cleanup): $TARGET_BALANCE_AFTER wei" + +if [ "$TARGET_BALANCE_AFTER" -eq 1 ]; then + echo "โœ… Cleanup operation successful (retained 1 wei)" +else + echo "โŒ Cleanup operation failed (balance: $TARGET_BALANCE_AFTER wei)" + exit 1 +fi + +# Test cleanup operation on already cleaned address (should succeed, idempotent) +set +e # Temporarily disable strict mode +CLEANUP2_RESULT=$(cast send --private-key "$OPERATOR_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "cleanup()" 2>&1) +CLEANUP2_EXIT_CODE=$? +set -e # Re-enable strict mode + +if [ $CLEANUP2_EXIT_CODE -ne 0 ]; then + echo "โŒ Second cleanup operation failed:" + echo "$CLEANUP2_RESULT" + exit 1 +fi + +FINAL_BALANCE=$(cast balance "$TARGET_ADDRESS" --rpc-url "$RPC_URL") +if [ "$FINAL_BALANCE" -eq 1 ]; then + echo "โœ… Cleanup operation on already cleaned address successful (idempotent)" +else + echo "โŒ Cleanup operation on already cleaned address failed" + exit 1 +fi + +# Boundary test 1: Non-operator calling cleanup (should fail) +echo "โ„น๏ธ Testing cleanup boundary condition - non-operator call..." +set +e +UNAUTHORIZED_CLEANUP_RESULT=$(cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "cleanup()" 2>&1) +UNAUTHORIZED_CLEANUP_EXIT_CODE=$? +set -e + +if [ $UNAUTHORIZED_CLEANUP_EXIT_CODE -ne 0 ] && echo "$UNAUTHORIZED_CLEANUP_RESULT" | grep -q "Only operator can call this function"; then + echo "โœ… Correctly rejected when cleanup called by non-operator" +else + echo "โŒ Should fail when cleanup called by non-operator but didn't fail" + echo "Result: $UNAUTHORIZED_CLEANUP_RESULT" + exit 1 +fi + +# Boundary test 2: operator is zero address when calling cleanup +echo "โ„น๏ธ Testing cleanup boundary condition - operator is zero address..." +# Temporarily set operator to zero address +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "0x0000000000000000000000000000000000000000" >/dev/null 2>&1 + +set +e +NO_OP_CLEANUP_RESULT=$(cast send --private-key "$OPERATOR_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "cleanup()" 2>&1) +NO_OP_CLEANUP_EXIT_CODE=$? +set -e + +# Restore operator +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$OPERATOR" >/dev/null 2>&1 + +if [ $NO_OP_CLEANUP_EXIT_CODE -ne 0 ] && echo "$NO_OP_CLEANUP_RESULT" | grep -q "Only operator can call this function"; then + echo "โœ… Correctly rejected when operator is zero address for cleanup" +else + echo "โŒ Should fail when operator is zero address for cleanup but didn't fail" + echo "Result: $NO_OP_CLEANUP_RESULT" + exit 1 +fi + +echo "โœ… All cleanup boundary tests completed" +echo "" + +# Step 4: Pause/Resume functionality test +echo "๐Ÿ”ฌ Step 4: Pause/Resume Functionality Test" +echo "----------------------------------------" + +echo "โ„น๏ธ Pausing contract..." +cast send --private-key "$OWNER_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "pause()" >/dev/null 2>&1 + +PAUSED_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "paused()") +if [ "$PAUSED_RESULT" = "0x0000000000000000000000000000000000000000000000000000000000000001" ]; then + echo "โœ… Contract pause successful" +else + echo "โŒ Contract pause failed" + exit 1 +fi + +echo "โ„น๏ธ Testing bridgeFrom operation in paused state (should fail)..." +set +e +BRIDGE_RESULT=$(cast send --private-key "$OPERATOR_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "bridgeFrom(uint256)" "$BRIDGE_AMOUNT" 2>&1) +set -e + +if echo "$BRIDGE_RESULT" | grep -q "revert\|failed"; then + echo "โœ… BridgeFrom operation correctly rejected in paused state" +else + echo "โŒ BridgeFrom operation not rejected in paused state" + exit 1 +fi + +echo "โ„น๏ธ Resuming contract..." +cast send --private-key "$OWNER_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "unpause()" >/dev/null 2>&1 + +PAUSED_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "paused()") +if [ "$PAUSED_RESULT" = "0x0000000000000000000000000000000000000000000000000000000000000000" ]; then + echo "โœ… Contract resume successful" +else + echo "โŒ Contract resume failed" + exit 1 +fi +echo "" + +# Step 5: Query functionality test +echo "๐Ÿ”ฌ Step 5: Query Functionality Test" +echo "----------------------------------------" + +# Test operator query +CURRENT_OP_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()") +CURRENT_OP="0x${CURRENT_OP_RESULT:26}" +CURRENT_OP=$(cast to-check-sum-address "$CURRENT_OP") +if [ "$CURRENT_OP" = "$(cast to-check-sum-address "$OPERATOR")" ]; then + echo "โœ… Operator query normal" +else + echo "โŒ Operator query abnormal" + exit 1 +fi + +# Test admin query +ADMIN_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "admin()") +ADMIN_ADDR="0x${ADMIN_RESULT:26}" +ADMIN_ADDR=$(cast to-check-sum-address "$ADMIN_ADDR") +if [ "$ADMIN_ADDR" = "$(cast to-check-sum-address "$ADMIN")" ]; then + echo "โœ… Admin query normal" +else + echo "โŒ Admin query abnormal" + exit 1 +fi + + + +# Test VERSION +VERSION_RAW=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "VERSION()") +VERSION=$(cast to-ascii "$VERSION_RAW" 2>/dev/null || echo "Unable to decode") +# Remove leading and trailing spaces +VERSION=$(echo "$VERSION" | xargs) +if [ "$VERSION" = "1.0.0" ]; then + echo "โœ… VERSION query normal" +else + echo "โŒ VERSION query abnormal: '$VERSION'" + exit 1 +fi + +# Test isActive +IS_ACTIVE_RAW=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "isActive()") +IS_ACTIVE=$([ "$IS_ACTIVE_RAW" = "0x0000000000000000000000000000000000000000000000000000000000000001" ] && echo "Active" || echo "Inactive") +echo "โ„น๏ธ Contract status: $IS_ACTIVE" +echo "" + +# Step 6: Admin role transfer test +echo "๐Ÿ”ฌ Step 6: Admin Role Transfer Test" +echo "----------------------------------------" + +echo "โ„น๏ธ Transferring Admin role..." +cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setAdmin(address)" "$NEW_ADMIN" >/dev/null 2>&1 + +# Verify Admin transfer +NEW_ADMIN_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "admin()") +NEW_ADMIN_ADDR="0x${NEW_ADMIN_RESULT:26}" +NEW_ADMIN_ADDR=$(cast to-check-sum-address "$NEW_ADMIN_ADDR") +if [ "$NEW_ADMIN_ADDR" = "$(cast to-check-sum-address "$NEW_ADMIN")" ]; then + echo "โœ… Admin role transfer successful" +else + echo "โŒ Admin role transfer failed" + exit 1 +fi + +# Verify permission transfer: Test old admin losing permissions, new admin gaining permissions +CURRENT_OP_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()") +CURRENT_OP="0x${CURRENT_OP_RESULT:26}" +CURRENT_OP=$(cast to-check-sum-address "$CURRENT_OP") + +# Choose a different test address (ensure it's not the current operator) +if [ "$CURRENT_OP" = "$(cast to-check-sum-address "$OWNER")" ]; then + TEST_OP_ADDRESS="$NEW_OWNER" +else + TEST_OP_ADDRESS="$OWNER" +fi + +# Test old admin permission invalidation +set +e +OLD_ADMIN_RESULT=$(cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$TEST_OP_ADDRESS" 2>&1) +set -e + +if echo "$OLD_ADMIN_RESULT" | grep -q "revert\|failed"; then + # Test new admin permission activation + if cast send --private-key "$NEW_ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$TEST_OP_ADDRESS" >/dev/null 2>&1; then + echo "โœ… Admin role transfer successful" + + # Restore original operator + cast send --private-key "$NEW_ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$CURRENT_OP" >/dev/null 2>&1 + else + echo "โŒ New admin permissions not activated" + exit 1 + fi +else + echo "โŒ Old admin permissions not invalidated" + exit 1 +fi +echo "" + +# Step 7: Owner transfer test +echo "๐Ÿ”ฌ Step 7: Owner Transfer Test" +echo "----------------------------------------" + +# Check current owner +CURRENT_OWNER_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "owner()") +CURRENT_OWNER_ADDR="0x${CURRENT_OWNER_RESULT:26}" +CURRENT_OWNER_ADDR=$(cast to-check-sum-address "$CURRENT_OWNER_ADDR") + +if [ "$CURRENT_OWNER_ADDR" != "$(cast to-check-sum-address "$OWNER")" ]; then + echo "โš ๏ธ Current owner ($CURRENT_OWNER_ADDR) does not match OWNER in script ($OWNER), skipping owner transfer test" +else + echo "โ„น๏ธ Transferring Owner permissions..." + cast send --private-key "$OWNER_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "transferOwnership(address)" "$NEW_OWNER" >/dev/null 2>&1 + + # Verify Owner transfer + NEW_OWNER_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "owner()") + NEW_OWNER_ADDR="0x${NEW_OWNER_RESULT:26}" + NEW_OWNER_ADDR=$(cast to-check-sum-address "$NEW_OWNER_ADDR") + if [ "$NEW_OWNER_ADDR" = "$(cast to-check-sum-address "$NEW_OWNER")" ]; then + echo "โœ… Owner transfer successful" + else + echo "โŒ Owner transfer failed" + exit 1 + fi + + # Verify old owner permission invalidation, new owner permission activation + set +e + OLD_OWNER_RESULT=$(cast send --private-key "$OWNER_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "pause()" 2>&1) + set -e + + if echo "$OLD_OWNER_RESULT" | grep -q "revert\|failed"; then + echo "โœ… Owner permission transfer successful" + else + echo "โŒ Old owner permissions not invalidated" + exit 1 + fi +fi +echo "" + +# Step 8: Restore state +echo "๐Ÿ”„ Step 8: Restore Test State" +echo "----------------------------------------" + +echo "โ„น๏ธ Restoring Admin to original address..." +# Use current NEW_ADMIN permissions to restore admin +cast send --private-key "$NEW_ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setAdmin(address)" "$ADMIN" >/dev/null 2>&1 + +# Verify Admin restoration +RESTORED_ADMIN_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "admin()") +RESTORED_ADMIN_ADDR="0x${RESTORED_ADMIN_RESULT:26}" +RESTORED_ADMIN_ADDR=$(cast to-check-sum-address "$RESTORED_ADMIN_ADDR") +if [ "$RESTORED_ADMIN_ADDR" = "$(cast to-check-sum-address "$ADMIN")" ]; then + echo "โœ… Admin restoration successful: $RESTORED_ADMIN_ADDR" +else + echo "โŒ Admin restoration failed" + exit 1 +fi + +echo "โ„น๏ธ Restoring Owner to original address..." +# Use current NEW_OWNER permissions to restore owner +cast send --private-key "$NEW_OWNER_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "transferOwnership(address)" "$OWNER" >/dev/null 2>&1 + +# Verify Owner restoration +RESTORED_OWNER_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "owner()") +RESTORED_OWNER_ADDR="0x${RESTORED_OWNER_RESULT:26}" +RESTORED_OWNER_ADDR=$(cast to-check-sum-address "$RESTORED_OWNER_ADDR") +if [ "$RESTORED_OWNER_ADDR" = "$(cast to-check-sum-address "$OWNER")" ]; then + echo "โœ… Owner restoration successful: $RESTORED_OWNER_ADDR" +else + echo "โŒ Owner restoration failed" + exit 1 +fi + +echo "โ„น๏ธ Restoring Operator to original address..." +# Check if current operator needs restoration +CURRENT_OPERATOR_RESULT=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "operator()") +CURRENT_OPERATOR_ADDR="0x${CURRENT_OPERATOR_RESULT:26}" +CURRENT_OPERATOR_ADDR=$(cast to-check-sum-address "$CURRENT_OPERATOR_ADDR") +EXPECTED_OPERATOR=$(cast to-check-sum-address "$OPERATOR") + +if [ "$CURRENT_OPERATOR_ADDR" != "$EXPECTED_OPERATOR" ]; then + # Use restored admin permissions to reset operator + cast send --private-key "$ADMIN_PRIVATE_KEY" --rpc-url "$RPC_URL" \ + "$PROXY_ADDRESS" "setOperator(address)" "$OPERATOR" >/dev/null 2>&1 + echo "โœ… Operator restoration successful: $EXPECTED_OPERATOR" +else + echo "โœ… Operator already at expected address: $EXPECTED_OPERATOR" +fi + +echo "โœ… All state restoration completed" +echo "" + +# # Step 9: Precompiled contract gas verification test +# echo "๐Ÿ”ฌ Step 9: Precompiled Contract Gas Verification Test" +# echo "----------------------------------------" + +# echo "โ„น๏ธ Verifying TEST_OP gas consumption..." +# TEST_OP_GAS=$(cast estimate --rpc-url "$RPC_URL" --from "$ADMIN" 0x0000000000000000000000000000000000001001 0x01) + +# # Calculate expected gas value +# # Base transaction gas: 21000 +# # Data gas: 16 (1 byte of 0x01, using EIP2028's TxDataNonZeroGasEIP2028) +# # TEST_OP gas: 700 (our modification) +# EXPECTED_GAS=21716 + +# if [ "$TEST_OP_GAS" -eq "$EXPECTED_GAS" ]; then +# echo "โœ… TEST_OP gas verification successful: $TEST_OP_GAS (expected: $EXPECTED_GAS)" +# else +# echo "โŒ TEST_OP gas verification failed:" +# echo " Actual: $TEST_OP_GAS" +# echo " Expected: $EXPECTED_GAS" +# echo " Difference: $((TEST_OP_GAS - EXPECTED_GAS))" +# exit 1 +# fi + +# echo "โ„น๏ธ Verifying precompiled contract call functionality..." +# TEST_OP_RESULT=$(cast call --rpc-url "$RPC_URL" --from "$ADMIN" 0x0000000000000000000000000000000000001001 0x01) + +# if [ "$TEST_OP_RESULT" = "0x4f4b" ]; then +# echo "โœ… TEST_OP call successful, returned 'OK'" +# else +# echo "โŒ TEST_OP call failed, returned: $TEST_OP_RESULT" +# exit 1 +# fi + +# echo "โœ… Precompiled contract gas verification test completed" +# echo "" + +echo "๐ŸŽ‰ All tests completed!" +echo " โœ… Operator management normal" +echo " โœ… BridgeFrom operations normal (including boundary tests)" +echo " โœ… Cleanup operations normal (including boundary tests)" +echo " โœ… Pause/Resume functionality normal" +echo " โœ… Query functionality normal" +echo " โœ… Admin transfer functionality normal" +echo " โœ… Owner transfer functionality normal" +echo " โœ… State restoration normal" +# echo " โœ… Precompiled contract gas verification normal"