Skip to content

Dev#104

Merged
JaCoderX merged 8 commits into
mainfrom
dev
Mar 5, 2026
Merged

Dev#104
JaCoderX merged 8 commits into
mainfrom
dev

Conversation

@JaCoderX
Copy link
Copy Markdown
Member

@JaCoderX JaCoderX commented Mar 5, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added stricter handler relationship enforcement to improve permission validation and security.
    • Introduced optional simulation mode control for transaction pre-flight checks (strict, warn-only, or skip).
  • Tests

    • Enhanced test error handling with improved revert data extraction and decoding.
    • Added utility functions for better error diagnostics and test reporting.
  • Chores

    • Version bump to 1.0.0-alpha.15 across package files.

JaCoderX added 8 commits March 4, 2026 22:02
This commit increments the version number to 1.0.0-alpha.15 in the package.json files for both the contracts and SDK packages. This version update ensures consistency across the project and prepares the packages for the upcoming release cycle.
This commit introduces a new internal function, `_validateMetaTxAction`, to enforce strict validation of meta-transaction actions within the EngineBlox library. The function ensures that the expected signer action aligns with the current workflow for meta-transaction requests, approvals, and cancellations. Additionally, the existing meta-transaction functions `txCancellationWithMetaTx`, `txApprovalWithMetaTx`, and `txRequestAndApproveWithMetaTx` are updated to utilize this new validation, improving the security and integrity of the transaction handling process. These changes contribute to a more robust and maintainable codebase.
This commit introduces new function selectors for time-lock operations in the GuardController tests, specifically `executeWithTimeLock`, `approveTimeLockExecutionWithMetaTx`, and `cancelTimeLockExecutionWithMetaTx`. It updates the ERC20MintControllerTests to verify that the MINT_REQUESTOR and MINT_APPROVER roles have the appropriate permissions for these selectors. Additionally, the test output messages are refined for clarity regarding token minting and balance verification. These changes improve the accuracy and maintainability of the testing framework.
…library

This commit updates the function schema in the BaseStateMachine contract and the EngineBlox library to include a new parameter, `enforceHandlerRelations`, which enforces strict alignment between handler selectors and schema definitions. The changes improve the clarity and functionality of the function schema, ensuring better validation of handler relationships during permission checks. Additionally, the documentation is updated to reflect these changes, enhancing the overall maintainability and robustness of the codebase.
…Machine

This commit introduces improvements to the BaseStateMachine class by allowing callers to specify an explicit gas limit for large calldata payloads, enhancing flexibility in transaction execution. Additionally, it adds a new `simulationMode` option to control pre-flight simulation behavior, offering three modes: 'strict', 'warn-only', and 'skip'. This enhancement improves error handling and user experience by providing more control over transaction simulations, ensuring better feedback during contract interactions. The documentation for the `TransactionOptions` interface is also updated to reflect these changes.
…itions

This commit introduces a new boolean parameter, `enforceHandlerRelations`, to various ABI definitions across multiple contracts, including AccountBlox, BaseStateMachine, CopyBlox, and others. This enhancement enforces strict alignment between handler selectors and schema definitions, improving validation during permission checks. The changes ensure better clarity and functionality in the contract interactions, contributing to a more robust and maintainable codebase. Additionally, relevant documentation is updated to reflect these modifications.
This commit introduces a new feature in the Sanity SDK test runner that allows users to specify the number of sequential test runs using the `--repeat N` option, defaulting to 1. The implementation includes updates to the usage instructions and modifies the test execution logic to accommodate multiple runs. This enhancement improves the flexibility and usability of the testing framework, enabling more thorough testing scenarios. Additionally, relevant documentation is updated to reflect these changes.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

A new enforceHandlerRelations boolean field is added to EngineBlox.FunctionSchema throughout the codebase. This field is integrated into function registration flows and validation logic to conditionally enforce stricter handler-to-selector relationship checks. Meta-transaction validation paths are enhanced with dedicated validation functions, and permission initialization in definition contracts is updated to set the flag appropriately per function schema.

Changes

Cohort / File(s) Summary
ABI Files
abi/*, sdk/typescript/abi/*
Added enforceHandlerRelations: bool field to EngineBlox.FunctionSchema struct in all ABI JSON files (AccountBlox, BaseStateMachine, CopyBlox, GuardController, GuardControllerDefinitions, IDefinition, RuntimeRBAC, RuntimeRBACDefinitions, SecureOwnable, SecureOwnableDefinitions, SimpleRWA20, SimpleRWA20Definitions, SimpleVault, SimpleVaultDefinitions). Field inserted after supportedActionsBitmap and before isProtected.
Core Contract Logic
contracts/core/lib/EngineBlox.sol, contracts/core/base/BaseStateMachine.sol
Added enforceHandlerRelations field to FunctionSchema struct and registerFunction signature. Introduced _validateMetaTxAction validator for stricter meta-transaction signer enforcement. Hardened handlerForSelectors validation to short-circuit when flag is false; otherwise validates alignment with schema handlers. Updated _registerFunction signature in BaseStateMachine to accept and propagate the new parameter through _loadDefinitions.
Guard/Execution Controllers
contracts/core/execution/GuardController.sol, contracts/core/execution/lib/definitions/GuardControllerDefinitions.sol
Set enforceHandlerRelations: true in _registerGuardedFunction call. Updated 8 function schema initializations in GuardControllerDefinitions to include the new field with varying boolean values (false for time-delay/meta-tx schemas, true for certain guard config batches).
Security/RBAC Definitions
contracts/core/access/lib/definitions/RuntimeRBACDefinitions.sol, contracts/core/security/lib/definitions/SecureOwnableDefinitions.sol
Added enforceHandlerRelations field to function schema initializations: true for meta-transaction handler, false for execution in RuntimeRBACDefinitions; true for 16 schemas in SecureOwnableDefinitions covering meta-tx, time-delay, and execution paths.
Application Definitions
contracts/examples/applications/*/\*Definitions.sol, contracts/examples/integrations/Safe/GuardianSafe/GuardianSafeDefinitions.sol
Added enforceHandlerRelations: true to function schema definitions in PayBloxDefinitions (3 schemas), SimpleRWA20Definitions (4 schemas), SimpleVaultDefinitions (7 schemas), and GuardianSafeDefinitions (7 schemas).
SDK TypeScript
sdk/typescript/interfaces/base.index.tsx, sdk/typescript/types/definition.index.tsx, sdk/typescript/contracts/core/BaseStateMachine.tsx, sdk/typescript/package.json
Added simulationMode?: 'strict' | 'warn-only' | 'skip' to TransactionOptions interface. Added enforceHandlerRelations: boolean to FunctionSchema interface. Introduced conditional pre-flight simulation handling in BaseStateMachine contract wrapper. Version bumped to 1.0.0-alpha.15.
Test Helpers & Fixtures
test/foundry/helpers/TestDefinitionContracts.sol, test/foundry/helpers/MaliciousDefinitions.sol, test/foundry/helpers/PaymentTestHelper.sol, test/foundry/fuzz/\*Fuzz.t.sol
Added enforceHandlerRelations: true to function schema initializations across test definition libraries. Updated fuzz tests to expect SharedValidation.HandlerForSelectorMismatch revert when handler mismatch occurs with enforcement enabled.
Sanity Test Scripts
scripts/sanity-sdk/guard-controller/base-test.ts, scripts/sanity-sdk/guard-controller/erc20-mint-controller-tests.ts, scripts/sanity-sdk/run-all-tests.ts, scripts/sanity/guard-controller/base-test.cjs, scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs, scripts/sanity-sdk/runtime-rbac/base-test.ts, scripts/sanity-sdk/runtime-rbac/rbac-tests.ts
Enhanced error handling to extract and decode revert data via getRevertDataFromError. Added new selector constants for timelock execution paths. Reworked ERC20 mint test flow from 4-step to 3-step with explicit timelock/meta-tx orchestration. Added support for --repeat flag in run-all-tests with post-sanity system state summary. Added HandlerForSelectorMismatch error handling in RBAC tests. Improved test orchestration with role verification, permission batching, and revert reason extraction.
Version Updates
package/package.json, sdk/typescript/package.json
Bumped version from 1.0.0-alpha.14 to 1.0.0-alpha.15.

Sequence Diagram

sequenceDiagram
    participant Client
    participant EngineBlox
    participant PermissionValidator
    participant SchemaRegistry

    rect rgba(100, 200, 100, 0.5)
    Note over Client,SchemaRegistry: Function Registration with enforceHandlerRelations
    Client->>EngineBlox: registerFunction(..., enforceHandlerRelations, ...)
    EngineBlox->>SchemaRegistry: Store FunctionSchema with enforceHandlerRelations flag
    SchemaRegistry-->>EngineBlox: Schema registered
    end

    rect rgba(100, 150, 200, 0.5)
    Note over Client,SchemaRegistry: Permission Validation Flow
    Client->>PermissionValidator: validateHandlerRelations(handlerForSelectors, schema)
    alt enforceHandlerRelations == true
        PermissionValidator->>SchemaRegistry: Fetch schema.handlerForSelectors
        SchemaRegistry-->>PermissionValidator: Return expected handlers
        PermissionValidator->>PermissionValidator: Validate each handler exists in schema
        alt Mismatch found
            PermissionValidator-->>Client: Revert(HandlerForSelectorMismatch)
        else All match
            PermissionValidator-->>Client: Validation passed
        end
    else enforceHandlerRelations == false
        PermissionValidator-->>Client: Skip validation (short-circuit)
    end
    end

    rect rgba(200, 100, 100, 0.5)
    Note over Client,SchemaRegistry: Meta-Transaction Validation
    Client->>EngineBlox: txApprovalWithMetaTx(metaTxPayload, signature)
    EngineBlox->>EngineBlox: _validateMetaTxAction(expectedAction)
    alt Action matches expected
        EngineBlox-->>Client: Proceed with execution
    else Action mismatch
        EngineBlox-->>Client: Revert(InvalidMetaTxAction)
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Work #92 — Modifies EngineBlox function-schema and permission validation paths, directly overlapping with the new handler relationship enforcement logic.
  • Dev #53 — Updates EngineBlox.FunctionSchema ABI/struct definition and related SDK types, foundational to this PR's schema extensions.
  • Dev #37 — Modifies function schema registration and _registerFunction behavior across EngineBlox and definition contracts, core infrastructure touched by this change.

