This repository contains a security audit of the HLYX Hyperlane Bridge contracts deployed on Ethereum mainnet.
CRITICAL: As of January 9, 2025, the bridge was controlled by EOAs (Externally Owned Accounts) instead of multisigs, creating severe centralization risks.
AUDIT_REPORT.md- Complete security audit report by M8B.devCRITICAL_FINDING_PROOF.md- Detailed proof of the critical EOA control issueHOW_TO_VERIFY_PROXY.md- Instructions for verifying proxy configuration
contracts/HypERC20Pausable.sol- Main pausable token contractcontracts/PausableController.sol- Abstract pause controller
.solhint.json- Solidity linting configurationhardhat.config.ts- Hardhat configurationpackage.json- Dependencies and scriptsremappings.txt- Solidity import mappings
verify_critical_finding.js- Script to verify EOA control on mainnetverify_pause_controller.js- Script to check pause controller statusscripts/run-security-audit.sh- Complete security audit runnerscripts/run-mythril-proper.sh- Mythril analysis scriptscripts/mythril-with-deps.sh- Dependency-aware Mythril runner
audit-results/- All tool outputs and findingsmythril-success.txt/json- Mythril analysis resultsslither-pausable.txt- Slither analysis resultssolhint-complete.txt- Solhint linting results
-
Single EOA Controls Everything:
0x8ea82F2DC6dAc2078eF0da432a66Ee43BB7193F9- Controls ProxyAdmin (can upgrade to steal funds)
- Controls Token ownership (can change pause controller)
- No multisig, no timelock
-
Pause Controller is also EOA:
0xaa997F2B5C6cf291dc14ab1723f041f4FFC235A3- Not a multisig
- Single key controls emergency pause
- ✅ Mythril: No vulnerabilities (contract code secure)
- ✅ Solhint: 0 errors, good code quality
⚠️ Slither: Found 1 structural issue (events outside contract scope)
- Clone the repository:
git clone <repo-url>
cd hlyx- Install dependencies:
npm install- Set up environment variables:
cp .env.example .env
# Edit .env and add your Infura/Alchemy key- Run verification scripts:
# Replace YOUR_INFURA_KEY in the scripts first
node verify_critical_finding.js
node verify_pause_controller.js📅 Note: These instructions show how to verify the current on-chain state. The findings below were accurate as of January 9, 2025, but may have changed.
-
Verify ProxyAdmin Owner:
- Go to: https://etherscan.io/address/0x32fDda6b6Cfe7571c25e3eADeFC9F4faD60C912B#readContract
- Click on
owner() - Result:
0x8ea82F2DC6dAc2078eF0da432a66Ee43BB7193F9(EOA)
-
Verify Token Owner:
- Go to: https://etherscan.io/address/0xC210B2cB65ed3484892167F5e05F7ab496Ab0598#readProxyContract
- Click on
owner() - Result:
0x8ea82F2DC6dAc2078eF0da432a66Ee43BB7193F9(same EOA!) - Click on
pausableController() - Result:
0xaa997F2B5C6cf291dc14ab1723f041f4FFC235A3(another EOA)
-
Check if addresses are EOAs or Contracts:
- Search each address on Etherscan
- If "Contract" tab is missing = EOA
- If "Contract" tab exists = Smart Contract
- Setup:
# Clone this repo
git clone <repo-url>
cd hlyx
npm install- Configure Infura Key:
# Edit the scripts and replace YOUR_INFURA_KEY with an actual key
# You can get a free key at: https://infura.io/
nano verify_critical_finding.js
# Replace: YOUR_INFURA_KEY with your actual key- Run Verification:
node verify_critical_finding.jsExpected output will show:
- ProxyAdmin Owner is EOA ❌
- Token Owner is same EOA ❌
- Pause Controller is EOA ❌
# Install Foundry first: https://getfoundry.sh/
# Check ProxyAdmin owner
cast call 0x32fDda6b6Cfe7571c25e3eADeFC9F4faD60C912B "owner()" --rpc-url https://eth.llamarpc.com
# Check Token owner
cast call 0xC210B2cB65ed3484892167F5e05F7ab496Ab0598 "owner()" --rpc-url https://eth.llamarpc.com
# Check pausableController
cast call 0xC210B2cB65ed3484892167F5e05F7ab496Ab0598 "pausableController()" --rpc-url https://eth.llamarpc.com
# Check if address is EOA (returns 0x if EOA)
cast code 0x8ea82F2DC6dAc2078eF0da432a66Ee43BB7193F9 --rpc-url https://eth.llamarpc.comOpen browser console and run:
// Using public RPC
const web3 = new Web3('https://eth.llamarpc.com');
// Check if address is EOA
const code = await web3.eth.getCode('0x8ea82F2DC6dAc2078eF0da432a66Ee43BB7193F9');
console.log(code === '0x' ? 'EOA' : 'Contract'); // Will print: EOA- Proxy (Token):
0xC210B2cB65ed3484892167F5e05F7ab496Ab0598 - ProxyAdmin:
0x32fDda6b6Cfe7571c25e3eADeFC9F4faD60C912B - Implementation:
0x28A32d8Ae251578421eD370cDC34faf01414ab22
IMMEDIATE ACTION REQUIRED:
- Deploy a multisig (minimum 3/5 signers)
- Transfer ProxyAdmin ownership to multisig
- Transfer Token ownership to multisig
- Set pausableController to multisig
- Implement timelock for upgrades
MIT
M8B.dev Security Team