# Coinbase Wallet SDK combines standalone payments with a wallet provider

> The Coinbase Wallet SDK ships standalone pay and subscribe functions that need no wallet connection, alongside the older EIP-1193 provider surface, and the README is explicit that a payment ID from the browser is a capability handle rather than proof of ownership.

**coinbase/coinbase-wallet-sdk** — An open protocol that lets users connect their mobile wallets to your DApp

- Repository: https://github.com/coinbase/coinbase-wallet-sdk
- Website: https://coinbase.github.io/coinbase-wallet-sdk/
- Stars: 1,766 · Forks: 763
- Language: TypeScript
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/coinbase-coinbase-wallet-sdk

## Two products sharing one npm package

The README opens by splitting the SDK in two. The first is Base Pay and Base Subscriptions, described as purely functional payment and subscription APIs that work immediately without any SDK setup or wallet connection. The second is the Coinbase Wallet SDK proper, a complete SDK for connecting to Coinbase Wallet and interacting with the Ethereum blockchain.

Both install from the same package.

```bash
# npm
npm install @coinbase/wallet-sdk
```

That is the practical shape of the project in 2026. The payment path is not a feature of a wallet SDK you configure, it is a set of exported functions you call directly, and the older provider surface still sits underneath it. Anyone evaluating this library should decide which half they actually need, because the setup model is entirely different.

Inside the repository the same split shows up in the build. The root package is private, version 0.0.1, and it is a yarn 4.8.1 workspace covering packages/, libs/ and examples/, with a test app under examples/testapp and a build script that delegates to the @coinbase/wallet-sdk workspace. The deploy script builds the packages and then exports the sdk-playground app.

## Base Pay takes a USDC amount and a recipient

The payment call is small, and the README says so plainly.

```typescript
import { pay } from '@coinbase/wallet-sdk';

const payment = await pay({
  amount: "10.50",
  to: "0xYourWalletAddress",
  dataSuffix: "0xabc123",
  testnet: true
});

console.log(`Payment successful! ID: ${payment.id}`);
```

The amount is a USDC string, to is your address, dataSuffix is optional attribution data, and testnet switches the network for testing. What comes back is an id, and that id is the only handle you have to the payment.

Everything else about the payment lifecycle is server-side, and the README is unusually explicit about why. getPaymentStatus takes that id, an optional expectedPayment object holding the amount and recipient from your order, and a testnet flag. Run the check on your backend before you fulfil.

The warning is the important part. Without expectedPayment, a status of completed confirms only that the operation contained a positive USDC transfer; it does not confirm the amount or the recipient. So a client can hand you a valid completed id for a payment of one cent to the wrong address and, if you skip the expected values, nothing in the response tells you. The README also instructs you to bind each payment ID to one order and reject ids already used for fulfilment.

## Subscriptions split into a browser path and a Node path

Recurring payments are created with the same shape as pay.

```typescript
import { subscribe } from '@coinbase/wallet-sdk';

const subscription = await subscribe({
  recurringCharge: "9.99",
  subscriptionOwner: "0xYourAppAddress",
  periodInDays: 30,
  testnet: true
});
```

getSubscriptionStatus reads back isSubscribed and nextPeriodStart for an id.

The charging step is where the two paths diverge, and the README's security note comes before either one: store the subscription id on your backend against the authenticated user when they subscribe, do not charge an arbitrary id supplied by the browser alone, and treat it as a capability handle rather than proof of ownership.

The browser path calls base.subscription.prepareCharge with the id, an amount or the literal max-remaining-charge, expectedSpender set to your app's spender address and expectedPayer set to the subscriber wallet from your session. That call only prepares call data; the README notes the transaction still has to be executed using your wallet provider.

The Node path imports from '@coinbase/wallet-sdk/node' and calls base.subscription.charge directly. Its constraint is harder: the README states charge() requires the permission spender to be your CDP smart wallet, so if your app signs with a different key this path is not available to you.

## The full SDK path is create, provider, request

The wallet connection surface is a four-step sequence with an EIP-1193 shape.

```js
const sdk = createCoinbaseWalletSDK({
  appName: 'SDK Playground',
});
```

```js
const provider = sdk.getProvider();
```

Connection is a standard request rather than a bespoke call.

```js
const addresses = provider.request({
  method: 'eth_requestAccounts',
});
```

After that, anything the provider supports, for example personal_sign with a hex-encoded message and an address.

```js
provider.request('personal_sign', [
  `0x${Buffer.from('test message', 'utf8').toString('hex')}`,
  addresses[0],
]);
```

Events are the standard set: connect, disconnect, accountsChanged, chainChanged and message, each registered with provider.on. accountsChanged and chainChanged are the two that matter for a real dApp, since both invalidate cached state.

