From 2ecce316b8bad62c40f31a5ceeb500726d926d1c Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Wed, 24 Sep 2025 22:22:07 +0530 Subject: [PATCH 01/11] feat: add merkle sigs natively into the account --- snapshots/BenchmarkTest.json | 30 +++++++++--------- src/IthacaAccount.sol | 59 ++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/snapshots/BenchmarkTest.json b/snapshots/BenchmarkTest.json index 2d5a899d..b32e01a9 100644 --- a/snapshots/BenchmarkTest.json +++ b/snapshots/BenchmarkTest.json @@ -16,11 +16,11 @@ "testERC20Transfer_ERC4337MinimalAccount": "171509", "testERC20Transfer_ERC4337MinimalAccount_AppSponsor": "168500", "testERC20Transfer_ERC4337MinimalAccount_ERC20SelfPay": "199831", - "testERC20Transfer_IthacaAccount": "128059", - "testERC20Transfer_IthacaAccountWithSpendLimits": "193602", - "testERC20Transfer_IthacaAccount_AppSponsor": "138595", - "testERC20Transfer_IthacaAccount_AppSponsor_ERC20": "143884", - "testERC20Transfer_IthacaAccount_ERC20SelfPay": "128548", + "testERC20Transfer_IthacaAccount": "128100", + "testERC20Transfer_IthacaAccountWithSpendLimits": "193589", + "testERC20Transfer_IthacaAccount_AppSponsor": "138636", + "testERC20Transfer_IthacaAccount_AppSponsor_ERC20": "143937", + "testERC20Transfer_IthacaAccount_ERC20SelfPay": "128589", "testERC20Transfer_Safe4337": "197561", "testERC20Transfer_Safe4337_AppSponsor": "191725", "testERC20Transfer_Safe4337_ERC20SelfPay": "221464", @@ -29,25 +29,25 @@ "testERC20Transfer_ZerodevKernel_ERC20SelfPay": "235489", "testERC20Transfer_batch100_AlchemyModularAccount": "10109066", "testERC20Transfer_batch100_AlchemyModularAccount_ERC20SelfPay": "11609298", - "testERC20Transfer_batch100_IthacaAccount": "7531912", - "testERC20Transfer_batch100_IthacaAccount_AppSponsor": "8140216", - "testERC20Transfer_batch100_IthacaAccount_AppSponsor_ERC20": "7966120", - "testERC20Transfer_batch100_IthacaAccount_ERC20SelfPay": "7353064", + "testERC20Transfer_batch100_IthacaAccount": "7535892", + "testERC20Transfer_batch100_IthacaAccount_AppSponsor": "8144196", + "testERC20Transfer_batch100_IthacaAccount_AppSponsor_ERC20": "7970208", + "testERC20Transfer_batch100_IthacaAccount_ERC20SelfPay": "7357152", "testERC20Transfer_batch100_ZerodevKernel": "12631318", "testERC20Transfer_batch100_ZerodevKernel_ERC20SelfPay": "14149937", "testNativeTransfer_AlchemyModularAccount": "180829", "testNativeTransfer_CoinbaseSmartWallet": "178916", - "testNativeTransfer_IthacaAccount": "129415", - "testNativeTransfer_IthacaAccount_AppSponsor": "139982", - "testNativeTransfer_IthacaAccount_ERC20SelfPay": "137204", + "testNativeTransfer_IthacaAccount": "129456", + "testNativeTransfer_IthacaAccount_AppSponsor": "140023", + "testNativeTransfer_IthacaAccount_ERC20SelfPay": "137245", "testNativeTransfer_Safe4337": "198595", "testNativeTransfer_ZerodevKernel": "208635", "testUniswapV2Swap_AlchemyModularAccount": "238647", "testUniswapV2Swap_CoinbaseSmartWallet": "237451", "testUniswapV2Swap_ERC4337MinimalAccount": "230691", - "testUniswapV2Swap_IthacaAccount": "187179", - "testUniswapV2Swap_IthacaAccount_AppSponsor": "197691", - "testUniswapV2Swap_IthacaAccount_ERC20SelfPay": "192480", + "testUniswapV2Swap_IthacaAccount": "187244", + "testUniswapV2Swap_IthacaAccount_AppSponsor": "197744", + "testUniswapV2Swap_IthacaAccount_ERC20SelfPay": "192533", "testUniswapV2Swap_Safe4337": "257333", "testUniswapV2Swap_ZerodevKernel": "266367" } \ No newline at end of file diff --git a/src/IthacaAccount.sol b/src/IthacaAccount.sol index 45b2d6c8..cbe96ff3 100644 --- a/src/IthacaAccount.sol +++ b/src/IthacaAccount.sol @@ -23,6 +23,7 @@ import {LibNonce} from "./libraries/LibNonce.sol"; import {TokenTransferLib} from "./libraries/TokenTransferLib.sol"; import {LibTStack} from "./libraries/LibTStack.sol"; import {IIthacaAccount} from "./interfaces/IIthacaAccount.sol"; +import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol"; /// @title Account /// @notice A account contract for EOAs with EIP7702. @@ -490,9 +491,52 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor { return isMultichain ? _hashTypedDataSansChainId(structHash) : _hashTypedData(structHash); } + /// @dev Verifies the merkle sig for the multi chain intents. + /// - Note: Each leaf of the merkle tree should be a standard intent digest, computed with chainId. + /// - Leaf intents do NOT need to have the multichain nonce prefix. + /// - The signature for multi chain intents using merkle verification is encoded as: + /// - bytes signature = abi.encode(bytes32[] proof, bytes32 root, bytes rootSig) + function _verifyMerkleSig(bytes32 digest, bytes calldata signature) + internal + view + returns (bool isValid, bytes32 keyHash) + { + bytes32[] calldata proof; + bytes32 root; + bytes calldata rootSig; + + assembly ("memory-safe") { + let proofOffset := add(signature.offset, calldataload(signature.offset)) + proof.length := calldataload(proofOffset) + proof.offset := add(proofOffset, 0x20) + + root := calldataload(add(signature.offset, 0x20)) + + let rootSigOffset := add(signature.offset, calldataload(add(signature.offset, 0x40))) + rootSig.length := calldataload(rootSigOffset) + rootSig.offset := add(rootSigOffset, 0x20) + } + + console.logBytes32(root); + + console.log("Proof length: ", proof.length); + console.logBytes32(proof[0]); + + if (MerkleProofLib.verifyCalldata(proof, root, digest)) { + console.log("Entered here"); + (isValid, keyHash) = unwrapAndValidateSignature(root, rootSig); + + console.logBytes32(keyHash); + console.log(isValid); + return (isValid, keyHash); + } + + return (false, bytes32(0)); + } + /// @dev Returns if the signature is valid, along with its `keyHash`. /// The `signature` is a wrapped signature, given by - /// `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`. + /// `abi.encode(bytes(innerSignature), bytes32(keyHash), bool(prehash), bool(merkle))`. function unwrapAndValidateSignature(bytes32 digest, bytes calldata signature) public view @@ -507,14 +551,25 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor { return (ECDSA.recoverCalldata(digest, signature) == address(this), 0); } + bool merkle; unchecked { - uint256 n = signature.length - 0x21; + uint256 n = signature.length - 0x22; keyHash = LibBytes.loadCalldata(signature, n); signature = LibBytes.truncatedCalldata(signature, n); // Do the prehash if last byte is non-zero. if (uint256(LibBytes.loadCalldata(signature, n + 1)) & 0xff != 0) { + console.log("prehash"); digest = EfficientHashLib.sha2(digest); // `sha256(abi.encode(digest))`. } + merkle = uint256(LibBytes.loadCalldata(signature, n + 2)) & 0xff != 0; + } + + console.logBytes(signature); + + console.log("Reached here", merkle); + if (merkle) { + console.log("merkle"); + return _verifyMerkleSig(digest, signature); } Key memory key = getKey(keyHash); From 557774798309a3b79c070634f20d96b2a465c52b Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Wed, 24 Sep 2025 22:23:47 +0530 Subject: [PATCH 02/11] fix: tests --- src/IthacaAccount.sol | 20 +++---------------- test/Account.t.sol | 40 ++++++++++++++++++++++++++++++++++++-- test/Base.t.sol | 11 ++++++----- test/Orchestrator.t.sol | 5 +++-- test/SimulateExecute.t.sol | 3 ++- 5 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/IthacaAccount.sol b/src/IthacaAccount.sol index cbe96ff3..1dc7f18b 100644 --- a/src/IthacaAccount.sol +++ b/src/IthacaAccount.sol @@ -491,10 +491,9 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor { return isMultichain ? _hashTypedDataSansChainId(structHash) : _hashTypedData(structHash); } - /// @dev Verifies the merkle sig for the multi chain intents. - /// - Note: Each leaf of the merkle tree should be a standard intent digest, computed with chainId. - /// - Leaf intents do NOT need to have the multichain nonce prefix. - /// - The signature for multi chain intents using merkle verification is encoded as: + /// @dev Verifies the merkle sig + /// - Note: Each leaf of the merkle tree should be a standard digest. + /// - The signature for using merkle verification is encoded as: /// - bytes signature = abi.encode(bytes32[] proof, bytes32 root, bytes rootSig) function _verifyMerkleSig(bytes32 digest, bytes calldata signature) internal @@ -517,17 +516,9 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor { rootSig.offset := add(rootSigOffset, 0x20) } - console.logBytes32(root); - - console.log("Proof length: ", proof.length); - console.logBytes32(proof[0]); - if (MerkleProofLib.verifyCalldata(proof, root, digest)) { - console.log("Entered here"); (isValid, keyHash) = unwrapAndValidateSignature(root, rootSig); - console.logBytes32(keyHash); - console.log(isValid); return (isValid, keyHash); } @@ -558,17 +549,12 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor { signature = LibBytes.truncatedCalldata(signature, n); // Do the prehash if last byte is non-zero. if (uint256(LibBytes.loadCalldata(signature, n + 1)) & 0xff != 0) { - console.log("prehash"); digest = EfficientHashLib.sha2(digest); // `sha256(abi.encode(digest))`. } merkle = uint256(LibBytes.loadCalldata(signature, n + 2)) & 0xff != 0; } - console.logBytes(signature); - - console.log("Reached here", merkle); if (merkle) { - console.log("merkle"); return _verifyMerkleSig(digest, signature); } diff --git a/test/Account.t.sol b/test/Account.t.sol index e4b8310b..67e3fe50 100644 --- a/test/Account.t.sol +++ b/test/Account.t.sol @@ -6,6 +6,8 @@ import "./Base.t.sol"; import {MockSampleDelegateCallTarget} from "./utils/mocks/MockSampleDelegateCallTarget.sol"; import {LibEIP7702} from "solady/accounts/LibEIP7702.sol"; +import {Merkle} from "murky/Merkle.sol"; + contract AccountTest is BaseTest { struct _TestExecuteWithSignatureTemps { TargetFunctionPayload[] targetFunctionPayloads; @@ -68,6 +70,41 @@ contract AccountTest is BaseTest { } } + function testMerkleSignature(bytes32 randomDigest) public { + DelegatedEOA memory d = _randomEIP7702DelegatedEOA(); + PassKey memory k = _randomSecp256k1PassKey(); + + k.k.isSuperAdmin = true; + + vm.prank(d.eoa); + d.d.authorize(k.k); + + bytes32 actualDigest = keccak256("actualDigest"); + + Merkle merkle = new Merkle(); + + bytes32[] memory leaves = new bytes32[](2); + leaves[0] = actualDigest; + leaves[1] = randomDigest; + bytes32 root = merkle.getRoot(leaves); + + bytes32[] memory proof = merkle.getProof(leaves, 0); + + bytes memory rootSig = abi.encode(proof, root, _sig(k, root)); + bytes memory sig = abi.encodePacked(rootSig, bytes32(0), uint8(0), uint8(1)); + + (bool isValid, bytes32 keyHash) = d.d.unwrapAndValidateSignature(actualDigest, sig); + + assertEq(isValid, true); + assertEq(keyHash, k.keyHash); + + // Test some random digest + bytes32 randomDigest2 = keccak256("randomDigest2"); + (isValid, keyHash) = d.d.unwrapAndValidateSignature(randomDigest2, sig); + assertEq(isValid, false); + assertEq(keyHash, bytes32(0)); + } + function testSignatureCheckerApproval(bytes32) public { DelegatedEOA memory d = _randomEIP7702DelegatedEOA(); PassKey memory k = _randomSecp256k1PassKey(); @@ -93,8 +130,7 @@ contract AccountTest is BaseTest { bytes32 replaySafeDigest = keccak256(abi.encode(d.d.SIGN_TYPEHASH(), digest)); - (, string memory name, string memory version,, address verifyingContract,,) = - d.d.eip712Domain(); + (,,,, address verifyingContract,,) = d.d.eip712Domain(); bytes32 domain = keccak256( abi.encode( 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749, // DOMAIN_TYPEHASH with only verifyingContract diff --git a/test/Base.t.sol b/test/Base.t.sol index 1765a651..f9f91ac4 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -215,7 +215,7 @@ contract BaseTest is SoladyTest { { (bytes32 r, bytes32 s) = vm.signP256(privateKey, digest); s = P256.normalized(s); - return abi.encodePacked(abi.encode(r, s), keyHash, uint8(prehash ? 1 : 0)); + return abi.encodePacked(abi.encode(r, s), keyHash, uint8(prehash ? 1 : 0), uint8(0)); } function _secp256k1Sig(uint256 privateKey, bytes32 keyHash, bytes32 digest) @@ -232,7 +232,8 @@ contract BaseTest is SoladyTest { returns (bytes memory) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(abi.encodePacked(r, s, v), keyHash, uint8(prehash ? 1 : 0)); + return + abi.encodePacked(abi.encodePacked(r, s, v), keyHash, uint8(prehash ? 1 : 0), uint8(0)); } function _multiSig(MultiSigKey memory k, bytes32 keyHash, bool preHash, bytes32 digest) @@ -245,7 +246,7 @@ contract BaseTest is SoladyTest { signatures[i] = _sig(k.owners[i], digest); } - return abi.encodePacked(abi.encode(signatures), keyHash, uint8(preHash ? 1 : 0)); + return abi.encodePacked(abi.encode(signatures), keyHash, uint8(preHash ? 1 : 0), uint8(0)); } function _estimateGasForEOAKey(Orchestrator.Intent memory i) @@ -277,7 +278,7 @@ contract BaseTest is SoladyTest { { (uint8 v, bytes32 r, bytes32 s) = vm.sign(uint128(_randomUniform()), bytes32(_randomUniform())); - i.signature = abi.encodePacked(abi.encodePacked(r, s, v), keyHash, uint8(0)); + i.signature = abi.encodePacked(abi.encodePacked(r, s, v), keyHash, uint8(0), uint8(0)); return _estimateGas(i); } @@ -285,7 +286,7 @@ contract BaseTest is SoladyTest { internal returns (uint256 gExecute, uint256 gCombined, uint256 gUsed) { - i.signature = abi.encodePacked(keccak256("a"), keccak256("b"), keyHash, uint8(0)); + i.signature = abi.encodePacked(keccak256("a"), keccak256("b"), keyHash, uint8(0), uint8(0)); return _estimateGas(i); } diff --git a/test/Orchestrator.t.sol b/test/Orchestrator.t.sol index c36a3248..c5cc3cc1 100644 --- a/test/Orchestrator.t.sol +++ b/test/Orchestrator.t.sol @@ -924,8 +924,9 @@ contract OrchestratorTest is BaseTest { if (_randomChance(16)) { u.combinedGas += 10_000; // Fill with some junk signature, but with the session `keyHash`. - u.signature = - abi.encodePacked(keccak256("a"), keccak256("b"), kSession.keyHash, uint8(0)); + u.signature = abi.encodePacked( + keccak256("a"), keccak256("b"), kSession.keyHash, uint8(0), uint8(0) + ); (t.gExecute, t.gCombined, t.gUsed) = _estimateGas(u); diff --git a/test/SimulateExecute.t.sol b/test/SimulateExecute.t.sol index 681266f7..aa3261cd 100644 --- a/test/SimulateExecute.t.sol +++ b/test/SimulateExecute.t.sol @@ -300,7 +300,8 @@ contract SimulateExecuteTest is BaseTest { // it needs to add the variance for non-precompile P256 verification. // We need the `keyHash` in the signature so that the simulation is able // to hit all the gas for the GuardedExecutor stuff for the `keyHash`. - i.signature = abi.encodePacked(keccak256("a"), keccak256("b"), k.keyHash, uint8(0)); + i.signature = + abi.encodePacked(keccak256("a"), keccak256("b"), k.keyHash, uint8(0), uint8(0)); uint256 snapshot = vm.snapshotState(); vm.deal(_ORIGIN_ADDRESS, type(uint192).max); From 995a3e17a01c2d9e12bcdbc9384cfdd4f00b1d4c Mon Sep 17 00:00:00 2001 From: Tanishk Goyal Date: Wed, 24 Sep 2025 22:29:40 +0530 Subject: [PATCH 03/11] test: add more sophisticated fuzz test --- test/Account.t.sol | 69 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/test/Account.t.sol b/test/Account.t.sol index 67e3fe50..21c7c36e 100644 --- a/test/Account.t.sol +++ b/test/Account.t.sol @@ -70,39 +70,68 @@ contract AccountTest is BaseTest { } } - function testMerkleSignature(bytes32 randomDigest) public { + function testMerkleSignature(uint256 seed) public { DelegatedEOA memory d = _randomEIP7702DelegatedEOA(); PassKey memory k = _randomSecp256k1PassKey(); - k.k.isSuperAdmin = true; vm.prank(d.eoa); d.d.authorize(k.k); - bytes32 actualDigest = keccak256("actualDigest"); - - Merkle merkle = new Merkle(); + // Fuzz number of leaves (2 to 256) + uint256 numLeaves = bound(seed, 2, 256); + bytes32[] memory leaves = new bytes32[](numLeaves); - bytes32[] memory leaves = new bytes32[](2); - leaves[0] = actualDigest; - leaves[1] = randomDigest; - bytes32 root = merkle.getRoot(leaves); + // Generate random leaves + for (uint256 i = 0; i < numLeaves; i++) { + leaves[i] = keccak256(abi.encodePacked("leaf", i, seed)); + } - bytes32[] memory proof = merkle.getProof(leaves, 0); + // Pick a random valid index + uint256 validIndex = seed % numLeaves; + bytes32 validDigest = leaves[validIndex]; - bytes memory rootSig = abi.encode(proof, root, _sig(k, root)); - bytes memory sig = abi.encodePacked(rootSig, bytes32(0), uint8(0), uint8(1)); + Merkle merkle = new Merkle(); + bytes32 root = merkle.getRoot(leaves); + bytes32[] memory proof = merkle.getProof(leaves, validIndex); - (bool isValid, bytes32 keyHash) = d.d.unwrapAndValidateSignature(actualDigest, sig); + // Test valid merkle proof + { + bytes memory rootSig = abi.encode(proof, root, _sig(k, root)); + bytes memory sig = abi.encodePacked(rootSig, bytes32(0), uint8(0), uint8(1)); + + (bool isValid, bytes32 keyHash) = d.d.unwrapAndValidateSignature(validDigest, sig); + assertEq(isValid, true); + assertEq(keyHash, k.keyHash); + + // Test invalid digest not in tree + bytes32 invalidDigest = keccak256(abi.encodePacked("not_in_tree", seed)); + (isValid, keyHash) = d.d.unwrapAndValidateSignature(invalidDigest, sig); + assertEq(isValid, false); + assertEq(keyHash, bytes32(0)); + } - assertEq(isValid, true); - assertEq(keyHash, k.keyHash); + // Test tampered proof (only if proof has elements to tamper) + if (proof.length > 0) { + bytes32[] memory tamperedProof = new bytes32[](proof.length); + for (uint256 i = 0; i < proof.length; i++) { + tamperedProof[i] = i == 0 ? bytes32(uint256(proof[i]) ^ 1) : proof[i]; + } + bytes memory tamperedRootSig = abi.encode(tamperedProof, root, _sig(k, root)); + bytes memory tamperedSig = + abi.encodePacked(tamperedRootSig, bytes32(0), uint8(0), uint8(1)); + (bool isValid,) = d.d.unwrapAndValidateSignature(validDigest, tamperedSig); + assertEq(isValid, false); + } - // Test some random digest - bytes32 randomDigest2 = keccak256("randomDigest2"); - (isValid, keyHash) = d.d.unwrapAndValidateSignature(randomDigest2, sig); - assertEq(isValid, false); - assertEq(keyHash, bytes32(0)); + // Test wrong root (tampered root should fail verification) + { + bytes32 wrongRoot = bytes32(uint256(root) ^ 1); + bytes memory wrongRootSig = abi.encode(proof, wrongRoot, _sig(k, wrongRoot)); + bytes memory wrongSig = abi.encodePacked(wrongRootSig, bytes32(0), uint8(0), uint8(1)); + (bool isValid,) = d.d.unwrapAndValidateSignature(validDigest, wrongSig); + assertEq(isValid, false); + } } function testSignatureCheckerApproval(bytes32) public { From 4918a12d97acfad5294fbbc151c7fa12c8c13b26 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 24 Sep 2025 17:05:28 +0000 Subject: [PATCH 04/11] chore: bump contract versions due to bytecode changes - Contracts updated: IthacaAccount --- src/IthacaAccount.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IthacaAccount.sol b/src/IthacaAccount.sol index 1dc7f18b..a8ffdbca 100644 --- a/src/IthacaAccount.sol +++ b/src/IthacaAccount.sol @@ -796,6 +796,6 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor { returns (string memory name, string memory version) { name = "IthacaAccount"; - version = "0.5.10"; + version = "0.5.11"; } } From e730f4120ad57055b4abed36f60175adfb08c402 Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Fri, 13 Mar 2026 15:56:45 +0700 Subject: [PATCH 05/11] pre-commit --- .husky/pre-commit | 0 deploy/execute_config.sh | 0 deploy/verify_config.sh | 0 lib/forge-std | 2 +- prep/check-bytecode-changes.js | 0 5 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 .husky/pre-commit mode change 100755 => 100644 deploy/execute_config.sh mode change 100755 => 100644 deploy/verify_config.sh mode change 100755 => 100644 prep/check-bytecode-changes.js diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100755 new mode 100644 diff --git a/deploy/execute_config.sh b/deploy/execute_config.sh old mode 100755 new mode 100644 diff --git a/deploy/verify_config.sh b/deploy/verify_config.sh old mode 100755 new mode 100644 diff --git a/lib/forge-std b/lib/forge-std index c2cf7017..0844d7e1 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit c2cf7017d27c1d20e74ace4dacb6c5ce4bbbe899 +Subproject commit 0844d7e1fc5e60d77b68e469bff60265f236c398 diff --git a/prep/check-bytecode-changes.js b/prep/check-bytecode-changes.js old mode 100755 new mode 100644 From 42416668eb6450d9fb45f00e74e0921eaa618a91 Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Fri, 13 Mar 2026 16:20:26 +0700 Subject: [PATCH 06/11] deploy execute_config.sh --- deploy/execute_config.sh | 0 deploy/verify_config.sh | 0 prep/check-bytecode-changes.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 deploy/execute_config.sh mode change 100755 => 100644 deploy/verify_config.sh mode change 100755 => 100644 prep/check-bytecode-changes.js diff --git a/deploy/execute_config.sh b/deploy/execute_config.sh old mode 100755 new mode 100644 diff --git a/deploy/verify_config.sh b/deploy/verify_config.sh old mode 100755 new mode 100644 diff --git a/prep/check-bytecode-changes.js b/prep/check-bytecode-changes.js old mode 100755 new mode 100644 From cbd478bfdb4285a3792129841643941141a5ae1f Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Fri, 13 Mar 2026 16:23:26 +0700 Subject: [PATCH 07/11] Update forge-std --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index 0844d7e1..c2cf7017 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 0844d7e1fc5e60d77b68e469bff60265f236c398 +Subproject commit c2cf7017d27c1d20e74ace4dacb6c5ce4bbbe899 From ab9e8839428e1a9f047cb65d708ae02e270b23e9 Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Fri, 10 Apr 2026 07:28:05 +0700 Subject: [PATCH 08/11] Normalize file modes and update forge-std submodule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove executable bit from deploy/execute_config.sh, deploy/verify_config.sh, and prep/check-bytecode-changes.js (mode 100755 → 100644). Update lib/forge-std submodule from commit c2cf7017d27c1d20e74ace4dacb6c5ce4bbbe899 to 07853315f998f94dc724e464b1bab1270888ee64. No other content changes. --- deploy/execute_config.sh | 0 deploy/verify_config.sh | 0 lib/forge-std | 2 +- prep/check-bytecode-changes.js | 0 4 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 deploy/execute_config.sh mode change 100755 => 100644 deploy/verify_config.sh mode change 100755 => 100644 prep/check-bytecode-changes.js diff --git a/deploy/execute_config.sh b/deploy/execute_config.sh old mode 100755 new mode 100644 diff --git a/deploy/verify_config.sh b/deploy/verify_config.sh old mode 100755 new mode 100644 diff --git a/lib/forge-std b/lib/forge-std index c2cf7017..07853315 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit c2cf7017d27c1d20e74ace4dacb6c5ce4bbbe899 +Subproject commit 07853315f998f94dc724e464b1bab1270888ee64 diff --git a/prep/check-bytecode-changes.js b/prep/check-bytecode-changes.js old mode 100755 new mode 100644 From 46c6b79e89202e1529dd6cc42c827eb376f779df Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Sat, 11 Apr 2026 17:28:20 +0700 Subject: [PATCH 09/11] Update LayerZero submodule & remove exec bits Normalize file permissions and advance submodule: change file modes from executable (100755) to non-executable (100644) for deploy/execute_config.sh, deploy/verify_config.sh, and prep/check-bytecode-changes.js. Update lib/LayerZero-v2 submodule commit from 88428755be6caa71cb1d2926141d73c8989296b5 to ab9b083410b9359285a5756807e1b6145d4711a7. --- deploy/execute_config.sh | 0 deploy/verify_config.sh | 0 lib/LayerZero-v2 | 2 +- prep/check-bytecode-changes.js | 0 4 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 deploy/execute_config.sh mode change 100755 => 100644 deploy/verify_config.sh mode change 100755 => 100644 prep/check-bytecode-changes.js diff --git a/deploy/execute_config.sh b/deploy/execute_config.sh old mode 100755 new mode 100644 diff --git a/deploy/verify_config.sh b/deploy/verify_config.sh old mode 100755 new mode 100644 diff --git a/lib/LayerZero-v2 b/lib/LayerZero-v2 index 88428755..ab9b0834 160000 --- a/lib/LayerZero-v2 +++ b/lib/LayerZero-v2 @@ -1 +1 @@ -Subproject commit 88428755be6caa71cb1d2926141d73c8989296b5 +Subproject commit ab9b083410b9359285a5756807e1b6145d4711a7 diff --git a/prep/check-bytecode-changes.js b/prep/check-bytecode-changes.js old mode 100755 new mode 100644 From 36249c78e5e0dcb50bafa0a262e3c0b1cffba637 Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Sat, 11 Apr 2026 18:01:29 +0700 Subject: [PATCH 10/11] Update LayerZero-v2 --- lib/LayerZero-v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/LayerZero-v2 b/lib/LayerZero-v2 index 88428755..ab9b0834 160000 --- a/lib/LayerZero-v2 +++ b/lib/LayerZero-v2 @@ -1 +1 @@ -Subproject commit 88428755be6caa71cb1d2926141d73c8989296b5 +Subproject commit ab9b083410b9359285a5756807e1b6145d4711a7 From d54a37b96fbf4cfc1815c7bdf385cbc01fec2aff Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Sat, 11 Apr 2026 19:03:24 +0700 Subject: [PATCH 11/11] Update LayerZero-v2 --- lib/LayerZero-v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/LayerZero-v2 b/lib/LayerZero-v2 index 88428755..ab9b0834 160000 --- a/lib/LayerZero-v2 +++ b/lib/LayerZero-v2 @@ -1 +1 @@ -Subproject commit 88428755be6caa71cb1d2926141d73c8989296b5 +Subproject commit ab9b083410b9359285a5756807e1b6145d4711a7