Poem

🐰 A new flag hops into schemas bright,
enforceHandlerRelations set just right,
Handlers aligned in a strict relation,
Meta-txs pass their validation!
The Protocol's tighter, the logic divine—
Another fine hopping, 🐇 time!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The pull request title 'Dev' is vague and does not convey meaningful information about the changeset, which adds an enforceHandlerRelations feature across multiple ABI files, contracts, and test utilities. Replace with a descriptive title that summarizes the main change, such as 'Add enforceHandlerRelations field to FunctionSchema' or 'Implement handler relations enforcement across ABIs and contracts'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (11)
scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs (3)

96-101: ⚠️ Potential issue | 🟠 Major

Verify wallet membership before treating Step 1 as idempotent.

These branches pass Step 1 after calling verifyStep1RolesAfter(), but that helper only checks that the roles exist. If the roles already exist with different members, this step still goes green and the failure is deferred to the later meta-tx path.

💡 Minimal diff
     async verifyStep1RolesAfter() {
-        const requestorExists = await this.roleExists(this.getRoleHash('MINT_REQUESTOR'));
-        const approverExists = await this.roleExists(this.getRoleHash('MINT_APPROVER'));
+        const requestorRoleHash = this.getRoleHash('MINT_REQUESTOR');
+        const approverRoleHash = this.getRoleHash('MINT_APPROVER');
+        const requestorExists = await this.roleExists(requestorRoleHash);
+        const approverExists = await this.roleExists(approverRoleHash);
         this.assertTest(requestorExists, 'MINT_REQUESTOR role exists after step 1');
         this.assertTest(approverExists, 'MINT_APPROVER role exists after step 1');
+
+        const [requestorAssigned, approverAssigned] = await Promise.all([
+            this.contract.methods.hasRole(requestorRoleHash, this.mintRequestorWallet.address).call(),
+            this.contract.methods.hasRole(approverRoleHash, this.mintApproverWallet.address).call()
+        ]);
+        this.assertTest(requestorAssigned, `MINT_REQUESTOR contains ${this.mintRequestorWallet.address}`);
+        this.assertTest(approverAssigned, `MINT_APPROVER contains ${this.mintApproverWallet.address}`);
     }

Also applies to: 137-140, 148-152

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs` around lines
96 - 101, The current idempotency branch checks only that roles exist (using
roleExists/getRoleHash and verifyStep1RolesAfter) but not that the role members
match expected wallets, so Step 1 can falsely pass; update the branch in the
handler that currently calls verifyStep1RolesAfter() and passTest('Create
MINT_REQUESTOR and MINT_APPROVER roles', ...) to also fetch and compare the
current role members to the expected wallet addresses (use whatever helper
exists for reading role members or add one), and only treat the step as
satisfied if both roles exist and their members exactly match the expected set;
if membership differs, proceed with the creation flow (or fail) instead of
passing. Ensure the same membership check is added to the other similar branches
around the lines that call verifyStep1RolesAfter() (the ones referenced near
lines 137-140 and 148-152).

545-555: ⚠️ Potential issue | 🟠 Major

This still exercises the legacy one-step handler instead of the timelock approval path.

Step 5 signs SIGN_META_REQUEST_AND_APPROVE and sends requestAndApproveExecution(...), so it never hits executeWithTimeLock or approveTimeLockExecutionWithMetaTx. The new controller permissions added above can regress without this test catching it.

Also applies to: 597-604

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs` around lines
545 - 555, The test currently uses SIGN_META_REQUEST_AND_APPROVE and calls
requestAndApproveExecution, which exercises the legacy one-step handler; change
it to create a request-only meta-tx (use SIGN_META_REQUEST or the equivalent
constant in tests when calling createExternalExecutionMetaTx) and then call the
requestExecution flow instead of requestAndApproveExecution so the code hits the
timelock path; after requesting, simulate the approver's timelock approval by
invoking approveTimeLockExecutionWithMetaTx (or building an approve meta-tx
signed by mintApproverWallet and calling executeWithTimeLock) so the test
exercises executeWithTimeLock and approveTimeLockExecutionWithMetaTx rather than
the one-step handler.

256-314: ⚠️ Potential issue | 🟠 Major

Step 4 can report success without all of the permissions this flow uses.

The idempotent gate and the final verification still miss several permissions configured below: the handler-side SIGN_META_REQUEST_AND_APPROVE, the broadcaster's REQUEST_AND_APPROVE_EXECUTION_SELECTOR permission, and the broadcaster's timelock approve/cancel controller permissions are never all asserted. That makes reruns prone to false positives.

Also applies to: 457-502

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs` around lines
256 - 314, The final success gate is missing checks for the handler-side
SIGN_META_REQUEST_AND_APPROVE and several broadcaster permissions; add
roleHasPermissionForSelector calls to assert: (1) the handler/approver role
(handlerRoleHash or approverRoleHash as appropriate) has
SIGN_META_REQUEST_AND_APPROVE on REQUEST_AND_APPROVE_EXECUTION_SELECTOR, (2) the
broadcasterRoleHash has REQUEST_AND_APPROVE_EXECUTION_SELECTOR (with the correct
TxAction), and (3) the broadcasterRoleHash has
APPROVE_TIMELOCK_EXECUTION_META_SELECTOR and
CANCEL_TIMELOCK_EXECUTION_META_SELECTOR with the appropriate timelock TxAction;
store each result in a descriptive const (e.g.
handlerHasSignMetaRequestAndApprove, broadcasterHasHandlerSelector,
broadcasterHasApproveMetaTx, broadcasterHasCancelMetaTx) and include those
consts in the big if(...) alongside the existing requestorOk/.../broadcasterOk
checks so the idempotent gate only reports success when all permissions are
present (use roleHasPermissionForSelector to perform each check).
test/foundry/helpers/PaymentTestHelper.sol (1)

185-198: ⚠️ Potential issue | 🟡 Minor

approveTransaction is missing the selector it now claims to enforce.

requestTransaction* were updated to advertise nativeTransferSelector, but approveTxHandlers still only contains approveTxSelector. With enforceHandlerRelations = true, approvals for the NATIVE_TRANSFER_SELECTOR requests this helper creates can now fail the handler-to-selector check. Either add nativeTransferSelector here or disable relation enforcement for this generic helper.

Suggested fix
-        bytes4[] memory approveTxHandlers = new bytes4[](1);
-        approveTxHandlers[0] = approveTxSelector; // Self-reference
+        bytes4[] memory approveTxHandlers = new bytes4[](2);
+        approveTxHandlers[0] = approveTxSelector; // Self-reference
+        approveTxHandlers[1] = nativeTransferSelector; // Approves NATIVE_TRANSFER_SELECTOR in payment tests
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/foundry/helpers/PaymentTestHelper.sol` around lines 185 - 198, The
approveTransaction registration is enforcing handler relations but
approveTxHandlers only contains approveTxSelector, so handlers won't match
requests that advertise nativeTransferSelector; update the register call in the
block that builds approveTxHandlers used by EngineBlox.registerFunction: either
expand approveTxHandlers to length 2 and push nativeTransferSelector alongside
approveTxSelector (ensure you reference nativeTransferSelector and
approveTxHandlers) or set enforceHandlerRelations to false in the
EngineBlox.registerFunction call; make the minimal change consistent with the
helper's intended behavior (prefer adding nativeTransferSelector if approvals
should cover native transfers).
contracts/core/execution/lib/definitions/GuardControllerDefinitions.sol (1)

224-247: ⚠️ Potential issue | 🟠 Major

The REGISTER_FUNCTION action must include enforceHandlerRelations to support dynamic registration.

Static schemas now support the enforceHandlerRelations boolean (e.g., schema[6] enables it at line 231), but the runtime registration payload does not. The encodeRegisterFunction() helper and getGuardConfigActionSpecs() format specification document REGISTER_FUNCTION as (string functionSignature, string operationName, TxAction[] supportedActions) only — no enforceHandlerRelations field. Functions registered dynamically via guardConfigBatch cannot set the new validation level and will silently default to weaker behavior.