The README also documents the upgrade loop, which is two commands each way.

```shell
# npm
npm outdated @coinbase/wallet-sdk
```

```shell
# npm
npm update @coinbase/wallet-sdk
```

Nothing here mentions chain configuration or default networks, so assume your application owns that.

## Where this SDK is the wrong dependency

Three boundaries are visible in the documentation.

First, the documented standalone functions accept USDC payments and create recurring USDC subscriptions. Other assets, chains, and wallet integrations are not documented here.

Second, the Node subscription path is gated on the permission spender being your CDP smart wallet. Teams that sign with their own custodial key cannot use charge() as written, and the browser path pushes transaction execution back onto your own wallet provider, which is work the library was supposed to remove.

Third, the security guidance is defensive by necessity rather than by design: ids from the browser are untrusted, expected values must come from your server, and already-used ids must be rejected. Build that in from the first commit. Adding it later means retrofitting idempotency onto a fulfilment path that may already have shipped duplicates.

There is a versioning wrinkle too. The newest tags are a canary build from 2025-06-25 and v4.3.4 from 2025-06-09, while the last push was on 2026-10-16. A moving master with an old stable tag means your lockfile, not the README, defines your version.

## Running the test app and the repository tooling

The hosted test app lives at coinbase.github.io/coinbase-wallet-sdk, and the local path is short.

```shell
# From the root dir
yarn install
```

```shell
# From the root dir
yarn dev
```

Forking and cloning are the stated prerequisites, which is a hint that the app is the intended way to try changes before touching your own integration.

The repository is a yarn 4.8.1 workspace pinned in packageManager, with .nvmrc and .yarnrc.yml fixing the toolchain, so the Node version is decided by the checkout. Formatting and linting run through Biome, configured in biome.json, rather than through ESLint and Prettier as the era would suggest. Every script is namespaced through workspaces foreach: build, dev, test, lint, format, format:check, typecheck and clean.

Two repository-level files are worth knowing about for a project that accepts outside contributions: AGENTS.md and CLAUDE.md sit at the root next to CODEOWNERS and CONTRIBUTING.md, and .codeflow.yml and .cursorrules suggest the project has formalised how automation and assistants work in it.

## Conclusion

Adopt the Base Pay standalone functions if you accept USDC payments without wiring a wallet connection, and adopt the full provider only if you need eth_requestAccounts and signing. Do not adopt the subscription charge path from the browser: the README is direct that the subscription id is a capability handle, not proof of ownership, and the Node path additionally requires the permission spender to be your CDP smart wallet. Verify first that getPaymentStatus runs on your server with expectedPayment filled in, since without it completed only proves that some positive USDC transfer happened, and that you bind one payment id to one order. The last push was on 2026-10-16 with the newest tag a canary from 2025-06-25, so pin a version deliberately.

## FAQ

### How do I accept a USDC payment with the Coinbase Wallet SDK?

Install @coinbase/wallet-sdk and call the exported pay function with an amount, your wallet address as to, an optional dataSuffix and a testnet flag. No SDK instantiation or wallet connection is required for that path.

### How do I verify a Base Pay payment before fulfilling an order?

Call getPaymentStatus on your backend with the payment id and an expectedPayment object holding the amount and recipient from your server-side order. The README warns that without expectedPayment a completed status only confirms a positive USDC transfer, not the amount or recipient, and it also says to bind each payment id to one order.

### What does it take to charge a subscription from Node?

Import base from '@coinbase/wallet-sdk/node' and call base.subscription.charge with the stored id, an amount and expectedPayer. The README states charge() requires the permission spender to be your CDP smart wallet.

### How do I connect a wallet with the full Coinbase Wallet SDK?

Create the SDK with createCoinbaseWalletSDK, get a provider from sdk.getProvider(), then call provider.request with the method eth_requestAccounts. Events such as accountsChanged and chainChanged are registered with provider.on.

### Which version of @coinbase/wallet-sdk should I use?

Check what you already have with npm outdated @coinbase/wallet-sdk and move with npm update. The repository's newest tags are v4.4.0-canary.20250625163917 and v4.3.4, so pin a version in your lockfile rather than tracking master.

## Sources

- [coinbase/coinbase-wallet-sdk on GitHub](https://github.com/coinbase/coinbase-wallet-sdk)
- [License: MIT](https://github.com/coinbase/coinbase-wallet-sdk/blob/master/LICENSE)
- [Project website](https://coinbase.github.io/coinbase-wallet-sdk/)
- [README](https://github.com/coinbase/coinbase-wallet-sdk/blob/master/README.md)
- [Releases](https://github.com/coinbase/coinbase-wallet-sdk/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/coinbase-coinbase-wallet-sdk
