Use this file to discover all available pages before exploring further.
WalletConnect Pay supports Solana payments, enabling users to pay with native SOL and SPL tokens. This guide covers how to extend your wallet’s WalletConnect Pay integration to support Solana payments end-to-end.
let evmAccounts = evmChains.map { chain in "eip155:\(chain.id):\(evmAddress)"}let solanaAccounts = solanaChains.map { chain in "solana:\(chain.reference):\(solanaPublicKey)"}let options = try await pay.getPaymentOptions( paymentId: paymentId, accounts: evmAccounts + solanaAccounts)
Only advertise Solana accounts if your wallet has Solana keys available. Wallets without Solana support should not include Solana accounts in the request.
Determine the signing wallet for each action using the namespace from the chain ID. Extract the namespace from actions[i].walletRpc.chainId rather than assuming a single wallet type for all actions.
TypeScript
Kotlin
Swift
function getNamespace(chainId: string): string { return chainId.split(':')[0];}for (const action of actions) { const namespace = getNamespace(action.walletRpc.chainId); switch (namespace) { case 'eip155': // Route to EVM wallet await handleEvmAction(action); break; case 'solana': // Route to Solana wallet await handleSolanaAction(action); break; default: throw new Error(`Unsupported namespace: ${namespace}`); }}
fun getNamespace(chainId: String): String = chainId.split(':')[0]for (action in actions) { val namespace = getNamespace(action.walletRpc.chainId) when (namespace) { "eip155" -> handleEvmAction(action) "solana" -> handleSolanaAction(action) else -> error("Unsupported namespace: $namespace") }}
func getNamespace(_ chainId: String) -> String { chainId.split(separator: ":").first.map(String.init) ?? ""}for action in actions { let namespace = getNamespace(action.walletRpc.chainId) switch namespace { case "eip155": try await handleEvmAction(action) case "solana": try await handleSolanaAction(action) default: throw PaymentError.unsupportedNamespace(namespace) }}
For Solana actions, implement the solana_signTransaction method. Sign the transaction and append the signed transaction blob, base64-encoded to the signatures array.
TypeScript
Kotlin
Swift
async function handleSolanaAction(action: Action): Promise<string> { const { method, params } = action.walletRpc; if (method !== 'solana_signTransaction') { throw new Error(`Unsupported Solana method: ${method}`); } // Params are array-wrapped: [{ transaction }] const [{ transaction }] = JSON.parse(params); // Decode the base64 transaction const txBuffer = Buffer.from(transaction, 'base64'); const tx = Transaction.from(txBuffer); // Sign the transaction tx.sign(solanaKeypair); // Return the signed transaction as base64 return tx.serialize().toString('base64');}
suspend fun handleSolanaAction(action: Action): String { val method = action.walletRpc.method require(method == "solana_signTransaction") { "Unsupported Solana method: $method" } // Params are array-wrapped: [{ transaction }] val params = JSONArray(action.walletRpc.params) val txData = params.getJSONObject(0).getString("transaction") // Decode and sign the transaction val txBytes = Base64.decode(txData, Base64.DEFAULT) val signedTx = solanaWallet.signTransaction(txBytes) // Return the signed transaction as base64 return Base64.encodeToString(signedTx, Base64.NO_WRAP)}
func handleSolanaAction(_ action: Action) async throws -> String { let method = action.walletRpc.method guard method == "solana_signTransaction" else { throw PaymentError.unsupportedMethod(method) } // Params are array-wrapped: [{ transaction }] let params = try JSONDecoder().decode( [[String: String]].self, from: action.walletRpc.params.data(using: .utf8)! ) let txData = params[0]["transaction"]! // Decode and sign the transaction let txBytes = Data(base64Encoded: txData)! let signedTx = try solanaWallet.signTransaction(txBytes) // Return the signed transaction as base64 return signedTx.base64EncodedString()}
Return the signed transaction, not the signature. Solana signer libraries may return both a detached bs58 signature and the signed transaction blob. WalletConnect Pay expects the complete base64-encoded signed transaction, not the detached signature.
Do not implement solana_signAndSendTransaction for Pay flows. The WalletConnect Pay backend handles broadcasting the signed transaction. Your wallet should only sign and return the signed transaction blob.
The Pay backend provides params in an array-wrapped structure: [{ transaction }]. Make sure to unwrap this structure when parsing the transaction data.
// Correct: unwrap the arrayconst [{ transaction }] = JSON.parse(params);// Incorrect: accessing params directlyconst { transaction } = JSON.parse(params); // This will fail
Guard action handlers against missing signers at execution time. If your wallet doesn’t have a Solana signer available when a Solana action is encountered, raise an error rather than silently skipping the action.
if (namespace === 'solana' && !solanaWallet) { throw new Error('Solana wallet not available');}
Reject unknown wallet-RPC methods instead of silently omitting them from result arrays. The signatures array must match the actions array in order and length.
default: throw new Error(`Unknown method: ${method}`);// Never silently skip or return undefined