Evidence
  • Static schema (line 231): schemas[6] sets enforceHandlerRelations: true
  • Runtime encoder (lines 479–485): encodeRegisterFunction() takes only functionSignature, operationName, supportedActions
  • Format spec (line 445): REGISTER_FUNCTION format is "(string functionSignature, string operationName, TxAction[] supportedActions)" — no boolean
  • Schema struct (EngineBlox.sol line 155): FunctionSchema has bool enforceHandlerRelations
  • Runtime enforcement (EngineBlox.sol line 2165): The check if (!schema.enforceHandlerRelations) confirms the field is actively used during execution
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/core/execution/lib/definitions/GuardControllerDefinitions.sol`
around lines 224 - 247, The runtime REGISTER_FUNCTION payload must include the
enforceHandlerRelations boolean so dynamically registered functions can set the
same validation level as static schemas; update the REGISTER_FUNCTION format
spec and the encoder/decoder to carry this field: modify the format string used
in getGuardConfigActionSpecs() and any spec docs from "(string
functionSignature, string operationName, TxAction[] supportedActions)" to
"(string functionSignature, string operationName, TxAction[] supportedActions,
bool enforceHandlerRelations)", then update encodeRegisterFunction() to accept
and encode the enforceHandlerRelations bool into the registration tuple and
ensure the runtime that constructs EngineBlox.FunctionSchema uses this value to
populate FunctionSchema.enforceHandlerRelations so the enforcement logic
(EngineBlox.FunctionSchema.enforceHandlerRelations) behaves identically for
dynamic registrations.
scripts/sanity-sdk/runtime-rbac/rbac-tests.ts (1)

1108-1170: ⚠️ Potential issue | 🟠 Major

Don't let HandlerForSelectorMismatch turn this helper into best-effort mode.

ensureRoleHasRequiredPermissions() is the setup gate for the rest of the RBAC workflow. This branch now returns even when only one required permission is present—or when neither can be verified—so Step 1 can pass and later steps fail with much harder-to-diagnose authorization errors.

💡 Suggested fix
-            } else if (isHandlerMismatch && (verifyHandler || verifyExecution)) {
-              console.log(`  ✅ Permissions partially or fully present after HandlerForSelectorMismatch (re-run safe)`);
-            } else if (isHandlerMismatch) {
-              console.log(`  ⚠️  HandlerForSelectorMismatch and permissions not found; continuing (may need contract fix for re-runs)`);
+            } else if (isHandlerMismatch) {
+              if (verifyHandler && verifyExecution) {
+                console.log(`  ✅ Permissions already present after HandlerForSelectorMismatch (re-run safe)`);
+              } else {
+                throw new Error(
+                  `HandlerForSelectorMismatch left required permissions missing. handler=${verifyHandler}, execution=${verifyExecution}`
+                );
+              }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity-sdk/runtime-rbac/rbac-tests.ts` around lines 1108 - 1170, The
helper ensureRoleHasRequiredPermissions currently treats
HandlerForSelectorMismatch as an acceptable outcome even when only one or
neither permission is verified; change it to require both verifyHandler and
verifyExecution to be true before proceeding. After the retry loop, remove the
early-success branches that allow partial success for
isHandlerMismatch/isResourceAlreadyExists; instead: if verifyHandler &&
verifyExecution, continue normally; else if isResourceAlreadyExists or
isHandlerMismatch but verification failed, throw a clear Error from
ensureRoleHasRequiredPermissions (include txStatus.error and roleHash) so the
setup gate fails fast; keep the special-case logging when verify succeeds to
allow re-runs, but do not let HandlerForSelectorMismatch turn this into
best-effort mode. Ensure references: isHandlerMismatch, isResourceAlreadyExists,
verifyHandler, verifyExecution, ROLE_CONFIG_BATCH_META_SELECTOR,
ROLE_CONFIG_BATCH_EXECUTE_SELECTOR, TxAction, and function
ensureRoleHasRequiredPermissions.
contracts/core/base/BaseStateMachine.sol (1)

702-720: ⚠️ Potential issue | 🔴 Critical

This is a storage layout violation in an upgradeable contract that could break existing proxy deployments.

FunctionSchema is persisted as a mapping in _secureState (EngineBlox.sol:179). The two bool fields enforceHandlerRelations (line 154) and isProtected (line 155) are packed into the same storage slot. If enforceHandlerRelations was added as a new field, existing proxies would have isProtected bits stored at the enforceHandlerRelations position, causing misinterpretation on read.

The contract is upgradeable (extends Initializable, ERC165Upgradeable, ReentrancyGuardUpgradeable), yet there are no __gap arrays or storage layout preservation mechanisms. If this field is being introduced to an existing system with deployed proxies, a migration strategy must be implemented before the upgrade is deployed.

Per coding guidelines: "Preserve storage layout using __gap arrays in upgradeable contracts."

Also applies to: lines 885-893 (_loadDefinitions calls _registerFunction with the new field)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/core/base/BaseStateMachine.sol` around lines 702 - 720, The new
boolean field packing introduced into FunctionSchema causes a storage layout
break for upgradeable proxies; update the upgrade to preserve layout by adding a
storage gap (e.g., __gap) to BaseStateMachine (or the contract that defines
FunctionSchema/_secureState) and implement an explicit migration routine that
safely initializes/moves the existing mapping data before deploying the change;
locate references to FunctionSchema, _secureState, _registerFunction and
_loadDefinitions and either (a) add reserved uint256[] __gap slots to the
contract defining persistent storage so the new fields do not shift existing
slots, or (b) provide a migration function that reads old packed values and
writes them into the new schema positions prior to using
_registerFunction/_loadDefinitions in the upgraded implementation.
contracts/core/lib/EngineBlox.sol (2)

149-158: ⚠️ Potential issue | 🟠 Major

This field insertion is not storage-safe for in-place upgrades.

FunctionSchema.supportedActionsBitmap and isProtected were previously packed into the same slot. Inserting enforceHandlerRelations between them means old isProtected=true entries will now be read back as enforceHandlerRelations=true and isProtected=false, which can silently unprotect previously protected schemas. Either append the new flag at the end of the struct or ship a migration that rewrites every stored schema before upgrade.

As per coding guidelines, "Preserve storage layout using __gap arrays in upgradeable contracts".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/core/lib/EngineBlox.sol` around lines 149 - 158, The insertion of
enforceHandlerRelations into FunctionSchema between supportedActionsBitmap and
isProtected breaks existing storage packing and will misread old data; to fix,
move the new boolean enforceHandlerRelations to the end of the struct (after
handlerForSelectors) or implement a migration that reads every stored
FunctionSchema and rewrites it with the new layout; update any code that
constructs or reads FunctionSchema (references: struct FunctionSchema, fields
supportedActionsBitmap, enforceHandlerRelations, isProtected,
handlerForSelectors) to match the new layout and ensure upgrade-safe storage (or
add an explicit migration step before activating the new contract).

479-486: ⚠️ Potential issue | 🔴 Critical

The new action check still leaves handler and transaction binding attacker-controlled.

These paths now pin metaTx.params.action, but requestAndApprove still trusts caller-supplied metaTx.params.handlerSelector / handlerContract, and the approval/cancel flows authorize against metaTx.txRecord.params.executionSelector from calldata even though they execute self.txRecords[txId]. A signer can therefore satisfy RBAC with one signed handler/selector and use it to drive a different entrypoint or pending transaction. Require the signed handler to match the current contract and entrypoint, and for existing-tx flows compare the signed params against self.txRecords[txId] (or rebuild the signed hash from storage) before permission checks and signature verification.

As per coding guidelines, "Validate EIP-712 signatures for meta-transactions before execution".

Also applies to: 500-506, 541-546, 1626-1657, 2234-2240

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/core/lib/EngineBlox.sol` around lines 479 - 486, The meta-tx flows
(e.g., txCancellationWithMetaTx and requestAndApprove) currently trust
caller-provided metaTx.params.handlerSelector/handlerContract and
metaTx.txRecord.params.executionSelector from calldata, enabling signer mismatch
attacks; fix by first comparing the signed handler fields to the actual handler
and selector the contract will use (require metaTx.params.handlerSelector ==
address(this).selector-equivalent or the actual entrypoint selector and
metaTx.params.handlerContract == address(this) as appropriate), and for flows
that operate on an existing txId (txCancellationWithMetaTx, approve/cancel
flows) require that metaTx.txRecord.params.executionSelector exactly matches
self.txRecords[txId].params.executionSelector (or rebuild the signed hash from
storage and validate equality) before calling
_validateExecutionAndHandlerPermissions or verifySignature; update
verifySignature/_validateExecutionAndHandlerPermissions call sites to perform
these equality checks first so signature and RBAC checks are applied to the
actual stored target, not attacker-supplied calldata.
scripts/sanity-sdk/guard-controller/erc20-mint-controller-tests.ts (2)

66-82: ⚠️ Potential issue | 🟡 Minor

The wrong-action regression test is currently inert.

executeTests() never calls step2aMintMetaTxWrongActionMustRevert(), and inside that helper any revert counts as success. That means the new NotSupported guard can regress without this suite noticing.

Minimal fix
   await this.step0RegisterMintSchemaIfNeeded();
   await this.step1WhitelistBasicErc20IfNeeded();
   await this.step1bEnsureMintRolesAndPermissions();
+  await this.step2aMintMetaTxWrongActionMustRevert();
   await this.step2Mint100ToAccountBloxViaMetaTx();
   await this.step3VerifyBalanceIncrease();
   await this.step4SnapshotMintReadinessState();

Please also assert that the decoded revert is NotSupported, not just that something threw.

Also applies to: 581-661

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity-sdk/guard-controller/erc20-mint-controller-tests.ts` around
lines 66 - 82, The test suite never invokes
step2aMintMetaTxWrongActionMustRevert() from executeTests(), and the helper
currently treats any revert as success; update executeTests() to call
step2aMintMetaTxWrongActionMustRevert() in the correct sequence (after
step2Mint100ToAccountBloxViaMetaTx or where the wrong-action flow belongs) and
modify step2aMintMetaTxWrongActionMustRevert() to assert that the transaction
reverts with the specific decoded error name "NotSupported" (decode the revert
payload and assert equality rather than accepting any thrown error) so the test
fails on regressions to the NotSupported guard.

901-948: ⚠️ Potential issue | 🟠 Major

Throw on unexpected byte fields instead of silently returning non-string values.

The toHex helper declares return type string but can return non-string values (numbers, objects) due to the type cast not enforcing runtime behavior. Only a console.warn is logged. Since this helper runs immediately before signing and sending, malformed inputs should throw rather than corrupt the meta-transaction payload.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity-sdk/guard-controller/erc20-mint-controller-tests.ts` around
lines 901 - 948, The toHex helper inside normalizeMetaTxToHex can return
non-string values and only logs a warning; change it to validate inputs and
throw on unexpected types so it always returns a proper hex string.
Specifically, update normalizeMetaTxToHex -> toHex to: accept string/Uint8Array
(and strings starting with 0x), convert Uint8Array via bytesToHex, verify the
result matches /^0x[0-9a-fA-F]*$/ (or pad/normalize as needed), and throw an
Error for any other type or malformed value instead of returning '0x' or
logging; ensure callers (message, signature, data, txRecord.message,
params.executionSelector, params.operationType) rely on this invariant and
adjust any fallback logic to avoid silent non-string returns.
🧹 Nitpick comments (5)
scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs (1)

341-387: Split broadcaster approve/cancel rights per selector.

Granting both EXECUTE_META_APPROVE and EXECUTE_META_CANCEL to both timelock controller selectors is broader than needed and makes the test less sensitive to selector/action mixups.

♻️ Minimal diff
-            const broadcasterApproveCancelActions = [this.TxAction.EXECUTE_META_APPROVE, this.TxAction.EXECUTE_META_CANCEL];
+            const broadcasterApproveActions = [this.TxAction.EXECUTE_META_APPROVE];
+            const broadcasterCancelActions = [this.TxAction.EXECUTE_META_CANCEL];
@@
-                broadcasterApproveCancelActions,
+                broadcasterApproveActions,
@@
-                broadcasterApproveCancelActions,
+                broadcasterCancelActions,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs` around lines
341 - 387, The test currently grants both EXECUTE_META_APPROVE and
EXECUTE_META_CANCEL (broadcasterApproveCancelActions) to both
APPROVE_TIMELOCK_EXECUTION_META_SELECTOR and
CANCEL_TIMELOCK_EXECUTION_META_SELECTOR, which is too broad; update the calls
that use addFunctionToRole for this.getRoleHash('BROADCASTER_ROLE') so
APPROVE_TIMELOCK_EXECUTION_META_SELECTOR receives only EXECUTE_META_APPROVE and
CANCEL_TIMELOCK_EXECUTION_META_SELECTOR receives only EXECUTE_META_CANCEL
(create two distinct action arrays or inline single-action arrays instead of
broadcasterApproveCancelActions), keeping the other
addFunctionToRoleWithHandlerForSelectors/addFunctionToRole calls and role hashes
(getRoleHash('BROADCASTER_ROLE'), REQUEST_AND_APPROVE_EXECUTION_SELECTOR,
ERC20_MINT_SELECTOR) unchanged.
sdk/typescript/abi/IDefinition.abi.json (1)

33-41: Ship this tuple expansion as a breaking SDK change.

Inserting enforceHandlerRelations before isProtected shifts every positional FunctionSchema field after supportedActionsBitmap. Please verify all generated bindings and any consumers that destructure these tuples positionally were regenerated, and version the published SDK accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/typescript/abi/IDefinition.abi.json` around lines 33 - 41, The insertion
of the new tuple field enforceHandlerRelations before isProtected in the ABI
shifts positional indexes for FunctionSchema fields after
supportedActionsBitmap; regenerate all generated TypeScript bindings that
consume IDefinition.abi (so FunctionSchema-related types/parsers), update any
consumers that destructure these tuples positionally to use named accessors or
the new index order (referencing enforceHandlerRelations, isProtected,
supportedActionsBitmap, FunctionSchema), and publish this as a breaking SDK
release with an appropriate major version bump so downstream users are aware of
the change.
test/foundry/fuzz/UnregisterFunctionFuzz.t.sol (1)

39-47: Cover both enforceHandlerRelations modes in this fuzz harness.

The harness now hardcodes true, so this suite still doesn't exercise the disabled path or any handler/selector mismatch edge cases introduced by the new flag. Please fuzz both boolean values and at least one mismatched-handler case, or move this to a regular unit test if it is meant to stay deterministic. As per coding guidelines, **/*.t.sol: Write fuzz tests for input validation and edge cases.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/foundry/fuzz/UnregisterFunctionFuzz.t.sol` around lines 39 - 47, The
harness currently calls EngineBlox.registerFunction with enforceHandlerRelations
hardcoded to true; change the fuzz input to include a boolean for
enforceHandlerRelations (fuzz both true and false) and add at least one branch
that constructs a mismatched handlers vs selectors scenario to exercise the
disabled-path edge cases (e.g., provide a handlers array that does not align
with functionSelector/bitmap) so both validation paths run; update the call site
in UnregisterFunctionFuzz.t.sol where EngineBlox.registerFunction(state,
functionSignature, functionSelector, "TEST_OPERATION", bitmap, true, false,
handlers) is invoked to use the new fuzzed boolean and include a seeded
mismatched-case path for handlers when the flag is false.
test/foundry/fuzz/ComprehensiveDefinitionSecurityFuzz.t.sol (1)

355-388: This "fuzz" case never uses the fuzzed input.

handlerSelector never feeds the schema or permission under test, so the runner repeats the same constant scenario on every iteration. Either make this a regular unit test or build the malicious fixture from the fuzzed selector.

As per coding guidelines, "**/*.t.sol: Write fuzz tests for input validation and edge cases."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/foundry/fuzz/ComprehensiveDefinitionSecurityFuzz.t.sol` around lines 355
- 388, The fuzz input handlerSelector is never
used—testFuzz_DefinitionWithSelfReferenceBehavior always exercises the same
constant fixture (MaliciousDefinitions_InvalidSelfReference.HANDLER_SELECTOR);
update the test to use the fuzzed handlerSelector by either (A) passing
handlerSelector into the malicious fixture helpers (replace calls to
MaliciousDefinitions_InvalidSelfReference.getRolePermissions() and
.getFunctionSchemas() with variants that accept handlerSelector) or (B) build
the EngineBlox.FunctionSchema[] and IDefinition.RolePermission structs inline
using handlerSelector instead of
MaliciousDefinitions_InvalidSelfReference.HANDLER_SELECTOR before calling
testStateMachine.loadDefinitionsForTesting, so the fuzz input actually affects
the schema/permission and triggers different validation paths in
testFuzz_DefinitionWithSelfReferenceBehavior.
scripts/sanity-sdk/guard-controller/base-test.ts (1)

199-202: Consider hardening ABI param serialization to handle potential bigint values.

While the current implementation of extractErrorInfo() converts all decoded parameters to strings (line 1032 in sdk/typescript/utils/contract-errors.ts), the params field is typed as Record<string, any>, allowing for future code paths that might introduce unconverted bigint values from ABI decoding. JSON.stringify will throw if it encounters a bigint, masking the original revert and turning the diagnostic message into the failure point.

The suggested fix adds a replacer function to safely serialize numeric types:

💡 Suggested hardening
-          console.error(`  📋 Revert decoded: ${decoded.name}${decoded.params ? ` ${JSON.stringify(decoded.params)}` : ''}`);
+          const decodedParams = decoded.params
+            ? JSON.stringify(decoded.params, (_, value) =>
+                typeof value === 'bigint' ? value.toString() : value
+              )
+            : '';
+          console.error(`  📋 Revert decoded: ${decoded.name}${decodedParams ? ` ${decodedParams}` : ''}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity-sdk/guard-controller/base-test.ts` around lines 199 - 202, The
revert logging can throw if decoded.params contains bigint values because
JSON.stringify fails on bigints; update the code that logs and concatenates
decoded.params (the use of decoded.params in the block using extractErrorInfo
and the console.error/ hint assembly) to serialize params with a safe replacer
that converts bigint (and other non-serializable numeric types) to strings
before JSON.stringify. Specifically, change the serialization of decoded.params
in the console.error and when building hint so it uses a JSON.stringify call
with a replacer that returns value.toString() for typeof value === 'bigint' (and
otherwise returns the value), ensuring no uncaught TypeError from
JSON.stringify.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@abi/CopyBlox.abi.json`:
- Around line 923-927: You inserted the new boolean enforceHandlerRelations into
the middle of the FunctionSchema tuple (before isProtected), which changes
positional ABI layout and will break positional decoders; to fix, move the
enforceHandlerRelations field to the end of the FunctionSchema tuple (or
alternatively only introduce it as part of an explicit
breaking-version/migration), updating the ABI so the original order of existing
fields (including isProtected) is preserved; reference the FunctionSchema tuple
and the fields enforceHandlerRelations and isProtected when making the change.

