From a3bf4b2473e654cd0f1d0097964f05deab31f742 Mon Sep 17 00:00:00 2001
From: Philippe d'Argent
Date: Fri, 19 Sep 2025 16:03:08 +0900
Subject: [PATCH 1/4] adding solana support for x402 action
---
typescript/agentkit/package.json | 1 +
.../x402/x402ActionProvider.ts | 17 +++-
.../cdpSolanaWalletProvider.ts | 43 +++++++--
.../privySvmWalletProvider.ts | 19 ++++
.../solanaKeypairWalletProvider.ts | 23 +++++
.../src/wallet-providers/svmWalletProvider.ts | 93 +++++++++++++++++++
.../examples/langchain-cdp-chatbot/chatbot.ts | 9 +-
typescript/pnpm-lock.yaml | 3 +
8 files changed, 196 insertions(+), 12 deletions(-)
diff --git a/typescript/agentkit/package.json b/typescript/agentkit/package.json
index 537907c06..dd8ca4459 100644
--- a/typescript/agentkit/package.json
+++ b/typescript/agentkit/package.json
@@ -48,6 +48,7 @@
"@jup-ag/api": "^6.0.39",
"@privy-io/public-api": "2.18.5",
"@privy-io/server-auth": "1.18.4",
+ "@solana/kit": "^2.1.1",
"@solana/spl-token": "^0.4.12",
"@solana/web3.js": "^1.98.1",
"@zerodev/ecdsa-validator": "^5.4.5",
diff --git a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
index be680b73b..5a3d09295 100644
--- a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
+++ b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
@@ -8,7 +8,7 @@ import {
DirectX402RequestSchema,
ListX402ServicesSchema,
} from "./schemas";
-import { EvmWalletProvider } from "../../wallet-providers";
+import { EvmWalletProvider, CdpSolanaWalletProvider, WalletProvider } from "../../wallet-providers";
import axios, { AxiosError } from "axios";
import { withPaymentInterceptor, decodeXPaymentResponse } from "x402-axios";
import { PaymentRequirements } from "x402/types";
@@ -418,11 +418,20 @@ Unless specifically instructed otherwise, prefer the two-step approach with make
schema: DirectX402RequestSchema,
})
async makeHttpRequestWithX402(
- walletProvider: EvmWalletProvider,
+ walletProvider: WalletProvider,
args: z.infer,
): Promise {
try {
- const account = walletProvider.toSigner();
+
+ if ( !(walletProvider instanceof CdpSolanaWalletProvider || walletProvider instanceof EvmWalletProvider)) {
+ return JSON.stringify({
+ error: true,
+ message: "Unsupported wallet provider",
+ details: "Only CdpSolanaWalletProvider and EvmWalletProvider are supported",
+ }, null, 2);
+ }
+ const account = walletProvider instanceof CdpSolanaWalletProvider ? walletProvider.serverAccount : walletProvider.toSigner();
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const api = withPaymentInterceptor(axios.create({}), account as any);
@@ -469,7 +478,7 @@ Unless specifically instructed otherwise, prefer the two-step approach with make
* @returns True if the network is supported, false otherwise
*/
supportsNetwork = (network: Network) =>
- network.protocolFamily === "evm" && SUPPORTED_NETWORKS.includes(network.networkId!);
+ SUPPORTED_NETWORKS.includes(network.networkId!);
}
export const x402ActionProvider = () => new X402ActionProvider();
diff --git a/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts b/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
index dfce07249..a2ad072e4 100644
--- a/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
@@ -51,9 +51,9 @@ interface ConfigureCdpSolanaWalletProviderWithWalletOptions {
*/
export class CdpSolanaWalletProvider extends SvmWalletProvider implements WalletProviderWithClient {
#connection: Connection;
- #serverAccount: Awaited>;
#cdp: CdpClient;
#network: Network;
+ public serverAccount: Awaited>;
/**
* Constructs a new CdpSolanaWalletProvider.
@@ -63,7 +63,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
private constructor(config: ConfigureCdpSolanaWalletProviderWithWalletOptions) {
super();
- this.#serverAccount = config.serverAccount;
+ this.serverAccount = config.serverAccount;
this.#cdp = config.cdp;
this.#connection = config.connection;
this.#network = config.network;
@@ -138,8 +138,8 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
*/
async exportWallet(): Promise<{ name: string | undefined; address: `0x${string}` }> {
return {
- name: this.#serverAccount.name,
- address: this.#serverAccount.address as `0x${string}`,
+ name: this.serverAccount.name,
+ address: this.serverAccount.address as `0x${string}`,
};
}
@@ -158,7 +158,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
* @returns The wallet's public key
*/
getPublicKey(): PublicKey {
- return new PublicKey(this.#serverAccount.address);
+ return new PublicKey(this.serverAccount.address);
}
/**
@@ -167,7 +167,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
* @returns The base58 encoded address of the wallet
*/
getAddress(): string {
- return this.#serverAccount.address;
+ return this.serverAccount.address;
}
/**
@@ -200,7 +200,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
const signedTransactionResponse = await this.#cdp.solana.signTransaction({
transaction: encodedSerializedTransaction,
- address: this.#serverAccount.address,
+ address: this.serverAccount.address,
});
const signedTransactionBytes = Buffer.from(
@@ -282,6 +282,26 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
return this.#cdp;
}
+ /**
+ * Sign a message.
+ *
+ * @param message - The message to sign as a Uint8Array
+ * @returns The signature as a Uint8Array
+ */
+ async signMessage(message: Uint8Array): Promise {
+ // Convert Uint8Array to string for CDP SDK
+ const messageString = Buffer.from(message).toString('utf8');
+
+ const { signature } = await this.#cdp.solana.signMessage({
+ address: this.serverAccount.address,
+ message: messageString,
+ });
+
+ // Convert signature string back to Uint8Array
+ // CDP returns signature as a hex string, convert to bytes
+ return new Uint8Array(Buffer.from(signature, 'hex'));
+ }
+
/**
* Transfer SOL from the wallet to another address
*
@@ -330,4 +350,13 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
return signature;
}
+
+ /**
+ * Get the keypair for this wallet.
+ *
+ * @returns The CryptoKeyPair for KeyPairSigner compatibility
+ */
+ getKeyPair(): CryptoKeyPair {
+ throw new Error("getKeyPair is not supported for SolanaKeypairWalletProvider");
+ }
}
diff --git a/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts b/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
index e33d7dfea..bc73ced54 100644
--- a/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
@@ -250,4 +250,23 @@ export class PrivySvmWalletProvider extends SvmWalletProvider {
getPublicKey(): PublicKey {
return new PublicKey(this.#address);
}
+
+ /**
+ * Sign a message.
+ *
+ * @param _ - The message to sign as a Uint8Array (unused)
+ * @returns Never - throws an error as message signing is not supported yet
+ */
+ async signMessage(_: Uint8Array): Promise {
+ throw new Error("Message signing is not supported yet for PrivySvmWalletProvider");
+ }
+
+ /**
+ * Get the keypair for this wallet.
+ *
+ * @returns The CryptoKeyPair for KeyPairSigner compatibility
+ */
+ getKeyPair(): CryptoKeyPair {
+ throw new Error("getKeyPair is not supported for SolanaKeypairWalletProvider");
+ }
}
diff --git a/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts b/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
index 2fff35687..e82606b66 100644
--- a/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
@@ -322,6 +322,17 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
return signature;
}
+ /**
+ * Sign a message.
+ *
+ * @param _ - The message to sign as a Uint8Array (unused)
+ * @returns Never - throws an error as message signing is not supported yet
+ */
+ async signMessage(_: Uint8Array): Promise {
+ throw new Error("Message signing is not supported yet for SolanaKeypairWalletProvider");
+ }
+
+
/**
* Request SOL tokens from the Solana faucet. This method only works on devnet and testnet networks.
*
@@ -330,5 +341,17 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
*/
async requestAirdrop(lamports: number): Promise {
return await this.#connection.requestAirdrop(this.#keypair.publicKey, lamports);
+
+ }
+
+
+ /**
+ * Get the keypair for this wallet.
+ *
+ * @returns The CryptoKeyPair for KeyPairSigner compatibility
+ */
+ getKeyPair(): CryptoKeyPair {
+ throw new Error("getKeyPair is not supported for SolanaKeypairWalletProvider");
}
}
+
diff --git a/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts b/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
index 009de9694..469dfbedc 100644
--- a/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
@@ -10,6 +10,18 @@ import {
VersionedTransaction,
SignatureResult,
} from "@solana/web3.js";
+import {
+ isKeyPairSigner,
+ KeyPairSigner,
+} from "@solana/kit";
+import type {
+ Address,
+ TransactionMessageBytes,
+ SignaturesMap,
+ BaseTransactionSignerConfig,
+ SignatureBytes,
+ MessagePartialSignerConfig
+} from "@solana/kit";
/**
* SvmWalletProvider is the abstract base class for all Solana wallet providers (non browsers).
@@ -75,4 +87,85 @@ export abstract class SvmWalletProvider extends WalletProvider {
abstract waitForSignatureResult(
signature: string,
): Promise>;
+
+ /**
+ * Sign a message.
+ *
+ * @param message - The message to sign as a Uint8Array
+ * @returns The signature as a Uint8Array
+ */
+ abstract signMessage(message: Uint8Array): Promise;
+
+
+ /**
+ * Convert the wallet provider to a Signer compatible with x402.
+ *
+ * @returns The signer instance
+ */
+ toSigner(): KeyPairSigner {
+ const self = this;
+
+ return {
+ address: self.getAddress() as Address,
+
+ keyPair: self.getKeyPair(),
+
+ signTransactions: async (
+ txs: readonly Readonly<{
+ messageBytes: TransactionMessageBytes;
+ signatures: SignaturesMap;
+ }>[],
+ _config?: BaseTransactionSignerConfig
+ ) => {
+ const out: { messageBytes: TransactionMessageBytes; signatures: SignaturesMap; }[] = [];
+ for (const { messageBytes, signatures } of txs) {
+ const sig = await self.signMessage(new Uint8Array(messageBytes));
+ const addr = self.getAddress() as Address;
+ out.push({ messageBytes, signatures: { ...signatures, [addr]: sig as SignatureBytes } });
+ }
+ return out;
+ },
+
+ signMessages: async (
+ msgs: readonly Readonly<{
+ content: Uint8Array;
+ signatures: Readonly>;
+ }>[],
+ _config?: Readonly
+ ) => {
+ const out: {
+ content: Uint8Array;
+ signatures: Readonly>;
+ }[] = [];
+ for (const { content, signatures } of msgs) {
+ const sig = await self.signMessage(new Uint8Array(content));
+ const addr = self.getAddress() as Address;
+ out.push({ content, signatures: { ...signatures, [addr]: sig as SignatureBytes } });
+ }
+ return out;
+ },
+ };
+ }
+
+ /**
+ * Get the keypair for this wallet.
+ *
+ * @returns The CryptoKeyPair for KeyPairSigner compatibility
+ */
+ abstract getKeyPair(): any;
+
+ /**
+ * Check if this wallet's signer is a valid KeyPairSigner for x402 compatibility.
+ *
+ * @returns True if the signer is a valid KeyPairSigner, false otherwise
+ */
+ isKeyPairSigner(): boolean {
+ try {
+ const signer = this.toSigner();
+ return isKeyPairSigner(signer);
+ } catch (error) {
+ console.warn("Error checking KeyPairSigner compatibility:", error);
+ return false;
+ }
+ }
}
diff --git a/typescript/examples/langchain-cdp-chatbot/chatbot.ts b/typescript/examples/langchain-cdp-chatbot/chatbot.ts
index ac27cd942..3eb2d5f5b 100644
--- a/typescript/examples/langchain-cdp-chatbot/chatbot.ts
+++ b/typescript/examples/langchain-cdp-chatbot/chatbot.ts
@@ -114,6 +114,13 @@ async function initializeAgent() {
const walletProvider = isSolana
? await CdpSolanaWalletProvider.configureWithWallet(cdpWalletConfig)
: await CdpEvmWalletProvider.configureWithWallet(cdpWalletConfig);
+
+ // Test SVM signer compatibility if using Solana wallet
+ if (isSolanaWalletProvider(walletProvider)) {
+ const isCompatible = walletProvider.isKeyPairSigner();
+ console.log(`✅ SVM wallet KeyPairSigner compatibility: ${isCompatible}`);
+ }
+
const actionProviders = [
walletActionProvider(),
cdpApiActionProvider(),
@@ -126,7 +133,7 @@ async function initializeAgent() {
x402ActionProvider(),
]
: isSolanaWalletProvider(walletProvider)
- ? [splActionProvider()]
+ ? [splActionProvider(), x402ActionProvider()]
: []),
];
diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml
index 8972eac54..2635bc156 100644
--- a/typescript/pnpm-lock.yaml
+++ b/typescript/pnpm-lock.yaml
@@ -89,6 +89,9 @@ importers:
'@privy-io/server-auth':
specifier: 1.18.4
version: 1.18.4(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(viem@2.23.15(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2))
+ '@solana/kit':
+ specifier: ^2.1.1
+ version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/spl-token':
specifier: ^0.4.12
version: 0.4.13(@solana/web3.js@1.98.1(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10)
From 4777b2308b637818fca2942393d5b0a6bd2a405f Mon Sep 17 00:00:00 2001
From: Philippe d'Argent
Date: Thu, 25 Sep 2025 09:54:18 +0900
Subject: [PATCH 2/4] implement getKeyPair for SolanaKeypairWalletProvider
---
typescript/agentkit/package.json | 1 +
.../x402/x402ActionProvider.ts | 4 +-
.../cdpSolanaWalletProvider.ts | 4 +-
.../privySvmWalletProvider.ts | 4 +-
.../solanaKeypairWalletProvider.ts | 18 ++-
.../src/wallet-providers/svmWalletProvider.ts | 10 +-
.../langchain-solana-chatbot/chatbot.ts | 7 +-
typescript/pnpm-lock.yaml | 104 ++++++++++++++++++
8 files changed, 138 insertions(+), 14 deletions(-)
diff --git a/typescript/agentkit/package.json b/typescript/agentkit/package.json
index dd8ca4459..0bdd5a368 100644
--- a/typescript/agentkit/package.json
+++ b/typescript/agentkit/package.json
@@ -48,6 +48,7 @@
"@jup-ag/api": "^6.0.39",
"@privy-io/public-api": "2.18.5",
"@privy-io/server-auth": "1.18.4",
+ "@solana/keys": "^3.0.3",
"@solana/kit": "^2.1.1",
"@solana/spl-token": "^0.4.12",
"@solana/web3.js": "^1.98.1",
diff --git a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
index 5a3d09295..b2e88c991 100644
--- a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
+++ b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
@@ -8,7 +8,7 @@ import {
DirectX402RequestSchema,
ListX402ServicesSchema,
} from "./schemas";
-import { EvmWalletProvider, CdpSolanaWalletProvider, WalletProvider } from "../../wallet-providers";
+import { EvmWalletProvider, CdpSolanaWalletProvider, WalletProvider, SvmWalletProvider } from "../../wallet-providers";
import axios, { AxiosError } from "axios";
import { withPaymentInterceptor, decodeXPaymentResponse } from "x402-axios";
import { PaymentRequirements } from "x402/types";
@@ -423,7 +423,7 @@ Unless specifically instructed otherwise, prefer the two-step approach with make
): Promise {
try {
- if ( !(walletProvider instanceof CdpSolanaWalletProvider || walletProvider instanceof EvmWalletProvider)) {
+ if ( !(walletProvider instanceof SvmWalletProvider || walletProvider instanceof EvmWalletProvider)) {
return JSON.stringify({
error: true,
message: "Unsupported wallet provider",
diff --git a/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts b/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
index a2ad072e4..f12b1055a 100644
--- a/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
@@ -356,7 +356,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
*
* @returns The CryptoKeyPair for KeyPairSigner compatibility
*/
- getKeyPair(): CryptoKeyPair {
- throw new Error("getKeyPair is not supported for SolanaKeypairWalletProvider");
+ async getKeyPair(): Promise {
+ throw new Error("getKeyPair is not supported for CdpSolanaWalletProvider");
}
}
diff --git a/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts b/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
index bc73ced54..b395edd2a 100644
--- a/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
@@ -266,7 +266,7 @@ export class PrivySvmWalletProvider extends SvmWalletProvider {
*
* @returns The CryptoKeyPair for KeyPairSigner compatibility
*/
- getKeyPair(): CryptoKeyPair {
- throw new Error("getKeyPair is not supported for SolanaKeypairWalletProvider");
+ async getKeyPair(): Promise {
+ throw new Error("getKeyPair is not supported for PrivySvmWalletProvider");
}
}
diff --git a/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts b/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
index e82606b66..b1f248c6d 100644
--- a/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
@@ -26,6 +26,7 @@ import {
SOLANA_TESTNET_GENESIS_BLOCK_HASH,
SOLANA_TESTNET_NETWORK_ID,
} from "../network/svm";
+import { createKeyPairFromBytes } from "@solana/keys";
/**
* SolanaKeypairWalletProvider is a wallet provider that uses a local Solana keypair.
@@ -36,6 +37,7 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
#keypair: Keypair;
#connection: Connection;
#genesisHash: string;
+ #cryptoKeyPair?: CryptoKeyPair;
/**
* Creates a new SolanaKeypairWalletProvider
@@ -67,6 +69,14 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
} else {
throw new Error(`Unknown network with genesis hash: ${genesisHash}`);
}
+ // Initialize crypto key pair asynchronously
+ this.#initCryptoKeyPair().catch(error => {
+ console.error("Failed to initialize CryptoKeyPair:", error);
+ });
+ }
+
+ async #initCryptoKeyPair() {
+ this.#cryptoKeyPair = await createKeyPairFromBytes(this.#keypair.secretKey /*, extractable? = false */);
}
/**
@@ -350,8 +360,12 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
*
* @returns The CryptoKeyPair for KeyPairSigner compatibility
*/
- getKeyPair(): CryptoKeyPair {
- throw new Error("getKeyPair is not supported for SolanaKeypairWalletProvider");
+ async getKeyPair(): Promise {
+ if (!this.#cryptoKeyPair) {
+ // Wait for initialization to complete
+ await this.#initCryptoKeyPair();
+ }
+ return this.#cryptoKeyPair!;
}
}
diff --git a/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts b/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
index 469dfbedc..9986e714a 100644
--- a/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
@@ -102,13 +102,13 @@ export abstract class SvmWalletProvider extends WalletProvider {
*
* @returns The signer instance
*/
- toSigner(): KeyPairSigner {
+ async toSigner(): Promise {
const self = this;
return {
address: self.getAddress() as Address,
- keyPair: self.getKeyPair(),
+ keyPair: await self.getKeyPair(),
signTransactions: async (
txs: readonly Readonly<{
@@ -152,16 +152,16 @@ export abstract class SvmWalletProvider extends WalletProvider {
*
* @returns The CryptoKeyPair for KeyPairSigner compatibility
*/
- abstract getKeyPair(): any;
+ abstract getKeyPair(): Promise;
/**
* Check if this wallet's signer is a valid KeyPairSigner for x402 compatibility.
*
* @returns True if the signer is a valid KeyPairSigner, false otherwise
*/
- isKeyPairSigner(): boolean {
+ async isKeyPairSigner(): Promise {
try {
- const signer = this.toSigner();
+ const signer = await this.toSigner();
return isKeyPairSigner(signer);
} catch (error) {
console.warn("Error checking KeyPairSigner compatibility:", error);
diff --git a/typescript/examples/langchain-solana-chatbot/chatbot.ts b/typescript/examples/langchain-solana-chatbot/chatbot.ts
index 7d25c74e5..dfe08924e 100644
--- a/typescript/examples/langchain-solana-chatbot/chatbot.ts
+++ b/typescript/examples/langchain-solana-chatbot/chatbot.ts
@@ -5,6 +5,7 @@ import {
splActionProvider,
walletActionProvider,
cdpApiActionProvider,
+ x402ActionProvider,
} from "@coinbase/agentkit";
import { getLangChainTools } from "@coinbase/agentkit-langchain";
import { HumanMessage } from "@langchain/core/messages";
@@ -89,12 +90,16 @@ async function initializeAgent() {
walletProvider = await SolanaKeypairWalletProvider.fromNetwork(network, solanaPrivateKey);
}
+ // Test SVM signer compatibility if using Solana wallet
+ const isCompatible = await walletProvider.isKeyPairSigner();
+ console.log(`✅ SVM wallet KeyPairSigner compatibility: ${isCompatible}`);
+
// Initialize AgentKit
const agentkit = await AgentKit.from({
cdpApiKeyId: process.env.CDP_API_KEY_ID,
cdpApiKeySecret: process.env.CDP_API_KEY_SECRET,
walletProvider,
- actionProviders: [splActionProvider(), walletActionProvider(), cdpApiActionProvider()],
+ actionProviders: [splActionProvider(), walletActionProvider(), cdpApiActionProvider(), x402ActionProvider()],
});
const tools = await getLangChainTools(agentkit);
diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml
index 2635bc156..d9d095f33 100644
--- a/typescript/pnpm-lock.yaml
+++ b/typescript/pnpm-lock.yaml
@@ -89,6 +89,9 @@ importers:
'@privy-io/server-auth':
specifier: 1.18.4
version: 1.18.4(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(viem@2.23.15(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2))
+ '@solana/keys':
+ specifier: ^3.0.3
+ version: 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
'@solana/kit':
specifier: ^2.1.1
version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -1809,6 +1812,12 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
+ '@solana/assertions@3.0.3':
+ resolution: {integrity: sha512-2qspxdbWp2y62dfCIlqeWQr4g+hE8FYSSwcaP6itwMwGRb8393yDGCJfI/znuzJh6m/XVWhMHIgFgsBwnevCmg==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/buffer-layout-utils@0.2.0':
resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==}
engines: {node: '>= 10'}
@@ -1834,6 +1843,12 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
+ '@solana/codecs-core@3.0.3':
+ resolution: {integrity: sha512-emKykJ3h1DmnDOY29Uv9eJXP8E/FHzvlUBJ6te+5EbKdFjj7vdlKYPfDxOI6iGdXTY+YC/ELtbNBh6QwF2uEDQ==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/codecs-data-structures@2.0.0-rc.1':
resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==}
peerDependencies:
@@ -1862,6 +1877,12 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
+ '@solana/codecs-numbers@3.0.3':
+ resolution: {integrity: sha512-pfXkH9J0glrM8qj6389GAn30+cJOxzXLR2FsPOHCUMXrqLhGjMMZAWhsQkpOQ37SGc/7EiQsT/gmyGC7gxHqJQ==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/codecs-strings@2.0.0-rc.1':
resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==}
peerDependencies:
@@ -1875,6 +1896,13 @@ packages:
fastestsmallesttextencoderdecoder: ^1.0.22
typescript: '>=5.3.3'
+ '@solana/codecs-strings@3.0.3':
+ resolution: {integrity: sha512-VHBXnnTVtcQ1j+7Vrz+qSYo38no+jiHRdGnhFspRXEHNJbllzwKqgBE7YN3qoIXH+MKxgJUcwO5KHmdzf8Wn2A==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ fastestsmallesttextencoderdecoder: ^1.0.22
+ typescript: '>=5.3.3'
+
'@solana/codecs@2.0.0-rc.1':
resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==}
peerDependencies:
@@ -1906,6 +1934,13 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
+ '@solana/errors@3.0.3':
+ resolution: {integrity: sha512-1l84xJlHNva6io62PcYfUamwWlc0eM95nHgCrKX0g0cLoC6D6QHYPCEbEVkR+C5UtP9JDgyQM8MFiv+Ei5tO9Q==}
+ engines: {node: '>=20.18.0'}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/fast-stable-stringify@2.3.0':
resolution: {integrity: sha512-KfJPrMEieUg6D3hfQACoPy0ukrAV8Kio883llt/8chPEG3FVTX9z/Zuf4O01a15xZmBbmQ7toil2Dp0sxMJSxw==}
engines: {node: '>=20.18.0'}
@@ -1930,6 +1965,12 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
+ '@solana/keys@3.0.3':
+ resolution: {integrity: sha512-tp8oK9tMadtSIc4vF4aXXWkPd4oU5XPW8nf28NgrGDWGt25fUHIydKjkf2hPtMt9i1WfRyQZ33B5P3dnsNqcPQ==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/kit@2.3.0':
resolution: {integrity: sha512-sb6PgwoW2LjE5oTFu4lhlS/cGt/NB3YrShEyx7JgWFWysfgLdJnhwWThgwy/4HjNsmtMrQGWVls0yVBHcMvlMQ==}
engines: {node: '>=20.18.0'}
@@ -1942,6 +1983,12 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
+ '@solana/nominal-types@3.0.3':
+ resolution: {integrity: sha512-aZavCiexeUAoMHRQg4s1AHkH3wscbOb70diyfjhwZVgFz1uUsFez7csPp9tNFkNolnadVb2gky7yBk3IImQJ6A==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/options@2.0.0-rc.1':
resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==}
peerDependencies:
@@ -2878,6 +2925,10 @@ packages:
resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+ chalk@5.6.2:
+ resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
+ engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+
char-regex@1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
@@ -2981,6 +3032,10 @@ packages:
resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==}
engines: {node: '>=18'}
+ commander@14.0.0:
+ resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==}
+ engines: {node: '>=20'}
+
commander@14.0.1:
resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==}
engines: {node: '>=20'}
@@ -8485,6 +8540,11 @@ snapshots:
'@solana/errors': 2.3.0(typescript@5.8.2)
typescript: 5.8.2
+ '@solana/assertions@3.0.3(typescript@5.8.2)':
+ dependencies:
+ '@solana/errors': 3.0.3(typescript@5.8.2)
+ typescript: 5.8.2
+
'@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)':
dependencies:
'@solana/buffer-layout': 4.0.1
@@ -8516,6 +8576,11 @@ snapshots:
'@solana/errors': 2.3.0(typescript@5.8.2)
typescript: 5.8.2
+ '@solana/codecs-core@3.0.3(typescript@5.8.2)':
+ dependencies:
+ '@solana/errors': 3.0.3(typescript@5.8.2)
+ typescript: 5.8.2
+
'@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.8.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2)
@@ -8548,6 +8613,12 @@ snapshots:
'@solana/errors': 2.3.0(typescript@5.8.2)
typescript: 5.8.2
+ '@solana/codecs-numbers@3.0.3(typescript@5.8.2)':
+ dependencies:
+ '@solana/codecs-core': 3.0.3(typescript@5.8.2)
+ '@solana/errors': 3.0.3(typescript@5.8.2)
+ typescript: 5.8.2
+
'@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2)
@@ -8564,6 +8635,14 @@ snapshots:
fastestsmallesttextencoderdecoder: 1.0.22
typescript: 5.8.2
+ '@solana/codecs-strings@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
+ dependencies:
+ '@solana/codecs-core': 3.0.3(typescript@5.8.2)
+ '@solana/codecs-numbers': 3.0.3(typescript@5.8.2)
+ '@solana/errors': 3.0.3(typescript@5.8.2)
+ fastestsmallesttextencoderdecoder: 1.0.22
+ typescript: 5.8.2
+
'@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2)
@@ -8604,6 +8683,12 @@ snapshots:
commander: 14.0.1
typescript: 5.8.2
+ '@solana/errors@3.0.3(typescript@5.8.2)':
+ dependencies:
+ chalk: 5.6.2
+ commander: 14.0.0
+ typescript: 5.8.2
+
'@solana/fast-stable-stringify@2.3.0(typescript@5.8.2)':
dependencies:
typescript: 5.8.2
@@ -8629,6 +8714,17 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
+ '@solana/keys@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
+ dependencies:
+ '@solana/assertions': 3.0.3(typescript@5.8.2)
+ '@solana/codecs-core': 3.0.3(typescript@5.8.2)
+ '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+ '@solana/errors': 3.0.3(typescript@5.8.2)
+ '@solana/nominal-types': 3.0.3(typescript@5.8.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+
'@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
@@ -8658,6 +8754,10 @@ snapshots:
dependencies:
typescript: 5.8.2
+ '@solana/nominal-types@3.0.3(typescript@5.8.2)':
+ dependencies:
+ typescript: 5.8.2
+
'@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2)
@@ -10372,6 +10472,8 @@ snapshots:
chalk@5.4.1: {}
+ chalk@5.6.2: {}
+
char-regex@1.0.2: {}
chardet@0.7.0: {}
@@ -10470,6 +10572,8 @@ snapshots:
commander@13.1.0: {}
+ commander@14.0.0: {}
+
commander@14.0.1: {}
commander@2.20.3: {}
From 4e1f3d64b8079134126d0bff29505844b89ceb3d Mon Sep 17 00:00:00 2001
From: Philippe d'Argent
Date: Thu, 25 Sep 2025 12:37:43 +0900
Subject: [PATCH 3/4] implement getKeyPairSigner for CdpSolanaWalletProvider
---
.../src/action-providers/x402/utils.ts | 61 +++++++--
.../x402/x402ActionProvider.test.ts | 62 ++++++++-
.../x402/x402ActionProvider.ts | 57 +++++---
.../cdpSolanaWalletProvider.ts | 49 ++++---
.../privySvmWalletProvider.ts | 13 +-
.../solanaKeypairWalletProvider.ts | 29 +----
.../src/wallet-providers/svmWalletProvider.ts | 122 +++++++-----------
.../examples/langchain-cdp-chatbot/chatbot.ts | 6 -
.../langchain-solana-chatbot/chatbot.ts | 4 -
9 files changed, 237 insertions(+), 166 deletions(-)
diff --git a/typescript/agentkit/src/action-providers/x402/utils.ts b/typescript/agentkit/src/action-providers/x402/utils.ts
index 1001a68dc..4cb7c0454 100644
--- a/typescript/agentkit/src/action-providers/x402/utils.ts
+++ b/typescript/agentkit/src/action-providers/x402/utils.ts
@@ -3,13 +3,21 @@ import { AxiosError } from "axios";
import { getTokenDetails } from "../erc20/utils";
import { TOKEN_ADDRESSES_BY_SYMBOLS } from "../erc20/constants";
import { formatUnits, parseUnits } from "viem";
-import { EvmWalletProvider } from "../../wallet-providers";
+import { EvmWalletProvider, SvmWalletProvider, WalletProvider } from "../../wallet-providers";
/**
* Supported network types for x402 protocol
*/
export type X402Network = "base" | "base-sepolia" | "solana" | "solana-devnet";
+/**
+ * USDC token addresses for Solana networks
+ */
+const SOLANA_USDC_ADDRESSES = {
+ "solana-devnet": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
+ "solana-mainnet": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // Standard USDC on Solana mainnet
+} as const;
+
/**
* Converts the internal network ID to the format expected by the x402 protocol.
*
@@ -90,15 +98,16 @@ export function handleHttpError(error: AxiosError, url: string): string {
*/
export async function formatPaymentOption(
option: { asset: string; maxAmountRequired: string; network: string },
- walletProvider: EvmWalletProvider,
+ walletProvider: WalletProvider,
): Promise {
const { asset, maxAmountRequired, network } = option;
// Check if this is an EVM network and we can use ERC20 helpers
const walletNetwork = walletProvider.getNetwork();
const isEvmNetwork = walletNetwork.protocolFamily === "evm";
+ const isSvmNetwork = walletNetwork.protocolFamily === "svm";
- if (isEvmNetwork) {
+ if (isEvmNetwork && walletProvider instanceof EvmWalletProvider) {
const networkId = walletNetwork.networkId as keyof typeof TOKEN_ADDRESSES_BY_SYMBOLS;
const tokenSymbols = TOKEN_ADDRESSES_BY_SYMBOLS[networkId];
@@ -124,7 +133,19 @@ export async function formatPaymentOption(
}
}
- // Fallback to original format for non-EVM networks or when token details can't be fetched
+ if (isSvmNetwork && walletProvider instanceof SvmWalletProvider) {
+ // Check if the asset is USDC on Solana networks
+ const networkId = walletNetwork.networkId as keyof typeof SOLANA_USDC_ADDRESSES;
+ const usdcAddress = SOLANA_USDC_ADDRESSES[networkId];
+
+ if (usdcAddress && asset === usdcAddress) {
+ // USDC has 6 decimals on Solana
+ const formattedAmount = formatUnits(BigInt(maxAmountRequired), 6);
+ return `${formattedAmount} USDC on ${network} network`;
+ }
+ }
+
+ // Fallback to original format for non-EVM/SVM networks or when token details can't be fetched
return `${asset} ${maxAmountRequired} on ${network} network`;
}
@@ -135,11 +156,12 @@ export async function formatPaymentOption(
* @param walletProvider - The wallet provider for network context
* @returns True if the asset is USDC, false otherwise
*/
-export function isUsdcAsset(asset: string, walletProvider: EvmWalletProvider): boolean {
+export function isUsdcAsset(asset: string, walletProvider: WalletProvider): boolean {
const walletNetwork = walletProvider.getNetwork();
const isEvmNetwork = walletNetwork.protocolFamily === "evm";
+ const isSvmNetwork = walletNetwork.protocolFamily === "svm";
- if (isEvmNetwork) {
+ if (isEvmNetwork && walletProvider instanceof EvmWalletProvider) {
const networkId = walletNetwork.networkId as keyof typeof TOKEN_ADDRESSES_BY_SYMBOLS;
const tokenSymbols = TOKEN_ADDRESSES_BY_SYMBOLS[networkId];
@@ -148,6 +170,15 @@ export function isUsdcAsset(asset: string, walletProvider: EvmWalletProvider): b
}
}
+ if (isSvmNetwork && walletProvider instanceof SvmWalletProvider) {
+ const networkId = walletNetwork.networkId as keyof typeof SOLANA_USDC_ADDRESSES;
+ const usdcAddress = SOLANA_USDC_ADDRESSES[networkId];
+
+ if (usdcAddress) {
+ return asset === usdcAddress;
+ }
+ }
+
return false;
}
@@ -162,13 +193,14 @@ export function isUsdcAsset(asset: string, walletProvider: EvmWalletProvider): b
export async function convertWholeUnitsToAtomic(
wholeUnits: number,
asset: string,
- walletProvider: EvmWalletProvider,
+ walletProvider: WalletProvider,
): Promise {
// Check if this is an EVM network and we can use ERC20 helpers
const walletNetwork = walletProvider.getNetwork();
const isEvmNetwork = walletNetwork.protocolFamily === "evm";
+ const isSvmNetwork = walletNetwork.protocolFamily === "svm";
- if (isEvmNetwork) {
+ if (isEvmNetwork && walletProvider instanceof EvmWalletProvider) {
const networkId = walletNetwork.networkId as keyof typeof TOKEN_ADDRESSES_BY_SYMBOLS;
const tokenSymbols = TOKEN_ADDRESSES_BY_SYMBOLS[networkId];
@@ -192,6 +224,17 @@ export async function convertWholeUnitsToAtomic(
}
}
- // Fallback to 18 decimals for unknown tokens or non-EVM networks
+ if (isSvmNetwork && walletProvider instanceof SvmWalletProvider) {
+ // Check if the asset is USDC on Solana networks
+ const networkId = walletNetwork.networkId as keyof typeof SOLANA_USDC_ADDRESSES;
+ const usdcAddress = SOLANA_USDC_ADDRESSES[networkId];
+
+ if (usdcAddress && asset === usdcAddress) {
+ // USDC has 6 decimals on Solana
+ return parseUnits(wholeUnits.toString(), 6).toString();
+ }
+ }
+
+ // Fallback to 18 decimals for unknown tokens or non-EVM/SVM networks
return parseUnits(wholeUnits.toString(), 18).toString();
}
diff --git a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.test.ts b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.test.ts
index bf7bf2f1a..e3a8a4ec5 100644
--- a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.test.ts
+++ b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.test.ts
@@ -5,6 +5,7 @@ import { AxiosError, AxiosResponse, AxiosRequestConfig, AxiosInstance } from "ax
import axios from "axios";
import * as x402axios from "x402-axios";
import * as x402Verify from "x402/verify";
+import * as utils from "./utils";
// Mock external facilitator dependency
jest.mock("@coinbase/x402", () => ({
@@ -15,6 +16,7 @@ jest.mock("@coinbase/x402", () => ({
jest.mock("axios");
jest.mock("x402-axios");
jest.mock("x402/verify");
+jest.mock("./utils");
// Create mock functions
const mockRequest = jest.fn();
@@ -61,6 +63,10 @@ const mockAxios = {
const mockWithPaymentInterceptor = jest.fn().mockReturnValue(mockAxiosInstance);
const mockDecodeXPaymentResponse = jest.fn();
const mockUseFacilitator = jest.fn();
+const mockIsUsdcAsset = jest.fn();
+const mockConvertWholeUnitsToAtomic = jest.fn();
+const mockFormatPaymentOption = jest.fn();
+const mockGetX402Network = jest.fn();
// Override the mocked modules
(axios as jest.Mocked).create = mockAxios.create;
@@ -72,12 +78,26 @@ jest.mocked(x402axios.withPaymentInterceptor).mockImplementation(mockWithPayment
jest.mocked(x402axios.decodeXPaymentResponse).mockImplementation(mockDecodeXPaymentResponse);
jest.mocked(x402Verify.useFacilitator).mockImplementation(mockUseFacilitator);
+// Mock utils functions
+jest.mocked(utils.isUsdcAsset).mockImplementation(mockIsUsdcAsset);
+jest.mocked(utils.convertWholeUnitsToAtomic).mockImplementation(mockConvertWholeUnitsToAtomic);
+jest.mocked(utils.formatPaymentOption).mockImplementation(mockFormatPaymentOption);
+jest.mocked(utils.getX402Network).mockImplementation(mockGetX402Network);
+jest.mocked(utils.handleHttpError).mockImplementation((error, url) => {
+ return JSON.stringify({
+ error: true,
+ message: error instanceof Error ? error.message : "Network error",
+ url: url,
+ });
+});
+
// Mock wallet provider
-const makeMockWalletProvider = (networkId: string) =>
- ({
- toSigner: jest.fn().mockReturnValue("mock-signer"),
- getNetwork: jest.fn().mockReturnValue({ protocolFamily: "evm", networkId }),
- }) as unknown as EvmWalletProvider;
+const makeMockWalletProvider = (networkId: string) => {
+ const mockProvider = Object.create(EvmWalletProvider.prototype);
+ mockProvider.toSigner = jest.fn().mockReturnValue("mock-signer");
+ mockProvider.getNetwork = jest.fn().mockReturnValue({ protocolFamily: "evm", networkId });
+ return mockProvider as EvmWalletProvider;
+};
// Sample responses based on real examples
const MOCK_PAYMENT_INFO_RESPONSE = {
@@ -136,6 +156,12 @@ describe("X402ActionProvider", () => {
("isAxiosError" in error || "response" in error || "request" in error),
),
);
+
+ // Reset all utility mocks to default behavior
+ mockGetX402Network.mockImplementation(network => network.networkId);
+ mockIsUsdcAsset.mockReturnValue(false);
+ mockConvertWholeUnitsToAtomic.mockResolvedValue("100000");
+ mockFormatPaymentOption.mockResolvedValue("mocked payment option");
});
afterEach(() => {
@@ -158,8 +184,13 @@ describe("X402ActionProvider", () => {
expect(provider.supportsNetwork(network)).toBe(false);
});
- it("should not support non-EVM networks", () => {
- const network: Network = { protocolFamily: "solana", networkId: "mainnet" };
+ it("should support SVM networks", () => {
+ const network: Network = { protocolFamily: "svm", networkId: "solana-mainnet" };
+ expect(provider.supportsNetwork(network)).toBe(true);
+ });
+
+ it("should not support non-EVM/SVM networks", () => {
+ const network: Network = { protocolFamily: "bitcoin", networkId: "mainnet" };
expect(provider.supportsNetwork(network)).toBe(false);
});
});
@@ -185,6 +216,9 @@ describe("X402ActionProvider", () => {
});
it("should handle 402 responses with payment options", async () => {
+ mockGetX402Network.mockReturnValue("base-sepolia");
+ mockFormatPaymentOption.mockResolvedValue("10000 USDC on base-sepolia network");
+
mockRequest.mockResolvedValue({
status: 402,
data: MOCK_PAYMENT_INFO_RESPONSE.data,
@@ -246,6 +280,7 @@ describe("X402ActionProvider", () => {
});
mockUseFacilitator.mockReturnValue({ list: mockList });
+ mockGetX402Network.mockReturnValue("base-sepolia");
const result = await provider.discoverX402Services(
makeMockWalletProvider("base-sepolia"),
@@ -311,6 +346,15 @@ describe("X402ActionProvider", () => {
mockUseFacilitator.mockReturnValue({ list: mockList });
+ // Mock the utility functions for this test
+ mockGetX402Network.mockReturnValue("base-sepolia");
+ mockIsUsdcAsset.mockReturnValue(true); // All assets are USDC
+ mockConvertWholeUnitsToAtomic
+ .mockResolvedValueOnce("100000") // 0.1 USDC in atomic units
+ .mockResolvedValueOnce("100000")
+ .mockResolvedValueOnce("100000");
+ mockFormatPaymentOption.mockResolvedValue("formatted payment option");
+
const result = await provider.discoverX402Services(makeMockWalletProvider("base-sepolia"), {
maxUsdcPrice: 0.1,
});
@@ -339,6 +383,7 @@ describe("X402ActionProvider", () => {
describe("retryHttpRequestWithX402", () => {
it("should successfully retry with payment", async () => {
mockDecodeXPaymentResponse.mockReturnValue(MOCK_PAYMENT_RESPONSE);
+ mockGetX402Network.mockReturnValue("base-sepolia");
mockRequest.mockResolvedValue({
status: 200,
@@ -383,6 +428,7 @@ describe("X402ActionProvider", () => {
error.request = {};
mockRequest.mockRejectedValue(error);
+ mockGetX402Network.mockReturnValue("base-sepolia");
const result = await provider.retryWithX402(makeMockWalletProvider("base-sepolia"), {
url: "https://www.x402.org/protected",
@@ -436,6 +482,8 @@ describe("X402ActionProvider", () => {
});
it("should handle successful non-payment requests", async () => {
+ mockDecodeXPaymentResponse.mockReturnValue(null); // No payment made
+
mockRequest.mockResolvedValue({
status: 200,
statusText: "OK",
diff --git a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
index b2e88c991..3d9aaeaed 100644
--- a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
+++ b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts
@@ -8,7 +8,7 @@ import {
DirectX402RequestSchema,
ListX402ServicesSchema,
} from "./schemas";
-import { EvmWalletProvider, CdpSolanaWalletProvider, WalletProvider, SvmWalletProvider } from "../../wallet-providers";
+import { EvmWalletProvider, WalletProvider, SvmWalletProvider } from "../../wallet-providers";
import axios, { AxiosError } from "axios";
import { withPaymentInterceptor, decodeXPaymentResponse } from "x402-axios";
import { PaymentRequirements } from "x402/types";
@@ -27,7 +27,7 @@ const SUPPORTED_NETWORKS = ["base-mainnet", "base-sepolia", "solana-mainnet", "s
/**
* X402ActionProvider provides actions for making HTTP requests, with optional x402 payment handling.
*/
-export class X402ActionProvider extends ActionProvider {
+export class X402ActionProvider extends ActionProvider {
/**
* Creates a new instance of X402ActionProvider.
* Initializes the provider with x402 capabilities.
@@ -46,11 +46,11 @@ export class X402ActionProvider extends ActionProvider {
@CreateAction({
name: "discover_x402_services",
description:
- "Discover available x402 services. Optionally filter by a maximum price in whole units of USDC (only USDC payment options will be considered when filter is applied).",
+ "Discover available x402 services. Only services available on the current network will be returned. Optionally filter by a maximum price in whole units of USDC (only USDC payment options will be considered when filter is applied).",
schema: ListX402ServicesSchema,
})
async discoverX402Services(
- walletProvider: EvmWalletProvider,
+ walletProvider: WalletProvider,
args: z.infer,
): Promise {
try {
@@ -209,7 +209,7 @@ If you receive a 402 Payment Required response, use retry_http_request_with_x402
schema: HttpRequestSchema,
})
async makeHttpRequest(
- walletProvider: EvmWalletProvider,
+ walletProvider: WalletProvider,
args: z.infer,
): Promise {
try {
@@ -291,7 +291,7 @@ DO NOT use this action directly without first trying make_http_request!`,
schema: RetryWithX402Schema,
})
async retryWithX402(
- walletProvider: EvmWalletProvider,
+ walletProvider: WalletProvider,
args: z.infer,
): Promise {
try {
@@ -311,8 +311,25 @@ DO NOT use this action directly without first trying make_http_request!`,
);
}
+ // Check if wallet provider is supported
+ if (
+ !(
+ walletProvider instanceof SvmWalletProvider || walletProvider instanceof EvmWalletProvider
+ )
+ ) {
+ return JSON.stringify(
+ {
+ error: true,
+ message: "Unsupported wallet provider",
+ details: "Only SvmWalletProvider and EvmWalletProvider are supported",
+ },
+ null,
+ 2,
+ );
+ }
+
// Make the request with payment handling
- const account = walletProvider.toSigner();
+ const account = await walletProvider.toSigner();
const paymentSelector = (accepts: PaymentRequirements[]) => {
const { scheme, network, maxAmountRequired, asset } = args.selectedPaymentOption;
@@ -422,15 +439,22 @@ Unless specifically instructed otherwise, prefer the two-step approach with make
args: z.infer,
): Promise {
try {
-
- if ( !(walletProvider instanceof SvmWalletProvider || walletProvider instanceof EvmWalletProvider)) {
- return JSON.stringify({
- error: true,
- message: "Unsupported wallet provider",
- details: "Only CdpSolanaWalletProvider and EvmWalletProvider are supported",
- }, null, 2);
+ if (
+ !(
+ walletProvider instanceof SvmWalletProvider || walletProvider instanceof EvmWalletProvider
+ )
+ ) {
+ return JSON.stringify(
+ {
+ error: true,
+ message: "Unsupported wallet provider",
+ details: "Only SvmWalletProvider and EvmWalletProvider are supported",
+ },
+ null,
+ 2,
+ );
}
- const account = walletProvider instanceof CdpSolanaWalletProvider ? walletProvider.serverAccount : walletProvider.toSigner();
+ const account = await walletProvider.toSigner();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const api = withPaymentInterceptor(axios.create({}), account as any);
@@ -477,8 +501,7 @@ Unless specifically instructed otherwise, prefer the two-step approach with make
* @param network - The network to check support for
* @returns True if the network is supported, false otherwise
*/
- supportsNetwork = (network: Network) =>
- SUPPORTED_NETWORKS.includes(network.networkId!);
+ supportsNetwork = (network: Network) => SUPPORTED_NETWORKS.includes(network.networkId!);
}
export const x402ActionProvider = () => new X402ActionProvider();
diff --git a/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts b/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
index f12b1055a..257ae1a3d 100644
--- a/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.ts
@@ -1,4 +1,5 @@
import { CdpClient } from "@coinbase/cdp-sdk";
+import type { KeyPairSigner } from "@solana/kit";
import {
clusterApiUrl,
ComputeBudgetProgram,
@@ -12,6 +13,7 @@ import {
SystemProgram,
VersionedTransaction,
} from "@solana/web3.js";
+import bs58 from "bs58";
import { Network } from "../network";
import {
SOLANA_DEVNET_NETWORK,
@@ -22,7 +24,7 @@ import {
SOLANA_TESTNET_NETWORK_ID,
} from "../network/svm";
import { WalletProviderWithClient, CdpWalletProviderConfig } from "./cdpShared";
-import { SvmWalletProvider } from "./svmWalletProvider";
+import { SvmWalletProvider, createSignerFromBytes } from "./svmWalletProvider";
interface ConfigureCdpSolanaWalletProviderWithWalletOptions {
/**
@@ -53,7 +55,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
#connection: Connection;
#cdp: CdpClient;
#network: Network;
- public serverAccount: Awaited>;
+ #serverAccount: Awaited>;
/**
* Constructs a new CdpSolanaWalletProvider.
@@ -63,7 +65,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
private constructor(config: ConfigureCdpSolanaWalletProviderWithWalletOptions) {
super();
- this.serverAccount = config.serverAccount;
+ this.#serverAccount = config.serverAccount;
this.#cdp = config.cdp;
this.#connection = config.connection;
this.#network = config.network;
@@ -138,8 +140,8 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
*/
async exportWallet(): Promise<{ name: string | undefined; address: `0x${string}` }> {
return {
- name: this.serverAccount.name,
- address: this.serverAccount.address as `0x${string}`,
+ name: this.#serverAccount.name,
+ address: this.#serverAccount.address as `0x${string}`,
};
}
@@ -158,7 +160,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
* @returns The wallet's public key
*/
getPublicKey(): PublicKey {
- return new PublicKey(this.serverAccount.address);
+ return new PublicKey(this.#serverAccount.address);
}
/**
@@ -167,7 +169,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
* @returns The base58 encoded address of the wallet
*/
getAddress(): string {
- return this.serverAccount.address;
+ return this.#serverAccount.address;
}
/**
@@ -200,7 +202,7 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
const signedTransactionResponse = await this.#cdp.solana.signTransaction({
transaction: encodedSerializedTransaction,
- address: this.serverAccount.address,
+ address: this.#serverAccount.address,
});
const signedTransactionBytes = Buffer.from(
@@ -290,16 +292,16 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
*/
async signMessage(message: Uint8Array): Promise {
// Convert Uint8Array to string for CDP SDK
- const messageString = Buffer.from(message).toString('utf8');
-
+ const messageString = Buffer.from(message).toString("utf8");
+
const { signature } = await this.#cdp.solana.signMessage({
- address: this.serverAccount.address,
+ address: this.#serverAccount.address,
message: messageString,
});
-
+
// Convert signature string back to Uint8Array
// CDP returns signature as a hex string, convert to bytes
- return new Uint8Array(Buffer.from(signature, 'hex'));
+ return new Uint8Array(Buffer.from(signature, "hex"));
}
/**
@@ -351,12 +353,21 @@ export class CdpSolanaWalletProvider extends SvmWalletProvider implements Wallet
return signature;
}
- /**
- * Get the keypair for this wallet.
+ /**
+ * Get the keypair signer for this wallet.
*
- * @returns The CryptoKeyPair for KeyPairSigner compatibility
+ * @returns The KeyPairSigner
*/
- async getKeyPair(): Promise {
- throw new Error("getKeyPair is not supported for CdpSolanaWalletProvider");
- }
+ async getKeyPairSigner(): Promise {
+ // Export the private key from CDP
+ const exportedPrivateKey = await this.#cdp.solana.exportAccount({
+ address: this.#serverAccount.address,
+ });
+
+ // Decode the base58 encoded private key to get the full 64-byte key
+ const fullKeyBytes = bs58.decode(exportedPrivateKey);
+
+ // Create and return the KeyPairSigner using the full key bytes
+ return createSignerFromBytes(fullKeyBytes);
+ }
}
diff --git a/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts b/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
index b395edd2a..d394d581f 100644
--- a/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.ts
@@ -1,4 +1,5 @@
import { PrivyClient, SolanaCaip2ChainId } from "@privy-io/server-auth";
+import type { KeyPairSigner } from "@solana/kit";
import { SvmWalletProvider } from "./svmWalletProvider";
import {
RpcResponseAndContext,
@@ -261,12 +262,12 @@ export class PrivySvmWalletProvider extends SvmWalletProvider {
throw new Error("Message signing is not supported yet for PrivySvmWalletProvider");
}
- /**
- * Get the keypair for this wallet.
+ /**
+ * Get the keypair signer for this wallet.
*
- * @returns The CryptoKeyPair for KeyPairSigner compatibility
+ * @returns The KeyPairSigner
*/
- async getKeyPair(): Promise {
- throw new Error("getKeyPair is not supported for PrivySvmWalletProvider");
- }
+ async getKeyPairSigner(): Promise {
+ throw new Error("getKeyPairSigner is not supported for PrivySvmWalletProvider");
+ }
}
diff --git a/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts b/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
index b1f248c6d..f488e72d1 100644
--- a/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/solanaKeypairWalletProvider.ts
@@ -1,4 +1,4 @@
-import { SvmWalletProvider } from "./svmWalletProvider";
+import { SvmWalletProvider, createSignerFromBytes } from "./svmWalletProvider";
import { Network } from "../network";
import {
Connection,
@@ -26,7 +26,7 @@ import {
SOLANA_TESTNET_GENESIS_BLOCK_HASH,
SOLANA_TESTNET_NETWORK_ID,
} from "../network/svm";
-import { createKeyPairFromBytes } from "@solana/keys";
+import { KeyPairSigner } from "@solana/kit";
/**
* SolanaKeypairWalletProvider is a wallet provider that uses a local Solana keypair.
@@ -37,7 +37,6 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
#keypair: Keypair;
#connection: Connection;
#genesisHash: string;
- #cryptoKeyPair?: CryptoKeyPair;
/**
* Creates a new SolanaKeypairWalletProvider
@@ -69,14 +68,6 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
} else {
throw new Error(`Unknown network with genesis hash: ${genesisHash}`);
}
- // Initialize crypto key pair asynchronously
- this.#initCryptoKeyPair().catch(error => {
- console.error("Failed to initialize CryptoKeyPair:", error);
- });
- }
-
- async #initCryptoKeyPair() {
- this.#cryptoKeyPair = await createKeyPairFromBytes(this.#keypair.secretKey /*, extractable? = false */);
}
/**
@@ -341,7 +332,6 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
async signMessage(_: Uint8Array): Promise {
throw new Error("Message signing is not supported yet for SolanaKeypairWalletProvider");
}
-
/**
* Request SOL tokens from the Solana faucet. This method only works on devnet and testnet networks.
@@ -351,21 +341,14 @@ export class SolanaKeypairWalletProvider extends SvmWalletProvider {
*/
async requestAirdrop(lamports: number): Promise {
return await this.#connection.requestAirdrop(this.#keypair.publicKey, lamports);
-
}
-
/**
- * Get the keypair for this wallet.
+ * Get the keypair signer for this wallet.
*
- * @returns The CryptoKeyPair for KeyPairSigner compatibility
+ * @returns The KeyPairSigner
*/
- async getKeyPair(): Promise {
- if (!this.#cryptoKeyPair) {
- // Wait for initialization to complete
- await this.#initCryptoKeyPair();
- }
- return this.#cryptoKeyPair!;
+ async getKeyPairSigner(): Promise {
+ return createSignerFromBytes(this.#keypair.secretKey);
}
}
-
diff --git a/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts b/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
index 9986e714a..ae6d61a7b 100644
--- a/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
@@ -10,17 +10,11 @@ import {
VersionedTransaction,
SignatureResult,
} from "@solana/web3.js";
-import {
- isKeyPairSigner,
+import {
+ isKeyPairSigner,
KeyPairSigner,
-} from "@solana/kit";
-import type {
- Address,
- TransactionMessageBytes,
- SignaturesMap,
- BaseTransactionSignerConfig,
- SignatureBytes,
- MessagePartialSignerConfig
+ createKeyPairSignerFromBytes,
+ createKeyPairSignerFromPrivateKeyBytes,
} from "@solana/kit";
/**
@@ -29,6 +23,30 @@ import type {
* @abstract
*/
export abstract class SvmWalletProvider extends WalletProvider {
+ /**
+ * Convert the wallet provider to a KeyPairSigner.
+ *
+ * @returns The KeyPairSigner instance
+ */
+ async toSigner(): Promise {
+ return this.getKeyPairSigner();
+ }
+
+ /**
+ * Check if this wallet's signer is a valid KeyPairSigner.
+ *
+ * @returns True if the signer is a valid KeyPairSigner, false otherwise
+ */
+ async isKeyPairSigner(): Promise {
+ try {
+ const signer = await this.toSigner();
+ return isKeyPairSigner(signer);
+ } catch (error) {
+ console.warn("Error checking KeyPairSigner compatibility:", error);
+ return false;
+ }
+ }
+
/**
* Get the connection instance.
*
@@ -96,76 +114,30 @@ export abstract class SvmWalletProvider extends WalletProvider {
*/
abstract signMessage(message: Uint8Array): Promise;
-
- /**
- * Convert the wallet provider to a Signer compatible with x402.
- *
- * @returns The signer instance
- */
- async toSigner(): Promise {
- const self = this;
-
- return {
- address: self.getAddress() as Address,
-
- keyPair: await self.getKeyPair(),
-
- signTransactions: async (
- txs: readonly Readonly<{
- messageBytes: TransactionMessageBytes;
- signatures: SignaturesMap;
- }>[],
- _config?: BaseTransactionSignerConfig
- ) => {
- const out: { messageBytes: TransactionMessageBytes; signatures: SignaturesMap; }[] = [];
- for (const { messageBytes, signatures } of txs) {
- const sig = await self.signMessage(new Uint8Array(messageBytes));
- const addr = self.getAddress() as Address;
- out.push({ messageBytes, signatures: { ...signatures, [addr]: sig as SignatureBytes } });
- }
- return out;
- },
-
- signMessages: async (
- msgs: readonly Readonly<{
- content: Uint8Array;
- signatures: Readonly>;
- }>[],
- _config?: Readonly
- ) => {
- const out: {
- content: Uint8Array;
- signatures: Readonly>;
- }[] = [];
- for (const { content, signatures } of msgs) {
- const sig = await self.signMessage(new Uint8Array(content));
- const addr = self.getAddress() as Address;
- out.push({ content, signatures: { ...signatures, [addr]: sig as SignatureBytes } });
- }
- return out;
- },
- };
- }
-
/**
* Get the keypair for this wallet.
*
* @returns The CryptoKeyPair for KeyPairSigner compatibility
*/
- abstract getKeyPair(): Promise;
+ abstract getKeyPairSigner(): Promise;
+}
- /**
- * Check if this wallet's signer is a valid KeyPairSigner for x402 compatibility.
- *
- * @returns True if the signer is a valid KeyPairSigner, false otherwise
- */
- async isKeyPairSigner(): Promise {
- try {
- const signer = await this.toSigner();
- return isKeyPairSigner(signer);
- } catch (error) {
- console.warn("Error checking KeyPairSigner compatibility:", error);
- return false;
- }
+/**
+ * Create a KeyPairSigner from raw bytes.
+ *
+ * @param bytes - The raw key bytes (32 bytes for private key only, 64 bytes for private + public key)
+ * @returns A KeyPairSigner instance
+ * @throws Error if the byte length is not 32 or 64
+ */
+export async function createSignerFromBytes(bytes: Uint8Array): Promise {
+ // generate a keypair signer from the bytes based on the byte-length
+ // 64 bytes represents concatenated private + public key
+ if (bytes.length === 64) {
+ return await createKeyPairSignerFromBytes(bytes);
+ }
+ // 32 bytes represents only the private key
+ if (bytes.length === 32) {
+ return await createKeyPairSignerFromPrivateKeyBytes(bytes);
}
+ throw new Error(`Unexpected key length: ${bytes.length}. Expected 32 or 64 bytes.`);
}
diff --git a/typescript/examples/langchain-cdp-chatbot/chatbot.ts b/typescript/examples/langchain-cdp-chatbot/chatbot.ts
index 3eb2d5f5b..0ea5fb408 100644
--- a/typescript/examples/langchain-cdp-chatbot/chatbot.ts
+++ b/typescript/examples/langchain-cdp-chatbot/chatbot.ts
@@ -115,12 +115,6 @@ async function initializeAgent() {
? await CdpSolanaWalletProvider.configureWithWallet(cdpWalletConfig)
: await CdpEvmWalletProvider.configureWithWallet(cdpWalletConfig);
- // Test SVM signer compatibility if using Solana wallet
- if (isSolanaWalletProvider(walletProvider)) {
- const isCompatible = walletProvider.isKeyPairSigner();
- console.log(`✅ SVM wallet KeyPairSigner compatibility: ${isCompatible}`);
- }
-
const actionProviders = [
walletActionProvider(),
cdpApiActionProvider(),
diff --git a/typescript/examples/langchain-solana-chatbot/chatbot.ts b/typescript/examples/langchain-solana-chatbot/chatbot.ts
index dfe08924e..6d582ec15 100644
--- a/typescript/examples/langchain-solana-chatbot/chatbot.ts
+++ b/typescript/examples/langchain-solana-chatbot/chatbot.ts
@@ -90,10 +90,6 @@ async function initializeAgent() {
walletProvider = await SolanaKeypairWalletProvider.fromNetwork(network, solanaPrivateKey);
}
- // Test SVM signer compatibility if using Solana wallet
- const isCompatible = await walletProvider.isKeyPairSigner();
- console.log(`✅ SVM wallet KeyPairSigner compatibility: ${isCompatible}`);
-
// Initialize AgentKit
const agentkit = await AgentKit.from({
cdpApiKeyId: process.env.CDP_API_KEY_ID,
From 65be922f89a44f2fb1752753e474c7fd60bf0b44 Mon Sep 17 00:00:00 2001
From: Philippe d'Argent
Date: Thu, 25 Sep 2025 13:17:55 +0900
Subject: [PATCH 4/4] updated tests
---
typescript/.changeset/empty-baths-send.md | 5 +
typescript/agentkit/package.json | 1 -
.../src/action-providers/x402/README.md | 2 +
.../src/action-providers/x402/utils.ts | 2 +-
.../cdpSolanaWalletProvider.test.ts | 21 +++
.../privySvmWalletProvider.test.ts | 17 +++
.../svmWalletProvider.test.ts | 18 +++
.../src/wallet-providers/svmWalletProvider.ts | 3 +-
.../langchain-solana-chatbot/chatbot.ts | 7 +-
typescript/pnpm-lock.yaml | 128 ++----------------
10 files changed, 85 insertions(+), 119 deletions(-)
create mode 100644 typescript/.changeset/empty-baths-send.md
diff --git a/typescript/.changeset/empty-baths-send.md b/typescript/.changeset/empty-baths-send.md
new file mode 100644
index 000000000..a125484d9
--- /dev/null
+++ b/typescript/.changeset/empty-baths-send.md
@@ -0,0 +1,5 @@
+---
+"@coinbase/agentkit": patch
+---
+
+Added svm support for x402 action
diff --git a/typescript/agentkit/package.json b/typescript/agentkit/package.json
index 0bdd5a368..dd8ca4459 100644
--- a/typescript/agentkit/package.json
+++ b/typescript/agentkit/package.json
@@ -48,7 +48,6 @@
"@jup-ag/api": "^6.0.39",
"@privy-io/public-api": "2.18.5",
"@privy-io/server-auth": "1.18.4",
- "@solana/keys": "^3.0.3",
"@solana/kit": "^2.1.1",
"@solana/spl-token": "^0.4.12",
"@solana/web3.js": "^1.98.1",
diff --git a/typescript/agentkit/src/action-providers/x402/README.md b/typescript/agentkit/src/action-providers/x402/README.md
index a478754bd..27ffec4a7 100644
--- a/typescript/agentkit/src/action-providers/x402/README.md
+++ b/typescript/agentkit/src/action-providers/x402/README.md
@@ -135,6 +135,8 @@ Successful responses include payment proof when payment was made:
The x402 provider currently supports the following networks:
- `base-mainnet`
- `base-sepolia`
+- `solana-mainnet`
+- `solana-devnet`
The provider requires EVM-compatible networks where the wallet can sign payment transactions.
diff --git a/typescript/agentkit/src/action-providers/x402/utils.ts b/typescript/agentkit/src/action-providers/x402/utils.ts
index 4cb7c0454..33ee9c0fe 100644
--- a/typescript/agentkit/src/action-providers/x402/utils.ts
+++ b/typescript/agentkit/src/action-providers/x402/utils.ts
@@ -15,7 +15,7 @@ export type X402Network = "base" | "base-sepolia" | "solana" | "solana-devnet";
*/
const SOLANA_USDC_ADDRESSES = {
"solana-devnet": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
- "solana-mainnet": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // Standard USDC on Solana mainnet
+ "solana-mainnet": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
} as const;
/**
diff --git a/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.test.ts b/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.test.ts
index 0adbfe27e..d10601bac 100644
--- a/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.test.ts
+++ b/typescript/agentkit/src/wallet-providers/cdpSolanaWalletProvider.test.ts
@@ -401,4 +401,25 @@ describe("CdpSolanaWalletProvider", () => {
await expect(provider.nativeTransfer(invalidAddress, amount)).rejects.toThrow();
});
});
+
+ // =========================================================
+ // KeyPairSigner tests
+ // =========================================================
+
+ describe("KeyPairSigner", () => {
+ it("should handle errors when getting KeyPairSigner", async () => {
+ // Mock exportAccount to throw an error
+ mockCdpClient.solana.exportAccount = jest.fn().mockRejectedValue(new Error("Export failed"));
+
+ await expect(provider.getKeyPairSigner()).rejects.toThrow("Export failed");
+ });
+
+ it("should handle errors gracefully in isKeyPairSigner", async () => {
+ // Mock exportAccount to throw an error
+ mockCdpClient.solana.exportAccount = jest.fn().mockRejectedValue(new Error("Export failed"));
+
+ const isValid = await provider.isKeyPairSigner();
+ expect(isValid).toBe(false);
+ });
+ });
});
diff --git a/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.test.ts b/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.test.ts
index d1c930877..c046aaf6b 100644
--- a/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.test.ts
+++ b/typescript/agentkit/src/wallet-providers/privySvmWalletProvider.test.ts
@@ -342,5 +342,22 @@ describe("PrivySvmWalletProvider", () => {
await expect(provider.getBalance()).rejects.toThrow("RPC endpoint error");
});
+
+ it("should throw error when trying to get KeyPairSigner", async () => {
+ await expect(provider.getKeyPairSigner()).rejects.toThrow(
+ "getKeyPairSigner is not supported for PrivySvmWalletProvider",
+ );
+ });
+
+ it("should throw error when trying to convert to signer", async () => {
+ await expect(provider.toSigner()).rejects.toThrow(
+ "getKeyPairSigner is not supported for PrivySvmWalletProvider",
+ );
+ });
+
+ it("should return false for isKeyPairSigner", async () => {
+ const isValid = await provider.isKeyPairSigner();
+ expect(isValid).toBe(false);
+ });
});
});
diff --git a/typescript/agentkit/src/wallet-providers/svmWalletProvider.test.ts b/typescript/agentkit/src/wallet-providers/svmWalletProvider.test.ts
index e98ab0de0..50740a2d2 100644
--- a/typescript/agentkit/src/wallet-providers/svmWalletProvider.test.ts
+++ b/typescript/agentkit/src/wallet-providers/svmWalletProvider.test.ts
@@ -87,4 +87,22 @@ describe("SvmWalletProvider", () => {
expect(getConnectionDescriptor).toBeDefined();
expect(typeof getConnectionDescriptor!.value).toBe("function");
});
+
+ it("should have toSigner method", () => {
+ const toSignerDescriptor = Object.getOwnPropertyDescriptor(
+ SvmWalletProvider.prototype,
+ "toSigner",
+ );
+ expect(toSignerDescriptor).toBeDefined();
+ expect(typeof toSignerDescriptor!.value).toBe("function");
+ });
+
+ it("should have isKeyPairSigner method", () => {
+ const isKeyPairSignerDescriptor = Object.getOwnPropertyDescriptor(
+ SvmWalletProvider.prototype,
+ "isKeyPairSigner",
+ );
+ expect(isKeyPairSignerDescriptor).toBeDefined();
+ expect(typeof isKeyPairSignerDescriptor!.value).toBe("function");
+ });
});
diff --git a/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts b/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
index ae6d61a7b..9531052ce 100644
--- a/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
+++ b/typescript/agentkit/src/wallet-providers/svmWalletProvider.ts
@@ -41,8 +41,7 @@ export abstract class SvmWalletProvider extends WalletProvider {
try {
const signer = await this.toSigner();
return isKeyPairSigner(signer);
- } catch (error) {
- console.warn("Error checking KeyPairSigner compatibility:", error);
+ } catch {
return false;
}
}
diff --git a/typescript/examples/langchain-solana-chatbot/chatbot.ts b/typescript/examples/langchain-solana-chatbot/chatbot.ts
index 6d582ec15..05b9c5cf9 100644
--- a/typescript/examples/langchain-solana-chatbot/chatbot.ts
+++ b/typescript/examples/langchain-solana-chatbot/chatbot.ts
@@ -95,7 +95,12 @@ async function initializeAgent() {
cdpApiKeyId: process.env.CDP_API_KEY_ID,
cdpApiKeySecret: process.env.CDP_API_KEY_SECRET,
walletProvider,
- actionProviders: [splActionProvider(), walletActionProvider(), cdpApiActionProvider(), x402ActionProvider()],
+ actionProviders: [
+ splActionProvider(),
+ walletActionProvider(),
+ cdpApiActionProvider(),
+ x402ActionProvider(),
+ ],
});
const tools = await getLangChainTools(agentkit);
diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml
index d9d095f33..8533e8e1f 100644
--- a/typescript/pnpm-lock.yaml
+++ b/typescript/pnpm-lock.yaml
@@ -89,9 +89,6 @@ importers:
'@privy-io/server-auth':
specifier: 1.18.4
version: 1.18.4(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(viem@2.23.15(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2))
- '@solana/keys':
- specifier: ^3.0.3
- version: 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
'@solana/kit':
specifier: ^2.1.1
version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -1812,12 +1809,6 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
- '@solana/assertions@3.0.3':
- resolution: {integrity: sha512-2qspxdbWp2y62dfCIlqeWQr4g+hE8FYSSwcaP6itwMwGRb8393yDGCJfI/znuzJh6m/XVWhMHIgFgsBwnevCmg==}
- engines: {node: '>=20.18.0'}
- peerDependencies:
- typescript: '>=5.3.3'
-
'@solana/buffer-layout-utils@0.2.0':
resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==}
engines: {node: '>= 10'}
@@ -1843,12 +1834,6 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
- '@solana/codecs-core@3.0.3':
- resolution: {integrity: sha512-emKykJ3h1DmnDOY29Uv9eJXP8E/FHzvlUBJ6te+5EbKdFjj7vdlKYPfDxOI6iGdXTY+YC/ELtbNBh6QwF2uEDQ==}
- engines: {node: '>=20.18.0'}
- peerDependencies:
- typescript: '>=5.3.3'
-
'@solana/codecs-data-structures@2.0.0-rc.1':
resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==}
peerDependencies:
@@ -1877,12 +1862,6 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
- '@solana/codecs-numbers@3.0.3':
- resolution: {integrity: sha512-pfXkH9J0glrM8qj6389GAn30+cJOxzXLR2FsPOHCUMXrqLhGjMMZAWhsQkpOQ37SGc/7EiQsT/gmyGC7gxHqJQ==}
- engines: {node: '>=20.18.0'}
- peerDependencies:
- typescript: '>=5.3.3'
-
'@solana/codecs-strings@2.0.0-rc.1':
resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==}
peerDependencies:
@@ -1896,13 +1875,6 @@ packages:
fastestsmallesttextencoderdecoder: ^1.0.22
typescript: '>=5.3.3'
- '@solana/codecs-strings@3.0.3':
- resolution: {integrity: sha512-VHBXnnTVtcQ1j+7Vrz+qSYo38no+jiHRdGnhFspRXEHNJbllzwKqgBE7YN3qoIXH+MKxgJUcwO5KHmdzf8Wn2A==}
- engines: {node: '>=20.18.0'}
- peerDependencies:
- fastestsmallesttextencoderdecoder: ^1.0.22
- typescript: '>=5.3.3'
-
'@solana/codecs@2.0.0-rc.1':
resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==}
peerDependencies:
@@ -1934,13 +1906,6 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
- '@solana/errors@3.0.3':
- resolution: {integrity: sha512-1l84xJlHNva6io62PcYfUamwWlc0eM95nHgCrKX0g0cLoC6D6QHYPCEbEVkR+C5UtP9JDgyQM8MFiv+Ei5tO9Q==}
- engines: {node: '>=20.18.0'}
- hasBin: true
- peerDependencies:
- typescript: '>=5.3.3'
-
'@solana/fast-stable-stringify@2.3.0':
resolution: {integrity: sha512-KfJPrMEieUg6D3hfQACoPy0ukrAV8Kio883llt/8chPEG3FVTX9z/Zuf4O01a15xZmBbmQ7toil2Dp0sxMJSxw==}
engines: {node: '>=20.18.0'}
@@ -1965,12 +1930,6 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
- '@solana/keys@3.0.3':
- resolution: {integrity: sha512-tp8oK9tMadtSIc4vF4aXXWkPd4oU5XPW8nf28NgrGDWGt25fUHIydKjkf2hPtMt9i1WfRyQZ33B5P3dnsNqcPQ==}
- engines: {node: '>=20.18.0'}
- peerDependencies:
- typescript: '>=5.3.3'
-
'@solana/kit@2.3.0':
resolution: {integrity: sha512-sb6PgwoW2LjE5oTFu4lhlS/cGt/NB3YrShEyx7JgWFWysfgLdJnhwWThgwy/4HjNsmtMrQGWVls0yVBHcMvlMQ==}
engines: {node: '>=20.18.0'}
@@ -1983,12 +1942,6 @@ packages:
peerDependencies:
typescript: '>=5.3.3'
- '@solana/nominal-types@3.0.3':
- resolution: {integrity: sha512-aZavCiexeUAoMHRQg4s1AHkH3wscbOb70diyfjhwZVgFz1uUsFez7csPp9tNFkNolnadVb2gky7yBk3IImQJ6A==}
- engines: {node: '>=20.18.0'}
- peerDependencies:
- typescript: '>=5.3.3'
-
'@solana/options@2.0.0-rc.1':
resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==}
peerDependencies:
@@ -2925,10 +2878,6 @@ packages:
resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
- chalk@5.6.2:
- resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
- engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
-
char-regex@1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
@@ -3032,10 +2981,6 @@ packages:
resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==}
engines: {node: '>=18'}
- commander@14.0.0:
- resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==}
- engines: {node: '>=20'}
-
commander@14.0.1:
resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==}
engines: {node: '>=20'}
@@ -6443,7 +6388,7 @@ snapshots:
'@babel/traverse': 7.27.0
'@babel/types': 7.27.0
convert-source-map: 2.0.0
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -6601,7 +6546,7 @@ snapshots:
'@babel/parser': 7.27.0
'@babel/template': 7.27.0
'@babel/types': 7.27.0
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -7885,7 +7830,7 @@ snapshots:
bufferutil: 4.0.9
cross-fetch: 4.1.0
date-fns: 2.30.0
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
eciesjs: 0.4.15
eventemitter2: 6.4.9
readable-stream: 3.6.2
@@ -7909,7 +7854,7 @@ snapshots:
'@paulmillr/qr': 0.2.1
bowser: 2.12.1
cross-fetch: 4.1.0
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
eciesjs: 0.4.15
eth-rpc-errors: 4.0.3
eventemitter2: 6.4.9
@@ -7936,7 +7881,7 @@ snapshots:
'@scure/base': 1.2.6
'@types/debug': 4.1.12
'@types/lodash': 4.17.20
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
lodash: 4.17.21
pony-cause: 2.1.11
semver: 7.7.1
@@ -7948,7 +7893,7 @@ snapshots:
dependencies:
'@ethereumjs/tx': 4.2.0
'@types/debug': 4.1.12
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
semver: 7.7.1
superstruct: 1.0.4
transitivePeerDependencies:
@@ -7961,7 +7906,7 @@ snapshots:
'@noble/hashes': 1.8.0
'@scure/base': 1.2.6
'@types/debug': 4.1.12
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
pony-cause: 2.1.11
semver: 7.7.1
uuid: 9.0.1
@@ -7975,7 +7920,7 @@ snapshots:
'@noble/hashes': 1.8.0
'@scure/base': 1.2.6
'@types/debug': 4.1.12
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
pony-cause: 2.1.11
semver: 7.7.1
uuid: 9.0.1
@@ -8540,11 +8485,6 @@ snapshots:
'@solana/errors': 2.3.0(typescript@5.8.2)
typescript: 5.8.2
- '@solana/assertions@3.0.3(typescript@5.8.2)':
- dependencies:
- '@solana/errors': 3.0.3(typescript@5.8.2)
- typescript: 5.8.2
-
'@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)':
dependencies:
'@solana/buffer-layout': 4.0.1
@@ -8576,11 +8516,6 @@ snapshots:
'@solana/errors': 2.3.0(typescript@5.8.2)
typescript: 5.8.2
- '@solana/codecs-core@3.0.3(typescript@5.8.2)':
- dependencies:
- '@solana/errors': 3.0.3(typescript@5.8.2)
- typescript: 5.8.2
-
'@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.8.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2)
@@ -8613,12 +8548,6 @@ snapshots:
'@solana/errors': 2.3.0(typescript@5.8.2)
typescript: 5.8.2
- '@solana/codecs-numbers@3.0.3(typescript@5.8.2)':
- dependencies:
- '@solana/codecs-core': 3.0.3(typescript@5.8.2)
- '@solana/errors': 3.0.3(typescript@5.8.2)
- typescript: 5.8.2
-
'@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2)
@@ -8635,14 +8564,6 @@ snapshots:
fastestsmallesttextencoderdecoder: 1.0.22
typescript: 5.8.2
- '@solana/codecs-strings@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
- dependencies:
- '@solana/codecs-core': 3.0.3(typescript@5.8.2)
- '@solana/codecs-numbers': 3.0.3(typescript@5.8.2)
- '@solana/errors': 3.0.3(typescript@5.8.2)
- fastestsmallesttextencoderdecoder: 1.0.22
- typescript: 5.8.2
-
'@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2)
@@ -8683,12 +8604,6 @@ snapshots:
commander: 14.0.1
typescript: 5.8.2
- '@solana/errors@3.0.3(typescript@5.8.2)':
- dependencies:
- chalk: 5.6.2
- commander: 14.0.0
- typescript: 5.8.2
-
'@solana/fast-stable-stringify@2.3.0(typescript@5.8.2)':
dependencies:
typescript: 5.8.2
@@ -8714,17 +8629,6 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- '@solana/keys@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
- dependencies:
- '@solana/assertions': 3.0.3(typescript@5.8.2)
- '@solana/codecs-core': 3.0.3(typescript@5.8.2)
- '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
- '@solana/errors': 3.0.3(typescript@5.8.2)
- '@solana/nominal-types': 3.0.3(typescript@5.8.2)
- typescript: 5.8.2
- transitivePeerDependencies:
- - fastestsmallesttextencoderdecoder
-
'@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
@@ -8754,10 +8658,6 @@ snapshots:
dependencies:
typescript: 5.8.2
- '@solana/nominal-types@3.0.3(typescript@5.8.2)':
- dependencies:
- typescript: 5.8.2
-
'@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2)
@@ -10472,8 +10372,6 @@ snapshots:
chalk@5.4.1: {}
- chalk@5.6.2: {}
-
char-regex@1.0.2: {}
chardet@0.7.0: {}
@@ -10572,8 +10470,6 @@ snapshots:
commander@13.1.0: {}
- commander@14.0.0: {}
-
commander@14.0.1: {}
commander@2.20.3: {}
@@ -10694,6 +10590,10 @@ snapshots:
dependencies:
ms: 2.1.2
+ debug@4.4.0:
+ dependencies:
+ ms: 2.1.3
+
debug@4.4.0(supports-color@5.5.0):
dependencies:
ms: 2.1.3
@@ -11979,7 +11879,7 @@ snapshots:
istanbul-lib-source-maps@4.0.1:
dependencies:
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
@@ -13169,7 +13069,7 @@ snapshots:
portfinder@1.0.35:
dependencies:
async: 3.2.6
- debug: 4.4.0(supports-color@5.5.0)
+ debug: 4.4.0
transitivePeerDependencies:
- supports-color