Skip to content

Feature/7702 remote signing#449

Merged
IAmKio merged 5 commits intostagingfrom
feature/7702-remote-signing
Nov 7, 2025
Merged

Feature/7702 remote signing#449
IAmKio merged 5 commits intostagingfrom
feature/7702-remote-signing

Conversation

@IAmKio
Copy link
Collaborator

@IAmKio IAmKio commented Nov 7, 2025

Description

  • Modified Pillar Wallet messaging interface to incorporate signAuthorization
  • All private key instantiations and storage / Pillar Wallet interfaces have been removed
  • Added tests for Pillar Wallet messaging interface
  • Added custom account for use inside Pillar Wallet with direct link to webview
  • Changed landing page to look for incoming EOA address instead of a private key flag

How Has This Been Tested?

  • Locally, on simulator and real device

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

    • Delegated custom-account signing via embedded wallet bridge.
    • EIP-7702 authorization signing support.
  • Improvements

    • Authentication flow reworked to prioritize external EOA addresses and better detect custom accounts.
    • Webview-based signing replaces persistent private-key retrieval.
  • Chores

    • Updated "@etherspot/transaction-kit" to 2.1.3.

@IAmKio IAmKio requested a review from RanaBug November 7, 2025 12:35
@IAmKio IAmKio self-assigned this Nov 7, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 7, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Refactors RN authentication from private-key retrieval to EOA-based custom accounts and signing via WebView. Adds signing helpers, BigInt-safe serialization, EIP‑7702 authorization support, wires eoaAddress/customAccount through Main → Authorized → transaction kit and verification hooks, and updates storage key usage.

Changes

Cohort / File(s) Summary
Package
package.json
Bumped @etherspot/transaction-kit dependency from 2.1.1 to 2.1.3.
Auth UI flags & logout
src/components/BottomMenu/index.tsx, src/components/BottomMenuModal/AccountModal.tsx
Replaced isPkAccount/ACCOUNT_VIA_PK with isCustomAccount/EOA_ADDRESS (hasCustomAccount guard). Adjusted logout to remove EOA_ADDRESS when applicable and updated effect dependencies/guards.
Send modal log
src/components/BottomMenuModal/SendModal/SendModalTokensTabView.tsx
Minor log text change: "Single transaction estimation error:" → "Estimated single transaction error message:".
Authorized props / kit wiring
src/containers/Authorized.tsx
Added eoaAddress?: string and customAccount?: Account props; pass eoaAddress to useWalletModeVerification; include viemLocalAccount: customAccount in Etherspot kit config; updated imports to include Account.
Main: custom account flow
src/containers/Main.tsx
Replaced private-key-based Pillar flow with EOA-driven customAccount via toAccount and WebView-delegated signing. Adds eoaAddress state, persists EOA_ADDRESS, builds customAccount, initializes WalletClient when present, and passes eoaAddress/customAccount to Authorized. Imports viem types and OUR_EIP7702_IMPLEMENTATION_ADDRESS.
Wallet verification hook
src/hooks/useWalletModeVerification.tsx
UseWalletModeVerificationProps now accepts optional eoaAddress?: string. Hook prefers provided eoaAddress over derived address, updates validation guard and effect deps, and uses resolved address for on-chain checks.
Landing page
src/pages/Landing.jsx
Removed setupPillarWalletMessaging flow. Extracts eoaAddress from URL query and saves to EOA_ADDRESS; simplified RN navigation path.
Pillar WebView messaging
src/utils/pillarWalletMessaging.ts
Replaced private-key retrieval API with signing-first API: added PillarWalletSigningRequest/PillarWalletSigningResponse, requestSigning, and helpers signMessageViaWebView, signTransactionViaWebView, signTypedDataViaWebView, signAuthorizationViaWebView. Added BigInt-safe JSON replacer, 60s timeout, response parsing (including SignedAuthorization). Removed requestPrivateKey, createWebViewMessageHandler, and setupPillarWalletMessaging.
Pillar messaging tests
src/utils/__tests__/pillarWalletMessaging.test.ts
New tests for requestSigning and signing helpers, verifying postMessage calls, response handling, BigInt stringification, error paths, and SignedAuthorization parsing.

Sequence Diagram