In `@abi/GuardControllerDefinitions.abi.json`:
- Around line 163-167: The helper ABI must accept and pass the new
enforceHandlerRelations flag: update the helper signature for
encodeRegisterFunction (currently
encodeRegisterFunction(string,string,TxAction[])) to include a boolean parameter
for enforceHandlerRelations, thread that boolean into the payload construction
where getFunctionSchemas/enforceHandlerRelations is set, and regenerate the ABI
and SDK artifacts so callers can supply the flag; ensure all usages of
encodeRegisterFunction, any wrapper helpers, and tests are updated to pass the
new boolean.

In `@abi/SimpleVault.abi.json`:
- Around line 1045-1049: The ABI change inserted the new boolean field
enforceHandlerRelations before isProtected, which alters the positional tuple
layout of EngineBlox.FunctionSchema and will break any consumer decoding
getFunctionSchema() by index; restore compatibility by moving
enforceHandlerRelations to the end of the struct/ABI ordering (or explicitly
mark this as a breaking change and bump the contract/ABI version), update any
consumers to use named decoding instead of index-based access, and ensure the
final ABI keeps the original field order (isProtected in its original position)
unless you deliberately release a breaking version.

In
`@contracts/examples/integrations/Safe/GuardianSafe/GuardianSafeDefinitions.sol`:
- Around line 97-98: The cancelTransaction(uint256) path is not authorized in
the execution-selector layer when enforceHandlerRelations is true; update the
permissions so the handler and the linked EXEC_SAFE_TX_SELECTOR agree: add
EngineBlox.TxAction.EXECUTE_TIME_DELAY_CANCEL to executionActions and to the
owner's EXEC_SAFE_TX_SELECTOR permission set (and any other owner/handler
permission mappings touched by enforceHandlerRelations) so cancelTransaction is
authorized end-to-end when enforceHandlerRelations and isProtected are enabled.

In `@scripts/sanity-sdk/run-all-tests.ts`:
- Around line 171-188: The post-sanity system state summary is being executed
unconditionally; guard the call to printPostSanitySystemStateSummary so it only
runs when allPassed is true (i.e., the all-green case). Move the try/catch that
calls this.printPostSanitySystemStateSummary(testsToRun) inside the if
(allPassed) branch (or wrap it with if (allPassed) { ... }) and keep the
existing process.exit(allPassed ? 0 : 1) behavior unchanged so failures do not
trigger the post-sanity dump.
- Around line 294-302: The current --repeat handling removes the next token
(args.splice) before validating it, so malformed values like "--repeat
--guard-controller" or non-numeric strings get stripped and silently ignored;
update the logic in the repeat parsing block (look for repeatIdx, parseInt,
Number.isInteger, this.repeatCount, args.splice) to validate the next token
exists and is a valid integer in range before mutating args: if repeatIdx >= 0
then first check args[repeatIdx + 1] is present and parseInt yields a
Number.isInteger(n) within 1..100; only then set this.repeatCount and call
args.splice(repeatIdx, 2); otherwise throw or log an error and exit (or leave
args untouched) to prevent removing the wrong token.

In `@sdk/typescript/abi/BaseStateMachine.abi.json`:
- Around line 1144-1148: The ABI change inserted the new boolean field
"enforceHandlerRelations" into the middle of EngineBlox.FunctionSchema, which
shifts tuple indexes returned by getFunctionSchema(); to fix this, move the
"enforceHandlerRelations" entry to the end of the EngineBlox.FunctionSchema
struct in BaseStateMachine.abi.json so existing positional decoding remains
stable, or if intentional, mark and release the ABI as a breaking change and
document that getFunctionSchema() tuple indexes have changed.

In `@sdk/typescript/abi/RuntimeRBAC.abi.json`:
- Around line 861-865: The new bool field enforceHandlerRelations was inserted
before isProtected in the public tuple EngineBlox.FunctionSchema, shifting all
subsequent tuple indices and breaking positional decoders; fix by moving
enforceHandlerRelations to the end of the FunctionSchema tuple layout (append
after all existing fields) so indices for existing fields remain stable, or if
you must keep the new position, treat it as a breaking ABI change by bumping the
ABI/contract version and update any positional decoders,
serializers/deserializers and tests that reference EngineBlox.FunctionSchema or
the isProtected index accordingly.

In `@sdk/typescript/abi/RuntimeRBACDefinitions.abi.json`:
- Around line 72-76: The ABI change inserted "enforceHandlerRelations" into the
middle of EngineBlox.FunctionSchema and shifted subsequent tuple indexes
(including "isProtected"), which breaks compatibility; move
"enforceHandlerRelations" to the end of the FunctionSchema tuple instead of
inserting it in the middle and then regenerate the ABI
(RuntimeRBACDefinitions.abi.json) so the tuple order preserves existing indexes
and consumers remain compatible.

In `@sdk/typescript/abi/SimpleRWA20Definitions.abi.json`:
- Around line 111-115: The ABI insertion of "enforceHandlerRelations" before
"isProtected" breaks positional decoding for getFunctionSchemas() which returns
an array of EngineBlox.FunctionSchema; either move the "enforceHandlerRelations"
entry to the end of the function schema object array (append it after existing
fields) to preserve existing field positions, or treat this as an explicit
breaking change by bumping the ABI/schema version and documenting the reorder;
locate the "enforceHandlerRelations" field in SimpleRWA20Definitions.abi.json
and adjust its position (or version) so decoders relying on
EngineBlox.FunctionSchema maintain correct positional mappings.

In `@sdk/typescript/abi/SimpleVault.abi.json`:
- Around line 1045-1049: The ABI change inserted the new boolean field
enforceHandlerRelations before isProtected, altering the positional tuple layout
used by EngineBlox.FunctionSchema and breaking clients that index tuple members;
to fix, move enforceHandlerRelations to the end of the return tuple (append it
after existing fields) in SimpleVault.abi.json so existing positional
deserialization remains stable, or if deliberate, mark this as an explicit
breaking ABI change in the SDK release notes and bump the ABI version referenced
by EngineBlox.FunctionSchema.

---

Outside diff comments:
In `@contracts/core/base/BaseStateMachine.sol`:
- Around line 702-720: The new boolean field packing introduced into
FunctionSchema causes a storage layout break for upgradeable proxies; update the
upgrade to preserve layout by adding a storage gap (e.g., __gap) to
BaseStateMachine (or the contract that defines FunctionSchema/_secureState) and
implement an explicit migration routine that safely initializes/moves the
existing mapping data before deploying the change; locate references to
FunctionSchema, _secureState, _registerFunction and _loadDefinitions and either
(a) add reserved uint256[] __gap slots to the contract defining persistent
storage so the new fields do not shift existing slots, or (b) provide a
migration function that reads old packed values and writes them into the new
schema positions prior to using _registerFunction/_loadDefinitions in the
upgraded implementation.

In `@contracts/core/execution/lib/definitions/GuardControllerDefinitions.sol`:
- Around line 224-247: The runtime REGISTER_FUNCTION payload must include the
enforceHandlerRelations boolean so dynamically registered functions can set the
same validation level as static schemas; update the REGISTER_FUNCTION format
spec and the encoder/decoder to carry this field: modify the format string used
in getGuardConfigActionSpecs() and any spec docs from "(string
functionSignature, string operationName, TxAction[] supportedActions)" to
"(string functionSignature, string operationName, TxAction[] supportedActions,
bool enforceHandlerRelations)", then update encodeRegisterFunction() to accept
and encode the enforceHandlerRelations bool into the registration tuple and
ensure the runtime that constructs EngineBlox.FunctionSchema uses this value to
populate FunctionSchema.enforceHandlerRelations so the enforcement logic
(EngineBlox.FunctionSchema.enforceHandlerRelations) behaves identically for
dynamic registrations.

In `@contracts/core/lib/EngineBlox.sol`:
- Around line 149-158: The insertion of enforceHandlerRelations into
FunctionSchema between supportedActionsBitmap and isProtected breaks existing
storage packing and will misread old data; to fix, move the new boolean
enforceHandlerRelations to the end of the struct (after handlerForSelectors) or
implement a migration that reads every stored FunctionSchema and rewrites it
with the new layout; update any code that constructs or reads FunctionSchema
(references: struct FunctionSchema, fields supportedActionsBitmap,
enforceHandlerRelations, isProtected, handlerForSelectors) to match the new
layout and ensure upgrade-safe storage (or add an explicit migration step before
activating the new contract).
- Around line 479-486: The meta-tx flows (e.g., txCancellationWithMetaTx and
requestAndApprove) currently trust caller-provided
metaTx.params.handlerSelector/handlerContract and
metaTx.txRecord.params.executionSelector from calldata, enabling signer mismatch
attacks; fix by first comparing the signed handler fields to the actual handler
and selector the contract will use (require metaTx.params.handlerSelector ==
address(this).selector-equivalent or the actual entrypoint selector and
metaTx.params.handlerContract == address(this) as appropriate), and for flows
that operate on an existing txId (txCancellationWithMetaTx, approve/cancel
flows) require that metaTx.txRecord.params.executionSelector exactly matches
self.txRecords[txId].params.executionSelector (or rebuild the signed hash from
storage and validate equality) before calling
_validateExecutionAndHandlerPermissions or verifySignature; update
verifySignature/_validateExecutionAndHandlerPermissions call sites to perform
these equality checks first so signature and RBAC checks are applied to the
actual stored target, not attacker-supplied calldata.

