npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arkade-os/wdk

v0.1.2

Published

WDK (Wallet Development Kit) implementation using Arkade Bitcoin SDK with Ark protocol support

Readme

@arkade-os/wdk

WDK-compatible Bitcoin wallet manager/account implementation built on top of @arkade-os/sdk, with optional Lightning support through @arkade-os/boltz-swap.

Current Status

Implemented:

  • WDK WalletManager integration (getAccount, getAccountByPath, getFeeRates, dispose)
  • WDK account methods for send/sign/verify/quote, BIP-322 message signing, and read-only conversion
  • Destination auto-detection in sendTransaction() for Ark addresses, BTC addresses, BOLT11 invoices, Lightning addresses, LNURL, and BIP21 URIs
  • Optional Lightning receive via createLightningInvoice() (Boltz reverse swap)
  • Optional Lightning send via sendTransaction() (Boltz submarine swap)
  • Transaction history via getTransactionHistory() (delegates to the SDK)
  • Subscription to incoming VTXOs via subscribeToIncomingFunds()

Account Model

Each call to getAccount(index) returns one account derived at BIP-86 path m/86'/<coin>/0'/0/<index> (coin = 0 for bitcoin, 1 for any other network). The index is just a key-derivation leaf — there is no per-index role.

Every account exposes three receive surfaces from the same underlying wallet:

| Surface | API | Used when | |---------|-----|-----------| | Ark address (offchain) | getAddress() | Receiving VTXO transfers from other Ark users | | Boarding address (on-chain) | getBoardingAddress() | Funding the wallet by depositing on-chain BTC | | Lightning invoice | createLightningInvoice(amount, description?) | Receiving Lightning payments via Boltz reverse swap |

Lightning is only available when swapProviderUrl is set in the wallet config; otherwise createLightningInvoice throws. getAddress() always returns the Ark address — do not use it as a QR code for Lightning receives.

Repository Structure

arkade-wdk/
├── src/
│   ├── lib/                      # address, bip21, bolt11, lnurl, fees, formatting, send routing
│   ├── wallet-manager-arkade.js  # WDK wallet manager implementation
│   ├── wallet-account-arkade.js  # WDK account implementation
│   ├── wallet-account-read-only-arkade.js  # WDK read-only account implementation
│   ├── types.js                  # ArkadeWalletConfig type
│   └── index.js                  # package exports
├── packages/
│   ├── pear-wrk-wdk/             # submodule: bare-kit worklet runtime (HRPC schema + handlers)
│   └── wdk-react-native-provider/# submodule: React Native provider (WDK service, contexts, UI wiring)
├── examples/
│   └── wdk-starter-react-native/ # submodule: Expo example app
├── patches/
│   ├── pear-wrk-wdk.patch
│   ├── wdk-react-native-provider.patch
│   └── wdk-starter-react-native.patch
└── scripts/
    ├── setup-dev.js              # local dev setup helper
    ├── apply-patches.js          # apply ./patches to each submodule
    └── generate-patches.js       # regenerate ./patches from submodule diffs

Installation

npm install @arkade-os/wdk @tetherto/wdk

@arkade-os/sdk and @arkade-os/boltz-swap are not required as a direct install for normal usage of this adapter. They are pulled transitively by @arkade-os/wdk. If your app imports @arkade-os/sdk or @arkade-os/boltz-swap directly, add them explicitly to your app dependencies.

For local monorepo development with submodules and links:

npm run setup:dev

Quick Start

import WdkManager from '@tetherto/wdk'
import WalletManagerArkade from '@arkade-os/wdk'

const seedPhrase = 'your twelve word seed phrase here'
const wdk = new WdkManager(seedPhrase)

wdk.registerWallet('bitcoin', WalletManagerArkade, {
  arkServerUrl: 'https://arkade.computer',
})

const account = await wdk.getAccount('bitcoin', 0)

const arkAddress = await account.getAddress()
const balance = await account.getBalance()

const quote = await account.quoteSendTransaction({
  to: arkAddress,
  value: 1000n,
})

const tx = await account.sendTransaction({
  to: arkAddress,
  value: 1000n,
})

console.log({ balance, quoteFee: quote.fee, txid: tx.hash })

Lightning and LNURL

Create Lightning invoice (enabled only when swapProviderUrl is configured):

const { invoice, paymentHash } = await account.createLightningInvoice(50_000, 'Payment for coffee')
console.log(invoice) // BOLT11 invoice string

Pay to Lightning address / LNURL:

await account.sendTransaction({ to: '[email protected]', value: 1000n })

Arkade-Specific Account Methods

