@arch-network/wallet-hub-sdk
v0.1.0
Published
TypeScript client for the Arch Wallet Hub API
Readme
Arch Wallet Hub SDK
TypeScript client for the Arch Wallet Hub API. It wraps the Hub's HTTP surface with typed request and response objects for wallet linking, Turnkey wallet management, signing requests, Bitcoin helper endpoints, and email recovery flows.
Installation
From this monorepo:
npm install
npm run buildOnce published:
npm install @arch-network/wallet-hub-sdkQuick Start
import { WalletHubClient } from "@arch-network/wallet-hub-sdk";
const walletHub = new WalletHubClient({
baseUrl: "https://your-wallet-hub.example.com/v1",
apiKey: process.env.WALLET_HUB_API_KEY,
network: "testnet",
});
const turnkeyConfig = await walletHub.getTurnkeyConfig();
console.log(turnkeyConfig.organizationId);baseUrl may include or omit /v1; the client normalizes it. network is sent as x-network and defaults to testnet. apiKey is sent as x-api-key when provided.
Runtime Support
The SDK uses the global fetch implementation by default. For older Node runtimes or custom test harnesses, pass fetchImpl:
const walletHub = new WalletHubClient({
baseUrl: "http://localhost:3005/v1",
apiKey: "dev-api-key",
fetchImpl: customFetch,
});Wallet Linking
Use wallet linking when a dApp needs to prove control of an external wallet address.
const challenge = await walletHub.createWalletLinkChallenge({
externalUserId: "user_123",
walletProvider: "unisat",
address: "tb1p...",
network: "testnet",
});
// Ask the user's wallet to sign challenge.message, then verify it.
const linked = await walletHub.verifyWalletLinkChallenge({
externalUserId: "user_123",
challengeId: challenge.challengeId,
signature: "base64-or-wallet-specific-signature",
schemeHint: "bip322",
});Turnkey Wallets
Create a passkey-backed Turnkey wallet:
const wallet = await walletHub.createTurnkeyPasskeyWallet({
idempotencyKey: crypto.randomUUID(),
body: {
externalUserId: "user_123",
walletName: "Primary wallet",
userEmail: "[email protected]",
passkey: {
challenge: "base64url-challenge",
attestation: passkeyAttestation,
},
},
});Create an email-only Turnkey wallet:
const wallet = await walletHub.createTurnkeyEmailWallet({
idempotencyKey: crypto.randomUUID(),
body: {
externalUserId: "user_123",
userEmail: "[email protected]",
walletName: "Email wallet",
},
});List or fetch known wallets:
const wallets = await walletHub.listTurnkeyWallets("user_123");
const walletDetails = await walletHub.getTurnkeyWallet({
externalUserId: "user_123",
resourceId: wallet.resourceId,
});Email Recovery
Recovery is a three-step flow:
- Discover wallet candidates for an email.
- Start OTP delivery for the selected candidate.
- Verify the OTP and receive an encrypted credential bundle.
const init = await walletHub.initRecoveryEmail({
email: "[email protected]",
});
if (init.candidates.length === 0) {
// Show neutral copy for anti-enumeration:
// "If a wallet exists for this email, you'll receive recovery instructions."
}
const candidate = init.candidates[0];
await walletHub.startRecoveryEmailOtp({
email: "[email protected]",
challengeId: init.challengeId,
candidateToken: candidate.candidateToken,
});
const verified = await walletHub.verifyRecoveryEmail({
challengeId: init.challengeId,
candidateToken: candidate.candidateToken,
code: "123456",
ephemeralPublicKey: "04...",
externalUserId: "user_123",
});verified.credentialBundle is HPKE-encrypted to the provided ephemeralPublicKey. The client must decrypt it locally and use the recovered credential according to verified.authMethod:
passkey: register a new authenticator on the recovered sub-organization.email: bootstrap an IndexedDB signing session.
Signing Requests
Create a signing request for an external signer or a Turnkey-backed wallet:
const request = await walletHub.createSigningRequest({
externalUserId: "user_123",
signer: {
kind: "turnkey",
resourceId: "wallet_resource_id",
},
action: {
type: "arch.transfer",
toAddress: "arch1...",
lamports: "1000000",
},
});
const status = await walletHub.getSigningRequest(request.signingRequestId);Submit a client-produced signature:
await walletHub.submitSigningRequest(request.signingRequestId, {
externalUserId: "user_123",
signature64Hex: "deadbeef...",
});Or ask the Hub to sign with Turnkey for a registered Turnkey wallet:
await walletHub.signWithTurnkey(request.signingRequestId, {
externalUserId: "user_123",
});Bitcoin Helpers
Estimate a fee:
const fee = await walletHub.estimateBitcoinFee({
externalUserId: "user_123",
turnkeyResourceId: "wallet_resource_id",
toAddress: "tb1q...",
amountSats: 10_000,
});Build and broadcast a Bitcoin transaction:
const psbt = await walletHub.buildBitcoinPsbt({
externalUserId: "user_123",
turnkeyResourceId: "wallet_resource_id",
toAddress: "tb1q...",
amountSats: 10_000,
});
// Sign and finalize psbt.unsignedPsbtHex locally, then broadcast:
const broadcast = await walletHub.broadcastBitcoinTransaction({
signedTxHex: "0200000000...",
});Error Handling
Non-2xx responses throw an Error with the HTTP status and response body:
try {
await walletHub.getTurnkeyConfig();
} catch (err) {
console.error(err);
}Development
cd packages/wallet-hub-sdk
npm install
npm run typecheck
npm run buildThe public entrypoint is src/index.ts, which re-exports WalletHubClient and all SDK types.
Publishing
The package is configured for public npm publishing as @arch-network/wallet-hub-sdk.
Before publishing for the first time:
- Make sure the npm account or automation token has publish access to the
@archnpm scope. - Decide and add the package license.
- Add an
NPM_TOKENrepository secret for the npm publish workflow.
Manual publish from a local authenticated npm session:
cd packages/wallet-hub-sdk
npm version 0.1.0 --no-git-tag-version
npm publish --access public