Solana Wallet APIs require an Ed25519 signer that can sign serialized Solana transactions. The signer parameter of createSmartWalletClient accepts a SolanaSigner when you use a Solana chain ID such as "solana:devnet" or "solana:mainnet".
type SolanaSigner = {
address: string;
signTransaction(input: {
transaction: Uint8Array;
}): Promise<{ signedTransaction: Uint8Array }>;
};Most apps do not need to implement this interface directly. Some providers already return this shape. For the rest, use an adapter from @alchemy/wallet-apis/solana.
npm install @alchemy/wallet-apisInstall the optional package that matches your signer source:
@solana/wallet-adapter-react:@solana/wallet-adapter-react @solana/web3.js- Injected providers such as Phantom:
@solana/web3.js @solana/kitsigners:@solana/kit- Wallet Standard discovery:
@wallet-standard/app
| What you have | What to use |
|---|---|
Privy Solana wallet from @privy-io/react-auth/solana | No adapter — see the Privy guide |
useWallet() from @solana/wallet-adapter-react | fromWalletAdapter |
Injected provider such as window.phantom.solana | fromWalletAdapter |
@solana/kit KeyPairSigner or TransactionPartialSigner | fromKitSigner |
| Raw Ed25519 keypair or key management service | fromKeypair |
| Low-level Wallet Standard wallet | fromWalletStandard |
Use fromWalletAdapter for wallets returned by useWallet(). This adapter handles the Uint8Array to VersionedTransaction conversion required by the wallet adapter API.
import {
alchemyWalletTransport,
createSmartWalletClient,
} from "@alchemy/wallet-apis";
import { fromWalletAdapter } from "@alchemy/wallet-apis/solana";
import { useWallet } from "@solana/wallet-adapter-react";
import { useMemo } from "react";
function useWalletAdapterSolanaClient() {
const wallet = useWallet();
return useMemo(() => {
if (!wallet.publicKey || !wallet.signTransaction) return undefined;
return createSmartWalletClient({
signer: fromWalletAdapter({
publicKey: wallet.publicKey,
signTransaction: wallet.signTransaction,
}),
transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
chain: "solana:devnet",
paymaster: { policyId: "YOUR_SOLANA_POLICY_ID" },
});
}, [wallet.publicKey, wallet.signTransaction]);
}Use the same fromWalletAdapter adapter for injected providers that expose publicKey and signTransaction(VersionedTransaction), such as window.phantom.solana.
import {
alchemyWalletTransport,
createSmartWalletClient,
} from "@alchemy/wallet-apis";
import { fromWalletAdapter } from "@alchemy/wallet-apis/solana";
import type { VersionedTransaction } from "@solana/web3.js";
interface PhantomSolanaProvider {
publicKey?: { toBase58(): string };
connect(): Promise<{ publicKey: { toBase58(): string } }>;
signTransaction<T extends VersionedTransaction>(transaction: T): Promise<T>;
}
declare global {
interface Window {
phantom?: { solana?: PhantomSolanaProvider };
}
}
const phantom = window.phantom?.solana;
if (!phantom) {
throw new Error("Install Phantom to use the injected Solana provider");
}
const { publicKey } = await phantom.connect();
const client = createSmartWalletClient({
signer: fromWalletAdapter({
publicKey,
signTransaction: (transaction) => phantom.signTransaction(transaction),
}),
transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
chain: "solana:devnet",
paymaster: { policyId: "YOUR_SOLANA_POLICY_ID" },
});Use fromKitSigner for @solana/kit signers that implement signTransactions, including KeyPairSigner and TransactionPartialSigner.
import {
alchemyWalletTransport,
createSmartWalletClient,
} from "@alchemy/wallet-apis";
import { fromKitSigner } from "@alchemy/wallet-apis/solana";
import { generateKeyPairSigner } from "@solana/kit";
const kitSigner = await generateKeyPairSigner();
const client = createSmartWalletClient({
signer: fromKitSigner(kitSigner),
transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
chain: "solana:devnet",
paymaster: { policyId: "YOUR_SOLANA_POLICY_ID" },
});Use fromKeypair when your signer exposes an address and a signMessage(bytes) method that returns a 64-byte Ed25519 signature. This is the right adapter for a bare Ed25519 keypair or a key management service.
import {
alchemyWalletTransport,
createSmartWalletClient,
} from "@alchemy/wallet-apis";
import { fromKeypair } from "@alchemy/wallet-apis/solana";
interface Ed25519Keypair {
address: string;
privateKey: CryptoKey;
}
async function loadEd25519Keypair(): Promise<Ed25519Keypair> {
// Load this from your app's key storage or KMS.
throw new Error("Implement loadEd25519Keypair for your app");
}
const { address, privateKey } = await loadEd25519Keypair();
const client = createSmartWalletClient({
signer: fromKeypair({
address,
async signMessage(message) {
const messageBytes = Uint8Array.from(message);
return new Uint8Array(
await crypto.subtle.sign("Ed25519", privateKey, messageBytes),
);
},
}),
transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
chain: "solana:devnet",
paymaster: { policyId: "YOUR_SOLANA_POLICY_ID" },
});Your signMessage implementation must return a valid 64-byte Ed25519 signature for the message bytes. Do not return placeholder bytes; Wallet APIs rejects missing or all-zero signatures.
Use fromWalletStandard when you are working directly with the low-level Wallet Standard API. Most app developers should use Privy or @solana/wallet-adapter-react instead.
import {
alchemyWalletTransport,
createSmartWalletClient,
} from "@alchemy/wallet-apis";
import {
fromWalletStandard,
type WalletStandardAccount,
type WalletStandardWallet,
} from "@alchemy/wallet-apis/solana";
import { getWallets } from "@wallet-standard/app";
type ConnectableWalletStandardWallet = Omit<
WalletStandardWallet,
"features"
> & {
features: {
readonly [name: string]: unknown;
readonly "standard:connect": {
connect(): Promise<{ accounts: readonly WalletStandardAccount[] }>;
};
readonly "solana:signTransaction": unknown;
};
};
function isSolanaStandardWallet(
wallet: WalletStandardWallet,
): wallet is ConnectableWalletStandardWallet {
return (
"standard:connect" in wallet.features &&
"solana:signTransaction" in wallet.features
);
}
const wallets: readonly WalletStandardWallet[] = getWallets().get();
const wallet = wallets.find(isSolanaStandardWallet);
if (!wallet) {
throw new Error("Connect a wallet that supports solana:signTransaction");
}
const { accounts } = await wallet.features["standard:connect"].connect();
const account = accounts[0];
if (!account) {
throw new Error("No Solana account returned by the wallet");
}
const client = createSmartWalletClient({
signer: fromWalletStandard(wallet, account),
transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
chain: "solana:devnet",
paymaster: { policyId: "YOUR_SOLANA_POLICY_ID" },
});Use your Solana signer to: