From 94fa9a4ee22d7283a7ab53e45aabbc79399b67e1 Mon Sep 17 00:00:00 2001 From: alberto-crossmint Date: Tue, 30 Sep 2025 13:56:21 +0200 Subject: [PATCH 1/2] Fix: sign raw payloads not working in NCS --- packages/wallets/src/signers/evm-api-key.ts | 7 +++ .../src/signers/evm-external-wallet.ts | 58 ++++++++++--------- .../signers/non-custodial/ncs-evm-signer.ts | 12 +++- .../src/signers/non-custodial/ncs-signer.ts | 1 + .../non-custodial/ncs-solana-signer.ts | 21 ++++--- .../non-custodial/ncs-stellar-signer.ts | 4 ++ packages/wallets/src/signers/passkey.ts | 12 ++-- .../wallets/src/signers/solana-api-key.ts | 4 ++ .../src/signers/solana-external-wallet.ts | 4 ++ .../src/signers/stellar-external-wallet.ts | 6 +- packages/wallets/src/signers/types.ts | 3 +- packages/wallets/src/wallets/wallet.ts | 10 +++- 12 files changed, 95 insertions(+), 47 deletions(-) diff --git a/packages/wallets/src/signers/evm-api-key.ts b/packages/wallets/src/signers/evm-api-key.ts index 4d70f979f..22fdcfab2 100644 --- a/packages/wallets/src/signers/evm-api-key.ts +++ b/packages/wallets/src/signers/evm-api-key.ts @@ -9,6 +9,13 @@ export class EVMApiKeySigner implements Signer { return this.config.locator; } + async sign(){ + return await Promise.reject( + new Error( + "API key signers do not support direct transaction signing - transaction are handled automatically by the backend" + ) + ); + } async signMessage() { return await Promise.reject( new Error( diff --git a/packages/wallets/src/signers/evm-external-wallet.ts b/packages/wallets/src/signers/evm-external-wallet.ts index e44a85a76..a90816aaf 100644 --- a/packages/wallets/src/signers/evm-external-wallet.ts +++ b/packages/wallets/src/signers/evm-external-wallet.ts @@ -25,36 +25,40 @@ export class EVMExternalWalletSigner implements Signer { return this.config.locator; } - async signMessage(message: string) { + async sign(payload: string): Promise<{ signature: string }> { if (this.provider != null) { - const signature = await this.provider.request({ - method: "personal_sign", - params: [message, this._address] as any, - }); - if (signature == null) { - throw new Error( - "[EVMExternalWalletSigner] Failed to sign message: EIP1193 provider signMessage returned null" - ); - } - return { signature }; - } - if (this.viemAccount?.signMessage != null) { - const signature = await this.viemAccount.signMessage({ - message: { - raw: message as `0x${string}`, - }, - }); - if (signature == null) { - throw new Error( - "[EVMExternalWalletSigner] Failed to sign message: Viem account signMessage returned null" - ); - } - return { signature }; - } - throw new Error("No signer provider or viem account provided"); + const signature = await this.provider.request({ + method: "personal_sign", + params: [payload, this._address] as any, + }); + if (signature == null) { + throw new Error( + "[EVMExternalWalletSigner] Failed to sign message: EIP1193 provider signMessage returned null" + ); + } + return { signature }; + } + if (this.viemAccount?.signMessage != null) { + const signature = await this.viemAccount.signMessage({ + message: { + raw: payload as `0x${string}`, + }, + }); + if (signature == null) { + throw new Error( + "[EVMExternalWalletSigner] Failed to sign message: Viem account signMessage returned null" + ); + } + return { signature }; + } + throw new Error("No signer provider or viem account provided"); + } + + async signMessage(message: string) { + return await this.sign(message); } async signTransaction(transaction: string) { - return await this.signMessage(transaction); + return await this.sign(transaction); } } diff --git a/packages/wallets/src/signers/non-custodial/ncs-evm-signer.ts b/packages/wallets/src/signers/non-custodial/ncs-evm-signer.ts index ecb5ab480..254299ef2 100644 --- a/packages/wallets/src/signers/non-custodial/ncs-evm-signer.ts +++ b/packages/wallets/src/signers/non-custodial/ncs-evm-signer.ts @@ -8,7 +8,13 @@ export class EVMNonCustodialSigner extends NonCustodialSigner { super(config); } - async signMessage(message: string) { + async signMessage(message: string, raw?: boolean,) { + if (raw && message) { + if (!isHex(message)){ + throw new Error("Invalid hex string in signMessage raw argument"); + } + return await this.sign(message); + } const messageRaw = isHex(message) ? (message as Hex) : toHex(message); const messageToSign = PersonalMessage.getSignPayload(messageRaw); return await this.sign(messageToSign); @@ -18,11 +24,11 @@ export class EVMNonCustodialSigner extends NonCustodialSigner { return await this.sign(transaction); } - private async sign(raw: string): Promise<{ signature: string }> { + async sign(payload: string): Promise<{ signature: string }> { await this.handleAuthRequired(); const jwt = this.getJwtOrThrow(); - const hexString = raw.replace("0x", ""); + const hexString = payload.replace("0x", ""); const res = await this.config.clientTEEConnection?.sendAction({ event: "request:sign", diff --git a/packages/wallets/src/signers/non-custodial/ncs-signer.ts b/packages/wallets/src/signers/non-custodial/ncs-signer.ts index d1b27e42b..c60257778 100644 --- a/packages/wallets/src/signers/non-custodial/ncs-signer.ts +++ b/packages/wallets/src/signers/non-custodial/ncs-signer.ts @@ -26,6 +26,7 @@ export abstract class NonCustodialSigner implements Signer { return this.config.address; } + abstract sign(payload: string): Promise; abstract signMessage(message: string): Promise; private async initialize() { diff --git a/packages/wallets/src/signers/non-custodial/ncs-solana-signer.ts b/packages/wallets/src/signers/non-custodial/ncs-solana-signer.ts index ddf96fa4d..bf3f633a6 100644 --- a/packages/wallets/src/signers/non-custodial/ncs-solana-signer.ts +++ b/packages/wallets/src/signers/non-custodial/ncs-solana-signer.ts @@ -9,16 +9,20 @@ export class SolanaNonCustodialSigner extends NonCustodialSigner { } async signMessage() { - return await Promise.reject(new Error("signMessage method not implemented for email signer")); + return await Promise.reject(new Error("signMessage method not implemented for Solana email signer")); } async signTransaction(transaction: string): Promise<{ signature: string }> { - await this.handleAuthRequired(); - const jwt = this.getJwtOrThrow(); - const transactionBytes = base58.decode(transaction); const deserializedTransaction = VersionedTransaction.deserialize(transactionBytes); - const messageData = deserializedTransaction.message.serialize(); + const messageData = base58.encode(deserializedTransaction.message.serialize()); + + return await this.sign(messageData); + } + + async sign(payload: string): Promise<{ signature: string }> { + await this.handleAuthRequired(); + const jwt = this.getJwtOrThrow(); const res = await this.config.clientTEEConnection?.sendAction({ event: "request:sign", @@ -30,7 +34,7 @@ export class SolanaNonCustodialSigner extends NonCustodialSigner { }, data: { keyType: "ed25519", - bytes: base58.encode(messageData), + bytes: payload, encoding: "base58", }, }, @@ -42,10 +46,11 @@ export class SolanaNonCustodialSigner extends NonCustodialSigner { } if (res?.signature == null) { - throw new Error("Failed to sign transaction"); + throw new Error("Failed to sign payload"); } SolanaNonCustodialSigner.verifyPublicKeyFormat(res.publicKey); - return { signature: res.signature.bytes }; + return {signature: res.signature.bytes}; + } static verifyPublicKeyFormat(publicKey: { encoding: string; keyType: string; bytes: string } | null) { diff --git a/packages/wallets/src/signers/non-custodial/ncs-stellar-signer.ts b/packages/wallets/src/signers/non-custodial/ncs-stellar-signer.ts index dc0660b98..3d23d91f5 100644 --- a/packages/wallets/src/signers/non-custodial/ncs-stellar-signer.ts +++ b/packages/wallets/src/signers/non-custodial/ncs-stellar-signer.ts @@ -6,6 +6,10 @@ export class StellarNonCustodialSigner extends NonCustodialSigner { super(config); } + async sign() { + return Promise.reject(new Error("sign method not implemented for stellar signer")); + } + async signMessage() { return await Promise.reject(new Error("signMessage method not implemented for stellar signer")); } diff --git a/packages/wallets/src/signers/passkey.ts b/packages/wallets/src/signers/passkey.ts index 1c47df6b8..fce8d4b12 100644 --- a/packages/wallets/src/signers/passkey.ts +++ b/packages/wallets/src/signers/passkey.ts @@ -13,13 +13,13 @@ export class PasskeySigner implements Signer { return this.config.locator; } - async signMessage(message: string): Promise { + async sign(payload: string): Promise { if (this.config.onSignWithPasskey) { - return await this.config.onSignWithPasskey(message); + return await this.config.onSignWithPasskey(payload); } const { signature, metadata } = await WebAuthnP256.sign({ credentialId: this.id, - challenge: message as `0x${string}`, + challenge: payload as `0x${string}`, }); return { @@ -31,7 +31,11 @@ export class PasskeySigner implements Signer { }; } + async signMessage(message: string): Promise { + return await this.sign(message); + } + async signTransaction(transaction: string) { - return await this.signMessage(transaction); + return await this.sign(transaction); } } diff --git a/packages/wallets/src/signers/solana-api-key.ts b/packages/wallets/src/signers/solana-api-key.ts index fa30b3cdc..e6ca63846 100644 --- a/packages/wallets/src/signers/solana-api-key.ts +++ b/packages/wallets/src/signers/solana-api-key.ts @@ -9,6 +9,10 @@ export class SolanaApiKeySigner implements Signer { return this.config.locator; } + async sign(payload: string) { + return await Promise.reject(new Error("sign method not implemented for solana api key signer")); + } + async signMessage() { return await Promise.reject( new Error( diff --git a/packages/wallets/src/signers/solana-external-wallet.ts b/packages/wallets/src/signers/solana-external-wallet.ts index 5a65af5f1..a8bba996f 100644 --- a/packages/wallets/src/signers/solana-external-wallet.ts +++ b/packages/wallets/src/signers/solana-external-wallet.ts @@ -25,6 +25,10 @@ export class SolanaExternalWalletSigner implements Signer { return this.config.locator; } + async sign(payload: string) { + return await Promise.reject(new Error("sign method not implemented for solana external wallet signer")); + } + async signMessage() { return await Promise.reject(new Error("signMessage method not implemented for solana external wallet signer")); } diff --git a/packages/wallets/src/signers/stellar-external-wallet.ts b/packages/wallets/src/signers/stellar-external-wallet.ts index 4484cea71..0bb1d71fe 100644 --- a/packages/wallets/src/signers/stellar-external-wallet.ts +++ b/packages/wallets/src/signers/stellar-external-wallet.ts @@ -1,4 +1,4 @@ -import type { ExternalWalletInternalSignerConfig, Signer } from "./types"; +import type { BaseSignResult, ExternalWalletInternalSignerConfig, PasskeySignResult, Signer } from "./types"; import type { StellarChain } from "@/chains/chains"; export class StellarExternalWalletSigner implements Signer { @@ -22,6 +22,10 @@ export class StellarExternalWalletSigner implements Signer { return this.config.locator; } + async sign(payload: string) { + return await Promise.reject(new Error("signMessage method not implemented for stellar external wallet signer")); + } + async signMessage() { return await Promise.reject(new Error("signMessage method not implemented for stellar external wallet signer")); } diff --git a/packages/wallets/src/signers/types.ts b/packages/wallets/src/signers/types.ts index 0d7720018..1940a7e74 100644 --- a/packages/wallets/src/signers/types.ts +++ b/packages/wallets/src/signers/types.ts @@ -138,6 +138,7 @@ export interface Signer { type: T; locator(): string; address?(): string; - signMessage(message: string): Promise; + sign(payload: string): Promise; + signMessage?(message: string): Promise; signTransaction(transaction: string): Promise; } diff --git a/packages/wallets/src/wallets/wallet.ts b/packages/wallets/src/wallets/wallet.ts index ff47a3fb6..033f265eb 100644 --- a/packages/wallets/src/wallets/wallet.ts +++ b/packages/wallets/src/wallets/wallet.ts @@ -388,10 +388,14 @@ export class Wallet { } } - protected get isSolanaWallet(): boolean { + protected isSolanaWallet(): boolean { return this.chain === "solana"; } + protected isStellarWallet(): boolean { + return this.chain === "stellar"; + } + protected async approveTransactionAndWait(transactionId: string, options?: ApproveOptions) { await this.approveTransactionInternal(transactionId, options); await this.sleep(1_000); // Rule of thumb: tx won't be confirmed in less than 1 second @@ -405,7 +409,7 @@ export class Wallet { } protected async approveSignatureInternal(signatureId: string, options?: ApproveOptions) { - if (this.isSolanaWallet) { + if (this.isSolanaWallet() || this.isStellarWallet()) { throw new Error("Approving signatures is only supported for EVM smart wallets"); } @@ -442,7 +446,7 @@ export class Wallet { throw new InvalidSignerError(`Signer ${pendingApproval.signer} not found in pending approvals`); } - return signer.signMessage(pendingApproval.message); + return signer.sign(pendingApproval.message); }) ); From ed53475944fd0053df60f5bf4b21dea38196bb84 Mon Sep 17 00:00:00 2001 From: alberto-crossmint Date: Tue, 30 Sep 2025 13:57:03 +0200 Subject: [PATCH 2/2] fmt --- packages/wallets/src/signers/evm-api-key.ts | 2 +- .../src/signers/evm-external-wallet.ts | 50 +++++++++---------- .../signers/non-custodial/ncs-evm-signer.ts | 6 +-- .../non-custodial/ncs-solana-signer.ts | 3 +- .../src/signers/stellar-external-wallet.ts | 4 +- packages/wallets/src/signers/types.ts | 2 +- 6 files changed, 33 insertions(+), 34 deletions(-) diff --git a/packages/wallets/src/signers/evm-api-key.ts b/packages/wallets/src/signers/evm-api-key.ts index 22fdcfab2..184624ca5 100644 --- a/packages/wallets/src/signers/evm-api-key.ts +++ b/packages/wallets/src/signers/evm-api-key.ts @@ -9,7 +9,7 @@ export class EVMApiKeySigner implements Signer { return this.config.locator; } - async sign(){ + async sign() { return await Promise.reject( new Error( "API key signers do not support direct transaction signing - transaction are handled automatically by the backend" diff --git a/packages/wallets/src/signers/evm-external-wallet.ts b/packages/wallets/src/signers/evm-external-wallet.ts index a90816aaf..dea11f02e 100644 --- a/packages/wallets/src/signers/evm-external-wallet.ts +++ b/packages/wallets/src/signers/evm-external-wallet.ts @@ -27,31 +27,31 @@ export class EVMExternalWalletSigner implements Signer { async sign(payload: string): Promise<{ signature: string }> { if (this.provider != null) { - const signature = await this.provider.request({ - method: "personal_sign", - params: [payload, this._address] as any, - }); - if (signature == null) { - throw new Error( - "[EVMExternalWalletSigner] Failed to sign message: EIP1193 provider signMessage returned null" - ); - } - return { signature }; - } - if (this.viemAccount?.signMessage != null) { - const signature = await this.viemAccount.signMessage({ - message: { - raw: payload as `0x${string}`, - }, - }); - if (signature == null) { - throw new Error( - "[EVMExternalWalletSigner] Failed to sign message: Viem account signMessage returned null" - ); - } - return { signature }; - } - throw new Error("No signer provider or viem account provided"); + const signature = await this.provider.request({ + method: "personal_sign", + params: [payload, this._address] as any, + }); + if (signature == null) { + throw new Error( + "[EVMExternalWalletSigner] Failed to sign message: EIP1193 provider signMessage returned null" + ); + } + return { signature }; + } + if (this.viemAccount?.signMessage != null) { + const signature = await this.viemAccount.signMessage({ + message: { + raw: payload as `0x${string}`, + }, + }); + if (signature == null) { + throw new Error( + "[EVMExternalWalletSigner] Failed to sign message: Viem account signMessage returned null" + ); + } + return { signature }; + } + throw new Error("No signer provider or viem account provided"); } async signMessage(message: string) { diff --git a/packages/wallets/src/signers/non-custodial/ncs-evm-signer.ts b/packages/wallets/src/signers/non-custodial/ncs-evm-signer.ts index 254299ef2..7dcd48131 100644 --- a/packages/wallets/src/signers/non-custodial/ncs-evm-signer.ts +++ b/packages/wallets/src/signers/non-custodial/ncs-evm-signer.ts @@ -8,9 +8,9 @@ export class EVMNonCustodialSigner extends NonCustodialSigner { super(config); } - async signMessage(message: string, raw?: boolean,) { + async signMessage(message: string, raw?: boolean) { if (raw && message) { - if (!isHex(message)){ + if (!isHex(message)) { throw new Error("Invalid hex string in signMessage raw argument"); } return await this.sign(message); @@ -24,7 +24,7 @@ export class EVMNonCustodialSigner extends NonCustodialSigner { return await this.sign(transaction); } - async sign(payload: string): Promise<{ signature: string }> { + async sign(payload: string): Promise<{ signature: string }> { await this.handleAuthRequired(); const jwt = this.getJwtOrThrow(); diff --git a/packages/wallets/src/signers/non-custodial/ncs-solana-signer.ts b/packages/wallets/src/signers/non-custodial/ncs-solana-signer.ts index bf3f633a6..ee99da8b3 100644 --- a/packages/wallets/src/signers/non-custodial/ncs-solana-signer.ts +++ b/packages/wallets/src/signers/non-custodial/ncs-solana-signer.ts @@ -49,8 +49,7 @@ export class SolanaNonCustodialSigner extends NonCustodialSigner { throw new Error("Failed to sign payload"); } SolanaNonCustodialSigner.verifyPublicKeyFormat(res.publicKey); - return {signature: res.signature.bytes}; - + return { signature: res.signature.bytes }; } static verifyPublicKeyFormat(publicKey: { encoding: string; keyType: string; bytes: string } | null) { diff --git a/packages/wallets/src/signers/stellar-external-wallet.ts b/packages/wallets/src/signers/stellar-external-wallet.ts index 0bb1d71fe..bcdc83e86 100644 --- a/packages/wallets/src/signers/stellar-external-wallet.ts +++ b/packages/wallets/src/signers/stellar-external-wallet.ts @@ -1,4 +1,4 @@ -import type { BaseSignResult, ExternalWalletInternalSignerConfig, PasskeySignResult, Signer } from "./types"; +import type { ExternalWalletInternalSignerConfig, Signer } from "./types"; import type { StellarChain } from "@/chains/chains"; export class StellarExternalWalletSigner implements Signer { @@ -23,7 +23,7 @@ export class StellarExternalWalletSigner implements Signer { } async sign(payload: string) { - return await Promise.reject(new Error("signMessage method not implemented for stellar external wallet signer")); + return await Promise.reject(new Error("signMessage method not implemented for stellar external wallet signer")); } async signMessage() { diff --git a/packages/wallets/src/signers/types.ts b/packages/wallets/src/signers/types.ts index 1940a7e74..eb464b027 100644 --- a/packages/wallets/src/signers/types.ts +++ b/packages/wallets/src/signers/types.ts @@ -139,6 +139,6 @@ export interface Signer { locator(): string; address?(): string; sign(payload: string): Promise; - signMessage?(message: string): Promise; + signMessage(message: string): Promise; signTransaction(transaction: string): Promise; }