In `@scripts/sanity-sdk/guard-controller/erc20-mint-controller-tests.ts`:
- Around line 66-82: The test suite never invokes
step2aMintMetaTxWrongActionMustRevert() from executeTests(), and the helper
currently treats any revert as success; update executeTests() to call
step2aMintMetaTxWrongActionMustRevert() in the correct sequence (after
step2Mint100ToAccountBloxViaMetaTx or where the wrong-action flow belongs) and
modify step2aMintMetaTxWrongActionMustRevert() to assert that the transaction
reverts with the specific decoded error name "NotSupported" (decode the revert
payload and assert equality rather than accepting any thrown error) so the test
fails on regressions to the NotSupported guard.
- Around line 901-948: The toHex helper inside normalizeMetaTxToHex can return
non-string values and only logs a warning; change it to validate inputs and
throw on unexpected types so it always returns a proper hex string.
Specifically, update normalizeMetaTxToHex -> toHex to: accept string/Uint8Array
(and strings starting with 0x), convert Uint8Array via bytesToHex, verify the
result matches /^0x[0-9a-fA-F]*$/ (or pad/normalize as needed), and throw an
Error for any other type or malformed value instead of returning '0x' or
logging; ensure callers (message, signature, data, txRecord.message,
params.executionSelector, params.operationType) rely on this invariant and
adjust any fallback logic to avoid silent non-string returns.

In `@scripts/sanity-sdk/runtime-rbac/rbac-tests.ts`:
- Around line 1108-1170: The helper ensureRoleHasRequiredPermissions currently
treats HandlerForSelectorMismatch as an acceptable outcome even when only one or
neither permission is verified; change it to require both verifyHandler and
verifyExecution to be true before proceeding. After the retry loop, remove the
early-success branches that allow partial success for
isHandlerMismatch/isResourceAlreadyExists; instead: if verifyHandler &&
verifyExecution, continue normally; else if isResourceAlreadyExists or
isHandlerMismatch but verification failed, throw a clear Error from
ensureRoleHasRequiredPermissions (include txStatus.error and roleHash) so the
setup gate fails fast; keep the special-case logging when verify succeeds to
allow re-runs, but do not let HandlerForSelectorMismatch turn this into
best-effort mode. Ensure references: isHandlerMismatch, isResourceAlreadyExists,
verifyHandler, verifyExecution, ROLE_CONFIG_BATCH_META_SELECTOR,
ROLE_CONFIG_BATCH_EXECUTE_SELECTOR, TxAction, and function
ensureRoleHasRequiredPermissions.

In `@scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs`:
- Around line 96-101: The current idempotency branch checks only that roles
exist (using roleExists/getRoleHash and verifyStep1RolesAfter) but not that the
role members match expected wallets, so Step 1 can falsely pass; update the
branch in the handler that currently calls verifyStep1RolesAfter() and
passTest('Create MINT_REQUESTOR and MINT_APPROVER roles', ...) to also fetch and
compare the current role members to the expected wallet addresses (use whatever
helper exists for reading role members or add one), and only treat the step as
satisfied if both roles exist and their members exactly match the expected set;
if membership differs, proceed with the creation flow (or fail) instead of
passing. Ensure the same membership check is added to the other similar branches
around the lines that call verifyStep1RolesAfter() (the ones referenced near
lines 137-140 and 148-152).
- Around line 545-555: The test currently uses SIGN_META_REQUEST_AND_APPROVE and
calls requestAndApproveExecution, which exercises the legacy one-step handler;
change it to create a request-only meta-tx (use SIGN_META_REQUEST or the
equivalent constant in tests when calling createExternalExecutionMetaTx) and
then call the requestExecution flow instead of requestAndApproveExecution so the
code hits the timelock path; after requesting, simulate the approver's timelock
approval by invoking approveTimeLockExecutionWithMetaTx (or building an approve
meta-tx signed by mintApproverWallet and calling executeWithTimeLock) so the
test exercises executeWithTimeLock and approveTimeLockExecutionWithMetaTx rather
than the one-step handler.
- Around line 256-314: The final success gate is missing checks for the
handler-side SIGN_META_REQUEST_AND_APPROVE and several broadcaster permissions;
add roleHasPermissionForSelector calls to assert: (1) the handler/approver role
(handlerRoleHash or approverRoleHash as appropriate) has
SIGN_META_REQUEST_AND_APPROVE on REQUEST_AND_APPROVE_EXECUTION_SELECTOR, (2) the
broadcasterRoleHash has REQUEST_AND_APPROVE_EXECUTION_SELECTOR (with the correct
TxAction), and (3) the broadcasterRoleHash has
APPROVE_TIMELOCK_EXECUTION_META_SELECTOR and
CANCEL_TIMELOCK_EXECUTION_META_SELECTOR with the appropriate timelock TxAction;
store each result in a descriptive const (e.g.
handlerHasSignMetaRequestAndApprove, broadcasterHasHandlerSelector,
broadcasterHasApproveMetaTx, broadcasterHasCancelMetaTx) and include those
consts in the big if(...) alongside the existing requestorOk/.../broadcasterOk
checks so the idempotent gate only reports success when all permissions are
present (use roleHasPermissionForSelector to perform each check).

In `@test/foundry/helpers/PaymentTestHelper.sol`:
- Around line 185-198: The approveTransaction registration is enforcing handler
relations but approveTxHandlers only contains approveTxSelector, so handlers
won't match requests that advertise nativeTransferSelector; update the register
call in the block that builds approveTxHandlers used by
EngineBlox.registerFunction: either expand approveTxHandlers to length 2 and
push nativeTransferSelector alongside approveTxSelector (ensure you reference
nativeTransferSelector and approveTxHandlers) or set enforceHandlerRelations to
false in the EngineBlox.registerFunction call; make the minimal change
consistent with the helper's intended behavior (prefer adding
nativeTransferSelector if approvals should cover native transfers).

---

Nitpick comments:
In `@scripts/sanity-sdk/guard-controller/base-test.ts`:
- Around line 199-202: The revert logging can throw if decoded.params contains
bigint values because JSON.stringify fails on bigints; update the code that logs
and concatenates decoded.params (the use of decoded.params in the block using
extractErrorInfo and the console.error/ hint assembly) to serialize params with
a safe replacer that converts bigint (and other non-serializable numeric types)
to strings before JSON.stringify. Specifically, change the serialization of
decoded.params in the console.error and when building hint so it uses a
JSON.stringify call with a replacer that returns value.toString() for typeof
value === 'bigint' (and otherwise returns the value), ensuring no uncaught
TypeError from JSON.stringify.

In `@scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs`:
- Around line 341-387: The test currently grants both EXECUTE_META_APPROVE and
EXECUTE_META_CANCEL (broadcasterApproveCancelActions) to both
APPROVE_TIMELOCK_EXECUTION_META_SELECTOR and
CANCEL_TIMELOCK_EXECUTION_META_SELECTOR, which is too broad; update the calls
that use addFunctionToRole for this.getRoleHash('BROADCASTER_ROLE') so
APPROVE_TIMELOCK_EXECUTION_META_SELECTOR receives only EXECUTE_META_APPROVE and
CANCEL_TIMELOCK_EXECUTION_META_SELECTOR receives only EXECUTE_META_CANCEL
(create two distinct action arrays or inline single-action arrays instead of
broadcasterApproveCancelActions), keeping the other
addFunctionToRoleWithHandlerForSelectors/addFunctionToRole calls and role hashes
(getRoleHash('BROADCASTER_ROLE'), REQUEST_AND_APPROVE_EXECUTION_SELECTOR,
ERC20_MINT_SELECTOR) unchanged.

In `@sdk/typescript/abi/IDefinition.abi.json`:
- Around line 33-41: The insertion of the new tuple field
enforceHandlerRelations before isProtected in the ABI shifts positional indexes
for FunctionSchema fields after supportedActionsBitmap; regenerate all generated
TypeScript bindings that consume IDefinition.abi (so FunctionSchema-related
types/parsers), update any consumers that destructure these tuples positionally
to use named accessors or the new index order (referencing
enforceHandlerRelations, isProtected, supportedActionsBitmap, FunctionSchema),
and publish this as a breaking SDK release with an appropriate major version
bump so downstream users are aware of the change.

In `@test/foundry/fuzz/ComprehensiveDefinitionSecurityFuzz.t.sol`:
- Around line 355-388: The fuzz input handlerSelector is never
used—testFuzz_DefinitionWithSelfReferenceBehavior always exercises the same
constant fixture (MaliciousDefinitions_InvalidSelfReference.HANDLER_SELECTOR);
update the test to use the fuzzed handlerSelector by either (A) passing
handlerSelector into the malicious fixture helpers (replace calls to
MaliciousDefinitions_InvalidSelfReference.getRolePermissions() and
.getFunctionSchemas() with variants that accept handlerSelector) or (B) build
the EngineBlox.FunctionSchema[] and IDefinition.RolePermission structs inline
using handlerSelector instead of
MaliciousDefinitions_InvalidSelfReference.HANDLER_SELECTOR before calling
testStateMachine.loadDefinitionsForTesting, so the fuzz input actually affects
the schema/permission and triggers different validation paths in
testFuzz_DefinitionWithSelfReferenceBehavior.

