Skip to content

fix/add-authorization-param-delegatesEoa-userOp - #194

Merged
IAmKio merged 1 commit into
masterfrom
fix/add-authorization-param-delegatesEoa-userOp
Oct 29, 2025
Merged

fix/add-authorization-param-delegatesEoa-userOp#194
IAmKio merged 1 commit into
masterfrom
fix/add-authorization-param-delegatesEoa-userOp

Conversation

@RanaBug

@RanaBug RanaBug commented Oct 29, 2025

Copy link
Copy Markdown
Collaborator

Description

  • Added authorization param to estimate, send, estimateBatches and sendBatches

How Has This Been Tested?

  • Unit tests and Manual Testing

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Summary by CodeRabbit

  • New Features

    • Added optional authorization parameter to transaction methods in delegatedEoa mode for atomic delegation-and-execution capabilities.
  • Documentation

    • Added comprehensive guide and examples for using the authorization parameter, including single transaction and batch operation scenarios.

@RanaBug
RanaBug requested a review from IAmKio October 29, 2025 13:45
@RanaBug RanaBug self-assigned this Oct 29, 2025
@coderabbitai

coderabbitai Bot commented Oct 29, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The PR adds optional authorization parameter support to transaction methods (estimate, send, estimateBatches, sendBatches) in delegatedEoa mode, enabling atomic delegation-and-execution flows via EIP-7702 authorization. Includes Kernel v3.3 validation, per-chain batch handling, comprehensive documentation, tests, and UI examples.

Changes

Cohort / File(s) Summary
Version & Documentation
CHANGELOG.md, package.json, README.md
Version bumped to 2.1.1. Added changelog entry documenting authorization parameter. Extended README with "Using the authorization parameter" section containing single and batch transaction examples, usage notes, and return value documentation.
Type Definitions
lib/interfaces/index.ts
Added optional authorization: SignAuthorizationReturnType field to four parameter interfaces: EstimateSingleTransactionParams, SendSingleTransactionParams, EstimateBatchesParams, and SendBatchesParams.
Core Implementation
lib/TransactionKit.ts
Introduced private validateKernelAuthorization() helper for Kernel v3.3 validation. Added authorization parameter to estimate(), send(), estimateBatches(), and sendBatches() methods with conditional propagation only in delegatedEoa mode. Implemented per-chain authorization validation in batch flows, error handling for modular mode, and detailed logging.
Tests
__tests__/EtherspotTransactionKit.test.ts
Added authorization parameter handling tests covering valid/invalid authorization, chainId mismatches, Kernel v3.3 validation, modular vs delegatedEoa modes, multi-chain batches, and per-chain authorization propagation. Replaced mocked constants with real imports from @zerodev/sdk/constants.
Example UI
example/src/App.tsx
Added comprehensive Authorization Tests panel for delegatedEoa mode with multiple test scenarios: with/without authorization, batch operations (same/mixed chains), and modular mode edge case. New button grid with descriptive logging.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Authorization validation logic: Kernel v3.3 address matching must be carefully verified across all methods
  • Batch per-chain handling: Complex logic for applying authorization selectively per chain in multi-chain scenarios; cross-chain leakage prevention needs validation
  • Error paths: Ensure modular mode correctly rejects authorization early; chainId mismatch and validation error propagation
  • Test coverage: Verify all edge cases including mixed-chain batches, authorization type compatibility, and error scenarios

Suggested reviewers

  • IAmKio

Poem

🐰 With authorization's gentle touch,
Delegation flows without a crutch—
Per-chain we validate with care,
EIP-7702 in the air!
Atomic dreams now come so true, 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "fix/add-authorization-param-delegatesEoa-userOp" is clearly and specifically related to the main changes in the changeset. The title directly references the authorization parameter addition to delegatedEoa mode, which aligns with the primary focus of the PR as evidenced by the raw summary showing authorization parameter additions to estimate(), send(), estimateBatches(), and sendBatches() methods. While the format resembles a branch name rather than a traditional prose-based title, the content is specific, readable, and clearly conveys the core change rather than being vague or generic.
Description Check ✅ Passed The PR description includes all required template sections: a Description section identifying the authorization parameter additions, a "How Has This Been Tested?" section indicating unit and manual testing, and the Types of changes checkboxes with appropriate selections (Bug fix and New feature marked). While the description content is brief and could benefit from additional context about the feature's purpose or scope, it is not largely incomplete or off-topic. All required structural elements are present, and the content directly relates to the changes documented in the raw summary.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/add-authorization-param-delegatesEoa-userOp

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🧹 Nitpick comments (10)
lib/TransactionKit.ts (2)

2518-2556: Consider moving Kernel validation after chainId check.

The current code validates Kernel authorization for every chain group, even if the authorization won't be used for that chain (due to chainId mismatch). This could cause confusing error messages.

Consider validating Kernel authorization only when it will actually be used:

-            // Validate authorization matches Kernel v3.3 implementation if provided
-            // Note: Authorization will only be used for chain groups where chainId matches
-            // (enforced by the conditional `authorization && !isDesignated` when calling bundler)
-            if (authorization) {
+            // Only use authorization if chain IDs match (for multi-chain batch compatibility)
+            const authChainId = authorization?.chainId;
+            const shouldUseAuthorization =
+              authorization &&
+              !isDesignated &&
+              authChainId !== undefined &&
+              authChainId === groupChainId;
+
+            // Validate authorization matches Kernel v3.3 implementation if it will be used
+            if (shouldUseAuthorization) {
               const isValidAuthorization = this.validateKernelAuthorization(
                 authorization,
                 groupChainId
               );
               if (!isValidAuthorization) {
                 const expectedKernelAddress = KernelVersionToAddressesMap[
                   KERNEL_V3_3
                 ].accountImplementationAddress as `0x${string}`;
                 const errorMessage =
                   `Invalid authorization: The provided authorization does not match Kernel v3.3 implementation. ` +
                   `Only Kernel v3.3 (${expectedKernelAddress}) authorizations are supported. ` +
                   `Please use delegateSmartAccountToEoa() to get a valid Kernel authorization.`;
                 groupTxs.forEach((tx) => {
                   const resultObj = {
                     to: tx.to || '',
                     value: tx.value?.toString(),
                     data: tx.data,
                     chainId: tx.chainId || groupChainId,
                     errorMessage,
                     errorType: 'VALIDATION_ERROR' as const,
                     isEstimatedSuccessfully: false,
                   };
                   groupEstimated.push(resultObj);
                   estimatedTransactions.push(resultObj);
                 });

                 chainGroups[groupChainId] = {
                   transactions: groupEstimated,
                   errorMessage,
                   isEstimatedSuccessfully: false,
                 };
                 batchAllGroupsSuccessful = false;
                 continue;
               }
             }

             // Log if using authorization for delegation
-            if (authorization && !isDesignated) {
+            if (shouldUseAuthorization) {
               log(
                 `estimateBatches(): Using provided authorization to delegate EOA during batch estimation (chain ${groupChainId})`,
                 undefined,
                 this.debugMode
               );
             }

             // ... (gas estimation code)
-            // Only use authorization if chain IDs match (for multi-chain batch compatibility)
-            // Require authChainId to be defined and match groupChainId for security
-            const authChainId = authorization?.chainId;
-            const shouldUseAuthorization =
-              authorization &&
-              !isDesignated &&
-              authChainId !== undefined &&
-              authChainId === groupChainId;
             const gasEstimate = await bundlerClient.estimateUserOperationGas({
               account: delegatedEoaAccount,
               calls,
               ...(shouldUseAuthorization ? { authorization } : {}),
             });

This reduces duplication and makes the logic clearer by checking shouldUseAuthorization once.


3356-3396: Consider moving Kernel validation after chainId check (same as estimateBatches).

Same issue as in estimateBatches - the Kernel validation runs for all chain groups even when the authorization won't be used for that chain due to chainId mismatch.

Apply the same refactoring suggested for estimateBatches to avoid redundant validation and confusing error messages for multi-chain batches.

README.md (1)

294-369: Good addition; add a brief security caveat and scope reminder.

  • Add a one‑liner: “Treat authorization like a signature; do not log or persist it in client apps.”
  • Consider amending the heading to “Using the authorization parameter (delegatedEoa mode only)” to reduce misuse.
example/src/App.tsx (3)

667-674: Stop using as any; types now accept authorization.

Interfaces include authorization?: SignAuthorizationReturnType, so the casts are unnecessary.

- const result = await named.estimate({ authorization: auth } as any);
+ const result = await named.estimate({ authorization: auth });

- const result = await named.send({ authorization: auth } as any);
+ const result = await named.send({ authorization: auth });

- const result = await kit.estimateBatches({ onlyBatchNames: ['sameAuthEstimate'], authorization: auth } as any);
+ const result = await kit.estimateBatches({ onlyBatchNames: ['sameAuthEstimate'], authorization: auth });

- const result = await kit.sendBatches({ onlyBatchNames: ['sameAuthSend'], authorization: auth } as any);
+ const result = await kit.sendBatches({ onlyBatchNames: ['sameAuthSend'], authorization: auth });

- const result = await named.estimate({ authorization: auth } as any);
+ const result = await named.estimate({ authorization: auth });

Also applies to: 742-747, 812-816, 974-979, 1133-1141


395-404: Avoid logging full authorization material.

Authorization contains signature parts (r/s/yParity). Log only non‑sensitive fields.

- logAndUpdateState(
-   `📝 Authorization: ${JSON.stringify(result.authorization, bigIntReplacer)}`
- );
+ const safeAuth = result.authorization
+   ? { address: result.authorization.address, chainId: result.authorization.chainId, nonce: result.authorization.nonce }
+   : undefined;
+ logAndUpdateState(`📝 Authorization (redacted): ${JSON.stringify(safeAuth)}`);

And when logging after signing:

- `📝 Authorization signed: address=${auth.address}, chainId=${auth.chainId}, nonce=${auth.nonce}`
+ `📝 Authorization signed: address=${auth.address}, chainId=${auth.chainId}, nonce=${auth.nonce}`
+ // Do not log r/s/yParity

Also applies to: 473-481, 650-653


1111-1144: Clarify modular‑mode failure handling in UI button.

This scenario label says “Should Fail”. Depending on core impl, it may either return a VALIDATION_ERROR result or throw. Wrap in try/catch and log both paths.

- const result = await named.estimate({ authorization: auth });
- logAndUpdateState(`✅ Modular estimate with auth (validation error expected): ${JSON.stringify(result, bigIntReplacer)}`);
+ try {
+   const result = await named.estimate({ authorization: auth });
+   logAndUpdateState(`ℹ️ Modular estimate with auth: ${JSON.stringify(result, bigIntReplacer)}`);
+ } catch (err) {
+   logAndUpdateState(`✅ Expected failure: ${(err as Error).message}`);
+ }
lib/interfaces/index.ts (1)

231-232: Type additions look correct; add a short note indicating delegatedEoa‑only.

Consider JSDoc on these fields to state they are only supported in delegatedEoa mode and must match the transaction chainId and Kernel v3.3.

- authorization?: SignAuthorizationReturnType;
+ /** delegatedEoa mode only; must match tx chainId and Kernel v3.3 */
+ authorization?: SignAuthorizationReturnType;

Also applies to: 237-238, 344-344, 349-350

__tests__/EtherspotTransactionKit.test.ts (3)

2425-2466: Mirror viem’s authorization shape in tests (optional).

Your test fixtures include v in addition to r/s/yParity. To better reflect SignAuthorizationReturnType, you can drop v and keep r/s/yParity. Not a blocker.

Also applies to: 2651-2671, 2822-2834, 3014-3026


2913-3011: Add a sendBatches multi‑chain + authorization test for symmetry.

You covered multi‑chain in estimateBatches; consider mirroring it for sendBatches to assert selective application of authorization per chain group.


2895-2911: Unify error semantics for modular-mode + authorization.

Single-tx methods (estimate/send at lines 2561, 2732) return structured error result objects with errorType: 'VALIDATION_ERROR', while batch methods (estimateBatches/sendBatches at lines 2894, 3101) throw errors via throwError(). For API consistency and predictable developer experience, adopt one approach across all four methods (estimate, send, estimateBatches, sendBatches)—either consistently return structured errors or consistently throw.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c9476a and 923aea8.

⛔ Files ignored due to path filters (2)
  • example/package-lock.json is excluded by !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • CHANGELOG.md (1 hunks)
  • README.md (2 hunks)
  • __tests__/EtherspotTransactionKit.test.ts (4 hunks)
  • example/src/App.tsx (2 hunks)
  • lib/TransactionKit.ts (28 hunks)
  • lib/interfaces/index.ts (2 hunks)
  • package.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
example/src/App.tsx (1)
lib/interfaces/index.ts (1)
  • INamedTransaction (125-145)
lib/TransactionKit.ts (1)
lib/utils/index.ts (1)
  • log (70-75)
🔇 Additional comments (6)
lib/TransactionKit.ts (3)

290-351: LGTM: Solid authorization validation logic.

The validation logic correctly:

  • Enforces delegatedEoa mode only
  • Validates the authorization address matches Kernel v3.3 implementation
  • Uses case-insensitive comparison for addresses
  • Provides detailed logging for debugging
  • Returns false for missing or invalid authorization without throwing

1332-1334: LGTM: Correct conditional authorization usage.

The conditional spread ...(authorization && !isDelegateSmartAccountToEoaDelegated ? { authorization } : {}) correctly applies authorization only when:

  1. Authorization is provided
  2. EOA is not yet designated

This enables atomic delegation during estimation while avoiding redundant authorization for already-designated EOAs.


2577-2589: Excellent multi-chain authorization safety.

The shouldUseAuthorization logic provides strong defense-in-depth for multi-chain batches:

const authChainId = authorization?.chainId;
const shouldUseAuthorization =
  authorization &&
  !isDesignated &&
  authChainId !== undefined &&
  authChainId === groupChainId;

This prevents authorization leakage across chains by requiring:

  1. Authorization exists
  2. EOA not already designated
  3. Authorization has explicit chainId
  4. ChainId exactly matches current chain group

This is a solid security pattern that prevents accidental cross-chain authorization application.

CHANGELOG.md (1)

3-8: Changelog entry reads well and matches the intent.

No issues from my side.

__tests__/EtherspotTransactionKit.test.ts (1)

2-5: Switch to real Kernel constants is a win.

Removes brittle stubs and validates against real addresses. LGTM.

package.json (1)

4-4: Version bump verified; ready for release.

package.json (2.1.1) and CHANGELOG.md (2.1.1 - 2025-01-27) are in sync. Create git tag v2.1.1 during the release workflow.

Comment thread lib/TransactionKit.ts
Comment on lines +1271 to +1284
if (authorization && transactionChainId) {
// Validate authorization chain ID matches transaction chain ID
const authChainId = authorization?.chainId;
if (
authChainId !== undefined &&
authChainId !== transactionChainId
) {
return setErrorAndReturn(
`Invalid authorization: Authorization chain ID (${authChainId}) does not match transaction chain ID (${transactionChainId}). ` +
`Please use an authorization signed for chain ${transactionChainId}.`,
'VALIDATION_ERROR',
{}
);
}

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

Validate authorization chainId more strictly.

The current validation allows authChainId to be undefined, which means an authorization without a chainId could be accepted. This is potentially unsafe as the authorization might be intended for a different chain.

Consider requiring authChainId to be defined:

           // Validate authorization chain ID matches transaction chain ID
           const authChainId = authorization?.chainId;
           if (
-            authChainId !== undefined &&
-            authChainId !== transactionChainId
+            authChainId === undefined
           ) {
             return setErrorAndReturn(
+              `Invalid authorization: Authorization is missing chainId. ` +
+                `Authorization must include a chainId to ensure it's intended for chain ${transactionChainId}.`,
+              'VALIDATION_ERROR',
+              {}
+            );
+          }
+          if (authChainId !== transactionChainId) {
+            return setErrorAndReturn(
               `Invalid authorization: Authorization chain ID (${authChainId}) does not match transaction chain ID (${transactionChainId}). ` +
                 `Please use an authorization signed for chain ${transactionChainId}.`,
               'VALIDATION_ERROR',
               {}
             );
           }
🤖 Prompt for AI Agents
In lib/TransactionKit.ts around lines 1271 to 1284, the authorization chain ID
check currently allows undefined authChainId which could accept authorizations
lacking a chainId; change the validation to require authChainId to be defined
and to equal the transactionChainId (i.e. treat undefined as invalid), and
update the error message to reflect both cases (missing chainId or mismatched
chainId) so the function returns a VALIDATION_ERROR when authChainId is
undefined or does not equal transactionChainId.

Comment thread lib/TransactionKit.ts
Comment on lines +1808 to +1820
// Validate authorization chain ID matches transaction chain ID
const authChainId = authorization?.chainId;
if (
authChainId !== undefined &&
authChainId !== transactionChainId
) {
return setErrorAndReturn(
`Invalid authorization: Authorization chain ID (${authChainId}) does not match transaction chain ID (${transactionChainId}). ` +
`Please use an authorization signed for chain ${transactionChainId}.`,
'VALIDATION_ERROR',
{}
);
}

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

Validate authorization chainId more strictly (same issue as estimate).

Same issue as in the estimate method - the validation allows authChainId to be undefined, which could allow cross-chain authorization misuse.

Apply the same fix as suggested for the estimate method:

           // Validate authorization chain ID matches transaction chain ID
           const authChainId = authorization?.chainId;
           if (
-            authChainId !== undefined &&
-            authChainId !== transactionChainId
+            authChainId === undefined
           ) {
             return setErrorAndReturn(
+              `Invalid authorization: Authorization is missing chainId. ` +
+                `Authorization must include a chainId to ensure it's intended for chain ${transactionChainId}.`,
+              'VALIDATION_ERROR',
+              {}
+            );
+          }
+          if (authChainId !== transactionChainId) {
+            return setErrorAndReturn(
               `Invalid authorization: Authorization chain ID (${authChainId}) does not match transaction chain ID (${transactionChainId}). ` +
                 `Please use an authorization signed for chain ${transactionChainId}.`,
               'VALIDATION_ERROR',
               {}
             );
           }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Validate authorization chain ID matches transaction chain ID
const authChainId = authorization?.chainId;
if (
authChainId !== undefined &&
authChainId !== transactionChainId
) {
return setErrorAndReturn(
`Invalid authorization: Authorization chain ID (${authChainId}) does not match transaction chain ID (${transactionChainId}). ` +
`Please use an authorization signed for chain ${transactionChainId}.`,
'VALIDATION_ERROR',
{}
);
}
// Validate authorization chain ID matches transaction chain ID
const authChainId = authorization?.chainId;
if (
authChainId === undefined
) {
return setErrorAndReturn(
`Invalid authorization: Authorization is missing chainId. ` +
`Authorization must include a chainId to ensure it's intended for chain ${transactionChainId}.`,
'VALIDATION_ERROR',
{}
);
}
if (authChainId !== transactionChainId) {
return setErrorAndReturn(
`Invalid authorization: Authorization chain ID (${authChainId}) does not match transaction chain ID (${transactionChainId}). ` +
`Please use an authorization signed for chain ${transactionChainId}.`,
'VALIDATION_ERROR',
{}
);
}
🤖 Prompt for AI Agents
In lib/TransactionKit.ts around lines 1808 to 1820, the current check permits
authChainId to be undefined which can allow cross-chain authorization misuse;
change the validation to require a present authorization.chainId and reject when
it's missing or does not exactly equal transactionChainId. Concretely, treat
undefined authChainId as an error (call setErrorAndReturn with a clear message
about missing authorization chain ID) and keep the mismatch branch for non-equal
values, mirroring the stricter check implemented in the estimate method.

@IAmKio IAmKio left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@IAmKio
IAmKio merged commit 44d06f8 into master Oct 29, 2025
5 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Nov 6, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants