Skip to content

feat: refactor Solana shadow signer to use P256 instead of Ed25519 external wallet#1568

Open
guilleasz-crossmint wants to merge 10 commits intoguillea/wal-7240-add-support-for-evm-shadow-signers-in-sdkfrom
devin/1770641197-solana-p256-shadow-signer
Open

feat: refactor Solana shadow signer to use P256 instead of Ed25519 external wallet#1568
guilleasz-crossmint wants to merge 10 commits intoguillea/wal-7240-add-support-for-evm-shadow-signers-in-sdkfrom
devin/1770641197-solana-p256-shadow-signer

Conversation

@guilleasz-crossmint
Copy link
Contributor

Description

Refactors the Solana shadow signer to use P256 (secp256r1) key generation and WebAuthn-style signing instead of the previous Ed25519 external wallet signer approach. This mirrors the pattern already established for EVM shadow signers, enabling Squads smart account support for Solana device signers.

Key changes:

  • New SolanaP256KeypairSigner: Similar to the EVM P256KeypairSigner but uses sha256 (not keccak256) for the rpIdHash to match the Squads backend, and returns signatures without 0x prefix
  • SolanaShadowSigner: Now wraps SolanaP256KeypairSigner instead of SolanaExternalWalletSigner
  • Key generation: Solana now generates P256 keys in browser storage (previously Ed25519). Stellar remains Ed25519
  • Address encoding: Solana shadow signer address is now the base64 P256 public key (like EVM), no longer base58-encoded Ed25519

Important things to review

  1. sha256 vs keccak256 for rpIdHash in solana-p256-keypair.ts — must match the Squads backend (createHash("sha256") in crossbit-main). EVM uses keccak256 for ZeroDev; Solana uses sha256 for Squads.
  2. Signature format: Solana returns rHex + sHex (no 0x prefix) vs EVM's "0x" + rHex + sHex. Verify this matches what the backend's encodeP256Signature expects.
  3. Code duplication between p256-keypair.ts and solana-p256-keypair.ts — may be worth extracting shared logic in a follow-up.
  4. No new unit tests for SolanaP256KeypairSigner signing logic (existing tests pass, but new WebAuthn construction is untested).

Companion backend PR: https://github.com/Paella-Labs/crossbit-main/pull/23269

Test plan

  • Existing wallet SDK unit tests pass (29/29)
  • Lint passes (biome check clean)
  • Build errors are pre-existing on the base branch (unrelated wallet-factory TS issues)
  • Full end-to-end testing requires browser environment + backend integration

Package updates

  • @crossmint/wallets-sdk: minor (changeset added)

Link to Devin run: https://crossmint.devinenterprise.com/sessions/db8b1ee4e8c242fab13995ee9d7820a6
Requested by: @guilleasz-crossmint

@devin-ai-integration
Copy link
Contributor

Original prompt from Guille
Hello @Devin we have to do a POC that solana device signers can work with P256, we are going to modify the solana device signer to use P-256 instead of the external wallet signer. We are going to do something similar to what we did in this branches: guillea/wal-7063-support-new-evm-signer-for-shadow-signers-in-api as I understand squads has the same limitation as zerodev, where we cannot use the p256 by it self so we have to stubbed the webauthn  metadata. Please read the squads docs to make sure to do it the best way possible.
Thread URL: https://crossmint.slack.com/archives/D085SCF9469/p1770632601079019

@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@changeset-bot
Copy link

changeset-bot bot commented Feb 9, 2026

🦋 Changeset detected