The adapter does not expose the underlying @arkade-os/sdk wallet instance. Use the account methods instead:

const balance = await account.getBalance()
const unsubscribe = await account.subscribeToIncomingFunds(() => {
  console.log('New Arkade funds detected')
})
const history = await account.getTransactionHistory()

unsubscribe()

API Reference (Current)

WalletManagerArkade

class WalletManagerArkade extends WalletManager {
  // inherited from @tetherto/wdk-wallet WalletManager
  static getRandomSeedPhrase(wordCount?: 12 | 24): string
  static isValidSeedPhrase(seedPhrase: string): boolean

  constructor(seed: string | Uint8Array, config?: ArkadeWalletConfig)
  getAccount(index?: number): Promise<WalletAccountArkade>
  getAccountByPath(path: string): Promise<WalletAccountArkade>
  getFeeRates(): Promise<{ normal: bigint; fast: bigint }>
  dispose(): Promise<void>
}

WalletAccountReadOnlyArkade

class WalletAccountReadOnlyArkade {
  getAddress(): Promise<string>
  getBoardingAddress(): Promise<string>
  getBalance(): Promise<bigint>
  getTransactionHistory(): Promise<ArkTransaction[]>
  verify(message: string, signature: string): Promise<boolean>
  getTransactionReceipt(hash: string): Promise<unknown | null>
  getTokenBalance(tokenAddress: string): Promise<bigint>
  quoteSendTransaction(tx: Transaction): Promise<{ fee: bigint }>
  quoteTransfer(options: TransferOptions): Promise<{ fee: bigint }>
}

WalletAccountArkade

class WalletAccountArkade extends WalletAccountReadOnlyArkade {
  readonly path: string             // full BIP-86 derivation path
  readonly index: number            // path leaf (last segment)
  readonly keyPair: { publicKey: Uint8Array; privateKey: Uint8Array | null }
  readonly arkadeSwaps: ArkadeSwaps | null

  sendTransaction(tx: Transaction): Promise<{ hash: string; fee: bigint }>
  quoteSendTransaction(tx: Transaction): Promise<{ fee: bigint }>
  transfer(options: TransferOptions): Promise<TransferResult>
  sign(message: string): Promise<string>
  subscribeToIncomingFunds(callback: (coins: IncomingFunds) => void): Promise<() => void>
  toReadOnlyAccount(): Promise<WalletAccountReadOnlyArkade>
  dispose(): void
  createLightningInvoice(amount: number, description?: string): Promise<{ invoice: string; paymentHash: string }>
  waitForLightningPayment(invoice: string): Promise<{ txid: string }>
  getPendingLightningReceives(): Promise<PendingReverseSwap[]>
  getPendingLightningSends(): Promise<PendingSubmarineSwap[]>
  getSwapHistory(): Promise<(PendingReverseSwap | PendingSubmarineSwap | PendingChainSwap)[]>
  getLightningLimits(): Promise<LimitsResponse>
  getLightningFees(): Promise<FeesResponse>
}

Package Exports

@arkade-os/wdk exports three values:

  • defaultWalletManagerArkade
  • WalletAccountArkade
  • WalletAccountReadOnlyArkade

The modules under src/lib/ (address detection, BIP21, BOLT11, LNURL, fees, formatting, send routing) back the destination auto-detection that sendTransaction() performs and are not part of the package's public API.

Configuration

import type { ArkadeWalletConfig } from '@arkade-os/wdk'

const config: ArkadeWalletConfig = {
  arkServerUrl: 'https://arkade.computer',
  // Optional: enables Lightning send/receive.
  // swapProviderUrl: 'https://api.boltz.exchange',
}

ArkadeWalletConfig extends the WDK WalletConfig and the @arkade-os/sdk WalletConfig (minus identity, which the manager fills in from the derived HD key) plus an optional swapProviderUrl.

Common fields:

| Field | Required | Purpose | |-------|-------------------------------|---------| | arkServerUrl | either this or arkProvider | URL used to construct a RestArkProvider. | | arkProvider | either this or arkServerUrl | A pre-built ArkProvider instance — useful for tests or custom transports. | | swapProviderUrl | optional | Boltz API URL. Enables createLightningInvoice, Lightning send via sendTransaction, and the swap query methods. | | storage | optional | { walletRepository, contractRepository }. Defaults to in-memory repositories; pass persistent ones (e.g. SQLite-backed) to keep VTXO state across restarts. | | swapRepository | optional | Boltz swap state storage. Forwarded into ArkadeSwaps when swapProviderUrl is set. |

