From f3ec03a6f95aa34fd3387b83abef0a235843d2f7 Mon Sep 17 00:00:00 2001 From: ReposCollector Date: Thu, 12 Dec 2024 12:42:45 +0900 Subject: [PATCH 1/4] (1) Update Liquifier so that Admin can {unPause, update the deposit cap}, (2) Add Deterministic contract deployment by Create2, (3) Improve the contract verification process --- foundry.toml | 1 + script/Create2Factory.sol | 15 ++++++++++ .../deploy_implementation_contract.s.sol | 29 +++++++++++++++++++ src/Liquifier.sol | 4 +-- test/EtherFiTimelock.sol | 14 +++------ test/TestSetup.sol | 21 +++++++++++++- 6 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 script/Create2Factory.sol create mode 100644 script/deploys/deploy_implementation_contract.s.sol diff --git a/foundry.toml b/foundry.toml index c91c11b49..e3ed0b178 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,6 +7,7 @@ gas_reports = ["*"] optimizer_runs = 2000 extra_output = ["storageLayout"] solc-version = '0.8.24' +bytecode_hash = 'none' # See more config options https://github.com/foundry-rs/foundry/tree/master/config [rpc_endpoints] diff --git a/script/Create2Factory.sol b/script/Create2Factory.sol new file mode 100644 index 000000000..ba8b2a470 --- /dev/null +++ b/script/Create2Factory.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/Create2.sol"; + +contract Create2Factory { + event Deployed(address addr, address deployer, bytes32 bytecode_hash, bytes32 salt); + + function deploy(bytes memory code, bytes32 salt) external payable returns (address) { + address addr = Create2.deploy(msg.value, salt, code); + + emit Deployed(addr, address(this), keccak256(code), salt); + return addr; + } +} \ No newline at end of file diff --git a/script/deploys/deploy_implementation_contract.s.sol b/script/deploys/deploy_implementation_contract.s.sol new file mode 100644 index 000000000..d1698a17e --- /dev/null +++ b/script/deploys/deploy_implementation_contract.s.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Script.sol"; +// import "../../src/eBtcRateProvider.sol"; +// import "../../src/helpers/EtherFiViewer.sol"; +import "../../src/EtherFiNodesManager.sol"; +import "../../src/EtherFiNode.sol"; +import "../../src/EtherFiAdmin.sol"; +import "../../src/EtherFiOracle.sol"; +import "../../src/LiquidityPool.sol"; +import "../../src/Liquifier.sol"; + +import "../Create2Factory.sol"; + + +contract Deploy is Script { + bytes32 immutable salt = keccak256("ETHER_FI"); + Create2Factory immutable factory = Create2Factory(0x6521991A0BC180a5df7F42b27F4eE8f3B192BA62); + + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.selectFork(vm.createFork(vm.envString("MAINNET_RPC_URL"))); + + vm.startBroadcast(deployerPrivateKey); + bytes memory code = abi.encodePacked(type(Liquifier).creationCode); + factory.deploy(code, salt); + } +} \ No newline at end of file diff --git a/src/Liquifier.sol b/src/Liquifier.sol index f142b30d3..401dd74ea 100644 --- a/src/Liquifier.sol +++ b/src/Liquifier.sol @@ -190,7 +190,7 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab tokenInfos[_token].isWhitelisted = _isWhitelisted; } - function updateDepositCap(address _token, uint32 _timeBoundCapInEther, uint32 _totalCapInEther) public onlyOwner { + function updateDepositCap(address _token, uint32 _timeBoundCapInEther, uint32 _totalCapInEther) public onlyAdmin { tokenInfos[_token].timeBoundCapInEther = _timeBoundCapInEther; tokenInfos[_token].totalCapInEther = _totalCapInEther; } @@ -238,7 +238,7 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab } //Unpauses the contract - function unPauseContract() external onlyOwner { + function unPauseContract() external onlyAdmin { _unpause(); } diff --git a/test/EtherFiTimelock.sol b/test/EtherFiTimelock.sol index 855d9f5be..faa448be4 100644 --- a/test/EtherFiTimelock.sol +++ b/test/EtherFiTimelock.sol @@ -333,18 +333,12 @@ contract TimelockTest is TestSetup { function test_upgrade_liquifier() public { initializeRealisticFork(MAINNET_FORK); - { - address target = address(liquifierInstance); - bytes memory data = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0xB27b1dc838898368E9a81F69c626AEC7e71f02c1); - _execute_timelock(target, data, true, true, true, true); - - } - - { + address new_impl = 0xA1A15FB15cbda9E6c480C5bca6E9ABA9C5E2ff95; + { + assertEq(new_impl, computeAddressByCreate2(address(create2factory), type(Liquifier).creationCode, keccak256("ETHER_FI"))); address target = address(liquifierInstance); - bytes memory data = abi.encodeWithSelector(Liquifier.initializeOnUpgrade.selector, 0x1B7a4C3797236A1C37f8741c0Be35c2c72736fFf); + bytes memory data = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0xE41aeaEf27EC73A6958c3D35E0daC95c3dE33F72); _execute_timelock(target, data, true, true, true, true); - } } diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 0f13eb4b9..d7c6e5b20 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -50,7 +50,10 @@ import "../src/EtherFiTimelock.sol"; import "../src/BucketRateLimiter.sol"; -contract TestSetup is Test { +import "../script/ContractCodeChecker.sol"; +import "../script/Create2Factory.sol"; + +contract TestSetup is Test, ContractCodeChecker { event Schedule(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay); event Execute(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt); @@ -60,6 +63,8 @@ contract TestSetup is Test { uint256 public constant kwei = 10 ** 3; uint256 public slippageLimit = 50; + Create2Factory immutable create2factory = Create2Factory(0x6521991A0BC180a5df7F42b27F4eE8f3B192BA62); + TestERC20 public rETH; TestERC20 public wstETH; TestERC20 public sfrxEth; @@ -1504,4 +1509,18 @@ contract TestSetup is Test { string memory output_path = string.concat(string("./release/logs/txns/"), string.concat(prefix, string(".json"))); // releast/logs/$(block_number)_{$(block_timestamp)}json stdJson.write(output, output_path); } + + function computeAddressByCreate2(address deployer, bytes memory code, bytes32 salt) public view returns (address) { + // Compute the final address: + // address = keccak256( 0xff ++ this.address ++ salt ++ keccak256(code))[12:] + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0xff), + deployer, + salt, + keccak256(code) + ) + ); + return address(uint160(uint256(hash))); + } } \ No newline at end of file From 287542b12262c4c411684c7dabe1827631a72a12 Mon Sep 17 00:00:00 2001 From: ReposCollector Date: Thu, 12 Dec 2024 12:45:36 +0900 Subject: [PATCH 2/4] fix the new impl contract address --- operations/20241212_upgrade_liquifier.json | 13 +++++++++++++ test/EtherFiTimelock.sol | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 operations/20241212_upgrade_liquifier.json diff --git a/operations/20241212_upgrade_liquifier.json b/operations/20241212_upgrade_liquifier.json new file mode 100644 index 000000000..4647afe1a --- /dev/null +++ b/operations/20241212_upgrade_liquifier.json @@ -0,0 +1,13 @@ +{ "version": "1.0", "chainId": "1", "meta": { "name": "Transactions Batch", "description": "", "txBuilderVersion": "1.16.5", "createdFromSafeAddress": "0xcdd57D11476c22d265722F68390b036f3DA48c21" }, "transactions": [ +{ + "to": "0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761", + "value": "0", + "data": "0x01d5062a0000000000000000000000009ffdf407cde9a93c47611799da23924af3ef764f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f48000000000000000000000000000000000000000000000000000000000000000243659cfe6000000000000000000000000a1a15fb15cbda9e6c480c5bca6e9aba9c5e2ff9500000000000000000000000000000000000000000000000000000000" +} +, +{ + "to": "0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761", + "value": "0", + "data": "0x134008d30000000000000000000000009ffdf407cde9a93c47611799da23924af3ef764f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000243659cfe6000000000000000000000000a1a15fb15cbda9e6c480c5bca6e9aba9c5e2ff9500000000000000000000000000000000000000000000000000000000" +} +] } diff --git a/test/EtherFiTimelock.sol b/test/EtherFiTimelock.sol index faa448be4..6e74e350e 100644 --- a/test/EtherFiTimelock.sol +++ b/test/EtherFiTimelock.sol @@ -337,7 +337,7 @@ contract TimelockTest is TestSetup { { assertEq(new_impl, computeAddressByCreate2(address(create2factory), type(Liquifier).creationCode, keccak256("ETHER_FI"))); address target = address(liquifierInstance); - bytes memory data = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0xE41aeaEf27EC73A6958c3D35E0daC95c3dE33F72); + bytes memory data = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, new_impl); _execute_timelock(target, data, true, true, true, true); } } From 96e054852b3bb4782b3e33335f94893301d348cf Mon Sep 17 00:00:00 2001 From: ReposCollector Date: Thu, 12 Dec 2024 12:48:47 +0900 Subject: [PATCH 3/4] clean --- ...sol => DeployImplementationContract.s.sol} | 0 .../DeployImplementationContracts.s.sol | 211 ------------------ 2 files changed, 211 deletions(-) rename script/deploys/{deploy_implementation_contract.s.sol => DeployImplementationContract.s.sol} (100%) delete mode 100644 script/deploys/DeployImplementationContracts.s.sol diff --git a/script/deploys/deploy_implementation_contract.s.sol b/script/deploys/DeployImplementationContract.s.sol similarity index 100% rename from script/deploys/deploy_implementation_contract.s.sol rename to script/deploys/DeployImplementationContract.s.sol diff --git a/script/deploys/DeployImplementationContracts.s.sol b/script/deploys/DeployImplementationContracts.s.sol deleted file mode 100644 index 4dfa1ed9a..000000000 --- a/script/deploys/DeployImplementationContracts.s.sol +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/Treasury.sol"; -import "../../src/NodeOperatorManager.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/EtherFiNode.sol"; -import "../../src/archive/ProtocolRevenueManager.sol"; -import "../../src/StakingManager.sol"; -import "../../src/AuctionManager.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/EETH.sol"; -import "../../src/WeETH.sol"; -import "../../src/archive/RegulationsManager.sol"; -import "../../src/UUPSProxy.sol"; -import "../../src/EtherFiOracle.sol"; -import "../../src/EtherFiAdmin.sol"; -import "../../src/WithdrawRequestNFT.sol"; -import "../../src/MembershipManager.sol"; -import "../../src/MembershipNFT.sol"; -import "../../src/BNFT.sol"; -import "../../src/TNFT.sol"; - - - -import "@openzeppelin/contracts/utils/Strings.sol"; - -import "../../test/TestERC20.sol"; - -contract DeployImplementationContractsScript is Script { - using Strings for string; - - BNFT public bNFT; - TNFT public tNFT; - WeETH public weEth; - AuctionManager public auctionManager; - StakingManager public stakingManager; - ProtocolRevenueManager public protocolRevenueManager; - EtherFiNodesManager public etherFiNodesManager; - LiquidityPool public liquidityPool; - EETH public eETH; - RegulationsManager public regulationsManager; - EtherFiNode public etherFiNode; - MembershipManager public membershipManager; - MembershipNFT public membershipNft; - - struct suiteAddresses { - // address treasury; - // address nodeOperatorManager; - address auctionManager; - address stakingManager; - address tNFT; - address bNFT; - address etherFiNodesManager; - // address protocolRevenueManager; - address etherFiNode; - // address regulationsManager; - address liquidityPool; - address eETH; - address weEth; - address membershipManager; - address membershipNft; - // address etherFiOracle; - // address etherFiAdmin; - // address withdrawRequestNFT; - } - - suiteAddresses suiteAddressesStruct; - - function run() external { - vm.startBroadcast(vm.envUint("PRIVATE_KEY")); - - // Deploy contracts - // Treasury treasury = new Treasury(); - // NodeOperatorManager nodeOperatorManager = new NodeOperatorManager(); - // auctionManager = new AuctionManager(); - // stakingManager = new StakingManager(); - // bNFT = new BNFT(); - // tNFT = new TNFT(); - // protocolRevenueManager = new ProtocolRevenueManager(); - etherFiNodesManager = new EtherFiNodesManager(); - // etherFiNode = new EtherFiNode(); - // regulationsManager = new RegulationsManager(); - // liquidityPool = new LiquidityPool(); - // eETH = new EETH(); - // weEth = new WeETH(); - // membershipManager = new MembershipManager(); - // membershipNft = new MembershipNFT(); - // EtherFiOracle etherFiOracle = new EtherFiOracle(); - // EtherFiAdmin etherFiAdmin = new EtherFiAdmin(); - // WithdrawRequestNFT withdrawRequestNFT = new WithdrawRequestNFT(); - - vm.stopBroadcast(); - - suiteAddressesStruct = suiteAddresses({ - // treasury: address(treasury), - // nodeOperatorManager: address(nodeOperatorManager), - auctionManager: address(auctionManager), - stakingManager: address(stakingManager), - tNFT: address(tNFT), - bNFT: address(bNFT), - // protocolRevenueManager: address(protocolRevenueManager), - etherFiNodesManager: address(etherFiNodesManager), - etherFiNode: address(etherFiNode), - // regulationsManager: address(regulationsManager), - liquidityPool: address(liquidityPool), - eETH: address(eETH), - weEth: address(weEth), - membershipManager: address(membershipManager), - membershipNft: address(membershipNft) - // etherFiOracle: address(etherFiOracle), - // etherFiAdmin: address(etherFiAdmin), - // withdrawRequestNFT: address(withdrawRequestNFT) - }); - - writeSuiteVersionFile(); - } - - function _stringToUint( - string memory numString - ) internal pure returns (uint256) { - uint256 val = 0; - bytes memory stringBytes = bytes(numString); - for (uint256 i = 0; i < stringBytes.length; i++) { - uint256 exp = stringBytes.length - i; - bytes1 ival = stringBytes[i]; - uint8 uval = uint8(ival); - uint256 jval = uval - uint256(0x30); - - val += (uint256(jval) * (10 ** (exp - 1))); - } - return val; - } - - function writeSuiteVersionFile() internal { - // Read Current version - string memory versionString = vm.readLine("release/logs/implementation/version.txt"); - - // Cast string to uint256 - uint256 version = _stringToUint(versionString); - - version++; - - // Overwrites the version.txt file with incremented version - vm.writeFile( - "release/logs/implementation/version.txt", - string(abi.encodePacked(Strings.toString(version))) - ); - - string memory one = string( - abi.encodePacked( - // "\nTreasury ", - // Strings.toHexString(suiteAddressesStruct.treasury), - // "\nNodeOperatorManager ", - // Strings.toHexString(suiteAddressesStruct.nodeOperatorManager), - // "\nProtocolRevenueManager ", - // Strings.toHexString(suiteAddressesStruct.protocolRevenueManager), - "\nAuctionManager ", - Strings.toHexString(suiteAddressesStruct.auctionManager), - "\nStakingManager ", - Strings.toHexString(suiteAddressesStruct.stakingManager), - "\nEtherFiNodesManager ", - Strings.toHexString(suiteAddressesStruct.etherFiNodesManager), - "\nEtherFiNode ", - Strings.toHexString(suiteAddressesStruct.etherFiNode), - "\nTNFT ", - Strings.toHexString(suiteAddressesStruct.tNFT), - "\nBNFT ", - Strings.toHexString(suiteAddressesStruct.bNFT) - ) - ); - string memory two = string( - abi.encodePacked( - // "\nRegulationsManager ", - // Strings.toHexString(suiteAddressesStruct.regulationsManager), - "\nLiquidityPool ", - Strings.toHexString(suiteAddressesStruct.liquidityPool), - "\nEETH ", - Strings.toHexString(suiteAddressesStruct.eETH), - "\nWeETH ", - Strings.toHexString(suiteAddressesStruct.weEth), - "\nMembershipManager ", - Strings.toHexString(suiteAddressesStruct.membershipManager), - "\nMembershipNFT ", - Strings.toHexString(suiteAddressesStruct.membershipNft) - // "\nEtherFiOracle ", - // Strings.toHexString(suiteAddressesStruct.etherFiOracle), - // "\nEtherFiAdmin ", - // Strings.toHexString(suiteAddressesStruct.etherFiAdmin), - // "\nWithdrawRequestNFT ", - // Strings.toHexString(suiteAddressesStruct.withdrawRequestNFT) - ) - ); - - // str = one + two; - string memory str = string(abi.encodePacked(one, two)); - - // Writes the data to .release file - vm.writeFile( - string( - abi.encodePacked( - "release/logs/implementation/", - Strings.toString(version), - ".release" - ) - ), - str - ); - } -} From 86506babd1614a84606fbb11c7fb14db8ebc0c56 Mon Sep 17 00:00:00 2001 From: jtfirek Date: Fri, 13 Dec 2024 08:20:27 -0500 Subject: [PATCH 4/4] adding contract code checker --- script/ContractCodeChecker.sol | 194 +++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 script/ContractCodeChecker.sol diff --git a/script/ContractCodeChecker.sol b/script/ContractCodeChecker.sol new file mode 100644 index 000000000..9ca038068 --- /dev/null +++ b/script/ContractCodeChecker.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {console} from "forge-std/console.sol"; +import {console2} from "forge-std/console2.sol"; + + +contract ContractCodeChecker { + + event ByteMismatchSegment( + uint256 startIndex, + uint256 endIndex, + bytes aSegment, + bytes bSegment + ); + + function compareBytes(bytes memory a, bytes memory b) internal returns (bool) { + if (a.length != b.length) { + // Length mismatch, emit one big segment for the difference if that’s desirable + // or just return false. For clarity, we can just return false here. + return false; + } + + uint256 len = a.length; + uint256 start = 0; + bool inMismatch = false; + bool anyMismatch = false; + + for (uint256 i = 0; i < len; i++) { + bool mismatch = (a[i] != b[i]); + if (mismatch && !inMismatch) { + // Starting a new mismatch segment + start = i; + inMismatch = true; + } else if (!mismatch && inMismatch) { + // Ending the current mismatch segment at i-1 + emitMismatchSegment(a, b, start, i - 1); + inMismatch = false; + anyMismatch = true; + } + } + + // If we ended with a mismatch still open, close it out + if (inMismatch) { + emitMismatchSegment(a, b, start, len - 1); + anyMismatch = true; + } + + // If no mismatch segments were found, everything matched + return !anyMismatch; + } + + function emitMismatchSegment( + bytes memory a, + bytes memory b, + uint256 start, + uint256 end + ) internal { + // endIndex is inclusive + uint256 segmentLength = end - start + 1; + + bytes memory aSegment = new bytes(segmentLength); + bytes memory bSegment = new bytes(segmentLength); + + for (uint256 i = 0; i < segmentLength; i++) { + aSegment[i] = a[start + i]; + bSegment[i] = b[start + i]; + } + + string memory aHex = bytesToHexString(aSegment); + string memory bHex = bytesToHexString(bSegment); + + console2.log("- Mismatch segment at index [%s, %s]", start, end); + console2.logString(string.concat(" - ", aHex)); + console2.logString(string.concat(" - ", bHex)); + + emit ByteMismatchSegment(start, end, aSegment, bSegment); + } + + function bytesToHexString(bytes memory data) internal pure returns (string memory) { + bytes memory alphabet = "0123456789abcdef"; + + // Every byte corresponds to two hex characters + bytes memory str = new bytes(2 + data.length * 2); + str[0] = '0'; + str[1] = 'x'; + for (uint256 i = 0; i < data.length; i++) { + str[2 + i * 2] = alphabet[uint8(data[i] >> 4)]; + str[3 + i * 2] = alphabet[uint8(data[i] & 0x0f)]; + } + return string(str); + } + + // Compare the full bytecode of two deployed contracts, ensuring a perfect match. + function verifyFullMatch(address deployedImpl, address localDeployed) public { + console2.log("Verifying full bytecode match..."); + bytes memory localBytecode = address(localDeployed).code; + bytes memory onchainRuntimeBytecode = address(deployedImpl).code; + + if (compareBytes(localBytecode, onchainRuntimeBytecode)) { + console2.log("-> Full Bytecode Match: Success\n"); + } else { + console2.log("-> Full Bytecode Match: Fail\n"); + } + } + + function verifyPartialMatch(address deployedImpl, address localDeployed) public { + console2.log("Verifying partial bytecode match..."); + + // Fetch runtime bytecode from on-chain addresses + bytes memory localBytecode = localDeployed.code; + bytes memory onchainRuntimeBytecode = deployedImpl.code; + + // Optionally check length first (not strictly necessary if doing a partial match) + if (localBytecode.length == 0 || onchainRuntimeBytecode.length == 0) { + revert("One of the bytecode arrays is empty, cannot verify."); + } + + // Attempt to trim metadata from both local and on-chain bytecode + bytes memory trimmedLocal = trimMetadata(localBytecode); + bytes memory trimmedOnchain = trimMetadata(onchainRuntimeBytecode); + + // If trimmed lengths differ significantly, it suggests structural differences in code + if (trimmedLocal.length != trimmedOnchain.length) { + revert("Post-trim length mismatch: potential code differences."); + } + + // Compare trimmed arrays byte-by-byte + if (compareBytes(trimmedLocal, trimmedOnchain)) { + console2.log("-> Partial Bytecode Match: Success\n"); + } else { + console2.log("-> Partial Bytecode Match: Fail\n"); + } + } + + function verifyLengthMatch(address deployedImpl, address localDeployed) public { + console2.log("Verifying length match..."); + bytes memory localBytecode = localDeployed.code; + bytes memory onchainRuntimeBytecode = deployedImpl.code; + + if (localBytecode.length == onchainRuntimeBytecode.length) { + console2.log("-> Length Match: Success\n"); + } else { + console2.log("-> Length Match: Fail\n"); + } + } + + function verifyContractByteCodeMatch(address deployedImpl, address localDeployed) public { + verifyLengthMatch(deployedImpl, localDeployed); + verifyPartialMatch(deployedImpl, localDeployed); + verifyFullMatch(deployedImpl, localDeployed); + } + + // A helper function to remove metadata (CBOR encoded) from the end of the bytecode. + // This is a heuristic based on known patterns in the metadata. + function trimMetadata(bytes memory code) internal pure returns (bytes memory) { + // Metadata usually starts with 0xa2 or a similar tag near the end. + // We can scan backward for a known marker. + // In Solidity 0.8.x, metadata often starts near the end with 0xa2 0x64 ... pattern. + // This is a simplified approach and may need refinement. + + // For a more robust approach, you'd analyze the last bytes. + // Typically, the CBOR metadata is at the very end of the bytecode. + uint256 length = code.length; + if (length < 4) { + // Bytecode too short to have metadata + return code; + } + + // Scan backward for a CBOR header (0xa2). + // We'll just look for 0xa2 from the end and truncate there. + for (uint256 i = length - 1; i > 0; i--) { + if (code[i] == 0xa2) { + console2.log("Found metadata start at index: ", i); + // print 8 bytes from this point + bytes memory tmp = new bytes(8); + for (uint256 j = 0; j < 8; j++) { + tmp[j] = code[i + j]; + } + + // Found a possible metadata start. We'll cut just before 0xa2. + bytes memory trimmed = new bytes(i); + for (uint256 j = 0; j < i; j++) { + trimmed[j] = code[j]; + } + return trimmed; + } + } + + // If no metadata marker found, return as is. + return code; + } + +}