sequenceDiagram
    participant App as Main
    participant Auth as Authorized
    participant Hook as useWalletModeVerification
    participant Kit as Transaction Kit
    participant Sign as pillarWalletMessaging
    participant WebView as RN WebView

    App->>App: read eoaAddress (URL / localStorage)
    App->>App: toAccount(...) -> customAccount
    App->>Auth: render with eoaAddress, customAccount
    Auth->>Kit: set viemLocalAccount = customAccount
    Auth->>Hook: verify wallet mode (eoaAddress)
    Hook->>Hook: resolveEoaAddress (prefer eoaAddress)
    Hook->>Kit: on-chain checks with resolvedEoaAddress

    Note over App,WebView: signing request flow
    App->>Sign: signMessageViaWebView(payload)
    Sign->>WebView: postMessage({type: pillarXSigningRequest,...})
    WebView->>Sign: postMessage({type: pillarWalletSigningResponse, value:{result}})
    Sign->>App: resolve signature (parse BigInt, SignedAuthorization if needed)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas needing extra attention:

  • src/containers/Main.tsx — customAccount construction, WalletClient init, precedence/fallback logic, EIP‑7702 wiring.
  • src/utils/pillarWalletMessaging.ts — BigInt serialization, 60s timeout cleanup, SignedAuthorization parsing and v/yParity handling.
  • src/hooks/useWalletModeVerification.tsx — resolvedEoaAddress correctness and effect dependency behavior.
  • Migration of storage key from ACCOUNT_VIA_PKEOA_ADDRESS and interactions with logout logic.

Possibly related PRs

Suggested reviewers

  • RanaBug

Poem

🐰 I hopped from keys to signatures bright,
EOA shining in morning light,
WebView whispers a signing tune,
Custom accounts now hum in tune,
Hooray — no keys in burrow tonight! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 title 'Feature/7702 remote signing' directly reflects the main change - implementing EIP-7702 remote signing capabilities through a custom account and Pillar Wallet webview integration.
Description check ✅ Passed The PR description covers all required template sections with clear, substantive information: describes the key changes (Pillar Wallet messaging, private key removal, custom account, landing page updates), specifies testing methodology (local, simulator, real device), and indicates both new feature and breaking change types.

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.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 7, 2025

Deploying x with  Cloudflare Pages  Cloudflare Pages

Latest commit: cae5279
Status: ✅  Deploy successful!
Preview URL: https://861e64a3.x-e62.pages.dev
Branch Preview URL: https://feature-7702-remote-signing.x-e62.pages.dev

View logs

@github-actions github-actions bot temporarily deployed to Preview (feature/7702-remote-signing) November 7, 2025 13:40 Inactive
@IAmKio IAmKio marked this pull request as ready for review November 7, 2025 13:43
Copy link
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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 53fd7cd and b9ac689.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • package.json (1 hunks)
  • src/components/BottomMenu/index.tsx (2 hunks)
  • src/components/BottomMenuModal/AccountModal.tsx (3 hunks)
  • src/components/BottomMenuModal/SendModal/SendModalTokensTabView.tsx (1 hunks)
  • src/containers/Authorized.tsx (4 hunks)
  • src/containers/Main.tsx (10 hunks)
  • src/hooks/useWalletModeVerification.tsx (5 hunks)
  • src/pages/Landing.jsx (2 hunks)
  • src/utils/__tests__/pillarWalletMessaging.test.ts (1 hunks)
  • src/utils/pillarWalletMessaging.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/components/BottomMenu/index.tsx
  • src/hooks/useWalletModeVerification.tsx
  • src/containers/Authorized.tsx
  • src/containers/Main.tsx
📚 Learning: 2025-10-15T10:31:06.760Z
Learnt from: IAmKio
Repo: pillarwallet/x PR: 432
File: src/containers/Main.tsx:135-141
Timestamp: 2025-10-15T10:31:06.760Z
Learning: In src/containers/Main.tsx, the React Native webview messaging setup intentionally only activates when the `devicePlatform` URL parameter is present ('ios' or 'android'). On reloads or direct navigation without URL params, the site should behave normally and not attempt to re-establish RN messaging. This is by design to ensure the site functions properly in both RN webview and standard browser contexts.

Applied to files:

  • src/pages/Landing.jsx
  • src/containers/Main.tsx
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/containers/Authorized.tsx
  • src/containers/Main.tsx
🧬 Code graph analysis (2)
src/utils/__tests__/pillarWalletMessaging.test.ts (1)
src/utils/pillarWalletMessaging.ts (4)
  • requestSigning (88-154)
  • signMessageViaWebView (161-179)
  • signTransactionViaWebView (186-190)
  • signAuthorizationViaWebView (206-256)
src/containers/Main.tsx (4)
src/utils/pillarWalletMessaging.ts (4)
  • signMessageViaWebView (161-179)
  • signTransactionViaWebView (186-190)
  • signTypedDataViaWebView (197-199)
  • signAuthorizationViaWebView (206-256)
src/utils/eip7702Authorization.ts (1)
  • OUR_EIP7702_IMPLEMENTATION_ADDRESS (5-6)
src/apps/deposit/utils/blockchain.tsx (1)
  • getNetworkViem (71-94)
src/utils/blockchain.ts (1)
  • visibleChains (161-163)

const { isConnected: wagmiIsConnected } = useAccount();
const [provider, setProvider] = useState<WalletClient | undefined>(undefined);
const [chainId, setChainId] = useState<number | undefined>(undefined);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want to keep privateKey and pkAccount? Because those were removed from other parts of the code in this PR (BottomMenuModal)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Whoops i forgot to remove the PK stuff here, i've removed it now


const walletChainId = 1; // default chain id is 1 (mainnet)

const newProvider = createWalletClient({
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it robust enough to set the provider on http() only (so public rpc?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes i would say so as this is the default that is shipped with all Viem installations. I don't actually think, in the end, that this http() is even used as ultimately it's the bundler that will make the transactions and anything else will do read operations using public RPC endpoints which is fine.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok

});

return;
} // END if (eoaAddress)
Copy link
Collaborator

Choose a reason for hiding this comment

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

?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's just something i add in my code to know which block i'm dealing with when i reach the end here - it's just for readability

Copy link
Collaborator

Choose a reason for hiding this comment

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

Gotcha

const searchParams = new URLSearchParams(window.location.search);
const devicePlatformFromUrl = searchParams.get('devicePlatform');
const devicePlatformFromStorage = localStorage.getItem('DEVICE_PLATFORM');
const eoaAddressFromUrl = searchParams.get('eoaAddress');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just checking on this, if a user is connected on pillarX with Privy, and then decides (edge case) to put their EOA address or ANY address in the url, will that not clash at some point?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the updateProvider has a priorty order - and in this case PillarX will load with the EOA address as it's priority. They just wouldn't be able to make any transactions

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oke doke

@github-actions github-actions bot requested a deployment to Preview (feature/7702-remote-signing) November 7, 2025 15:07 Abandoned
@github-actions github-actions bot requested a deployment to Preview (feature/7702-remote-signing) November 7, 2025 15:10 Abandoned
Copy link
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: 1

🧹 Nitpick comments (2)
src/containers/Main.tsx (2)

249-286: Review type assertions in signing methods.

Lines 276 and 285 use type assertions that could hide type mismatches:

  • Line 276: transaction as TransactionSerializable
  • Line 285: typedData as unknown as TypedData (double assertion)

Consider validating types or using type guards:

 async signTransaction(transaction) {
-  return signTransactionViaWebView(
-    transaction as TransactionSerializable
-  );
+  // Validate transaction shape before passing to WebView
+  if (!transaction || typeof transaction !== 'object') {
+    throw new Error('Invalid transaction object');
+  }
+  return signTransactionViaWebView(transaction as TransactionSerializable);
 },
 async signTypedData(typedData) {
-  return signTypedDataViaWebView(typedData as unknown as TypedData);
+  // Validate typedData shape
+  if (!typedData || typeof typedData !== 'object') {
+    throw new Error('Invalid typed data');
+  }
+  return signTypedDataViaWebView(typedData as TypedData);
 },

322-346: Consider removing or documenting the commented verification code.

Lines 322-346 contain extensive commented-out signature verification logic. While the inline comment explains it's kept for potential future use, commented code can create maintenance burden and confusion.

Consider one of these approaches:

  1. Remove the commented code and rely on git history if needed later
  2. If verification is genuinely useful for debugging, extract it to a separate utility function that can be conditionally enabled via a feature flag or environment variable
  3. Add a more prominent comment explaining under what circumstances this verification should be uncommented and used
-           /**
-            * NOTE: This is commented out but is being left in
-            * just incase we need to verify the signature again
-            * in future.
-            */
-           // Optional: Verify the signature (for debugging)
-           // Serialize the signature to hex format for verification
-           // if (!signedAuthorization.v) {
-           //   throw new Error('Signature missing v value');
-           // }
-           // const vHex = signedAuthorization.v.toString(16).padStart(2, '0');
-           // ...
+           // Signature verification was removed - see git history (commit XYZ) if needed for debugging
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9ac689 and db6f68f.

📒 Files selected for processing (2)
  • package.json (1 hunks)
  • src/containers/Main.tsx (12 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/containers/Main.tsx
📚 Learning: 2025-10-15T10:31:06.760Z
Learnt from: IAmKio
Repo: pillarwallet/x PR: 432
File: src/containers/Main.tsx:135-141
Timestamp: 2025-10-15T10:31:06.760Z
Learning: In src/containers/Main.tsx, the React Native webview messaging setup intentionally only activates when the `devicePlatform` URL parameter is present ('ios' or 'android'). On reloads or direct navigation without URL params, the site should behave normally and not attempt to re-establish RN messaging. This is by design to ensure the site functions properly in both RN webview and standard browser contexts.

Applied to files:

  • src/containers/Main.tsx
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/containers/Main.tsx
🧬 Code graph analysis (1)
src/containers/Main.tsx (2)
src/utils/pillarWalletMessaging.ts (4)
  • signMessageViaWebView (161-179)
  • signTransactionViaWebView (186-190)
  • signTypedDataViaWebView (197-199)
  • signAuthorizationViaWebView (206-256)
src/utils/eip7702Authorization.ts (1)
  • OUR_EIP7702_IMPLEMENTATION_ADDRESS (5-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint
  • GitHub Check: unit-tests
🔇 Additional comments (6)
src/containers/Main.tsx (6)

12-20: LGTM: Imports align with remote signing architecture.

The new imports support the custom account flow with WebView signing delegation and EIP-7702 authorization. All imports are utilized in the custom account setup.

Also applies to: 36-41, 60-60


85-88: LGTM: State variables support the new authentication model.

The eoaAddress and customAccount states correctly replace the previous private-key-based approach, aligning with the PR's objective of remote signing.


127-204: LGTM: WebView messaging setup correctly extracts and persists eoaAddress.

The logic properly handles URL parameters, localStorage persistence, and React Native context detection. The removal of private key requests aligns with PR objectives. (Note: Address validation should be added as flagged in line 92 review.)


730-730: LGTM: Dependency array correctly includes eoaAddress.

The addition of eoaAddress to the dependency array ensures the provider is re-initialized when the EOA address changes, which is correct given that eoaAddress-based authentication takes priority over other methods.


765-766: LGTM: New props correctly wired to Authorized component.

The eoaAddress and customAccount props enable the Authorized component to leverage the custom account for transaction kit configuration and wallet mode verification.


292-320: No changes needed. Authorization parameter handling is correct.

The fallback to OUR_EIP7702_IMPLEMENTATION_ADDRESS ('0xd6CEDDe84be40893d153Be9d467CD6aD37875b28') is the correct Kernel V3.3 account implementation address for EIP-7702 delegation. The conditional parameter logic correctly ensures only one of contractAddress or address is passed to viem's AuthorizationRequest, preventing invalid state. The implementation is consistent with its usage in useWalletModeVerification.tsx where the same constant validates delegation addresses. While explicit address validation in the JavaScript layer isn't present, viem's type system and the React Native signing app provide implicit validation.

const previouslyAuthenticated = !!localStorage.getItem('privy:token');
const isAppReady = ready && !isLoadingAllowedApps;
const isAuthenticated = authenticated || wagmiIsConnected || !!pkAccount;
const isAuthenticated = authenticated || wagmiIsConnected || !!eoaAddress;
Copy link
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 eoaAddress format before considering user authenticated.

The eoaAddress (extracted from URL parameters on line 132) is treated as sufficient for authentication without format validation. A malformed address could cause issues in downstream operations.

Consider adding validation:

+import { isAddress } from 'viem';
+
 const isAuthenticated = authenticated || wagmiIsConnected || !!eoaAddress;
+
+// Validate eoaAddress format if present
+useEffect(() => {
+  if (eoaAddress && !isAddress(eoaAddress)) {
+    console.error('Invalid EOA address format:', eoaAddress);
+    localStorage.removeItem('EOA_ADDRESS');
+    setEoaAddress(undefined);
+  }
+}, [eoaAddress]);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/containers/Main.tsx around line 92 (and note eoaAddress is extracted
around line 132), the code currently treats any eoaAddress truthy value as proof
of authentication; validate the address format first and only count it as
authentication if valid. Update the logic so isAuthenticated uses a proper
address validator (e.g., ethers.utils.isAddress or a strict regex) against
eoaAddress, optionally normalizing/checksumming it before use; if validation
fails, ignore the parameter (do not set authenticated via eoaAddress) and
log/handle the invalid value where appropriate.

@IAmKio IAmKio merged commit 6f848cc into staging Nov 7, 2025
6 checks passed
@IAmKio IAmKio deleted the feature/7702-remote-signing branch November 8, 2025 10:24
@coderabbitai coderabbitai bot mentioned this pull request Nov 13, 2025
3 tasks
@coderabbitai coderabbitai bot mentioned this pull request Jan 9, 2026
3 tasks
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.

2 participants