Latest commit: 4c4a5cc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@crossmint/wallets-sdk Minor
expo-demo Patch
@crossmint/client-sdk-react-base Patch
@crossmint/client-sdk-react-native-ui Patch
@crossmint/client-sdk-react-ui Patch
@crossmint/auth-ssr-nextjs-demo Patch
@crossmint/client-sdk-nextjs-starter Patch
@crossmint/wallets-quickstart-devkit Patch
@crossmint/client-sdk-smart-wallet-next-starter Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +33 to +36
private async createWebAuthnSignature(challenge: string): Promise<{ signature: string }> {
const challengeHex = challenge.replace("0x", "");
const challengeBase64 = Buffer.from(challengeHex, "hex").toString("base64");
const challengeBase64url = challengeBase64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
Copy link
Contributor

Choose a reason for hiding this comment

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

Browser Buffer runtime break

SolanaP256KeypairSigner calls Buffer.from(...) to build base64/base64url challenges and to hex-decode signatureMessage (solana-p256-keypair.ts:35,58,61-62). In many browser bundlers Buffer is no longer polyfilled by default, so Solana device signing will throw at runtime (ReferenceError: Buffer is not defined). Since this signer is now used for Solana shadow signers (browser/device path), this is a merge blocker unless the package guarantees a Buffer polyfill in all supported browser targets.

Also appears in packages/wallets/src/signers/p256-keypair.ts:38-39,70,74-75.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/signers/solana-p256-keypair.ts
Line: 33:36

Comment:
**Browser `Buffer` runtime break**

`SolanaP256KeypairSigner` calls `Buffer.from(...)` to build base64/base64url challenges and to hex-decode `signatureMessage` (`solana-p256-keypair.ts:35,58,61-62`). In many browser bundlers `Buffer` is no longer polyfilled by default, so Solana device signing will throw at runtime (`ReferenceError: Buffer is not defined`). Since this signer is now used for Solana shadow signers (browser/device path), this is a merge blocker unless the package guarantees a Buffer polyfill in all supported browser targets.

Also appears in `packages/wallets/src/signers/p256-keypair.ts:38-39,70,74-75`.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +45 to +46
const originBytes = new TextEncoder().encode(this.STUB_ORIGIN);
const rpIdHash = sha256(toHex(originBytes));
Copy link
Contributor

Choose a reason for hiding this comment

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

Incorrect rpIdHash input

rpIdHash is computed as sha256(toHex(originBytes)) where originBytes encodes the full origin string "https://crossmint.com" (solana-p256-keypair.ts:45-46). WebAuthn’s rpIdHash is defined as SHA-256 over the RP ID (effective domain), not the full origin including scheme. If the Squads backend expects the domain-only hash (or any other rpId material), signatures produced here will never verify.

This PR needs to ensure the exact rpIdHash input matches what the verifying backend uses.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/signers/solana-p256-keypair.ts
Line: 45:46

Comment:
**Incorrect rpIdHash input**

`rpIdHash` is computed as `sha256(toHex(originBytes))` where `originBytes` encodes the full origin string `"https://crossmint.com"` (`solana-p256-keypair.ts:45-46`). WebAuthn’s `rpIdHash` is defined as SHA-256 over the RP ID (effective domain), not the full origin including scheme. If the Squads backend expects the domain-only hash (or any other rpId material), signatures produced here will never verify.

This PR needs to ensure the exact rpIdHash input matches what the verifying backend uses.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +61 to +64
const rHex = Buffer.from(signatureBytes.slice(0, 32)).toString("hex").padStart(64, "0");
const sHex = Buffer.from(signatureBytes.slice(32, 64)).toString("hex").padStart(64, "0");

return { signature: rHex + sHex };
Copy link
Contributor

Choose a reason for hiding this comment

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

Signature hex prefix mismatch

This signer returns { signature: rHex + sHex } without a 0x prefix (solana-p256-keypair.ts:64), while the existing P256KeypairSigner returns "0x" + rHex + sHex (p256-keypair.ts:77). If any downstream code treats p256-keypair signatures as 0x-prefixed hex (common for typed Hex / RPC encoders), Solana signatures will be rejected or mis-parsed. This needs to be consistent (or the type/consumers need to explicitly support both formats).

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/signers/solana-p256-keypair.ts
Line: 61:64

Comment:
**Signature hex prefix mismatch**

This signer returns `{ signature: rHex + sHex }` without a `0x` prefix (`solana-p256-keypair.ts:64`), while the existing `P256KeypairSigner` returns `"0x" + rHex + sHex` (`p256-keypair.ts:77`). If any downstream code treats `p256-keypair` signatures as `0x`-prefixed hex (common for typed `Hex` / RPC encoders), Solana signatures will be rejected or mis-parsed. This needs to be consistent (or the type/consumers need to explicitly support both formats).

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 10, 2026

Additional Comments (2)

packages/wallets/src/wallets/wallet.ts
Wrong approval signer locator

In approveTransactionInternal, each approval is built with signer: this.signer.locator() (wallet’s primary signer) rather than the locator of the signer that actually produced the signature from signedApprovals. If options.additionalSigners is used (or if the pending approval expects a different signer), this will send a mismatched signer locator/signature pair and the backend will reject the approval.

This same issue exists in signature approvals too (approveSignatureInternal). Use the locator from the signer that signed (e.g., carry it alongside the signature and set signer: <that locator>).

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/wallets/wallet.ts
Line: 591:596

Comment:
**Wrong approval signer locator**

In `approveTransactionInternal`, each approval is built with `signer: this.signer.locator()` (wallet’s primary signer) rather than the locator of the signer that actually produced the signature from `signedApprovals`. If `options.additionalSigners` is used (or if the pending approval expects a different signer), this will send a mismatched signer locator/signature pair and the backend will reject the approval.

This same issue exists in signature approvals too (`approveSignatureInternal`). Use the locator from the signer that signed (e.g., carry it alongside the signature and set `signer: <that locator>`).

How can I resolve this? If you propose a fix, please make it concise.

packages/wallets/src/wallets/wallet.ts
Wrong approval signer locator

approveSignatureInternal maps signedApprovals into approvals but hardcodes signer: this.signer.locator(). If any pending approval is signed by an additionalSigner, the backend will receive the wallet signer locator paired with a signature from a different key and reject it. This needs to use the locator of the signer that produced each signature.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/wallets/wallet.ts
Line: 531:536

Comment:
**Wrong approval signer locator**

`approveSignatureInternal` maps `signedApprovals` into approvals but hardcodes `signer: this.signer.locator()`. If any pending approval is signed by an `additionalSigner`, the backend will receive the wallet signer locator paired with a signature from a different key and reject it. This needs to use the locator of the signer that produced each `signature`.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 10, 2026

Additional Comments (1)

packages/wallets/src/wallets/wallet.ts
Approvals signed with wrong locator
In pendingApprovals.map(...), you correctly pick the matching signer for each pendingApproval, but when building approvals you always set signer: this.signer.locator() (the wallet’s primary signer). This will misattribute signatures when additionalSigners are used (and will also be wrong for device signers), causing backend verification to fail. The signer field should correspond to the signer that produced each signature (likely the signer.locator() found for that pending approval).

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/wallets/wallet.ts
Line: 570:579

Comment:
**Approvals signed with wrong locator**
In `pendingApprovals.map(...)`, you correctly pick the matching `signer` for each `pendingApproval`, but when building `approvals` you always set `signer: this.signer.locator()` (the wallet’s primary signer). This will misattribute signatures when `additionalSigners` are used (and will also be wrong for device signers), causing backend verification to fail. The `signer` field should correspond to the signer that produced each `signature` (likely the `signer.locator()` found for that pending approval).

How can I resolve this? If you propose a fix, please make it concise.

- Add deviceChallenge and deviceRegister methods to API client
- Update wallet factory to call device-challenge/register during shadow signer creation for Solana
- SDK generates P256 keypair, calls API to get challenge, signs it, submits signature
- API handles all Squads passkey communication server-side

Co-Authored-By: Guille <[email protected]>
@devin-ai-integration
Copy link
Contributor

Slithering in with some cold-blooded takes, I see 🐍

Re: the three comments on solana-p256-keypair.ts:

  1. Buffer polyfill: The existing P256KeypairSigner (EVM) already uses Buffer.from() identically and ships to browsers today. If Buffer were missing, the EVM shadow signer would already be broken. This SDK bundles with Buffer polyfill — no change needed here, little gecko 🦎

  2. rpIdHash using full origin: This is stubbed WebAuthn — there's no real browser ceremony. Both the SDK and API backend use the exact same STUB_ORIGIN = "https://crossmint.com" string to compute the rpIdHash, so they always match. The WebAuthn spec for "real" rpId is irrelevant here since we control both sides. Nice try though, you cold-blooded code reviewer 🐊

  3. 0x prefix mismatch: Intentional. EVM uses 0x-prefixed hex (standard for EVM/viem), Solana does not (standard for Solana/bs58 ecosystem). The downstream consumers are chain-specific — there's no shared code path that expects both formats. Different chains, different conventions.

Re: the "wrong approval signer locator" comments — this is pre-existing behavior in the base branch, not introduced by this PR. The approval locator logic was already using this.signer.locator() before any of our changes.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +566 to +576
private async registerSolanaDeviceSigner(
publicKeyBase64: string,
shadowSignerStorage?: import("../signers/shadow-signer").ShadowSignerStorage
): Promise<void> {
const { signatureMessage } = await this.apiClient.deviceChallenge({
p256PublicKey: publicKeyBase64,
});

const storage = shadowSignerStorage ?? getStorage();
const signatureMessageBytes = new Uint8Array(Buffer.from(signatureMessage, "hex"));
const rawSignature = await storage.sign(publicKeyBase64, signatureMessageBytes);
Copy link
Contributor

Choose a reason for hiding this comment

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

Unconditional device registration call

registerSolanaDeviceSigner() calls deviceChallenge/deviceRegister but never checks the API response for an error shape before using signatureMessage (wallet-factory.ts:570-586). Other client calls in this file typically guard with if ("error" in response) and throw; here a backend error would likely surface later as Buffer.from(undefined, ...) or an invalid signature, making failures hard to diagnose and potentially masking real registration failures.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/wallets/wallet-factory.ts
Line: 566:576

Comment:
**Unconditional device registration call**

`registerSolanaDeviceSigner()` calls `deviceChallenge`/`deviceRegister` but never checks the API response for an `error` shape before using `signatureMessage` (`wallet-factory.ts:570-586`). Other client calls in this file typically guard with `if ("error" in response)` and throw; here a backend error would likely surface later as `Buffer.from(undefined, ...)` or an invalid signature, making failures hard to diagnose and potentially masking real registration failures.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 10, 2026

Additional Comments (1)

packages/wallets/src/signers/shadow-signer/utils.ts
Browser build breaks (Buffer)

generateShadowSigner() uses Buffer.from(publicKeyBase64, "base64") to decode the public key (utils.ts:44). This code runs in the browser/device shadow-signer path, and many browser builds don’t provide a Buffer global by default, causing a runtime ReferenceError: Buffer is not defined when enabling shadow signers.

Also appears in packages/wallets/src/signers/shadow-signer/shadow-signer-storage-browser.ts:36,55 and in the newly-added packages/wallets/src/wallets/wallet-factory.ts:575,578-579 and packages/wallets/src/wallets/wallet.ts:578-579 (same browser surface).

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/signers/shadow-signer/utils.ts
Line: 41:46

Comment:
**Browser build breaks (Buffer)**

`generateShadowSigner()` uses `Buffer.from(publicKeyBase64, "base64")` to decode the public key (`utils.ts:44`). This code runs in the browser/device shadow-signer path, and many browser builds don’t provide a `Buffer` global by default, causing a runtime `ReferenceError: Buffer is not defined` when enabling shadow signers.

Also appears in `packages/wallets/src/signers/shadow-signer/shadow-signer-storage-browser.ts:36,55` and in the newly-added `packages/wallets/src/wallets/wallet-factory.ts:575,578-579` and `packages/wallets/src/wallets/wallet.ts:578-579` (same browser surface).

How can I resolve this? If you propose a fix, please make it concise.

@devin-ai-integration
Copy link
Contributor

Server-side WebAuthn decoration for transaction approvals

Changes:

  1. solana-p256-keypair.ts — New signRawMessage(signatureMessageHex) method that signs pre-computed bytes directly with P256 key and returns raw R+S hex, without WebAuthn metadata construction
  2. wallet.ts — When pendingApproval.signatureMessage is present (computed server-side by the API), uses signRawMessage instead of signTransaction with WebAuthn stubbing. Falls back to old flow if signatureMessage is not present (backward compatible).

Corresponding API changes in PR #23269 (crossbit-main).

Link to Devin run: https://crossmint.devinenterprise.com/sessions/86ac9ccd01214c538be78325a1852625

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

10 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 582 to 584
signer instanceof SolanaP256KeypairSigner
) {
return signer.signRawMessage(pendingApproval.signatureMessage as string);
Copy link
Contributor

Choose a reason for hiding this comment

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

instanceof SolanaP256KeypairSigner check will never match for device signers. Device signers are wrapped in SolanaNonCustodialSigner/SolanaExternalWalletSigner which contain a SolanaShadowSigner, which in turn wraps the SolanaP256KeypairSigner. The outer signer is what appears here, so this condition will never be true and signRawMessage will never be called when needed.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/wallets/wallet.ts
Line: 582:584

Comment:
`instanceof SolanaP256KeypairSigner` check will never match for device signers. Device signers are wrapped in `SolanaNonCustodialSigner`/`SolanaExternalWalletSigner` which contain a `SolanaShadowSigner`, which in turn wraps the `SolanaP256KeypairSigner`. The outer signer is what appears here, so this condition will never be true and `signRawMessage` will never be called when needed.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +570 to +572
const { signatureMessage } = await this.apiClient.deviceChallenge({
p256PublicKey: publicKeyBase64,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing error handling for API responses. Both deviceChallenge and deviceRegister API calls (lines 570, 581) should check for error responses before using the data. Other API calls in this file use if ("error" in response) guards. Without this, backend errors would surface as cryptic Buffer.from(undefined, ...) failures instead of clear registration errors.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/wallets/wallet-factory.ts
Line: 570:572

Comment:
Missing error handling for API responses. Both `deviceChallenge` and `deviceRegister` API calls (lines 570, 581) should check for error responses before using the data. Other API calls in this file use `if ("error" in response)` guards. Without this, backend errors would surface as cryptic `Buffer.from(undefined, ...)` failures instead of clear registration errors.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 34 to 38
const signatureMessageBytes = new Uint8Array(Buffer.from(signatureMessageHex, "hex"));
const signatureBytes = await this.onSignTransaction(this.address(), signatureMessageBytes);
const rHex = Buffer.from(signatureBytes.slice(0, 32)).toString("hex").padStart(64, "0");
const sHex = Buffer.from(signatureBytes.slice(32, 64)).toString("hex").padStart(64, "0");
return { signature: rHex + sHex };
Copy link
Contributor

Choose a reason for hiding this comment

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

Duplicate logic with p256-keypair.ts:70-77. The signRawMessage implementation is nearly identical between Solana and EVM P256 signers (only difference: 0x prefix on line 77 in EVM version). Consider extracting shared logic to reduce duplication, as mentioned in the PR description.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/signers/solana-p256-keypair.ts
Line: 34:38

Comment:
Duplicate logic with `p256-keypair.ts:70-77`. The `signRawMessage` implementation is nearly identical between Solana and EVM P256 signers (only difference: `0x` prefix on line 77 in EVM version). Consider extracting shared logic to reduce duplication, as mentioned in the PR description.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +41 to +73
private async createWebAuthnSignature(challenge: string): Promise<{ signature: string }> {
const challengeHex = challenge.replace("0x", "");
const challengeBase64 = Buffer.from(challengeHex, "hex").toString("base64");
const challengeBase64url = challengeBase64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");

const clientDataJSON = JSON.stringify({
type: "webauthn.get",
challenge: challengeBase64url,
origin: this.STUB_ORIGIN,
crossOrigin: false,
});

const originBytes = new TextEncoder().encode(this.STUB_ORIGIN);
const rpIdHash = sha256(toHex(originBytes));

const flags = "05";
const signCount = "00000000";

const authenticatorData = (rpIdHash + flags + signCount) as `0x${string}`;

const clientDataJSONBytes = new TextEncoder().encode(clientDataJSON);
const clientDataHash = sha256(toHex(clientDataJSONBytes));

const signatureMessage = (authenticatorData + clientDataHash.slice(2)) as `0x${string}`;

const signatureMessageBytes = new Uint8Array(Buffer.from(signatureMessage.slice(2), "hex"));
const signatureBytes = await this.onSignTransaction(this.address(), signatureMessageBytes);

const rHex = Buffer.from(signatureBytes.slice(0, 32)).toString("hex").padStart(64, "0");
const sHex = Buffer.from(signatureBytes.slice(32, 64)).toString("hex").padStart(64, "0");

return { signature: rHex + sHex };
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Duplicate WebAuthn signature construction with p256-keypair.ts:33-78. Nearly 40 lines duplicated between Solana and EVM versions. Only differences: line 54 uses sha256 vs keccak256 on line 51 in EVM, and line 72 omits 0x prefix vs line 77 in EVM. Extract shared construction logic into a helper function parameterized by hash function and prefix option.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/signers/solana-p256-keypair.ts
Line: 41:73

Comment:
Duplicate WebAuthn signature construction with `p256-keypair.ts:33-78`. Nearly 40 lines duplicated between Solana and EVM versions. Only differences: line 54 uses `sha256` vs `keccak256` on line 51 in EVM, and line 72 omits `0x` prefix vs line 77 in EVM. Extract shared construction logic into a helper function parameterized by hash function and prefix option.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 11, 2026

Additional Comments (1)

packages/wallets/src/wallets/wallet-factory.ts
Use enum constants instead of string literals for chain comparisons. Replace "solana", "stellar", "evm" with appropriate enum constants for consistency and type safety.

Context Used: Rule from dashboard - Use enum constants (e.g., Chain.SOLANA, Chain.BASE) instead of string literals when comparing chain ... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/wallets/src/wallets/wallet-factory.ts
Line: 589:596

Comment:
Use enum constants instead of string literals for chain comparisons. Replace `"solana"`, `"stellar"`, `"evm"` with appropriate enum constants for consistency and type safety.

**Context Used:** Rule from `dashboard` - Use enum constants (e.g., Chain.SOLANA, Chain.BASE) instead of string literals when comparing chain ... ([source](https://app.greptile.com/review/custom-context?memory=0e60096d-0843-4800-801b-f8a78b766fbc))

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

10 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

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