Releases: reown-com/react-native-examples
WalletConnect Pay Solana support
This release captures the changes that extend the React Native CLI wallet's WalletConnect Pay reference implementation to support Solana payments end-to-end, alongside the existing EVM flows. It is intended as a documentation/reference checkpoint for other wallet teams adding Solana to their WCPay integration — this repo has no semver product stream and uses these release tags purely as integration milestones.
Scope is intentionally narrow: only the single PR listed under Included PRs below. Unrelated CI, dependabot, and other-wallet changes on main are not part of this release. See the attached patch for the exact diff.
Prerequisites. This release assumes your wallet already has:
- A working WCPay action-chain executor for EVM — see the
rn-cli-wallet-wcpay-usdtrelease if you have not implemented that yet. - A general Solana signing capability — an ed25519 keypair and a
signTransactionfunction that takes an unsigned base64 transaction and returns a fully signed base64 transaction. The reference wallet usesSolanaLib(added in #500), but any equivalent signer works.
Everything below is purely the WCPay-flow integration on top of those two prerequisites.
What this enables
- SOL-native payments via WCPay. When a Pay link's merchant supports Solana, the wallet now surfaces a SOL payment option in the picker, signs the backend-returned transaction with the user's Solana key, and forwards the signed blob to
confirmPayment. The backend broadcasts; the wallet never touches a Solana RPC for this flow. - Mixed-namespace option lists. A single Pay link can return EVM and Solana options side-by-side. The wallet sends both
eip155:…:0x…andsolana:…:<pubkey>entries inaccounts[], lets the user pick, and routes the chosen action chain to the correct signer at execution time. - Per-action wallet resolution. The Pay action executor no longer assumes one wallet type per payment. Each returned action is dispatched by its CAIP-2 namespace, so future namespaces (Sui, TON, etc.) can be added in the same handler without restructuring it.
Implementation requirements for other wallets
These are the wallet-integration contracts other implementers must follow to add Solana to WCPay. They are the public takeaway from this release. The EVM contracts from the previous release (rn-cli-wallet-wcpay-usdt) still apply — these are additive.
- Advertise Solana accounts in
getPaymentOptions. Include the user's Solana account(s) in theaccounts[]array as CAIP-10 strings —solana:<chainReference>:<base58Pubkey>— alongside any EVM entries. Without this, the backend cannot return SOL-denominated options even when the merchant supports them. Mainnet reference issolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp. - Resolve the signing wallet per action by CAIP-2 namespace — not once per payment. The wallet for action
iis determined byactions[i].walletRpc.chainId.split(':')[0]. An eager EVM-wallet lookup at the top ofapprovePaymentis incorrect: it will throw on Solana-only payments even when the Solana wallet is initialized, and it will pick the wrong key on mixed-namespace payments. - Implement the
solana_signTransactionmethod. Sign the transaction with the user's Solana key and append the signed transaction blob, base64-encoded, to thesignatures[]array. This is the same array used for EVM tx hashes and typed-data signatures — keep ordering: indexiofsignaturescorresponds to actioni. - Do NOT submit the bs58 detached signature. The Solana backend expects the fully signed transaction, base64-encoded, and broadcasts it server-side. Submitting the bs58 signature from
nacl.sign.detachedwill fail with a validation error. Most Solana signer libraries return both — be explicit about which one you forward. - Do NOT implement
solana_signAndSendTransactionfor the Pay flow. It is intentionally out of scope: the backend broadcasts. If your wallet's generic session_request handler implements it for non-Pay requests, that is fine, but the Pay action executor should not call it. solana_signTransactionparams are array-wrapped. The Pay backend sends params as a single-element array wrapping the payload —[{ transaction }]— matching the WC-style array invariant used by EVM methods. Thetransactionfield is the unsigned tx, base64-encoded.- Defensively validate the signer at action-execution time. Even though a Solana action will only be returned when your wallet advertised a Solana account in
accounts[], the wallet's signer state may have changed between option fetch and action execution (e.g. the user cleared keys, switched profiles). Guard the action handler against a missing signer and surface an error rather than silently skipping the action — the backend will reject a shortsignatures[]array regardless. - Reject unknown wallet-RPC methods. Same rule as the EVM contract: throw rather than skip. Partial result arrays will be rejected.
Reference implementation (all in PR #501):
- Per-action namespace dispatch:
PaymentStore.approvePayment— see theswitch (namespace)block that resolves eitherevmWalletorsolanaWalletper action. solana_signTransactioncase: same file, theSOLANA_SIGNING_METHODS.SOLANA_SIGN_TRANSACTIONbranch — notesignatures.push(signedTransaction), the base64 blob, not the bs58signature.- Account advertisement:
usePairing.handlePaymentLink—accounts[]is concatenated from EVM and Solana chain maps.
End-to-end flow
- User → Wallet: paste a Pay link.
- Wallet → Pay backend:
getPaymentOptions({ accounts: ["eip155:…:0x…", "solana:…:<pubkey>"] }). - Pay backend → Wallet: options list, including SOL-denominated options.
- User → Wallet: tap Pay on the SOL option.
- Wallet → Pay backend:
getRequiredPaymentActions({ paymentId, optionId }). - Pay backend → Wallet:
[{ chainId: "solana:…", method: "solana_signTransaction", params: [{ transaction }] }]. - Wallet → Solana signer:
signTransaction({ transaction }). - Solana signer → Wallet:
{ transaction: <signedB64>, signature: <bs58> }. - Wallet → Pay backend:
confirmPayment({ signatures: [<signedB64>] })— forwarding the base64 signed transaction, not the bs58 signature. - Pay backend → Wallet:
succeeded(backend broadcasts server-side).
Validate your integration
Run your wallet through these flows to confirm Solana support implements the contracts above correctly:
- SOL-only Pay link: merchant supports only Solana; SOL option appears in the picker;
solana_signTransactionaction signs andconfirmPaymentreturnssucceeded. Inspect thesignatures[]payload and confirm it is the base64 signed-transaction blob, not the bs58 signature. - Mixed EVM+SOL Pay link: both EVM and SOL options appear; picking SOL completes via the Solana signer; picking the EVM option still completes via the EVM action chain. Both should work in the same wallet session without restarting.
- EVM regression: existing Permit2 / USDT / native-token flows from the
rn-cli-wallet-wcpay-usdtrelease continue to work end-to-end. The per-action namespace dispatch must not regress EVM behavior. - No Solana account → no SOL option: if the wallet has not generated/imported a Solana key, it must not advertise a
solana:…entry inaccounts[]. A merchant that supports only Solana should result in the Pay backend returning no compatible options (or the wallet's "no options" empty state), never a SOL option the user cannot complete. - Param parsing: confirm your wallet correctly unwraps the array-wrapped
params = [{ transaction }]shape sent by the Pay backend and feeds the innertransactionstring into your signer. See the array-wrapped Solana case inPaymentStore.test.ts.
Included PRs
This release contains exactly this one merged PR and nothing else. The patch is attached to this release for verification.
- #501 — feat(rn_cli_wallet): add Solana support in WalletConnect Pay (merge
b4e7cb57).usePairingadvertises Solana accounts ingetPaymentOptions,PaymentStore.approvePaymentresolves the wallet per action by CAIP-2 namespace, and adds thesolana_signTransactioncase which unwraps the backend's array-wrapped[{ transaction }]params and forwards the base64 signed blob (not the bs58 signature) toconfirmPayment. Three new unit tests cover the Solana action path. AndroidversionCodebumped 65 → 66.
Tagged at b4e7cb57 on origin/main.
WalletConnect Pay USDT support
This release captures the changes that make the React Native CLI wallet a working reference implementation for WalletConnect Pay with ERC-20 / USDT support, end-to-end. It is intended as a documentation/reference checkpoint for other wallet teams integrating WCPay — this repo has no prior GitHub releases and does not follow a semver product stream.
Scope is intentionally narrow: only the three PRs listed under Included PRs below. Unrelated CI, dependabot, and other-wallet changes on main between those PRs are not part of this release. See the attached patches for the exact diff.
What this enables
- First-time USDT (and other ERC-20) payments on chains that require Permit2 (e.g. Polygon). The wallet now executes the WCPay-returned two-action flow — an
eth_sendTransaction(Permit2 approval) followed by aneth_signTypedData_v4(Permit2 typed-data signature) — and forwards the per-action results toconfirmPaymentin order. - Repeat USDT payments with no approval step. After the one-time Permit2 setup, WCPay returns a single typed-data action; the wallet signs and submits without an on-chain tx.
- Native-token payments (e.g. ETH on Base) now complete reliably. The wallet broadcasts the action's
eth_sendTransaction, waits for confirmation, and includes the resulting transaction hash in thesignaturesarray passed toconfirmPayment. Prior to this release, native flows posted an emptysignaturesarray and the backend rejected confirmation with400: Validation error: Next result not present. - Per-option fee estimates and one-time-setup UX so users can compare options and understand why USDT requires an extra step the first time.
Implementation requirements for other wallets
These are the wallet-integration contracts other implementers must follow. They are the public takeaway from this release.
- Call
pay.getRequiredPaymentActions({ paymentId, optionId })exactly once, at the moment the user confirms payment — not during option preview, not when fee estimates are computed, not on selection change. The call is committal: once it returns, the wallet must execute every action and callconfirmPayment. There is no "preview" or "go back" path after this point. Surface fee estimates and option previews from your own price/fee pipeline (the reference wallet usesPaymentTransactionUtil), not from this RPC. Do not assume one action per option — the returned array can have one or more entries. - Execute every returned action, in order. The action array is ordered; you must execute index
0, then1, etc. Do not reorder, skip, or batch. - For
eth_sendTransactionactions: broadcast the tx, wait for confirmation, then append the transaction hash to asignaturesarray. (This is the rule #495 enforces. It applies to native-token payments and to Permit2 approval txs.) - For
eth_signTypedData,eth_signTypedData_v3,eth_signTypedData_v4actions: sign the typed data and append the returned signature to the samesignaturesarray. - Maintain the invariant
signatures.length === paymentActions.lengthwhen callingpay.confirmPayment({ paymentId, optionId, signatures }). Ordering must match the action ordering returned by WCPay — indexiinsignaturescorresponds to actioni. - Reject unknown wallet-RPC methods. Throw rather than silently skipping; partial result arrays will be rejected by the backend.
Reference implementation: PaymentStore.approvePayment — this handler runs when the user presses PAY. getRequiredPaymentActions is called once at line 519, and only inside this handler — never during option preview. The loop at lines 529–667 is the canonical action-chain executor; the signatures.push(tx.hash) at line 613 is the rule from #495.
Recommended UX
These are guidance, not protocol requirements. The reference wallet implements all of them.
- Preload per-option fee estimates before the user picks an option, so options can be compared with realistic gas. See
PaymentTransactionUtiland the fee-estimate pipeline added in #480. - Show one-time-setup messaging when the action chain includes an approval. The reference wallet uses
shouldShowSetupLoader(PaymentUtil.ts) — true whenactions.length > 1and an approval action is present — and surfaces "Setting up TOKEN for one-time setup… Future TOKEN payments will be instant." - Explain gas separately when ERC-20 payments carry a native-token gas cost. The reference wallet ships a gas explainer view.
- Persist the last-paid token unit for faster repeat payments — auto-select it next time the user pays, but only when the saved unit is present in the new payment's available options. If the saved unit isn't offered (different merchant, different chains, expired token, etc.), fall back to manual option selection. The reference wallet does this via
findPreferredOption(options, lastPaidTokenUnit)returning null when there's no match. - Guard against stale action fetches (e.g. user changes option mid-fetch) and locally-expired payments (drop the request before signing). The reference wallet implements both.
Validate your integration
Run your wallet through these flows to confirm it implements the contracts above correctly:
- First-time ERC-20 payment requiring Permit2: approval
eth_sendTransactionsucceeds and is confirmed, followed by aneth_signTypedData_v4signature;confirmPaymentreceives[txHash, signature]and returnssucceeded. - Repeat ERC-20 payment (Permit2 already in place): a single
eth_signTypedData_v4;confirmPaymentreceives[signature]and returnssucceeded. No approval prompt is shown. - Native-token payment: a single
eth_sendTransaction; the broadcast tx hash is included insignatures;confirmPaymentreceives[txHash]and returnssucceeded. (Regression test for #495.) - Rapid option switching: repeatedly tapping different options does not leak stale fee or action data into the currently selected option (stale-fetch guard).
- Locally-expired payment: if
expiresAthas passed before the user taps "Approve", the wallet sets anexpiredresult and does not prompt for signatures.
Included PRs
This release contains exactly these three merged PRs and nothing else. Per-PR and combined patches are attached to this release for verification.
- #472 — feat(rn_cli_wallet): support Permit2 two-action payment flow (squash
91d19034). Protocol enabler: introduces the two-action executor inPaymentStore.approvePayment, thePaymentTransactionUtilfee/gas pipeline, the Permit2-revoke test utility, and thegetApprovalAction/requiresApproval/shouldShowSetupLoaderhelpers. - #480 — feat(rn_cli_wallet): redesign payment options UI with per-option fee estimates (squash
a6312b5e). UX layer: per-option preloaded fee estimates, one-time-setup messaging, gas explainer, last-paid-token persistence, shimmer / loading states, and a substantialPaymentStore.test.tssuite. - #495 — fix(wallet): include broadcast tx hash in WCPay signatures (merge
b640016d, fix commit4928f132). Action-chain rule: appends the broadcast tx hash tosignaturesaftereth_sendTransactionactions sosignatures.length === paymentActions.lengthalways holds. Documented here as a generic WCPay contract, not just a bugfix.
Tagged at c791039e on origin/main.