In `@test/foundry/fuzz/UnregisterFunctionFuzz.t.sol`:
- Around line 39-47: The harness currently calls EngineBlox.registerFunction
with enforceHandlerRelations hardcoded to true; change the fuzz input to include
a boolean for enforceHandlerRelations (fuzz both true and false) and add at
least one branch that constructs a mismatched handlers vs selectors scenario to
exercise the disabled-path edge cases (e.g., provide a handlers array that does
not align with functionSelector/bitmap) so both validation paths run; update the
call site in UnregisterFunctionFuzz.t.sol where
EngineBlox.registerFunction(state, functionSignature, functionSelector,
"TEST_OPERATION", bitmap, true, false, handlers) is invoked to use the new
fuzzed boolean and include a seeded mismatched-case path for handlers when the
flag is false.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7f86ee9e-df70-41ab-a711-8f55421a084d

📥 Commits

Reviewing files that changed from the base of the PR and between 5bd0404 and ab99b08.

📒 Files selected for processing (56)
  • abi/AccountBlox.abi.json
  • abi/BaseStateMachine.abi.json
  • abi/CopyBlox.abi.json
  • abi/GuardController.abi.json
  • abi/GuardControllerDefinitions.abi.json
  • abi/IDefinition.abi.json
  • abi/RuntimeRBAC.abi.json
  • abi/RuntimeRBACDefinitions.abi.json
  • abi/SecureOwnable.abi.json
  • abi/SecureOwnableDefinitions.abi.json
  • abi/SimpleRWA20.abi.json
  • abi/SimpleRWA20Definitions.abi.json
  • abi/SimpleVault.abi.json
  • abi/SimpleVaultDefinitions.abi.json
  • contracts/core/access/lib/definitions/RuntimeRBACDefinitions.sol
  • contracts/core/base/BaseStateMachine.sol
  • contracts/core/execution/GuardController.sol
  • contracts/core/execution/lib/definitions/GuardControllerDefinitions.sol
  • contracts/core/lib/EngineBlox.sol
  • contracts/core/security/lib/definitions/SecureOwnableDefinitions.sol
  • contracts/examples/applications/PayBlox/PayBloxDefinitions.sol
  • contracts/examples/applications/SimpleRWA20/SimpleRWA20Definitions.sol
  • contracts/examples/applications/SimpleVault/SimpleVaultDefinitions.sol
  • contracts/examples/integrations/Safe/GuardianSafe/GuardianSafeDefinitions.sol
  • package/package.json
  • scripts/sanity-sdk/guard-controller/base-test.ts
  • scripts/sanity-sdk/guard-controller/erc20-mint-controller-tests.ts
  • scripts/sanity-sdk/run-all-tests.ts
  • scripts/sanity-sdk/runtime-rbac/base-test.ts
  • scripts/sanity-sdk/runtime-rbac/rbac-tests.ts
  • scripts/sanity-sdk/secure-ownable/base-test.ts
  • scripts/sanity/guard-controller/base-test.cjs
  • scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs
  • sdk/typescript/abi/AccountBlox.abi.json
  • sdk/typescript/abi/BaseStateMachine.abi.json
  • sdk/typescript/abi/CopyBlox.abi.json
  • sdk/typescript/abi/GuardController.abi.json
  • sdk/typescript/abi/GuardControllerDefinitions.abi.json
  • sdk/typescript/abi/IDefinition.abi.json
  • sdk/typescript/abi/RuntimeRBAC.abi.json
  • sdk/typescript/abi/RuntimeRBACDefinitions.abi.json
  • sdk/typescript/abi/SecureOwnable.abi.json
  • sdk/typescript/abi/SecureOwnableDefinitions.abi.json
  • sdk/typescript/abi/SimpleRWA20.abi.json
  • sdk/typescript/abi/SimpleRWA20Definitions.abi.json
  • sdk/typescript/abi/SimpleVault.abi.json
  • sdk/typescript/abi/SimpleVaultDefinitions.abi.json
  • sdk/typescript/contracts/core/BaseStateMachine.tsx
  • sdk/typescript/interfaces/base.index.tsx
  • sdk/typescript/package.json
  • sdk/typescript/types/definition.index.tsx
  • test/foundry/fuzz/ComprehensiveDefinitionSecurityFuzz.t.sol
  • test/foundry/fuzz/UnregisterFunctionFuzz.t.sol
  • test/foundry/helpers/MaliciousDefinitions.sol
  • test/foundry/helpers/PaymentTestHelper.sol
  • test/foundry/helpers/TestDefinitionContracts.sol

