Wallet SDK
The TypeScript library that makes Rayos usable from any frontend app.
Repository: wallet-sdk
Package: @rayos/wallet-sdk
What Is It?β
The wallet-sdk is a TypeScript npm package. Every frontend app (web dashboard, mobile app, demo app) installs it as a dependency. It is the single abstraction layer between apps and the underlying passkeys + smart contracts.
Without the SDK, a developer would need to:
- Understand WebAuthn API calls
- Build and sign Soroban XDR transactions manually
- Talk to the relay backend directly
- Handle session key caching
With the SDK, it's just:
const receipt = await walletSdk.signAndSubmit(xdr, opts);
Module Structureβ
graph TD
APP["App (web / mobile / demo)"] --> WS["WalletSdk class\nsrc/index.ts"]
WS --> PM["Passkey Module\nsrc/passkey/"]
WS --> CC["Contract Clients\nsrc/contracts/"]
WS --> RC["Relay Client\nsrc/relay/"]
WS --> SM["Session Manager\nsrc/session/"]
PM --> WA["WebAuthn API\n(browser) or native adapter\n(mobile)"]
CC --> GEN["Generated Bindings\nsrc/contracts/generated/"]
RC --> RELAY["relay-backend API"]
| Module | File(s) | What It Does |
|---|---|---|
| Passkey | src/passkey/ | Register and sign with WebAuthn credentials |
| Contract Clients | src/contracts/ | High-level wrappers for wallet + policy contracts |
| Relay Client | src/relay/client.ts | Submit and sponsor transactions via the relay |
| Session Manager | src/session/ | Create, cache, expire, and revoke session keys |
Key Flowsβ
Wallet Creationβ
sequenceDiagram
participant APP as App
participant SDK as wallet-sdk
participant WA as WebAuthn (Browser)
participant RELAY as relay-backend
participant STELLAR as Stellar Testnet
APP->>SDK: createWallet(options)
SDK->>WA: createCredential() β biometric prompt
WA-->>SDK: PasskeyCredential
SDK->>RELAY: sponsorTx() β request fee-bump
RELAY-->>SDK: fee-bump XDR
SDK->>RELAY: submitTx()
RELAY->>STELLAR: Submit to network
STELLAR-->>RELAY: transaction hash
RELAY-->>SDK: {hash, status}
SDK-->>APP: {address, credentialId}
Sign & Submitβ
sequenceDiagram
participant APP as App
participant SDK as wallet-sdk
participant WA as WebAuthn
participant RELAY as relay-backend
APP->>SDK: signAndSubmit(xdr, opts)
SDK->>WA: signTransaction() β biometric prompt
WA-->>SDK: signedXdr
SDK->>RELAY: submitTransaction(signedXdr)
RELAY-->>SDK: SubmitResponse
SDK-->>APP: SubmitResponse
The PasskeyProvider Interfaceβ
This is the SDK's secret superpower for cross-platform compatibility.
The SDK defines:
interface PasskeyProvider {
createCredential(options: PasskeyRegistrationOptions): Promise<PasskeyCredential>;
signTransaction(xdr: string, options: PasskeySignOptions): Promise<PasskeyAssertion>;
}
- Web browsers use
@simplewebauthn/browserβ the default, works out of the box - Mobile apps inject
native/passkey-adapter.tsβ usesreact-native-passkeysfor native biometrics
Everything above this interface is 100% platform-agnostic. The same hooks, screens, and business logic work on both web and mobile.
Generated Bindings β Never Edit These!β
The src/contracts/generated/ folder contains auto-generated TypeScript type bindings for all three Soroban contracts. They are produced by:
bash scripts/regenerate-bindings.sh
# Runs: stellar contract bindings typescript against deployed WASMs
Rule: Never hand-edit files in
src/contracts/generated/. They are regenerated when contracts change. Any manual edits will be overwritten.
Session Keysβ
Session keys reduce how often users are asked for their fingerprint. Here's the idea:
- User signs once with their passkey to create a session key
- The session key is stored locally (and registered on-chain via the policy contract)
- For the next N hours, routine transactions use the session key β no biometric prompt needed
- Session expires, or user revokes it manually
The SDK's SessionManager handles local caching. The policy contract handles on-chain enforcement. Losing the local cache is inconvenient (you'll be prompted sooner), but never a security risk.
Error Hierarchyβ
Error
βββ SdkError (code: string, contractCode?: number)
βββ WalletError
β βββ UNAUTHORIZED
β βββ INVALID_SIGNATURE
βββ PolicyError
βββ SPEND_LIMIT_EXCEEDED
βββ INVALID_SESSION
All errors carry a typed code string so you can handle them predictably:
try {
await walletSdk.signAndSubmit(xdr, opts);
} catch (err) {
if (err instanceof PolicyError && err.code === 'SPEND_LIMIT_EXCEEDED') {
showToast('You have hit your spending limit for today.');
}
}