Keyban JavaScript SDK
The official JavaScript SDK for Keyban's Wallet as a Service (WaaS). Integrate secure MPC wallets into your JavaScript or Node.js applications with support for multiple blockchains, NFTs, tokens, and advanced features like Digital Product Passports and loyalty programs.
Installation
npm install @keyban/sdk-base @keyban/types
Quick Start
import { KeybanClient, Network } from "@keyban/sdk-base";
// Initialize the client
const client = new KeybanClient({
appId: "your-app-id",
network: Network.PolygonAmoy,
});
// Get an account
const account = await client.initialize();
console.log("Address:", account.address);
// Transfer native currency
const txHash = await account.transfer(
"0xRecipientAddress",
1_000_000_000_000_000_000n // 1 token in wei
);
Key Features
- Multi-Blockchain Support - EVM (Ethereum, Polygon), Starknet, Stellar
- MPC Security - Non-custodial wallets with threshold signatures
- NFT & Token Support - ERC-20, ERC-721, ERC-1155, Soroban tokens
- Digital Product Passports - Claim and manage tokenized products
- Loyalty Programs - Points, rewards, wallet pass integration
- GraphQL API - Real-time subscriptions for balances and transfers
- TypeScript-First - Full type safety with
@keyban/types - Custom Storage - Flexible client share persistence
Blockchain Support
| Feature | EVM (Ethereum, Polygon) | Starknet | Stellar |
|---|---|---|---|
| Native Transfers | ✅ ETH, POL | ✅ STRK | ✅ XLM |
| ERC-20 Tokens | ✅ | 🚧 | ✅ Soroban |
| NFTs (ERC-721/1155) | ✅ | 🚧 | 🚧 |
| Message Signing | ✅ Hex | ✅ String[] | ✅ Base64 |
| Fee Estimation | ✅ EIP-1559 | ✅ | ✅ Stroops |
| Networks | Anvil, Amoy | Devnet, Sepolia, Mainnet | Quickstart, Testnet, Mainnet |
Core Operations
Initialize Account
const account = await client.initialize();
console.log({
address: account.address,
publicKey: account.publicKey,
accountId: account.accountId,
});
Sign Message
// EVM - returns hex string
const signature = await account.signMessage("Hello, Keyban!");
// Stellar - returns base64 string
const signature = await stellarAccount.signMessage("Hello, Keyban!");
// Starknet - returns string array
const signatures = await starknetAccount.signMessage("Hello, Keyban!");
Transfer Native Currency
// Estimate fees first
const fees = await account.estimateTransfer("0xRecipient");
console.log("Estimated fees:", fees);
// Perform transfer
const txHash = await account.transfer(
"0xRecipient",
1_000_000_000_000_000_000n, // Amount in smallest unit (wei)
fees // Optional: use custom fees
);
Transfer ERC-20 Tokens
const txHash = await account.transferERC20({
contractAddress: "0xTokenContract",
to: "0xRecipient",
value: 1_000_000n, // 1 USDC (6 decimals)
});
Transfer NFTs
// ERC-721
await account.transferNft({
contractAddress: "0xNftContract",
to: "0xRecipient",
tokenId: "123",
standard: "ERC721",
});
// ERC-1155
await account.transferNft({
contractAddress: "0xNftContract",
to: "0xRecipient",
tokenId: "456",
standard: "ERC1155",
value: 5n, // Transfer 5 copies
});
GraphQL Queries
The SDK provides pre-built GraphQL documents for querying blockchain data.
Query Token Balances
import { walletTokenBalancesDocument } from "@keyban/sdk-base/graphql";
const { data } = await client.apolloClient.query({
query: walletTokenBalancesDocument,
variables: {
walletId: account.address,
first: 20,
},
});
console.log("Token balances:", data.wallet.tokenBalances.nodes);
Query NFTs
import { walletNftsDocument } from "@keyban/sdk-base/graphql";
const { data } = await client.apolloClient.query({
query: walletNftsDocument,
variables: {
walletId: account.address,
first: 20,
},
});
console.log("NFTs:", data.wallet.nftBalances.nodes);
Real-Time Subscriptions
import { tokenBalancesSubscriptionDocument } from "@keyban/sdk-base/graphql";
const subscription = client.apolloClient.subscribe({
query: tokenBalancesSubscriptionDocument,
variables: { walletId: account.address },
}).subscribe({
next: ({ data }) => {
console.log("Balance updated:", data.tokenBalances);
},
error: (err) => console.error("Subscription error:", err),
});
// Don't forget to unsubscribe when done
subscription.unsubscribe();
Query Transfer History
import { walletAssetTransfersDocument } from "@keyban/sdk-base/graphql";
const { data } = await client.apolloClient.query({
query: walletAssetTransfersDocument,
variables: {
walletId: account.address,
first: 50,
},
});
console.log("Transaction history:", data.wallet.assetTransfers.nodes);
Digital Product Passports (DPP)
Claim tokenized products using the DPP service.
// Claim a product with authentication
const result = await client.api.dpp.claim({
productSheetId: "sheet-uuid",
password: "product-password",
});
// Magic claim (passwordless)
const result = await client.api.dpp.magicClaim({
productSheetId: "sheet-uuid",
magicToken: "magic-token-uuid",
});
// Get product sheet details
const sheet = await client.api.dpp.getProductSheet("sheet-uuid");
console.log("Product:", sheet.name, sheet.status);
Loyalty Programs
Access loyalty points, rewards, and wallet passes.
// Get loyalty balance
const balance = await client.api.loyalty.getBalance();
console.log("Points:", balance.points);
// Get reward tiers
const tiers = await client.api.loyalty.getRewardTiers();
console.log("Available rewards:", tiers);
// Add to Apple/Google Wallet
const passUrl = await client.api.loyalty.addToWalletPass("google");
window.open(passUrl, "_blank");
Balance Formatting
import { formatBalance } from "@keyban/sdk-base";
const balance = {
raw: 1_500_000_000_000_000_000n,
decimals: 18,
symbol: "ETH",
isNative: true,
};
const formatted = formatBalance(client, balance);
console.log(formatted); // "1.5 ETH"
Error Handling
The SDK provides typed errors for precise error handling.
import { SdkError, SdkErrorTypes } from "@keyban/sdk-base";
try {
await account.transfer(recipient, amount);
} catch (error) {
if (error instanceof SdkError) {
switch (error.type) {
case SdkErrorTypes.InsufficientFunds:
console.error("Not enough balance");
break;
case SdkErrorTypes.AddressInvalid:
console.error("Invalid recipient address");
break;
case SdkErrorTypes.AmountInvalid:
console.error("Invalid amount");
break;
default:
console.error("SDK error:", error.message);
}
} else {
console.error("Unexpected error:", error);
}
}
Available Error Types
SdkErrorTypes:
AddressInvalid- Invalid blockchain address formatAmountInvalid- Invalid amount (zero, negative, or exceeds limits)AmountRequired- Amount is required but not providedAmountIrrelevant- Amount provided when not neededRecipientAddressEqualsSender- Cannot send to yourselfEstimateGasExecution- Gas estimation failedInsufficientFunds- Insufficient balanceInvalidNftStandard- Unsupported NFT standardNftNotFound- NFT not foundTokenBalanceNotFound- Token balance not foundUnknownTransactionError- Unknown transaction errorUnknownIframeApiError- Unknown API communication error
Other Error Classes:
CryptoError- Cryptographic operations (encrypt, decrypt, key import)JwtError- Token parsing/validationIFrameApiError- IFrame communication errors
Custom Client Share Provider
Implement custom storage for MPC client shares.
import { ClientShareProvider } from "@keyban/sdk-base";
class MyClientShareProvider implements ClientShareProvider {
async get(key: string): Promise<string | null> {
// Retrieve from your backend
const response = await fetch(`/api/shares/${key}`, {
headers: { Authorization: `Bearer ${userToken}` },
});
return response.ok ? await response.text() : null;
}
async set(key: string, clientShare: string): Promise<void> {
// Store in your backend
await fetch("/api/shares", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${userToken}`,
},
body: JSON.stringify({ key, clientShare }),
});
}
}
// Use custom provider
const client = new KeybanClient({
appId: "your-app-id",
network: Network.PolygonAmoy,
clientShareProvider: new MyClientShareProvider(),
});
Security Best Practices
- ✅ Protect storage endpoints with authentication
- ✅ Use per-user keys (not shared keys)
- ✅ Enable audit logging for access
- ✅ Never expose client shares in logs or error messages
- ✅ Consider encryption at rest
- ✅ Implement key rotation if possible
Authentication & API Services
The SDK provides access to additional services through client.api:
// Authentication
await client.api.auth.signUp({ email, password });
await client.api.auth.signIn({ email, password });
await client.api.auth.sendOtp({ email });
// Account info
const address = await client.api.account.getAddress();
const accountId = await client.api.account.getAccountId();
// Application info
const app = await client.api.application.getApplication();
console.log("App:", app.name, app.features);
// Signer operations (advanced)
await client.api.signerEcdsa.dkg();
const signature = await client.api.signerEcdsa.sign(message);
Network Configuration
Fee Units
| Network | Fee Unit | Decimals | Example |
|---|---|---|---|
| EVM (Ethereum, Polygon) | gwei | 9 | 20 gwei = 20_000_000_000 wei |
| Starknet | FRI | 18 | 1 FRI = 1_000_000_000_000_000_000 |
| Stellar | stroop | 7 | 100 stroops = 0.00001 XLM |
Native Currency
| Network | Symbol | Decimals |
|---|---|---|
| Ethereum Anvil | ETH | 18 |
| Polygon Amoy | POL | 18 |
| Starknet | STRK | 18 |
| Stellar | XLM | 7 |
Development
# Build the package
npm run build
# Type check
npm run typecheck
# Run tests
npm test
# Lint
npm run lint
Compatibility
- Node.js: 18+ recommended
- Browsers: Modern browsers with ESM support
- TypeScript: 5.0+ for best type inference
- Bundlers: Vite, webpack, rollup supported
Related Packages
- @keyban/types - Shared TypeScript types and Zod schemas
- @keyban/sdk-react - React hooks and components
- API Documentation - Full TypeDoc reference
License
See the main repository for license information.