Comment thread abi/CopyBlox.abi.json
Comment on lines +923 to +927
{
"internalType": "bool",
"name": "enforceHandlerRelations",
"type": "bool"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid inserting the new field mid-tuple unless this is a breaking release.

Adding enforceHandlerRelations before isProtected changes the positional ABI layout of FunctionSchema. Any client decoding this tuple positionally will now read the old isProtected slot as the new boolean and shift the remaining fields. If backward compatibility matters, append the field at the end instead, or ship this behind an explicit breaking-version/migration update.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@abi/CopyBlox.abi.json` around lines 923 - 927, You inserted the new boolean
enforceHandlerRelations into the middle of the FunctionSchema tuple (before
isProtected), which changes positional ABI layout and will break positional
decoders; to fix, move the enforceHandlerRelations field to the end of the
FunctionSchema tuple (or alternatively only introduce it as part of an explicit
breaking-version/migration), updating the ABI so the original order of existing
fields (including isProtected) is preserved; reference the FunctionSchema tuple
and the fields enforceHandlerRelations and isProtected when making the change.

Comment on lines +163 to +167
{
"internalType": "bool",
"name": "enforceHandlerRelations",
"type": "bool"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The new schema flag is not configurable through this helper ABI.

getFunctionSchemas now exposes enforceHandlerRelations, but this definitions surface still only exposes encodeRegisterFunction(string,string,TxAction[]). Callers using the helper API have no way to set the new flag when producing registration payloads, so they either get stale behavior or an implicit hardcoded default. Please thread the boolean through the register-function helper and regenerate the ABI/SDK artifacts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@abi/GuardControllerDefinitions.abi.json` around lines 163 - 167, The helper
ABI must accept and pass the new enforceHandlerRelations flag: update the helper
signature for encodeRegisterFunction (currently
encodeRegisterFunction(string,string,TxAction[])) to include a boolean parameter
for enforceHandlerRelations, thread that boolean into the payload construction
where getFunctionSchemas/enforceHandlerRelations is set, and regenerate the ABI
and SDK artifacts so callers can supply the flag; ensure all usages of
encodeRegisterFunction, any wrapper helpers, and tests are updated to pass the
new boolean.

Comment thread abi/SimpleVault.abi.json
Comment on lines +1045 to +1049
{
"internalType": "bool",
"name": "enforceHandlerRelations",
"type": "bool"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This is a breaking ABI reorder.

Inserting enforceHandlerRelations before isProtected shifts the positional tuple layout of EngineBlox.FunctionSchema. Any existing consumer decoding getFunctionSchema() by index will now read the wrong slot, so this needs to be treated as an explicit breaking change or appended at the end of the struct instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@abi/SimpleVault.abi.json` around lines 1045 - 1049, The ABI change inserted
the new boolean field enforceHandlerRelations before isProtected, which alters
the positional tuple layout of EngineBlox.FunctionSchema and will break any
consumer decoding getFunctionSchema() by index; restore compatibility by moving
enforceHandlerRelations to the end of the struct/ABI ordering (or explicitly
mark this as a breaking change and bump the contract/ABI version), update any
consumers to use named decoding instead of index-based access, and ensure the
final ABI keeps the original field order (isProtected in its original position)
unless you deliberately release a breaking version.

Comment on lines +97 to 98
enforceHandlerRelations: true,
isProtected: true,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

cancelTransaction is not fully wired for enforced handler relations.

With enforceHandlerRelations enabled, the cancel path now relies on the linked EXEC_SAFE_TX_SELECTOR, but executionActions and the owner's EXEC_SAFE_TX_SELECTOR permission set still omit EngineBlox.TxAction.EXECUTE_TIME_DELAY_CANCEL. That leaves cancelTransaction(uint256) authorized at the handler layer and rejected at the linked execution-selector layer.

🐛 Suggested fix
-        EngineBlox.TxAction[] memory executionActions = new EngineBlox.TxAction[](8);
+        EngineBlox.TxAction[] memory executionActions = new EngineBlox.TxAction[](9);
         executionActions[0] = EngineBlox.TxAction.EXECUTE_TIME_DELAY_REQUEST;
         executionActions[1] = EngineBlox.TxAction.EXECUTE_TIME_DELAY_APPROVE;
+        executionActions[2] = EngineBlox.TxAction.EXECUTE_TIME_DELAY_CANCEL;
-        executionActions[2] = EngineBlox.TxAction.SIGN_META_APPROVE;
-        executionActions[3] = EngineBlox.TxAction.SIGN_META_CANCEL;
-        executionActions[4] = EngineBlox.TxAction.SIGN_META_REQUEST_AND_APPROVE;
-        executionActions[5] = EngineBlox.TxAction.EXECUTE_META_APPROVE;
-        executionActions[6] = EngineBlox.TxAction.EXECUTE_META_CANCEL;
-        executionActions[7] = EngineBlox.TxAction.EXECUTE_META_REQUEST_AND_APPROVE;
+        executionActions[3] = EngineBlox.TxAction.SIGN_META_APPROVE;
+        executionActions[4] = EngineBlox.TxAction.SIGN_META_CANCEL;
+        executionActions[5] = EngineBlox.TxAction.SIGN_META_REQUEST_AND_APPROVE;
+        executionActions[6] = EngineBlox.TxAction.EXECUTE_META_APPROVE;
+        executionActions[7] = EngineBlox.TxAction.EXECUTE_META_CANCEL;
+        executionActions[8] = EngineBlox.TxAction.EXECUTE_META_REQUEST_AND_APPROVE;

-        EngineBlox.TxAction[] memory ownerExecutionActions = new EngineBlox.TxAction[](5);
+        EngineBlox.TxAction[] memory ownerExecutionActions = new EngineBlox.TxAction[](6);
         ownerExecutionActions[0] = EngineBlox.TxAction.EXECUTE_TIME_DELAY_REQUEST;
         ownerExecutionActions[1] = EngineBlox.TxAction.EXECUTE_TIME_DELAY_APPROVE;
-        ownerExecutionActions[2] = EngineBlox.TxAction.SIGN_META_APPROVE;
-        ownerExecutionActions[3] = EngineBlox.TxAction.SIGN_META_CANCEL;
-        ownerExecutionActions[4] = EngineBlox.TxAction.SIGN_META_REQUEST_AND_APPROVE;
+        ownerExecutionActions[2] = EngineBlox.TxAction.EXECUTE_TIME_DELAY_CANCEL;
+        ownerExecutionActions[3] = EngineBlox.TxAction.SIGN_META_APPROVE;
+        ownerExecutionActions[4] = EngineBlox.TxAction.SIGN_META_CANCEL;
+        ownerExecutionActions[5] = EngineBlox.TxAction.SIGN_META_REQUEST_AND_APPROVE;

As per coding guidelines, "Implement role-based access control (RBAC) with per-function permissions and protected roles."

Also applies to: 157-158

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@contracts/examples/integrations/Safe/GuardianSafe/GuardianSafeDefinitions.sol`
around lines 97 - 98, The cancelTransaction(uint256) path is not authorized in
the execution-selector layer when enforceHandlerRelations is true; update the
permissions so the handler and the linked EXEC_SAFE_TX_SELECTOR agree: add
EngineBlox.TxAction.EXECUTE_TIME_DELAY_CANCEL to executionActions and to the
owner's EXEC_SAFE_TX_SELECTOR permission set (and any other owner/handler
permission mappings touched by enforceHandlerRelations) so cancelTransaction is
authorized end-to-end when enforceHandlerRelations and isProtected are enabled.

Comment on lines +171 to +188
const allPassed = this.results.failed === 0;

if (allPassed) {
console.log('\n🎉 All tests passed!');
process.exit(0);
} else {
console.log('\n⚠️ Some tests failed. Please review the output above.');
process.exit(1);
}

// Post-sanity system state summary (only when all tests passed)
try {
await this.printPostSanitySystemStateSummary(testsToRun);
} catch (e: any) {
console.warn(
`\n⚠️ Post-sanity system state summary failed: ${e?.message ?? String(e)}`
);
}

process.exit(allPassed ? 0 : 1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

The post-sanity state dump still runs after failures.

The comment says this should run only on an all-green pass, but the call is unconditional. Failed runs will still print chain state, which is usually stale or partially-mutated and makes the failure summary noisier.

💡 Suggested fix
-    // Post-sanity system state summary (only when all tests passed)
-    try {
-      await this.printPostSanitySystemStateSummary(testsToRun);
-    } catch (e: any) {
-      console.warn(
-        `\n⚠️  Post-sanity system state summary failed: ${e?.message ?? String(e)}`
-      );
+    if (allPassed) {
+      try {
+        await this.printPostSanitySystemStateSummary(testsToRun);
+      } catch (e: any) {
+        console.warn(
+          `\n⚠️  Post-sanity system state summary failed: ${e?.message ?? String(e)}`
+        );
+      }
     }
 
     process.exit(allPassed ? 0 : 1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity-sdk/run-all-tests.ts` around lines 171 - 188, The post-sanity
system state summary is being executed unconditionally; guard the call to
printPostSanitySystemStateSummary so it only runs when allPassed is true (i.e.,
the all-green case). Move the try/catch that calls
this.printPostSanitySystemStateSummary(testsToRun) inside the if (allPassed)
branch (or wrap it with if (allPassed) { ... }) and keep the existing
process.exit(allPassed ? 0 : 1) behavior unchanged so failures do not trigger
the post-sanity dump.

Comment on lines +1144 to +1148
{
"internalType": "bool",
"name": "enforceHandlerRelations",
"type": "bool"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This is a breaking ABI reorder.

Adding the new field in the middle of EngineBlox.FunctionSchema changes the tuple indexes returned by getFunctionSchema(). Any SDK consumer relying on positional decoding now gets a different layout, so this should either be appended at the end of the struct or released as an explicitly breaking ABI change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/typescript/abi/BaseStateMachine.abi.json` around lines 1144 - 1148, The
ABI change inserted the new boolean field "enforceHandlerRelations" into the
middle of EngineBlox.FunctionSchema, which shifts tuple indexes returned by
getFunctionSchema(); to fix this, move the "enforceHandlerRelations" entry to
the end of the EngineBlox.FunctionSchema struct in BaseStateMachine.abi.json so
existing positional decoding remains stable, or if intentional, mark and release
the ABI as a breaking change and document that getFunctionSchema() tuple indexes
have changed.

Comment on lines +861 to +865
{
"internalType": "bool",
"name": "enforceHandlerRelations",
"type": "bool"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This is a breaking ABI reorder.

EngineBlox.FunctionSchema is a public tuple shape here, and inserting enforceHandlerRelations before isProtected shifts every later index. That breaks existing positional decoders, so this should be treated as a breaking release or moved to the end of the struct layout.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/typescript/abi/RuntimeRBAC.abi.json` around lines 861 - 865, The new bool
field enforceHandlerRelations was inserted before isProtected in the public
tuple EngineBlox.FunctionSchema, shifting all subsequent tuple indices and
breaking positional decoders; fix by moving enforceHandlerRelations to the end
of the FunctionSchema tuple layout (append after all existing fields) so indices
for existing fields remain stable, or if you must keep the new position, treat
it as a breaking ABI change by bumping the ABI/contract version and update any
positional decoders, serializers/deserializers and tests that reference
EngineBlox.FunctionSchema or the isProtected index accordingly.

Comment on lines +72 to +76
{
"internalType": "bool",
"name": "enforceHandlerRelations",
"type": "bool"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This is a breaking ABI reorder.

This inserts enforceHandlerRelations into the middle of the returned EngineBlox.FunctionSchema tuple, which shifts the index of isProtected and everything after it. If compatibility matters for current SDK consumers, append the field at the end of the struct and regenerate the ABI instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/typescript/abi/RuntimeRBACDefinitions.abi.json` around lines 72 - 76, The
ABI change inserted "enforceHandlerRelations" into the middle of
EngineBlox.FunctionSchema and shifted subsequent tuple indexes (including
"isProtected"), which breaks compatibility; move "enforceHandlerRelations" to
the end of the FunctionSchema tuple instead of inserting it in the middle and
then regenerate the ABI (RuntimeRBACDefinitions.abi.json) so the tuple order
preserves existing indexes and consumers remain compatible.

Comment on lines +111 to +115
{
"internalType": "bool",
"name": "enforceHandlerRelations",
"type": "bool"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This is a breaking ABI reorder.

Because getFunctionSchemas() returns an array of EngineBlox.FunctionSchema, inserting the new boolean before isProtected changes the positional layout of every element. Existing decoders will mis-map fields unless this is treated as an explicit breaking change or the field is appended instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/typescript/abi/SimpleRWA20Definitions.abi.json` around lines 111 - 115,
The ABI insertion of "enforceHandlerRelations" before "isProtected" breaks
positional decoding for getFunctionSchemas() which returns an array of
EngineBlox.FunctionSchema; either move the "enforceHandlerRelations" entry to
the end of the function schema object array (append it after existing fields) to
preserve existing field positions, or treat this as an explicit breaking change
by bumping the ABI/schema version and documenting the reorder; locate the
"enforceHandlerRelations" field in SimpleRWA20Definitions.abi.json and adjust
its position (or version) so decoders relying on EngineBlox.FunctionSchema
maintain correct positional mappings.

Comment on lines +1045 to +1049
{
"internalType": "bool",
"name": "enforceHandlerRelations",
"type": "bool"
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This is a breaking ABI reorder.

The new field is inserted before isProtected, so the positional return layout of EngineBlox.FunctionSchema changes in the SDK ABI as well. Any existing client reading tuple members by index will deserialize the wrong values unless this is shipped as an explicit breaking change or the field is appended instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/typescript/abi/SimpleVault.abi.json` around lines 1045 - 1049, The ABI
change inserted the new boolean field enforceHandlerRelations before
isProtected, altering the positional tuple layout used by
EngineBlox.FunctionSchema and breaking clients that index tuple members; to fix,
move enforceHandlerRelations to the end of the return tuple (append it after
existing fields) in SimpleVault.abi.json so existing positional deserialization
remains stable, or if deliberate, mark this as an explicit breaking ABI change
in the SDK release notes and bump the ABI version referenced by
EngineBlox.FunctionSchema.

@JaCoderX JaCoderX merged commit 874b2e0 into main Mar 5, 2026
3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Mar 9, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Apr 11, 2026
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant