# 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

```bash
npm install @keyban/sdk-base @keyban/types
```

## Quick Start

```typescript
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

```typescript
const account = await client.initialize();
console.log({
  address: account.address,
  publicKey: account.publicKey,
  accountId: account.accountId,
});
```

### Sign Message

```typescript
// 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

```typescript
// 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

```typescript
const txHash = await account.transferERC20({
  contractAddress: "0xTokenContract",
  to: "0xRecipient",
  value: 1_000_000n, // 1 USDC (6 decimals)
});
```

### Transfer NFTs

```typescript
// 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

```typescript
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

```typescript
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

```typescript
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

```typescript
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.

```typescript
// Claim a product with authentication
const result = await client.api.dpp.claim({
  productId: "product-uuid",
  password: "product-password",
});

// Magic claim (passwordless)
const result = await client.api.dpp.magicClaim({
  productId: "product-uuid",
  magicToken: "magic-token-uuid",
});

// Get product details
const product = await client.api.dpp.getProduct("product-uuid");
console.log("Product:", product.name, product.status);
```

## Loyalty Programs

Access loyalty points, rewards, and wallet passes.

```typescript
// 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

```typescript
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.

```typescript
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 format
- `AmountInvalid` - Invalid amount (zero, negative, or exceeds limits)
- `AmountRequired` - Amount is required but not provided
- `AmountIrrelevant` - Amount provided when not needed
- `RecipientAddressEqualsSender` - Cannot send to yourself
- `EstimateGasExecution` - Gas estimation failed
- `InsufficientFunds` - Insufficient balance
- `InvalidNftStandard` - Unsupported NFT standard
- `NftNotFound` - NFT not found
- `TokenBalanceNotFound` - Token balance not found
- `UnknownTransactionError` - Unknown transaction error
- `UnknownIframeApiError` - Unknown API communication error

**Other Error Classes:**

- `CryptoError` - Cryptographic operations (encrypt, decrypt, key import)
- `JwtError` - Token parsing/validation
- `IFrameApiError` - IFrame communication errors

## Custom Client Share Provider

Implement custom storage for MPC client shares.

```typescript
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`:

```typescript
// 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

```bash
# 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](https://docs.keyban.io/api/types/)** - Shared TypeScript types and Zod schemas
- **[@keyban/sdk-react](https://docs.keyban.io/api/sdk-react/)** - React hooks and components
- **[API Documentation](https://docs.keyban.io/api/sdk-base/)** - Full TypeDoc reference

## License

See the [main repository](https://github.com/keyban-io/dap) for license information.