The manager fetches arkProvider.getInfo() once at construction time (with a single retry on failure) and reuses the result for fee rates, network detection, and wallet initialization. Wallet.create is wrapped in a 30-second timeout; the rejection mentions the unreachable Ark server so misconfigurations surface quickly.

Temporary Workarounds

Arkade balances resolve on the RN side

The normal WDK path for balances is: RN provider -> HRPC getAddressBalance -> worklet. Arkade now takes a different route: the RN provider initializes an @arkade-os/wdk account locally with Expo adapters and asks that account for its total balance.

Current Arkade balance path:

  • RN provider -> WalletAccountArkade.getBalance()
  • getBalance() -> SDK wallet.getBalance().total
  • provider may subtract current boarding UTXOs via getBoardingAddress() and esploraUrl before presenting spendable Arkade / Lightning balance

This keeps the SDK wallet encapsulated inside the account classes and leaves Arkade-specific balance shaping in the provider layer instead of expanding the wallet API.

Transaction history uses HRPC (not a workaround)

Unlike balances, transaction history goes through the full HRPC path: RN provider -> HRPC getTransactionHistory -> worklet -> SDK wallet.getTransactionHistory(). The SDK returns ArkTransaction[] which is serialized as JSON through HRPC and mapped to the provider's Transaction interface on the RN side.

Git Submodules and Patches

The packages/ and examples/ directories are git submodules pointing at upstream repositories we don't have write access to. Local modifications are stored as patch files under ./patches/ and applied on top of pinned upstream commits.

Submodules

| Path | Pinned at | |------|-----------| | packages/pear-wrk-wdk | one commit before v2.0.0-beta.1 (commit ed8cd00) | | packages/wdk-react-native-provider | one commit past v1.0.0-beta.3 (commit 79462d4) | | examples/wdk-starter-react-native | develop (commit f010fda) |

Run git submodule status to confirm the pinned commit hashes in the parent repo.

After cloning

Use the setup script — it initializes submodules, applies all patches, installs dependencies, builds the root package, and links everything for local development:

npm run setup:dev

[!TIP] To run the example app: cd examples/wdk-starter-react-native && npm run android # or ios

If you only need to (re-)apply patches without running the rest of the setup:

node scripts/apply-patches.js          # apply all patches
node scripts/apply-patches.js --check  # dry-run (verify they apply cleanly)

Editing a submodule

Submodule working trees are kept dirty: the patches in ./patches/ are applied on top of the pinned upstream commits. To make a change:

  1. Edit files inside the submodule directly:

    cd packages/wdk-react-native-provider
    # ... edit files ...
  2. From the parent repo, regenerate the patch:

    cd ../..
    node scripts/generate-patches.js

    The script defaults to the SHA the parent repo has pinned for each submodule, so the patch only captures your local changes — not divergence from upstream.

  3. Commit the updated patch in the parent repo:

    git add patches/wdk-react-native-provider.patch
    git commit -m "Update wdk-react-native-provider patch"

Provider build

After editing provider source, regenerate bundles and type definitions before committing:

cd packages/wdk-react-native-provider
npm run prepare   # runs bob build + gen:secret-manager-bundle + gen:worker-bundle

This re-bundles the worklet (picking up any HRPC schema changes from pear-wrk-wdk) and type-checks with the stricter bob build settings.

Releasing to npm

Releases are driven from a local workstation by scripts/release.js (invoked through npm run release). There is no automated release pipeline.

Prerequisites

  • npm account with access to the @arkade-os org
  • Logged in: npm login
  • Working tree on the commit you want to publish (the release script does not bump the version itself)

Steps

  1. Bump the version with --no-git-tag-version (the release script creates the tag itself; using plain npm version would create the tag twice and the script would refuse to run):

    npm version --no-git-tag-version patch   # or minor / major
    git commit -am "Bump version"
    git push
  2. Run the release script:

    npm run release

    It reads the version from package.json, creates the vX.Y.Z tag locally, runs npm publish (which fires the prepublishOnly hook that regenerates type declarations in types/), and pushes the tag to origin. If publish fails the tag is removed so a retry can re-tag the same commit.

Only src/ and types/ are included in the tarball (per the files field in package.json).

CI

.github/workflows/ci.yml runs on every push and pull request targeting master. The job uses Node.js 22 and runs:

npm ci
npm run lint    # eslint + tsc --noEmit via jsconfig.json
npm test        # node --test src/__tests__/*.test.js

Tests use Node's built-in test runner — no extra framework or runtime dependency is required.

Development

npm install
npm run lint
npm run format

Testing:

npm test

License

MIT

Resources