From 1faac96eb80e0d412d061b61f65cf1d1636afe13 Mon Sep 17 00:00:00 2001 From: Collins Ikechukwu Date: Sat, 11 Apr 2026 12:23:03 +0100 Subject: [PATCH 1/3] docs: initialize Mintlify documentation site Adds a full Mintlify docs structure under docs/ covering the complete SDK surface getting started, API reference, guides, and migration. --- docs/advanced/builder-functions.mdx | 189 +++++++++++++ docs/advanced/indexer-client.mdx | 120 +++++++++ docs/advanced/relayer-client.mdx | 88 ++++++ docs/advanced/wallet-adapters.mdx | 94 +++++++ docs/api-reference/context-rule-manager.mdx | 121 +++++++++ docs/api-reference/credential-manager.mdx | 113 ++++++++ .../api-reference/external-signer-manager.mdx | 119 +++++++++ docs/api-reference/multi-signer-manager.mdx | 113 ++++++++ docs/api-reference/policy-manager.mdx | 90 +++++++ docs/api-reference/signer-manager.mdx | 104 ++++++++ docs/api-reference/smart-account-kit.mdx | 250 ++++++++++++++++++ docs/configuration.mdx | 112 ++++++++ docs/guides/error-handling.mdx | 121 +++++++++ docs/guides/events.mdx | 82 ++++++ docs/guides/fee-sponsoring.mdx | 69 +++++ docs/guides/multi-sig.mdx | 115 ++++++++ docs/guides/storage-adapters.mdx | 100 +++++++ docs/introduction.mdx | 67 +++++ docs/logo/dark.svg | 1 + docs/logo/light.svg | 1 + docs/migration/v0.7.0.mdx | 59 +++++ docs/mint.json | 74 ++++++ docs/quickstart.mdx | 159 +++++++++++ 23 files changed, 2361 insertions(+) create mode 100644 docs/advanced/builder-functions.mdx create mode 100644 docs/advanced/indexer-client.mdx create mode 100644 docs/advanced/relayer-client.mdx create mode 100644 docs/advanced/wallet-adapters.mdx create mode 100644 docs/api-reference/context-rule-manager.mdx create mode 100644 docs/api-reference/credential-manager.mdx create mode 100644 docs/api-reference/external-signer-manager.mdx create mode 100644 docs/api-reference/multi-signer-manager.mdx create mode 100644 docs/api-reference/policy-manager.mdx create mode 100644 docs/api-reference/signer-manager.mdx create mode 100644 docs/api-reference/smart-account-kit.mdx create mode 100644 docs/configuration.mdx create mode 100644 docs/guides/error-handling.mdx create mode 100644 docs/guides/events.mdx create mode 100644 docs/guides/fee-sponsoring.mdx create mode 100644 docs/guides/multi-sig.mdx create mode 100644 docs/guides/storage-adapters.mdx create mode 100644 docs/introduction.mdx create mode 100644 docs/logo/dark.svg create mode 100644 docs/logo/light.svg create mode 100644 docs/migration/v0.7.0.mdx create mode 100644 docs/mint.json create mode 100644 docs/quickstart.mdx diff --git a/docs/advanced/builder-functions.mdx b/docs/advanced/builder-functions.mdx new file mode 100644 index 0000000..775fac6 --- /dev/null +++ b/docs/advanced/builder-functions.mdx @@ -0,0 +1,189 @@ +--- +title: Builder Functions +description: Utility functions for constructing signers, context rule types, and policy parameters. +--- + +## Signer Builders + +```typescript +import { + createDelegatedSigner, + createExternalSigner, + createWebAuthnSigner, + createEd25519Signer, +} from 'smart-account-kit'; +``` + +### `createWebAuthnSigner()` + +Create a passkey (secp256r1) signer. + +```typescript +const signer = createWebAuthnSigner( + verifierAddress: string, + publicKey: Uint8Array, + credentialId: string +); +``` + +### `createDelegatedSigner()` + +Create a delegated Stellar G-address signer. + +```typescript +const signer = createDelegatedSigner( + address: string, // G-address + ed25519VerifierAddress: string +); +``` + +### `createEd25519Signer()` + +Create an Ed25519 key signer. + +```typescript +const signer = createEd25519Signer( + verifierAddress: string, + publicKey: Uint8Array +); +``` + +### `createExternalSigner()` + +Create a signer for a custom on-chain verifier contract. + +```typescript +const signer = createExternalSigner( + verifierAddress: string, + publicKey: Uint8Array +); +``` + +--- + +## Context Rule Type Builders + +```typescript +import { + createDefaultContext, + createCallContractContext, + createCreateContractContext, +} from 'smart-account-kit'; +``` + +### `createDefaultContext()` + +A rule that matches any operation. Use as a catch-all fallback rule. + +```typescript +const context = createDefaultContext(); +``` + +### `createCallContractContext()` + +A rule that only matches invocations of a specific contract. + +```typescript +const context = createCallContractContext(contractAddress: string); +``` + +### `createCreateContractContext()` + +A rule that only matches contract deployments. + +```typescript +const context = createCreateContractContext(); +``` + +--- + +## Policy Parameter Builders + +```typescript +import { + createThresholdParams, + createWeightedThresholdParams, + createSpendingLimitParams, + LEDGERS_PER_HOUR, + LEDGERS_PER_DAY, + LEDGERS_PER_WEEK, +} from 'smart-account-kit'; +``` + +### `createThresholdParams()` + +M-of-N multisig: require `m` signatures from any of the rule's signers. + +```typescript +const params = createThresholdParams(m: number); +``` + +```typescript +const twoOfThree = createThresholdParams(2); +``` + +### `createWeightedThresholdParams()` + +Weighted voting: each signer has a weight, and the transaction must accumulate enough weight to reach the threshold. + +```typescript +const params = createWeightedThresholdParams( + threshold: number, + weights: Array<{ signerIndex: number; weight: number }> +); +``` + +```typescript +const params = createWeightedThresholdParams(100, [ + { signerIndex: 0, weight: 70 }, + { signerIndex: 1, weight: 50 }, +]); +``` + +### `createSpendingLimitParams()` + +Cap token spending over a rolling ledger window. + +```typescript +const params = createSpendingLimitParams( + tokenContract: string, + limit: bigint, // in stroops (1 XLM = 10_000_000 stroops) + resetPeriod: number // in ledgers +); +``` + +```typescript +const params = createSpendingLimitParams( + 'CDLZFC3...', + BigInt(100 * 10_000_000), // 100 XLM + LEDGERS_PER_DAY // resets daily +); +``` + +### Ledger Constants + +```typescript +LEDGERS_PER_HOUR // 720 +LEDGERS_PER_DAY // 17_280 +LEDGERS_PER_WEEK // 120_960 +``` + +--- + +## Signer Helper Functions + +```typescript +import { + getCredentialIdFromSigner, + signersEqual, + truncateAddress, + formatSignerForDisplay, +} from 'smart-account-kit'; +``` + +| Function | Description | +|----------|-------------| +| `getCredentialIdFromSigner(signer)` | Extract the credential ID from a WebAuthn signer | +| `signersEqual(a, b)` | Deep-compare two signers | +| `truncateAddress(address)` | Shorten a G/C-address for display (e.g. `GABC...WXYZ`) | +| `formatSignerForDisplay(signer)` | Format a signer as a human-readable string | diff --git a/docs/advanced/indexer-client.mdx b/docs/advanced/indexer-client.mdx new file mode 100644 index 0000000..50a4521 --- /dev/null +++ b/docs/advanced/indexer-client.mdx @@ -0,0 +1,120 @@ +--- +title: IndexerClient +description: Reverse-lookup smart account contracts by credential or address. +--- + +## Overview + +`IndexerClient` provides reverse lookups from passkey credentials and Stellar addresses to smart account contracts. It's used internally by `kit.rules.list()`, `kit.multiSigners.getAvailableSigners()`, and contract discovery methods. + +```typescript +import { IndexerClient, DEFAULT_INDEXER_URLS } from 'smart-account-kit'; +``` + +## Via SmartAccountKit (Recommended) + +The SDK auto-configures an indexer for testnet and mainnet: + +```typescript +// Discover contracts by credential +const { contracts } = await kit.discoverContractsByCredential(credentialId); + +// Discover by address +const { contracts } = await kit.discoverContractsByAddress('GABC...'); + +// Full contract details +const details = await kit.getContractDetailsFromIndexer('CABC...'); + +// Access the client directly +if (kit.indexer) { + const healthy = await kit.indexer.isHealthy(); + const stats = await kit.indexer.getStats(); +} +``` + +## Direct Usage + +### `IndexerClient.forNetwork()` + +Create a client pre-configured for a known Stellar network: + +```typescript +// Testnet +const indexer = IndexerClient.forNetwork('Test SDF Network ; September 2015'); + +// Mainnet +const indexer = IndexerClient.forNetwork('Public Global Stellar Network ; September 2015'); +``` + +### Custom URL + +```typescript +const indexer = new IndexerClient({ + baseUrl: 'https://smart-account-indexer.sdf-ecosystem.workers.dev', + timeout: 10000, +}); +``` + +## Methods + +### `lookupByCredentialId()` + +Find contracts associated with a WebAuthn credential ID. + +```typescript +const { contracts } = await indexer.lookupByCredentialId( + credentialIdHex: string +): Promise +``` + +### `lookupByAddress()` + +Find contracts associated with a Stellar G/C-address. + +```typescript +const { contracts } = await indexer.lookupByAddress( + address: string +): Promise +``` + +### `getContractDetails()` + +Fetch full details for a smart account contract including context rules, signers, and policies. + +```typescript +const details = await indexer.getContractDetails( + contractId: string +): Promise +``` + +### `isHealthy()` + +Check if the indexer is reachable and healthy. + +```typescript +const healthy = await indexer.isHealthy(); // boolean +``` + +### `getStats()` + +Fetch indexer stats (indexed contracts, latest ledger, etc.). + +```typescript +const stats = await indexer.getStats(); // IndexerStatsResponse +``` + +## Types + +```typescript +import type { + IndexerConfig, + IndexedContractSummary, + IndexedSigner, + IndexedPolicy, + IndexedContextRule, + CredentialLookupResponse, + AddressLookupResponse, + ContractDetailsResponse, + IndexerStatsResponse, +} from 'smart-account-kit'; +``` diff --git a/docs/advanced/relayer-client.mdx b/docs/advanced/relayer-client.mdx new file mode 100644 index 0000000..22771f0 --- /dev/null +++ b/docs/advanced/relayer-client.mdx @@ -0,0 +1,88 @@ +--- +title: RelayerClient +description: Fee-sponsored transaction submission via a relayer proxy. +--- + +## Overview + +`RelayerClient` handles communication with a relayer proxy for fee-sponsored transactions. It's created automatically when you pass `relayerUrl` to `SmartAccountKit`, and accessed via `kit.relayer`. + +```typescript +import { RelayerClient } from 'smart-account-kit'; +``` + +## Via SmartAccountKit (Recommended) + +Configure a `relayerUrl` and the SDK handles routing automatically: + +```typescript +const kit = new SmartAccountKit({ + // ... + relayerUrl: 'https://my-relayer.example.com', +}); + +// Uses relayer automatically +await kit.transfer(tokenContract, recipient, amount); + +// Bypass relayer for a specific call +await kit.transfer(tokenContract, recipient, amount, { forceMethod: 'rpc' }); + +// Access client directly +if (kit.relayer) { + const result = await kit.relayer.sendXdr(signedTx); +} +``` + +## Direct Usage + +```typescript +const relayer = new RelayerClient('https://my-relayer.example.com'); +``` + +### `send()` + +Submit a transaction via func + auth (for `invokeHostFunction` flows). The relayer wraps it in a fee bump and sponsors fees. + +```typescript +const result = await relayer.send( + funcXdr: string, + authXdrs: string[] +): Promise +``` + +### `sendXdr()` + +Submit a fully signed transaction XDR. Used for deployments and other non-invoke flows. + +```typescript +const result = await relayer.sendXdr( + signedTransactionXdr: string +): Promise +``` + +## Response + +```typescript +import type { RelayerResponse } from 'smart-account-kit'; + +interface RelayerResponse { + success: boolean; + hash?: string; // Transaction hash on success + error?: string; // Error message on failure + errorCode?: RelayerErrorCode; +} +``` + +## Error Codes + +```typescript +import { RelayerErrorCodes } from 'smart-account-kit'; +import type { RelayerErrorCode } from 'smart-account-kit'; +``` + +| Code | Description | +|------|-------------| +| `NETWORK_ERROR` | Could not reach the relayer | +| `SUBMISSION_FAILED` | Relayer received the request but the transaction failed | +| `INVALID_REQUEST` | Malformed request | +| `TIMEOUT` | Relayer did not respond in time | diff --git a/docs/advanced/wallet-adapters.mdx b/docs/advanced/wallet-adapters.mdx new file mode 100644 index 0000000..1d92a29 --- /dev/null +++ b/docs/advanced/wallet-adapters.mdx @@ -0,0 +1,94 @@ +--- +title: Wallet Adapters +description: Connect external Stellar wallets (Freighter, Lobstr, etc.) as signers. +--- + +## Overview + +Wallet adapters bridge external Stellar wallet extensions with Smart Account Kit's `ExternalSignerManager`. The SDK ships with a first-class adapter for `@creit-tech/stellar-wallets-kit`. + +## StellarWalletsKitAdapter + +`@creit-tech/stellar-wallets-kit` is a peer dependency that provides a unified interface for Freighter, Lobstr, and other Stellar wallet extensions. + +### Installation + +```bash +pnpm add @creit-tech/stellar-wallets-kit +``` + +### Usage + +```typescript +import { StellarWalletsKit, WalletNetwork, FREIGHTER_ID } from '@creit-tech/stellar-wallets-kit'; +import { StellarWalletsKitAdapter } from 'smart-account-kit'; + +// Create the StellarWalletsKit instance +const swk = new StellarWalletsKit({ + network: WalletNetwork.TESTNET, + selectedWalletId: FREIGHTER_ID, + // ... other config +}); + +// Wrap it in the adapter +const adapter = new StellarWalletsKitAdapter({ + kit: swk, + onConnectionChange: (connected: boolean) => { + console.log('External wallet:', connected ? 'connected' : 'disconnected'); + }, +}); + +// Register with SmartAccountKit +await kit.externalSigners.addFromWallet(adapter); +``` + +### `StellarWalletsKitAdapterConfig` + +```typescript +import type { StellarWalletsKitAdapterConfig } from 'smart-account-kit'; + +interface StellarWalletsKitAdapterConfig { + kit: StellarWalletsKit; + onConnectionChange?: (connected: boolean) => void; +} +``` + +## Custom Adapter + +Implement `ExternalWalletAdapter` to connect any wallet or signing service: + +```typescript +import type { ExternalWalletAdapter } from 'smart-account-kit'; +import type { xdr } from '@stellar/stellar-sdk'; + +class MyWalletAdapter implements ExternalWalletAdapter { + async getAddress(): Promise { + // Return the wallet's G-address + return await myWallet.getPublicKey(); + } + + async signAuthEntry( + authEntry: xdr.SorobanAuthorizationEntry + ): Promise { + // Sign the auth entry and return the signed version + return await myWallet.signAuthEntry(authEntry); + } + + async disconnect(): Promise { + await myWallet.disconnect(); + } +} + +// Register it +await kit.externalSigners.addFromWallet(new MyWalletAdapter()); +``` + +## Restoring Connections + +On page load, restore previously persisted wallet connections: + +```typescript +await kit.externalSigners.restoreConnections(); +``` + +This re-connects any external wallets that were active in the previous session. diff --git a/docs/api-reference/context-rule-manager.mdx b/docs/api-reference/context-rule-manager.mdx new file mode 100644 index 0000000..6f7e819 --- /dev/null +++ b/docs/api-reference/context-rule-manager.mdx @@ -0,0 +1,121 @@ +--- +title: ContextRuleManager +description: Create, read, update, and delete context rules via kit.rules. +--- + +## Overview + +Context rules define what signers and policies are required for different operations on your smart account. Access the manager via `kit.rules`. + +```typescript +// Create a rule +await kit.rules.add(contextType, 'My Rule', signers, policies); + +// Read a rule +const rule = (await kit.rules.get(0)).result; + +// List all active rules (requires indexer) +const rules = await kit.rules.list(); +``` + +## Methods + +### `add()` + +Create a new context rule on the smart account. + +```typescript +await kit.rules.add( + contextType: ContextRuleType, + name: string, + signers: ContractSigner[], + policies: PolicyConfig[] +); +``` + +**Example:** + +```typescript +import { createDefaultContext, createWebAuthnSigner } from 'smart-account-kit'; + +const context = createDefaultContext(); +const signer = createWebAuthnSigner(verifierAddress, publicKey, credentialId); + +await kit.rules.add(context, 'Default Rule', [signer], []); +``` + +### `get()` + +Read a specific context rule directly from the contract by ID. + +```typescript +const result = await kit.rules.get(contextRuleId: number); +const rule = result.result; // ContextRule +``` + + + `get()` reads directly from the chain. For listing active rules after deletions, use `list()` or `getAll()` - these are indexer-backed because the contract does not expose an iterator over active rule IDs. + + +### `list()` + +List all active context rules. **Requires indexer access.** + +```typescript +const rules = await kit.rules.list(); // ContextRule[] +``` + +### `getAll()` + +Get all active context rules of a specific type. **Requires indexer access.** + +```typescript +const rules = await kit.rules.getAll(contextType: ContextRuleType); +``` + +### `remove()` + +Delete a context rule by ID. + +```typescript +await kit.rules.remove(contextRuleId: number); +``` + +### `updateName()` + +Update the display name of a context rule. + +```typescript +await kit.rules.updateName(contextRuleId: number, name: string); +``` + +### `updateExpiration()` + +Update the expiration ledger for a context rule. After this ledger, the rule is no longer active. + +```typescript +await kit.rules.updateExpiration(contextRuleId: number, expirationLedger: number); +``` + +## Context Rule Types + +Use the builder functions to create context rule type values: + +```typescript +import { + createDefaultContext, // Matches any operation + createCallContractContext, // Matches calls to a specific contract + createCreateContractContext, // Matches contract deployments +} from 'smart-account-kit'; + +// Default - matches everything +const defaultCtx = createDefaultContext(); + +// Call contract - only matches invocations of CTARGET +const callCtx = createCallContractContext('CTARGET...'); + +// Create contract - matches deployments +const deployCtx = createCreateContractContext(); +``` + +See [Builder Functions](/advanced/builder-functions) for the full reference. diff --git a/docs/api-reference/credential-manager.mdx b/docs/api-reference/credential-manager.mdx new file mode 100644 index 0000000..a3076cd --- /dev/null +++ b/docs/api-reference/credential-manager.mdx @@ -0,0 +1,113 @@ +--- +title: CredentialManager +description: Manage locally stored passkey credentials via kit.credentials. +--- + +## Overview + +`CredentialManager` handles the local lifecycle of WebAuthn credentials - creating, saving, deploying, syncing, and deleting them. Access it via `kit.credentials`. + +Credentials start as **pending** (registered but not yet deployed on-chain). After deployment, they become **active** and are associated with a smart account contract. + +``` +create() / save() → pending credential +deploy() → connects and deploys the wallet +sync() / syncAll() → reconcile pending state with on-chain state +delete() → remove a pending credential that never deployed +``` + +## Methods + +### `getAll()` + +Return all stored credentials (both pending and active). + +```typescript +const all = await kit.credentials.getAll(); // StoredCredential[] +``` + +### `getForWallet()` + +Return credentials associated with the currently connected wallet. + +```typescript +const creds = await kit.credentials.getForWallet(); // StoredCredential[] +``` + +### `getPending()` + +Return credentials that are pending deployment. + +```typescript +const pending = await kit.credentials.getPending(); // StoredCredential[] +``` + +### `create()` + +Register a new WebAuthn passkey and store the credential locally as pending. + +```typescript +const credential = await kit.credentials.create(options?: CreateCredentialOptions); +``` + +### `save()` + +Save a pre-built credential object to local storage. + +```typescript +await kit.credentials.save(credential: StoredCredential); +``` + +### `deploy()` + +Deploy a pending credential - moves it through the wallet connection flow and deploys the smart account contract. + +```typescript +const result = await kit.credentials.deploy( + credentialId: string, + options?: { autoSubmit?: boolean } +); +``` + +### `sync()` + +Reconcile a single credential against on-chain state. + +```typescript +await kit.credentials.sync(credentialId: string); +``` + +### `syncAll()` + +Reconcile all stored credentials against on-chain state. Useful on page load to clean up stale pending state. + +```typescript +await kit.credentials.syncAll(); +``` + +### `delete()` + +Remove a pending credential from local storage. Only use this for credentials that never deployed. + +```typescript +await kit.credentials.delete(credentialId: string); +``` + + + `delete()` only removes the local record. If the credential was used on-chain, it remains on the contract. Use `kit.signers.remove()` to remove an on-chain signer. + + +## Types + +```typescript +import type { StoredCredential, CredentialDeploymentStatus } from 'smart-account-kit'; + +interface StoredCredential { + credentialId: string; + contractId?: string; // Set once deployed + publicKey: string; + status: CredentialDeploymentStatus; // 'pending' | 'failed' + nickname?: string; + createdAt: number; +} +``` diff --git a/docs/api-reference/external-signer-manager.mdx b/docs/api-reference/external-signer-manager.mdx new file mode 100644 index 0000000..8f06a79 --- /dev/null +++ b/docs/api-reference/external-signer-manager.mdx @@ -0,0 +1,119 @@ +--- +title: ExternalSignerManager +description: Manage G-address (delegated) signers via kit.externalSigners. +--- + +## Overview + +`ExternalSignerManager` manages Stellar G-address signers - either raw keypairs or connected wallet extensions. Access it via `kit.externalSigners`. + +```typescript +// Add a keypair (in-memory only) +kit.externalSigners.addFromSecret('SXXX...'); + +// Connect an external wallet (e.g. Freighter) +await kit.externalSigners.addFromWallet(walletAdapter); +``` + +## Methods + +### `addFromSecret()` + +Add a Stellar keypair as an external signer. Stored in memory only - not persisted to storage. + +```typescript +kit.externalSigners.addFromSecret(secretKey: string); +``` + + + This method is primarily for testing and development. Never use it with real secret keys in production. + + +### `addFromWallet()` + +Connect an external wallet (e.g. Freighter, Lobstr) as a signer. + +```typescript +await kit.externalSigners.addFromWallet(adapter: ExternalWalletAdapter); +``` + +See [Wallet Adapters](/advanced/wallet-adapters) for available adapters. + +### `restoreConnections()` + +Restore previously persisted wallet connections on page load. + +```typescript +await kit.externalSigners.restoreConnections(); +``` + +### `canSignFor()` + +Check if a signer is available for a given G-address. + +```typescript +const can = kit.externalSigners.canSignFor(address: string); // boolean +``` + +### `signAuthEntry()` + +Sign a single auth entry using the signer for the given address. + +```typescript +const signed = await kit.externalSigners.signAuthEntry( + address: string, + authEntry: xdr.SorobanAuthorizationEntry +); +``` + +### `getAll()` + +Return all registered external signers. + +```typescript +const signers = kit.externalSigners.getAll(); // ConnectedWallet[] +``` + +### `remove()` + +Remove an external signer by address. + +```typescript +kit.externalSigners.remove(address: string); +``` + +## Types + +```typescript +import type { ExternalWalletAdapter, ConnectedWallet } from 'smart-account-kit'; + +interface ExternalWalletAdapter { + getAddress(): Promise; + signAuthEntry(authEntry: xdr.SorobanAuthorizationEntry): Promise; + disconnect?(): Promise; +} + +interface ConnectedWallet { + address: string; + adapter: ExternalWalletAdapter; +} +``` + +## StellarWalletsKit Integration + +The SDK ships with a `StellarWalletsKitAdapter` for first-class `@creit-tech/stellar-wallets-kit` support: + +```typescript +import { StellarWalletsKitAdapter } from 'smart-account-kit'; + +const adapter = new StellarWalletsKitAdapter({ + kit: stellarWalletsKit, + onConnectionChange: (connected) => { + console.log('External wallet:', connected ? 'connected' : 'disconnected'); + }, +}); + +await kit.externalSigners.addFromWallet(adapter); +``` + +See [Wallet Adapters](/advanced/wallet-adapters) for the full guide. diff --git a/docs/api-reference/multi-signer-manager.mdx b/docs/api-reference/multi-signer-manager.mdx new file mode 100644 index 0000000..85bae27 --- /dev/null +++ b/docs/api-reference/multi-signer-manager.mdx @@ -0,0 +1,113 @@ +--- +title: MultiSignerManager +description: Coordinate multi-signer transaction flows via kit.multiSigners. +--- + +## Overview + +`MultiSignerManager` handles transactions that require signatures from multiple signers. Access it via `kit.multiSigners`. + +```typescript +// Get all available signers (indexer-backed) +const signers = await kit.multiSigners.getAvailableSigners(); + +if (kit.multiSigners.needsMultiSigner(signers)) { + const selected = kit.multiSigners.buildSelectedSigners(signers, activeCredentialId); + await kit.multiSigners.transfer('CTOKEN...', 'GRECIPIENT...', 100, selected); +} +``` + + + `getAvailableSigners()` is indexer-backed - it requires a configured indexer. This mirrors `kit.rules.list()` for the same reason: the contract does not expose an iterator over active rule IDs after deletions. + + +## Methods + +### `getAvailableSigners()` + +Discover all signers across all active context rules. **Requires indexer access.** + +```typescript +const signers = await kit.multiSigners.getAvailableSigners(); // ContractSigner[] +``` + +### `needsMultiSigner()` + +Check whether the current operation requires more than one signer. + +```typescript +const needs = kit.multiSigners.needsMultiSigner(signers: ContractSigner[]); // boolean +``` + +### `buildSelectedSigners()` + +Assemble a `SelectedSigner[]` array for use in multi-sig operations. + +```typescript +const selected = kit.multiSigners.buildSelectedSigners( + signers: ContractSigner[], + activeCredentialId?: string +); +``` + +Pass `activeCredentialId` to mark which passkey credential is the currently authenticated one. + +### `transfer()` + +Execute a token transfer requiring signatures from multiple signers. + +```typescript +const result = await kit.multiSigners.transfer( + tokenContract: string, + recipient: string, + amount: number | bigint | string, + selectedSigners: SelectedSigner[] +); +``` + +### `operation()` + +Execute any assembled transaction requiring multi-signer authorization. + +```typescript +const result = await kit.multiSigners.operation( + assembledTx: AssembledTransaction, + selectedSigners: SelectedSigner[], + options?: SubmissionOptions +); +``` + +## Full Multi-Sig Example + +```typescript +// 1. Discover all signers +const signers = await kit.multiSigners.getAvailableSigners(); + +// 2. Check if multi-sig is needed +if (!kit.multiSigners.needsMultiSigner(signers)) { + // Single signer - use the normal transfer path + await kit.transfer(tokenContract, recipient, amount); + return; +} + +// 3. Build the signer selection +const selected = kit.multiSigners.buildSelectedSigners( + signers, + kit.activeCredentialId // mark which passkey is currently authenticated +); + +// 4. Execute +const result = await kit.multiSigners.transfer(tokenContract, recipient, amount, selected); +``` + +## Types + +```typescript +import type { SelectedSigner } from 'smart-account-kit'; + +interface SelectedSigner { + signer: ContractSigner; + credentialId?: string; // For passkey signers + address?: string; // For G-address signers +} +``` diff --git a/docs/api-reference/policy-manager.mdx b/docs/api-reference/policy-manager.mdx new file mode 100644 index 0000000..4ae2ac3 --- /dev/null +++ b/docs/api-reference/policy-manager.mdx @@ -0,0 +1,90 @@ +--- +title: PolicyManager +description: Attach and detach policies on context rules via kit.policies. +--- + +## Overview + +Policies add additional authorization logic on top of signers - threshold multisig, spending limits, weighted voting, etc. Access the manager via `kit.policies`. + +```typescript +// Add a threshold policy to rule 0 +await kit.policies.add(0, 'CPOLICY...', installParams); + +// Remove it +await kit.policies.remove(0, 'CPOLICY...'); +``` + +## Methods + +### `add()` + +Attach a policy contract to a context rule. + +```typescript +await kit.policies.add( + contextRuleId: number, + policyAddress: string, + installParams: xdr.ScVal +); +``` + +**Example (threshold multisig):** + +```typescript +import { createThresholdParams } from 'smart-account-kit'; + +const params = createThresholdParams(2); // 2-of-N +const installParams = kit.convertPolicyParams(params); + +await kit.policies.add(0, process.env.THRESHOLD_POLICY_ADDRESS!, installParams); +``` + +**Example (spending limit):** + +```typescript +import { createSpendingLimitParams, LEDGERS_PER_DAY } from 'smart-account-kit'; + +const params = createSpendingLimitParams( + 'CTOKEN...', + BigInt(1000 * 10_000_000), // 1000 tokens + LEDGERS_PER_DAY +); +const installParams = kit.convertPolicyParams(params); + +await kit.policies.add(0, process.env.SPENDING_LIMIT_POLICY_ADDRESS!, installParams); +``` + +### `remove()` + +Detach a policy from a context rule. + +```typescript +await kit.policies.remove( + contextRuleId: number, + policyAddress: string +); +``` + +## Policy Types + +The SDK ships with builders for all supported policy types: + +| Builder | Policy | Description | +|---------|--------|-------------| +| `createThresholdParams(m)` | Threshold | M-of-N multisig | +| `createWeightedThresholdParams(threshold, weights)` | Weighted threshold | Weighted voting | +| `createSpendingLimitParams(token, limit, period)` | Spending limit | Time-limited per-token cap | + +```typescript +import { + createThresholdParams, + createWeightedThresholdParams, + createSpendingLimitParams, + LEDGERS_PER_HOUR, + LEDGERS_PER_DAY, + LEDGERS_PER_WEEK, +} from 'smart-account-kit'; +``` + +See [Builder Functions](/advanced/builder-functions) for full details. diff --git a/docs/api-reference/signer-manager.mdx b/docs/api-reference/signer-manager.mdx new file mode 100644 index 0000000..b470cba --- /dev/null +++ b/docs/api-reference/signer-manager.mdx @@ -0,0 +1,104 @@ +--- +title: SignerManager +description: Add and remove signers on context rules via kit.signers. +--- + +## Overview + +`SignerManager` manages signers attached to context rules on your smart account. Access it via `kit.signers`. + +```typescript +// Add a passkey signer to rule 0 +await kit.signers.addPasskey(0, 'My App', 'Recovery Key'); + +// Add a G-address signer +await kit.signers.addDelegated(0, 'GABC...'); + +// Remove a signer +await kit.signers.remove(0, signer); +``` + + + `batch_add_signer` is intentionally left on the raw `kit.wallet` client - the SDK does not add enough ergonomics to justify a wrapper. + + +## Methods + +### `addPasskey()` + +Register a new WebAuthn passkey and add it as a signer on the given context rule. + +```typescript +const { credentialId, transaction } = await kit.signers.addPasskey( + contextRuleId: number, + appName: string, + userName: string, + options?: AddPasskeyOptions +); +``` + +**Parameters:** + + + The ID of the context rule to add the signer to. + + + + Display name for the relying party shown during passkey creation. + + + + Label for the new passkey (e.g. "Recovery Key", "YubiKey"). + + + + Optional local nickname for the credential. + + +**Example:** + +```typescript +const { credentialId } = await kit.signers.addPasskey( + 0, + 'My App', + 'Backup YubiKey', + { nickname: 'Backup YubiKey' } +); +``` + +### `addDelegated()` + +Add a Stellar G-address as a delegated signer on a context rule. + +```typescript +await kit.signers.addDelegated( + contextRuleId: number, + address: string +); +``` + +**Example:** + +```typescript +await kit.signers.addDelegated(0, 'GABC...'); +``` + +### `remove()` + +Remove a signer from a context rule. The SDK resolves signer IDs internally - pass the signer value directly. + +```typescript +await kit.signers.remove( + contextRuleId: number, + signer: ContractSigner +); +``` + +**Example:** + +```typescript +const rule = (await kit.rules.get(0)).result; +const signer = rule.signers[0]; + +await kit.signers.remove(0, signer); +``` diff --git a/docs/api-reference/smart-account-kit.mdx b/docs/api-reference/smart-account-kit.mdx new file mode 100644 index 0000000..ee4fe95 --- /dev/null +++ b/docs/api-reference/smart-account-kit.mdx @@ -0,0 +1,250 @@ +--- +title: SmartAccountKit +description: The main SDK client class for deploying and managing smart accounts. +--- + +## Import + +```typescript +import { SmartAccountKit } from 'smart-account-kit'; +``` + +## Constructor + +```typescript +const kit = new SmartAccountKit(config: SmartAccountConfig); +``` + +See [Configuration](/configuration) for all available options. + +## Sub-Manager Properties + +After construction, the following sub-managers are available as properties: + +| Property | Type | Description | +|----------|------|-------------| +| `kit.signers` | `SignerManager` | Add/remove signers on context rules | +| `kit.rules` | `ContextRuleManager` | CRUD for context rules | +| `kit.policies` | `PolicyManager` | Attach/detach policies | +| `kit.credentials` | `CredentialManager` | Local credential lifecycle | +| `kit.multiSigners` | `MultiSignerManager` | Multi-signer transaction flows | +| `kit.externalSigners` | `ExternalSignerManager` | G-address / wallet signers | +| `kit.indexer` | `IndexerClient \| null` | Contract discovery | +| `kit.relayer` | `RelayerClient \| null` | Fee-sponsored submission | +| `kit.events` | `SmartAccountEventEmitter` | Event subscriptions | +| `kit.wallet` | `WalletClient \| null` | Raw contract client (after connect) | + +## Wallet Lifecycle + +### `createWallet()` + +Create a new smart account secured by a WebAuthn passkey. + +```typescript +const { contractId, credentialId } = await kit.createWallet( + appName: string, + userName: string, + options?: CreateWalletOptions +); +``` + +**Options:** + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `autoSubmit` | boolean | false | Deploy the wallet immediately | +| `autoFund` | boolean | false | Fund via Friendbot (testnet only) | +| `nativeTokenContract` | string | - | Required when `autoFund` is true | + +```typescript +const { contractId, credentialId } = await kit.createWallet('My App', 'user@example.com', { + autoSubmit: true, + autoFund: true, + nativeTokenContract: 'CDLZFC3...', +}); +``` + +### `connectWallet()` + +Restore a session or prompt for passkey selection. + +```typescript +const result = await kit.connectWallet(options?: ConnectWalletOptions); +// Returns ConnectWalletResult | null +``` + +**Options:** + +| Option | Type | Description | +|--------|------|-------------| +| `prompt` | boolean | Show passkey selection UI | +| `fresh` | boolean | Ignore session, always prompt | +| `credentialId` | string | Connect with a specific credential | +| `contractId` | string | Connect with a specific contract | + +```typescript +await kit.connectWallet(); // Silent restore +await kit.connectWallet({ prompt: true }); // Always prompt +await kit.connectWallet({ fresh: true }); // Ignore cached session +await kit.connectWallet({ credentialId: '...' }); // Specific credential +await kit.connectWallet({ contractId: 'C...' }); // Specific contract +``` + +### `disconnect()` + +Clear the session. The passkey and contract remain on-chain. + +```typescript +await kit.disconnect(); +``` + +### `authenticatePasskey()` + +Perform passkey authentication without connecting to a wallet. + +```typescript +const authResult = await kit.authenticatePasskey(); +``` + +## Transaction Methods + +### `signAndSubmit()` + +Sign auth entries for a transaction and submit it. **This is the recommended path** for smart-account auth flows. + +```typescript +const result = await kit.signAndSubmit( + transaction: Transaction | FeeBumpTransaction, + options?: SubmissionOptions +): Promise +``` + +### `executeAndSubmit()` + +Build a smart-account mediated contract call, sign it, and submit. **Preferred for arbitrary contract calls.** + +```typescript +const result = await kit.executeAndSubmit( + target: string, + targetFn: string, + targetArgs: xdr.ScVal[], + options?: SubmissionOptions +): Promise +``` + +```typescript +const result = await kit.executeAndSubmit('CTARGET...', 'set_config', [owner, threshold]); +``` + +### `execute()` + +Build the assembled transaction for a smart-account mediated call without submitting. + +```typescript +const tx = await kit.execute( + target: string, + targetFn: string, + targetArgs: xdr.ScVal[] +): Promise +``` + +### `sign()` + +Sign auth entries on a transaction. Use `signAndSubmit()` unless you need to inspect the signed entries. + +```typescript +const signedTx = await kit.sign(transaction, options?: SubmissionOptions); +``` + +### `signAuthEntry()` + +Sign a single auth entry. + +```typescript +const signedEntry = await kit.signAuthEntry(authEntry, options?); +``` + +### `transfer()` + +Transfer tokens from the connected smart account. + +```typescript +const result = await kit.transfer( + tokenContract: string, + recipient: string, + amount: number | bigint | string, + options?: { forceMethod?: 'rpc' | 'relayer' } +): Promise +``` + +```typescript +// Transfer via relayer (if configured) or RPC +await kit.transfer('CTOKEN...', 'GRECIPIENT...', 100); + +// Force RPC even if relayer is configured +await kit.transfer('CTOKEN...', 'GRECIPIENT...', 100, { forceMethod: 'rpc' }); +``` + +## Contract Discovery + +### `discoverContractsByCredential()` + +Find smart account contracts associated with a credential ID (via indexer). + +```typescript +const contracts = await kit.discoverContractsByCredential(credentialId: string); +``` + +### `discoverContractsByAddress()` + +Find smart account contracts associated with a Stellar address (via indexer). + +```typescript +const contracts = await kit.discoverContractsByAddress(address: string); +``` + +### `getContractDetailsFromIndexer()` + +Fetch full contract details including rules, signers, and policies. + +```typescript +const details = await kit.getContractDetailsFromIndexer(contractId: string); +``` + +## Utility Methods + +### `fundWallet()` + +Fund the connected wallet via Friendbot (testnet only). + +```typescript +await kit.fundWallet(nativeTokenContract: string); +``` + +### `convertPolicyParams()` + +Convert policy parameter objects to `ScVal` for on-chain use. + +```typescript +const scVal = kit.convertPolicyParams(params: PolicyConfig); +``` + +### `buildPoliciesScVal()` + +Build a policies `ScVal` array for context rule creation. + +```typescript +const policiesScVal = kit.buildPoliciesScVal(policies: PolicyConfig[]); +``` + +## Raw Wallet Escape Hatch + +The generated contract client is accessible as `kit.wallet` after connecting. Use it directly for contract methods the SDK intentionally does not wrap: `upgrade`, `batch_add_signer`, `get_signer_id`, `get_policy_id`, and `get_context_rules_count`. + +```typescript +// Only available after kit.connectWallet() +const count = await kit.wallet?.get_context_rules_count(); +const signerId = await kit.wallet?.get_signer_id({ signer }); +``` + +**Rule of thumb:** if the SDK adds orchestration, session handling, signer resolution, or submission logic, use the wrapper. If it's a thin contract call, use `kit.wallet` directly. diff --git a/docs/configuration.mdx b/docs/configuration.mdx new file mode 100644 index 0000000..130d4b2 --- /dev/null +++ b/docs/configuration.mdx @@ -0,0 +1,112 @@ +--- +title: Configuration +description: All SmartAccountKit initialization options. +--- + +## SmartAccountConfig + +Pass a config object to the `SmartAccountKit` constructor: + +```typescript +import { SmartAccountKit, IndexedDBStorage } from 'smart-account-kit'; + +const kit = new SmartAccountKit({ + rpcUrl: 'https://soroban-testnet.stellar.org', + networkPassphrase: 'Test SDF Network ; September 2015', + accountWasmHash: 'YOUR_WASM_HASH', + webauthnVerifierAddress: 'YOUR_VERIFIER_ADDRESS', +}); +``` + +## Options + + + Stellar RPC endpoint URL. Use `https://soroban-testnet.stellar.org` for testnet or `https://mainnet.stellar.validationcloud.io/v1/` for mainnet. + + + + Network passphrase. Use the `Networks` enum from `@stellar/stellar-sdk`: + - Testnet: `'Test SDF Network ; September 2015'` + - Mainnet: `'Public Global Stellar Network ; September 2015'` + + + + WASM hash of the uploaded smart account contract. Used to deploy new wallet instances. The testnet default is in [`demo/.env.example`](https://github.com/kalepail/smart-account-kit/blob/main/demo/.env.example). + + + + Contract address of the deployed WebAuthn verifier. This is the on-chain verifier used to validate passkey signatures. + + + + Credential storage backend. Defaults to `IndexedDBStorage` if not provided. + + ```typescript + import { IndexedDBStorage, LocalStorageAdapter, MemoryStorage } from 'smart-account-kit'; + + storage: new IndexedDBStorage() // Recommended for web + storage: new LocalStorageAdapter() // Simple fallback + storage: new MemoryStorage() // Testing only + ``` + + + + Transaction timeout in seconds. Transactions that are not included in a ledger within this window will expire. + + + + WebAuthn Relying Party ID. Defaults to the current hostname (`window.location.hostname`). Override this if your domain differs from the RP ID registered during passkey creation. + + + + WebAuthn Relying Party display name. Shown to the user during passkey creation. + + + + URL of a relayer proxy for fee-sponsored transactions. When set, the SDK routes all submissions through the relayer instead of the RPC endpoint. + + ```typescript + relayerUrl: 'https://my-relayer.example.com' + ``` + + See the [Fee Sponsoring guide](/guides/fee-sponsoring) for details. + + + + Override the default indexer URL. The SDK auto-selects a default indexer based on `networkPassphrase`. Only set this if you're running a custom indexer. + + +## Environment Variables (Demo / Vite) + +If you're using Vite, the recommended pattern is to read from environment variables: + +```typescript +const kit = new SmartAccountKit({ + rpcUrl: import.meta.env.VITE_RPC_URL, + networkPassphrase: import.meta.env.VITE_NETWORK_PASSPHRASE, + accountWasmHash: import.meta.env.VITE_ACCOUNT_WASM_HASH, + webauthnVerifierAddress: import.meta.env.VITE_WEBAUTHN_VERIFIER_ADDRESS, + relayerUrl: import.meta.env.VITE_RELAYER_URL, +}); +``` + +```bash .env +VITE_RPC_URL=https://soroban-testnet.stellar.org +VITE_NETWORK_PASSPHRASE=Test SDF Network ; September 2015 +VITE_ACCOUNT_WASM_HASH= +VITE_WEBAUTHN_VERIFIER_ADDRESS= +VITE_RELAYER_URL=https://my-relayer.example.com +``` + +## Testnet Defaults + +The [demo `.env.example`](https://github.com/kalepail/smart-account-kit/blob/main/demo/.env.example) ships with pre-deployed testnet contract addresses so you can get started without deploying your own contracts. + +| Variable | Description | +|----------|-------------| +| `VITE_ACCOUNT_WASM_HASH` | Smart account WASM hash (testnet) | +| `VITE_WEBAUTHN_VERIFIER_ADDRESS` | WebAuthn verifier contract (testnet) | +| `VITE_ED25519_VERIFIER_ADDRESS` | Ed25519 verifier contract (testnet) | +| `VITE_THRESHOLD_POLICY_ADDRESS` | Threshold multisig policy (testnet) | +| `VITE_SPENDING_LIMIT_POLICY_ADDRESS` | Spending limit policy (testnet) | +| `VITE_NATIVE_TOKEN_CONTRACT` | Native XLM SAC contract (testnet) | diff --git a/docs/guides/error-handling.mdx b/docs/guides/error-handling.mdx new file mode 100644 index 0000000..9ff4cb1 --- /dev/null +++ b/docs/guides/error-handling.mdx @@ -0,0 +1,121 @@ +--- +title: Error Handling +description: Handle SDK errors with typed error classes and error codes. +--- + +## Overview + +Smart Account Kit uses typed error classes that extend `SmartAccountError`. Catch them specifically to give users clear, actionable feedback. + +## Error Classes + +```typescript +import { + SmartAccountError, + WalletNotConnectedError, + CredentialNotFoundError, + SignerNotFoundError, + SimulationError, + SubmissionError, + ValidationError, + WebAuthnError, + SessionError, +} from 'smart-account-kit'; +``` + +| Class | When thrown | +|-------|-------------| +| `WalletNotConnectedError` | A method requiring a connected wallet is called before connecting | +| `CredentialNotFoundError` | The requested credential ID is not in storage | +| `SignerNotFoundError` | The signer cannot be found on-chain | +| `SimulationError` | Soroban simulation rejected the transaction | +| `SubmissionError` | Transaction submission to the network failed | +| `ValidationError` | Input validation failed | +| `WebAuthnError` | WebAuthn browser API failed or user cancelled | +| `SessionError` | Session restore or save failed | + +## Usage + +### Catching specific errors + +```typescript +import { + WalletNotConnectedError, + SimulationError, + WebAuthnError, + SubmissionError, +} from 'smart-account-kit'; + +try { + await kit.transfer(tokenContract, recipient, amount); +} catch (error) { + if (error instanceof WalletNotConnectedError) { + showConnectPrompt(); + } else if (error instanceof WebAuthnError) { + // User cancelled the passkey prompt or the device rejected it + showToast('Authentication cancelled or failed', 'warning'); + } else if (error instanceof SimulationError) { + showToast('Transaction simulation failed: ' + error.message, 'error'); + } else if (error instanceof SubmissionError) { + showToast('Transaction failed to submit', 'error'); + } else { + throw error; // Re-throw unknown errors + } +} +``` + +### Using error codes + +Each error class carries an `errorCode` property: + +```typescript +import { SmartAccountError, SmartAccountErrorCode } from 'smart-account-kit'; + +try { + await kit.signAndSubmit(tx); +} catch (error) { + if (error instanceof SmartAccountError) { + switch (error.errorCode) { + case SmartAccountErrorCode.WALLET_NOT_CONNECTED: + // ... + break; + case SmartAccountErrorCode.SIMULATION_FAILED: + // ... + break; + default: + console.error('Unhandled SDK error:', error.errorCode, error.message); + } + } +} +``` + +### `wrapError` utility + +Wrap unknown caught values into `SmartAccountError`: + +```typescript +import { wrapError } from 'smart-account-kit'; + +try { + await someOperation(); +} catch (err) { + const wrapped = wrapError(err); + console.error(wrapped.errorCode, wrapped.message); +} +``` + +## WebAuthn Cancellation + +`WebAuthnError` is thrown when the user dismisses the passkey prompt or the browser rejects the WebAuthn ceremony. Always handle it gracefully - it's expected user behaviour, not a bug: + +```typescript +try { + await kit.connectWallet({ prompt: true }); +} catch (error) { + if (error instanceof WebAuthnError) { + // User cancelled - stay on the connect screen + return; + } + throw error; +} +``` diff --git a/docs/guides/events.mdx b/docs/guides/events.mdx new file mode 100644 index 0000000..39e6829 --- /dev/null +++ b/docs/guides/events.mdx @@ -0,0 +1,82 @@ +--- +title: Events +description: Subscribe to SDK lifecycle events via kit.events. +--- + +## Overview + +Smart Account Kit emits events for key lifecycle moments. Use `kit.events` to subscribe, handle one-time events, or clean up listeners. + +## Available Events + +| Event | Payload | Description | +|-------|---------|-------------| +| `walletConnected` | `{ contractId: string }` | Fired when connected to a wallet | +| `walletDisconnected` | `{}` | Fired on disconnect | +| `credentialCreated` | `{ credentialId: string }` | Fired when a passkey is registered | +| `credentialDeleted` | `{ credentialId: string }` | Fired when a credential is removed | +| `sessionExpired` | `{}` | Fired when the session expires | +| `transactionSigned` | `{ transaction: ... }` | Fired after auth entries are signed | +| `transactionSubmitted` | `{ hash: string; success: boolean }` | Fired after submission | + +## Usage + +### `on()` - Persistent listener + +```typescript +kit.events.on('walletConnected', ({ contractId }) => { + console.log('Connected to:', contractId); + updateUI(contractId); +}); + +kit.events.on('transactionSubmitted', ({ hash, success }) => { + if (success) { + showToast(`Transaction confirmed: ${hash}`); + } else { + showToast('Transaction failed', 'error'); + } +}); + +kit.events.on('sessionExpired', () => { + // Prompt user to reconnect + showReconnectBanner(); +}); +``` + +### `once()` - Fire once then remove + +```typescript +kit.events.once('walletConnected', ({ contractId }) => { + // Only fires on the next connection + console.log('First connection:', contractId); +}); +``` + +### `off()` - Remove a listener + +```typescript +const handler = ({ contractId }: { contractId: string }) => { + console.log('Connected:', contractId); +}; + +kit.events.on('walletConnected', handler); + +// Later... +kit.events.off('walletConnected', handler); +``` + +## Types + +```typescript +import type { SmartAccountEventMap, EventListener } from 'smart-account-kit'; + +type SmartAccountEventMap = { + walletConnected: { contractId: string }; + walletDisconnected: {}; + credentialCreated: { credentialId: string }; + credentialDeleted: { credentialId: string }; + sessionExpired: {}; + transactionSigned: { transaction: AssembledTransaction }; + transactionSubmitted: { hash: string; success: boolean }; +}; +``` diff --git a/docs/guides/fee-sponsoring.mdx b/docs/guides/fee-sponsoring.mdx new file mode 100644 index 0000000..9efef52 --- /dev/null +++ b/docs/guides/fee-sponsoring.mdx @@ -0,0 +1,69 @@ +--- +title: Fee Sponsoring +description: Enable gasless transactions by routing submissions through a relayer proxy. +--- + +## Overview + +By default, Smart Account Kit submits transactions directly via the Stellar RPC. Configure a `relayerUrl` to route submissions through a relayer proxy that sponsors fees - making your app effectively gasless for end users. + +## Setup + +```typescript +const kit = new SmartAccountKit({ + rpcUrl: 'https://soroban-testnet.stellar.org', + networkPassphrase: 'Test SDF Network ; September 2015', + accountWasmHash: 'YOUR_WASM_HASH', + webauthnVerifierAddress: 'YOUR_VERIFIER', + relayerUrl: 'https://my-relayer.example.com', // ← add this +}); +``` + +Once configured, all `transfer()`, `signAndSubmit()`, and `executeAndSubmit()` calls automatically route through the relayer. + +## Bypassing the Relayer + +To use RPC directly for a specific operation: + +```typescript +// Force RPC for this transfer only +await kit.transfer(tokenContract, recipient, amount, { forceMethod: 'rpc' }); +``` + +## Relayer Protocol + +The relayer proxy must accept POST requests with one of two formats: + +**For `invokeHostFunction` flows (func + auth):** +```json +{ "func": "", "auth": ["", ...] } +``` + +**For signed transactions (e.g. deployments):** +```json +{ "xdr": "" } +``` + +The relayer wraps the transaction in a fee bump, sponsors fees, and returns the submitted transaction hash. + +## Using RelayerClient Directly + +Access the relayer client directly via `kit.relayer`: + +```typescript +if (kit.relayer) { + // Submit func + auth for fee sponsoring + const result = await kit.relayer.send(funcXdr, authXdrs); + + // Or submit a signed transaction + const result = await kit.relayer.sendXdr(signedTx); + + if (result.success) { + console.log('Hash:', result.hash); + } else { + console.error('Error:', result.error, result.errorCode); + } +} +``` + +See [RelayerClient](/advanced/relayer-client) for the full API reference. diff --git a/docs/guides/multi-sig.mdx b/docs/guides/multi-sig.mdx new file mode 100644 index 0000000..db55802 --- /dev/null +++ b/docs/guides/multi-sig.mdx @@ -0,0 +1,115 @@ +--- +title: Multi-Sig +description: Set up threshold and weighted multi-signer flows. +--- + +## Overview + +Smart Account Kit supports multiple signer types and policy-enforced multi-sig authorization. You can require M-of-N passkey signatures, weighted voting, or spending limits - all enforced on-chain by policy contracts. + +## Adding Multiple Signers + +Add additional passkeys to an existing context rule: + +```typescript +// Add a backup passkey to rule 0 +await kit.signers.addPasskey(0, 'My App', 'Recovery Key', { + nickname: 'Backup YubiKey', +}); + +// Add a G-address signer (hardware wallet, co-signer, etc.) +await kit.signers.addDelegated(0, 'GABC...'); +``` + +## Threshold Policy (M-of-N) + +Require M signatures from N signers before authorizing a transaction: + +```typescript +import { createThresholdParams } from 'smart-account-kit'; + +// 2-of-N threshold +const params = createThresholdParams(2); +const installParams = kit.convertPolicyParams(params); + +await kit.policies.add( + 0, // context rule ID + process.env.THRESHOLD_POLICY_ADDRESS!, + installParams +); +``` + +## Weighted Threshold Policy + +Assign weights to signers; a transaction requires reaching a total weight threshold: + +```typescript +import { createWeightedThresholdParams } from 'smart-account-kit'; + +const params = createWeightedThresholdParams( + 100, // threshold (total weight required) + [ // weights per signer index + { signerIndex: 0, weight: 70 }, + { signerIndex: 1, weight: 50 }, + ] +); +const installParams = kit.convertPolicyParams(params); + +await kit.policies.add(0, process.env.WEIGHTED_THRESHOLD_POLICY_ADDRESS!, installParams); +``` + +## Executing a Multi-Sig Transaction + +Use `MultiSignerManager` when a transaction requires signatures from multiple signers: + +```typescript +// 1. Discover available signers +const signers = await kit.multiSigners.getAvailableSigners(); + +// 2. Check if multi-sig is needed +if (!kit.multiSigners.needsMultiSigner(signers)) { + await kit.transfer(tokenContract, recipient, amount); + return; +} + +// 3. Build signer selection +const selected = kit.multiSigners.buildSelectedSigners( + signers, + kit.activeCredentialId +); + +// 4. Execute multi-sig transfer +const result = await kit.multiSigners.transfer( + tokenContract, + recipient, + amount, + selected +); +``` + +## Spending Limit Policy + +Cap token spending over a time window (enforced per-token, per-reset-period): + +```typescript +import { createSpendingLimitParams, LEDGERS_PER_DAY } from 'smart-account-kit'; + +const params = createSpendingLimitParams( + 'CDLZFC3...', // token contract (native XLM) + BigInt(100 * 10_000_000), // 100 XLM in stroops + LEDGERS_PER_DAY // resets every ~24 hours +); +const installParams = kit.convertPolicyParams(params); + +await kit.policies.add(0, process.env.SPENDING_LIMIT_POLICY_ADDRESS!, installParams); +``` + +## Time Constants + +```typescript +import { LEDGERS_PER_HOUR, LEDGERS_PER_DAY, LEDGERS_PER_WEEK } from 'smart-account-kit'; + +LEDGERS_PER_HOUR // ~720 +LEDGERS_PER_DAY // ~17,280 +LEDGERS_PER_WEEK // ~120,960 +``` diff --git a/docs/guides/storage-adapters.mdx b/docs/guides/storage-adapters.mdx new file mode 100644 index 0000000..a1494dd --- /dev/null +++ b/docs/guides/storage-adapters.mdx @@ -0,0 +1,100 @@ +--- +title: Storage Adapters +description: Choose and configure the right credential storage backend for your app. +--- + +## Overview + +Smart Account Kit needs to persist passkey credentials and session data across page loads. It does this through a `StorageAdapter` interface with three built-in implementations. + +## Built-in Adapters + +### IndexedDBStorage (Recommended) + +Uses the browser's IndexedDB API. Best for production web apps - supports large payloads and works across tabs. + +```typescript +import { IndexedDBStorage } from 'smart-account-kit'; + +const kit = new SmartAccountKit({ + // ... + storage: new IndexedDBStorage(), +}); +``` + +### LocalStorageAdapter + +Uses `window.localStorage`. Simpler fallback for environments where IndexedDB is unavailable. + +```typescript +import { LocalStorageAdapter } from 'smart-account-kit'; + +storage: new LocalStorageAdapter() +``` + + + `localStorage` has a ~5MB limit and is synchronous. Use `IndexedDBStorage` in production. + + +### MemoryStorage + +Stores credentials in memory only - data is lost on page reload. Use for testing. + +```typescript +import { MemoryStorage } from 'smart-account-kit'; + +storage: new MemoryStorage() +``` + +## Custom Adapter + +Implement the `StorageAdapter` interface to use any backend (e.g. a server-side store, encrypted storage, or React Native AsyncStorage): + +```typescript +import type { StorageAdapter, StoredCredential, StoredSession } from 'smart-account-kit'; + +class MyStorage implements StorageAdapter { + async save(credential: StoredCredential): Promise { + // Persist credential + } + + async get(credentialId: string): Promise { + // Retrieve by ID + return null; + } + + async getAll(): Promise { + return []; + } + + async delete(credentialId: string): Promise { + // Remove credential + } + + async saveSession(session: StoredSession): Promise { + // Persist session + } + + async getSession(): Promise { + return null; + } + + async clearSession(): Promise { + // Clear session + } +} + +const kit = new SmartAccountKit({ + // ... + storage: new MyStorage(), +}); +``` + +## What Gets Stored + +| Data | Description | +|------|-------------| +| `StoredCredential` | Passkey public key, credential ID, associated contract ID, and metadata | +| `StoredSession` | Short-lived session token for silent reconnection on page reload | + +Session data is cleared by `kit.disconnect()`. Credentials persist until explicitly deleted via `kit.credentials.delete()`. diff --git a/docs/introduction.mdx b/docs/introduction.mdx new file mode 100644 index 0000000..6aeb8fc --- /dev/null +++ b/docs/introduction.mdx @@ -0,0 +1,67 @@ +--- +title: Introduction +description: TypeScript SDK for deploying and managing OpenZeppelin Smart Account contracts on Stellar/Soroban with WebAuthn passkey authentication. +--- + +## What is Smart Account Kit? + +Smart Account Kit is a TypeScript SDK that makes it easy to build wallet-aware applications on Stellar/Soroban using [OpenZeppelin's smart account contracts](https://github.com/OpenZeppelin/stellar-contracts). It handles the complexity of WebAuthn passkey authentication, session persistence, and multi-signer flows so you can focus on building your app. + +## Key Features + + + + Create and manage smart wallets secured by WebAuthn passkeys - no seed phrases required. + + + Automatic session persistence for seamless reconnection across page loads. + + + Support for passkeys (secp256r1), Ed25519 keys, and policy signers. + + + Fine-grained authorization control for different operations. + + + Threshold multisig, spending limits, and custom policies. + + + Flexible credential storage with IndexedDB, localStorage, or custom backends. + + + +## How It Works + +Smart Account Kit wraps the on-chain smart account contract with ergonomic TypeScript APIs: + +1. **Create** - Register a passkey and deploy a smart account contract on Stellar +2. **Connect** - Restore a session or prompt the user to select a passkey +3. **Sign & Submit** - Sign transactions via WebAuthn and submit to the network +4. **Manage** - Add/remove signers, create context rules, and attach policies + +## Architecture + +``` +Your App + ↓ +SmartAccountKit ← Main entry point + ├── SignerManager ← Add/remove signers on context rules + ├── ContextRuleManager ← CRUD context rules + ├── PolicyManager ← Attach/detach policies + ├── CredentialManager ← Local credential lifecycle + ├── MultiSignerManager ← Multi-sig transaction flows + ├── ExternalSignerManager ← G-address / wallet signer support + ├── RelayerClient ← Fee-sponsored submission + └── IndexerClient ← Contract discovery by credential/address +``` + +## Next Steps + + + + Get up and running in minutes. + + + Explore all SDK initialization options. + + diff --git a/docs/logo/dark.svg b/docs/logo/dark.svg new file mode 100644 index 0000000..50c2bab --- /dev/null +++ b/docs/logo/dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/logo/light.svg b/docs/logo/light.svg new file mode 100644 index 0000000..05990e2 --- /dev/null +++ b/docs/logo/light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/migration/v0.7.0.mdx b/docs/migration/v0.7.0.mdx new file mode 100644 index 0000000..6ec7436 --- /dev/null +++ b/docs/migration/v0.7.0.mdx @@ -0,0 +1,59 @@ +--- +title: Migrating to v0.7.0-rc.2 +description: Breaking changes and migration steps for the stellar-contracts v0.7.0-rc.2 upgrade. +--- + +## Overview + +This release targets the regenerated smart account contract surface from the latest uploaded testnet WASM set. It is a **clean-cut migration** - the SDK no longer preserves the legacy `Signatures` tuple flow or the removed `get_context_rules(...)` contract method. + +## New Contract Addresses (Testnet) + +| Contract | Address / Hash | +|----------|----------------| +| Smart account WASM hash | `3e51f5b222dec74650f0b33367acb42a41ce497f72639230463070e666abba2c` | +| WebAuthn verifier WASM hash | `d84af9e7c31afece287fee8276ef7d6a64b236d596c043594c003e0f4032d1c7` | +| Ed25519 verifier WASM hash | `e88b7989f8c5e69d6a72cda8419844ef2753ab249fef422f31436c5c32e28623` | +| Threshold policy WASM hash | `5c87cedc0e485152a084c4b5435bdec88e41304a4316e82e37a84910715639f6` | +| Spending-limit policy WASM hash | `eca96954a8e76e366e74fbc95eced11666c939e130a5cc302b8363622e931018` | +| WebAuthn verifier | `CATPTBRWVMH5ZCIKO5HN2F4FMPXVZEXC56RKGHRXCM7EEZGGXK7PICEH` | +| Ed25519 verifier | `CAIKK32K3BZJYTWVTXHZFPIEEDBR6YCVTGPABH4UQUQ4XFA3OLYXG27G` | +| Threshold policy | `CDDQLFG7CV74QHWPSP6NZIPNBR2PPCMTUVYCJF4P3ONDYHODRFGR7LWC` | +| Spending-limit policy | `CBYLPYZGLQ6JVY2IQ5P23QLQPR3KAMMKMZLNWG6RUUKJDNYGPLVHK7U4` | + + + There is intentionally no checked-in smart-account contract ID in the default config. Smart-account deployment is parameterized and requires constructor args for `signers` and `policies`, so the repo defaults to the uploaded smart-account WASM hash. + + +## Breaking Changes + +### Signing flow + +The old `Signatures` tuple encoding has been replaced with explicit `AuthPayload` encoding. If you were manually constructing auth payloads, update them to use the new `AuthPayload` type. + +### Removed contract methods + +`get_context_rules(...)` has been removed from the contract surface. Use `kit.rules.list()` (indexer-backed) or `kit.rules.get(id)` (direct chain read) instead. + +### Generated bindings + +`packages/smart-account-kit-bindings` has been regenerated from the new WASM hash. The following contract surface changes apply: + +| Change | Details | +|--------|---------| +| `add_signer` | Now returns `u32` (the new signer ID) | +| `add_policy` | Now returns `u32` (the new policy ID) | +| `get_context_rules_count` | New - returns the total count of context rules | +| `get_signer_id` | New - resolve a signer to its on-chain ID | +| `get_policy_id` | New - resolve a policy to its on-chain ID | +| `AuthPayload` | New - explicit auth payload type used in signing flow | + +## Migration Steps + +1. **Update your `.env`** - Replace all contract addresses and WASM hashes with the new testnet values above (or deploy your own from [stellar-contracts](https://github.com/OpenZeppelin/stellar-contracts)). + +2. **Regenerate bindings** - Run `pnpm run build:all` to fetch the new contract metadata and regenerate TypeScript bindings. + +3. **Remove legacy auth code** - If you were manually building `Signatures` tuples or calling `get_context_rules(...)`, remove that code. Use `kit.rules.list()` for rule enumeration. + +4. **Update auth payload handling** - If you interact with auth payloads directly, import and use the new `AuthPayload` type from `smart-account-kit`. diff --git a/docs/mint.json b/docs/mint.json new file mode 100644 index 0000000..81b3e6e --- /dev/null +++ b/docs/mint.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://mintlify.com/schema.json", + "name": "Smart Account Kit", + "logo": { + "light": "/logo/light.svg", + "dark": "/logo/dark.svg" + }, + "favicon": "/favicon.svg", + "colors": { + "primary": "#0E76FD", + "light": "#60A5FA", + "dark": "#0E76FD" + }, + "topbarLinks": [ + { + "name": "GitHub", + "url": "https://github.com/kalepail/smart-account-kit" + } + ], + "topbarCtaButton": { + "name": "npm", + "url": "https://www.npmjs.com/package/smart-account-kit" + }, + "navigation": [ + { + "group": "Getting Started", + "pages": [ + "introduction", + "quickstart", + "configuration" + ] + }, + { + "group": "API Reference", + "pages": [ + "api-reference/smart-account-kit", + "api-reference/signer-manager", + "api-reference/context-rule-manager", + "api-reference/policy-manager", + "api-reference/credential-manager", + "api-reference/multi-signer-manager", + "api-reference/external-signer-manager" + ] + }, + { + "group": "Guides", + "pages": [ + "guides/storage-adapters", + "guides/fee-sponsoring", + "guides/multi-sig", + "guides/events", + "guides/error-handling" + ] + }, + { + "group": "Advanced", + "pages": [ + "advanced/builder-functions", + "advanced/relayer-client", + "advanced/indexer-client", + "advanced/wallet-adapters" + ] + }, + { + "group": "Migration", + "pages": [ + "migration/v0.7.0" + ] + } + ], + "footerSocials": { + "github": "https://github.com/kalepail/smart-account-kit" + } +} diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx new file mode 100644 index 0000000..195e35d --- /dev/null +++ b/docs/quickstart.mdx @@ -0,0 +1,159 @@ +--- +title: Quick Start +description: Install Smart Account Kit and create your first passkey-secured wallet. +--- + +## Installation + + +```bash pnpm +pnpm add smart-account-kit +``` + +```bash npm +npm install smart-account-kit +``` + +```bash yarn +yarn add smart-account-kit +``` + + +## Initialize the SDK + +```typescript +import { SmartAccountKit, IndexedDBStorage } from 'smart-account-kit'; + +const kit = new SmartAccountKit({ + rpcUrl: 'https://soroban-testnet.stellar.org', + networkPassphrase: 'Test SDF Network ; September 2015', + accountWasmHash: 'YOUR_ACCOUNT_WASM_HASH', + webauthnVerifierAddress: 'YOUR_WEBAUTHN_VERIFIER_ADDRESS', + storage: new IndexedDBStorage(), +}); +``` + + + Testnet contract addresses (WASM hashes, verifier addresses, policy addresses) are available in the [demo `.env.example`](https://github.com/kalepail/smart-account-kit/blob/main/demo/.env.example). + + +## Create a Wallet + +Prompt the user to register a passkey and deploy a new smart account: + +```typescript +const { contractId, credentialId } = await kit.createWallet('My App', 'user@example.com', { + autoSubmit: true, // Deploy immediately + autoFund: true, // Fund via Friendbot (testnet only) + nativeTokenContract: 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC', +}); + +console.log('Wallet deployed at:', contractId); +``` + +## Connect to an Existing Wallet + +On subsequent page loads, silently restore a session or prompt for passkey selection: + +```typescript +// Silent restore (no UI prompt) +const result = await kit.connectWallet(); + +if (!result) { + // No stored session - show your connect button + showConnectButton(); +} + +// User clicks "Connect" - prompt passkey selection +await kit.connectWallet({ prompt: true }); +``` + +## Sign and Submit a Transaction + +```typescript +import { TransactionBuilder, Networks, Operation, Asset } from '@stellar/stellar-sdk'; + +// Build your transaction +const tx = new TransactionBuilder(account, { fee: BASE_FEE, networkPassphrase: Networks.TESTNET }) + .addOperation(Operation.payment({ destination, asset: Asset.native(), amount: '10' })) + .setTimeout(30) + .build(); + +// Sign and submit via the smart account +const result = await kit.signAndSubmit(tx); + +if (result.success) { + console.log('Submitted:', result.hash); +} +``` + +## Transfer Tokens + +For simple token transfers, use the `transfer` shortcut: + +```typescript +await kit.transfer( + 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC', // token contract + 'GRECIPIENT...', // recipient address + 100 // amount +); +``` + +## Disconnect + +```typescript +await kit.disconnect(); // Clears session; passkey and contract remain intact +``` + +## Full Example + +```typescript +import { SmartAccountKit, IndexedDBStorage } from 'smart-account-kit'; + +const kit = new SmartAccountKit({ + rpcUrl: 'https://soroban-testnet.stellar.org', + networkPassphrase: 'Test SDF Network ; September 2015', + accountWasmHash: process.env.ACCOUNT_WASM_HASH!, + webauthnVerifierAddress: process.env.WEBAUTHN_VERIFIER!, + storage: new IndexedDBStorage(), +}); + +// On page load +const session = await kit.connectWallet(); +if (session) { + console.log('Restored session for:', session.contractId); +} else { + // Show UI to create or connect wallet +} + +// Create wallet button +async function handleCreate() { + const { contractId } = await kit.createWallet('My App', 'user@example.com', { + autoSubmit: true, + autoFund: true, + }); + console.log('Created:', contractId); +} + +// Connect wallet button +async function handleConnect() { + await kit.connectWallet({ prompt: true }); +} +``` + +## Next Steps + + + + Full list of SDK initialization options. + + + Explore all available methods. + + + Choose the right storage backend for your app. + + + Set up threshold and weighted multi-signer flows. + + From d5b89a7c4ffe5346dd85a9b5c4e31e578d58cb98 Mon Sep 17 00:00:00 2001 From: Collins Ikechukwu Date: Sat, 11 Apr 2026 17:52:54 +0100 Subject: [PATCH 2/3] docs: add agent skills (agentskills.io spec) Adds five SKILL.md files, a discovery index, and an AI/Skills docs page so AI agents can discover and use the SDK via the agentskills.io protocol. Skills added: - smart-account-kit (SDK init, config, sub-managers) - create-stellar-wallet (wallet creation workflow) - sign-stellar-transactions (signing and submission) - manage-signers (signers and context rules) - setup-policies (threshold, spending limits, weighted voting) --- .../skills/create-stellar-wallet/SKILL.md | 117 +++++++++++++ docs/.mintlify/skills/manage-signers/SKILL.md | 160 +++++++++++++++++ docs/.mintlify/skills/setup-policies/SKILL.md | 165 ++++++++++++++++++ .../skills/sign-stellar-transactions/SKILL.md | 145 +++++++++++++++ .../skills/smart-account-kit/SKILL.md | 123 +++++++++++++ docs/.well-known/agent-skills/index.json | 30 ++++ docs/ai/skills.mdx | 124 +++++++++++++ docs/mint.json | 6 + 8 files changed, 870 insertions(+) create mode 100644 docs/.mintlify/skills/create-stellar-wallet/SKILL.md create mode 100644 docs/.mintlify/skills/manage-signers/SKILL.md create mode 100644 docs/.mintlify/skills/setup-policies/SKILL.md create mode 100644 docs/.mintlify/skills/sign-stellar-transactions/SKILL.md create mode 100644 docs/.mintlify/skills/smart-account-kit/SKILL.md create mode 100644 docs/.well-known/agent-skills/index.json create mode 100644 docs/ai/skills.mdx diff --git a/docs/.mintlify/skills/create-stellar-wallet/SKILL.md b/docs/.mintlify/skills/create-stellar-wallet/SKILL.md new file mode 100644 index 0000000..6d31ba7 --- /dev/null +++ b/docs/.mintlify/skills/create-stellar-wallet/SKILL.md @@ -0,0 +1,117 @@ +--- +name: create-stellar-wallet +description: "Create and deploy a WebAuthn passkey-secured smart account wallet on Stellar using smart-account-kit. Use when a user wants to create a new wallet, register a passkey, deploy a smart account, or set up a new account on Stellar/Soroban." +license: Apache-2.0 +compatibility: Designed for Claude Code. Requires a browser environment for WebAuthn. +metadata: + author: kalepail + version: "0.3.0" +allowed-tools: Read Write Edit +--- + +# Create a Stellar Smart Wallet + +Creates a WebAuthn passkey and deploys a smart account contract on Stellar/Soroban. + +## Prerequisites + +- `SmartAccountKit` initialized with `accountWasmHash` and `webauthnVerifierAddress` +- Browser environment (WebAuthn requires `window.navigator.credentials`) +- Testnet: a funded account or `autoFund: true` + +## Basic Creation + +```typescript +const { contractId, credentialId } = await kit.createWallet( + 'My App', // relying party name shown during passkey creation + 'user@example.com', // username label for the passkey + { + autoSubmit: true, // deploy immediately (recommended) + autoFund: true, // fund via Friendbot - testnet only + nativeTokenContract: process.env.VITE_NATIVE_TOKEN_CONTRACT!, + } +); + +console.log('Wallet deployed at:', contractId); +console.log('Credential ID:', credentialId); +``` + +## Options + +| Option | Default | Description | +|--------|---------|-------------| +| `autoSubmit` | false | Deploy the contract immediately after passkey creation | +| `autoFund` | false | Fund via Friendbot (testnet only, requires `nativeTokenContract`) | +| `nativeTokenContract` | - | Native XLM SAC contract ID, required for `autoFund` | + +## Step-by-Step (Manual) + +For custom flows where you need control between creation and deployment: + +```typescript +// 1. Create credential (registers passkey locally, no chain interaction) +const credential = await kit.credentials.create(); + +// 2. Save to storage +await kit.credentials.save(credential); + +// 3. Deploy when ready +const result = await kit.credentials.deploy(credential.credentialId, { + autoSubmit: true, +}); +``` + +## Post-Creation + +After `createWallet()` the kit is automatically connected. You can immediately: + +```typescript +// Transfer tokens +await kit.transfer(tokenContract, recipient, amount); + +// Sign arbitrary transactions +await kit.signAndSubmit(tx); + +// Access the connected contract ID +console.log(kit.contractId); +``` + +## Connecting on Return Visits + +On page reload, restore without showing the passkey prompt: + +```typescript +// Silent restore from stored session +const result = await kit.connectWallet(); + +if (!result) { + // No session - show create/connect UI +} +``` + +To always prompt: + +```typescript +await kit.connectWallet({ prompt: true }); // always show passkey picker +await kit.connectWallet({ fresh: true }); // ignore session, always prompt +``` + +## Common Issues + +**User cancels passkey prompt** +`WebAuthnError` is thrown. Catch it and stay on the create screen - this is expected behaviour, not a bug. + +```typescript +try { + await kit.createWallet(appName, userName, { autoSubmit: true }); +} catch (err) { + if (err instanceof WebAuthnError) return; // user cancelled + throw err; +} +``` + +**Wallet not funded (mainnet)** +On mainnet, remove `autoFund` and ensure the deploying account has enough XLM to cover the transaction fee before calling `createWallet()`. + +**Duplicate passkey names** +The `userName` parameter is a display label only - it does not need to be unique. However, using a meaningful label (e.g. the user's email) helps users identify the correct passkey in their device's credential picker. diff --git a/docs/.mintlify/skills/manage-signers/SKILL.md b/docs/.mintlify/skills/manage-signers/SKILL.md new file mode 100644 index 0000000..4ca9fc1 --- /dev/null +++ b/docs/.mintlify/skills/manage-signers/SKILL.md @@ -0,0 +1,160 @@ +--- +name: manage-signers +description: "Add, remove, and manage signers and context rules on a Stellar smart account using smart-account-kit. Use when a developer wants to add a passkey signer, add a G-address signer, remove a signer, create context rules, update rules, or manage who can authorize transactions on a smart account." +license: Apache-2.0 +compatibility: Designed for Claude Code. Requires a connected SmartAccountKit instance with indexer access for list operations. +metadata: + author: kalepail + version: "0.3.0" +allowed-tools: Read Write Edit +--- + +# Manage Signers and Context Rules + +Add and remove signers, and manage context rules that define authorization requirements for a smart account. + +## Prerequisites + +- `SmartAccountKit` initialized and connected +- For `list()` and `getAll()`: indexer access (auto-configured for testnet/mainnet) + +## Context Rules + +Context rules define what signers and policies are required for different operations. Every smart account needs at least one context rule. + +### Create a Context Rule + +```typescript +import { + createDefaultContext, + createCallContractContext, + createWebAuthnSigner, +} from 'smart-account-kit'; + +// Default context - matches any operation +const context = createDefaultContext(); + +// Or: only match calls to a specific contract +const context = createCallContractContext('CTARGET_CONTRACT...'); + +// Create the rule with an initial signer +const signer = createWebAuthnSigner( + verifierAddress, + publicKey, + credentialId +); + +await kit.rules.add(context, 'My Rule', [signer], []); +``` + +### Read Rules + +```typescript +// Read a specific rule directly from chain (by ID) +const rule = (await kit.rules.get(0)).result; + +// List all active rules (indexer-backed) +const rules = await kit.rules.list(); + +// All rules of a specific context type (indexer-backed) +const callRules = await kit.rules.getAll(createCallContractContext('C...')); +``` + +> `get()` reads from chain directly. `list()` and `getAll()` are indexer-backed because the contract does not expose an active-rule iterator after deletions. + +### Update a Rule + +```typescript +await kit.rules.updateName(0, 'New Name'); +await kit.rules.updateExpiration(0, expirationLedger); +``` + +### Remove a Rule + +```typescript +await kit.rules.remove(0); +``` + +## Signers + +### Add a Passkey Signer + +Registers a new WebAuthn passkey and adds it to a context rule: + +```typescript +const { credentialId } = await kit.signers.addPasskey( + 0, // context rule ID + 'My App', // app name shown during passkey creation + 'Recovery Key', // label for this passkey + { nickname: 'Backup YubiKey' } // optional local nickname +); +``` + +### Add a G-Address (Delegated) Signer + +Adds a Stellar G-address (hardware wallet, co-signer service, etc.): + +```typescript +await kit.signers.addDelegated( + 0, // context rule ID + 'GABC...' // Stellar address +); +``` + +### Remove a Signer + +Pass the signer value directly - the SDK resolves the on-chain signer ID internally: + +```typescript +// Get the signer object first +const rule = (await kit.rules.get(0)).result; +const signer = rule.signers[0]; + +await kit.signers.remove(0, signer); +``` + +### Batch Add Signers + +For adding multiple signers in one transaction, use the raw contract client: + +```typescript +// kit.wallet is available after connectWallet() +await kit.wallet?.batch_add_signer({ signers: [signer1, signer2] }); +``` + +## Signer Types + +| Builder | Signer Type | +|---------|-------------| +| `createWebAuthnSigner(verifier, pubKey, credId)` | Passkey (secp256r1) | +| `createDelegatedSigner(address, ed25519Verifier)` | G-address | +| `createEd25519Signer(verifier, pubKey)` | Ed25519 key | +| `createExternalSigner(verifier, pubKey)` | Custom verifier | + +## Context Rule Types + +| Builder | Matches | +|---------|---------| +| `createDefaultContext()` | Any operation | +| `createCallContractContext(contractId)` | Calls to a specific contract | +| `createCreateContractContext()` | Contract deployments | + +## Credential Lifecycle + +Local credential state is managed separately from on-chain signers: + +```typescript +// Get all stored credentials +const all = await kit.credentials.getAll(); + +// Get credentials for the current wallet +const mine = await kit.credentials.getForWallet(); + +// Sync local state against on-chain +await kit.credentials.syncAll(); + +// Delete a pending (never-deployed) credential +await kit.credentials.delete(credentialId); +``` + +> `credentials.delete()` only removes the local record. Use `kit.signers.remove()` to remove an on-chain signer. diff --git a/docs/.mintlify/skills/setup-policies/SKILL.md b/docs/.mintlify/skills/setup-policies/SKILL.md new file mode 100644 index 0000000..bda5c0e --- /dev/null +++ b/docs/.mintlify/skills/setup-policies/SKILL.md @@ -0,0 +1,165 @@ +--- +name: setup-policies +description: "Attach and configure authorization policies on Stellar smart account context rules using smart-account-kit. Use when a developer wants to set up threshold multisig, spending limits, weighted voting, M-of-N signatures, or any policy-enforced authorization on a Stellar smart account." +license: Apache-2.0 +compatibility: Designed for Claude Code. Requires a connected SmartAccountKit instance and deployed policy contracts. +metadata: + author: kalepail + version: "0.3.0" +allowed-tools: Read Write Edit +--- + +# Set Up Smart Account Policies + +Attach on-chain policy contracts to context rules to enforce authorization rules like M-of-N multisig, weighted voting, or spending limits. + +## Prerequisites + +- `SmartAccountKit` initialized and connected +- Deployed policy contract addresses (testnet defaults in `demo/.env.example`) +- A context rule to attach the policy to (see [manage-signers](../manage-signers/SKILL.md)) + +## Available Policy Types + +| Policy | Builder | Description | +|--------|---------|-------------| +| Threshold | `createThresholdParams(m)` | Require M signatures from any of the rule's signers | +| Weighted | `createWeightedThresholdParams(threshold, weights)` | Weighted voting with a total-weight threshold | +| Spending limit | `createSpendingLimitParams(token, limit, period)` | Cap token spending per time window | + +## Threshold Multisig (M-of-N) + +Require M signatures from the signers on a context rule: + +```typescript +import { createThresholdParams } from 'smart-account-kit'; + +// Require 2 signatures from any of the rule's signers +const params = createThresholdParams(2); +const installParams = kit.convertPolicyParams(params); + +await kit.policies.add( + 0, // context rule ID + process.env.VITE_THRESHOLD_POLICY_ADDRESS!, + installParams +); +``` + +## Weighted Threshold + +Assign per-signer weights; transaction is authorized when cumulative weight reaches the threshold: + +```typescript +import { createWeightedThresholdParams } from 'smart-account-kit'; + +const params = createWeightedThresholdParams( + 100, // required total weight + [ + { signerIndex: 0, weight: 70 }, + { signerIndex: 1, weight: 50 }, + ] +); +const installParams = kit.convertPolicyParams(params); + +await kit.policies.add( + 0, + process.env.VITE_WEIGHTED_THRESHOLD_POLICY_ADDRESS!, + installParams +); +``` + +## Spending Limit + +Cap how much of a token can be spent within a rolling ledger window: + +```typescript +import { createSpendingLimitParams, LEDGERS_PER_DAY } from 'smart-account-kit'; + +const params = createSpendingLimitParams( + process.env.VITE_NATIVE_TOKEN_CONTRACT!, // token to limit + BigInt(100 * 10_000_000), // 100 XLM (in stroops) + LEDGERS_PER_DAY // resets every ~24h +); +const installParams = kit.convertPolicyParams(params); + +await kit.policies.add( + 0, + process.env.VITE_SPENDING_LIMIT_POLICY_ADDRESS!, + installParams +); +``` + +## Time Constants + +```typescript +import { LEDGERS_PER_HOUR, LEDGERS_PER_DAY, LEDGERS_PER_WEEK } from 'smart-account-kit'; + +LEDGERS_PER_HOUR // ~720 ledgers +LEDGERS_PER_DAY // ~17,280 ledgers +LEDGERS_PER_WEEK // ~120,960 ledgers +``` + +## Multiple Policies on One Rule + +A single context rule can have multiple policies. All must be satisfied for a transaction to be authorized: + +```typescript +// Attach threshold policy +await kit.policies.add(0, thresholdPolicyAddress, thresholdInstallParams); + +// Also attach spending limit +await kit.policies.add(0, spendingLimitAddress, spendingInstallParams); +``` + +## Remove a Policy + +```typescript +await kit.policies.remove(0, policyAddress); +``` + +## Executing Multi-Sig Transactions + +Once a threshold policy is attached, use `MultiSignerManager` for transactions: + +```typescript +// Discover all available signers +const signers = await kit.multiSigners.getAvailableSigners(); + +if (kit.multiSigners.needsMultiSigner(signers)) { + const selected = kit.multiSigners.buildSelectedSigners( + signers, + kit.activeCredentialId + ); + + // Execute a transfer requiring multiple signatures + const result = await kit.multiSigners.transfer( + tokenContract, + recipient, + amount, + selected + ); +} +``` + +For arbitrary multi-sig operations: + +```typescript +const tx = await kit.execute('CTARGET...', 'function_name', [arg1]); +const result = await kit.multiSigners.operation(tx, selected); +``` + +## Common Patterns + +**2-of-3 multisig wallet** +1. Create a context rule +2. Add 3 passkey signers via `kit.signers.addPasskey()` +3. Attach a threshold policy with `createThresholdParams(2)` + +**Daily spending limit with single signer** +1. Create a default context rule with 1 signer +2. Attach a spending limit policy capped to your daily budget + +**High-value operation requires co-signer** +1. Create a `createCallContractContext` rule for the high-value contract +2. Add both your passkey and a hardware wallet G-address +3. Attach threshold with `createThresholdParams(2)` diff --git a/docs/.mintlify/skills/sign-stellar-transactions/SKILL.md b/docs/.mintlify/skills/sign-stellar-transactions/SKILL.md new file mode 100644 index 0000000..8d298ec --- /dev/null +++ b/docs/.mintlify/skills/sign-stellar-transactions/SKILL.md @@ -0,0 +1,145 @@ +--- +name: sign-stellar-transactions +description: "Sign and submit Stellar transactions using a smart account passkey via smart-account-kit. Use when a developer needs to sign transactions, submit operations, execute contract calls, or transfer tokens on Stellar/Soroban using a connected smart account." +license: Apache-2.0 +compatibility: Designed for Claude Code. Requires a connected SmartAccountKit instance. +metadata: + author: kalepail + version: "0.3.0" +allowed-tools: Read Write Edit +--- + +# Sign and Submit Stellar Transactions + +Sign and submit transactions through a connected smart account using WebAuthn passkey authentication. + +## Prerequisites + +- `SmartAccountKit` initialized and connected (`kit.connectWallet()` returned a result) +- A wallet contract deployed on the target network + +## Token Transfer (Shortcut) + +For simple token transfers, use `kit.transfer()`: + +```typescript +const result = await kit.transfer( + 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC', // token contract + 'GRECIPIENT...', + 100 +); + +if (result.success) { + console.log('Hash:', result.hash); +} +``` + +To bypass the relayer for a specific call: +```typescript +await kit.transfer(token, recipient, amount, { forceMethod: 'rpc' }); +``` + +## Arbitrary Contract Calls + +For calling any Soroban contract through the smart account: + +```typescript +// Build + sign + submit in one step (preferred) +const result = await kit.executeAndSubmit( + 'CTARGET_CONTRACT...', // contract to call + 'function_name', // function + [arg1, arg2] // xdr.ScVal[] arguments +); +``` + +To inspect the assembled transaction before submitting: +```typescript +const tx = await kit.execute('CTARGET...', 'function_name', [arg1, arg2]); +// tx is an AssembledTransaction - inspect, modify, then: +const result = await kit.signAndSubmit(tx.toXDR()); +``` + +## Sign a Pre-built Transaction + +When you already have a `Transaction` object: + +```typescript +import { TransactionBuilder, Networks, Operation, Asset } from '@stellar/stellar-sdk'; + +const tx = new TransactionBuilder(sourceAccount, { + fee: BASE_FEE, + networkPassphrase: Networks.TESTNET, +}) + .addOperation(Operation.payment({ + destination: 'GRECIPIENT...', + asset: Asset.native(), + amount: '10', + })) + .setTimeout(30) + .build(); + +const result = await kit.signAndSubmit(tx); +``` + +## Sign Auth Entries Only + +When you need the signed auth entries without submitting: + +```typescript +// Sign all auth entries on a transaction +const signedTx = await kit.sign(tx); + +// Sign a single auth entry +const signedEntry = await kit.signAuthEntry(authEntry); +``` + +## Transaction Result + +All submission methods return `TransactionResult`: + +```typescript +interface TransactionResult { + success: boolean; + hash?: string; + error?: string; +} + +const result = await kit.transfer(token, recipient, amount); +if (result.success) { + console.log('Confirmed:', result.hash); +} else { + console.error('Failed:', result.error); +} +``` + +## Fee Sponsoring + +If a `relayerUrl` was configured during initialization, all submissions are automatically routed through the relayer: + +```typescript +const kit = new SmartAccountKit({ + // ... + relayerUrl: 'https://my-relayer.example.com', +}); + +// Uses relayer automatically - user pays no fees +await kit.transfer(token, recipient, amount); +``` + +Access the relayer client directly for custom flows: +```typescript +if (kit.relayer) { + const result = await kit.relayer.sendXdr(signedXdr); +} +``` + +## Choosing the Right Method + +| Scenario | Method | +|----------|--------| +| Token transfer | `kit.transfer()` | +| Arbitrary contract call | `kit.executeAndSubmit()` | +| Pre-built transaction | `kit.signAndSubmit()` | +| Need signed tx, not submit | `kit.sign()` | +| Single auth entry | `kit.signAuthEntry()` | +| Multi-signer required | `kit.multiSigners.transfer()` or `kit.multiSigners.operation()` | diff --git a/docs/.mintlify/skills/smart-account-kit/SKILL.md b/docs/.mintlify/skills/smart-account-kit/SKILL.md new file mode 100644 index 0000000..64f206e --- /dev/null +++ b/docs/.mintlify/skills/smart-account-kit/SKILL.md @@ -0,0 +1,123 @@ +--- +name: smart-account-kit +description: "Initialize, configure, and use the smart-account-kit TypeScript SDK for deploying WebAuthn passkey-secured smart accounts on Stellar/Soroban. Use when a developer asks about smart-account-kit, passkey wallets on Stellar, or smart account integration." +license: Apache-2.0 +compatibility: Designed for Claude Code. Requires Node.js >= 22 and pnpm >= 10. +metadata: + author: kalepail + version: "0.3.0" +allowed-tools: Bash(pnpm:*) Read Write Edit +--- + +# Smart Account Kit + +TypeScript SDK for deploying and managing OpenZeppelin Smart Account contracts on Stellar/Soroban with WebAuthn passkey authentication. + +## Installation + +```bash +pnpm add smart-account-kit +``` + +## SDK Initialization + +Always initialize `SmartAccountKit` before any wallet operations: + +```typescript +import { SmartAccountKit, IndexedDBStorage } from 'smart-account-kit'; + +const kit = new SmartAccountKit({ + rpcUrl: 'https://soroban-testnet.stellar.org', + networkPassphrase: 'Test SDF Network ; September 2015', + accountWasmHash: process.env.VITE_ACCOUNT_WASM_HASH!, + webauthnVerifierAddress: process.env.VITE_WEBAUTHN_VERIFIER_ADDRESS!, + storage: new IndexedDBStorage(), + // Optional: + relayerUrl: process.env.VITE_RELAYER_URL, // fee sponsoring + timeoutInSeconds: 30, +}); +``` + +## Required Config Fields + +| Field | Description | +|-------|-------------| +| `rpcUrl` | Stellar RPC endpoint | +| `networkPassphrase` | Network passphrase (`Networks.TESTNET` or `Networks.PUBLIC`) | +| `accountWasmHash` | WASM hash of uploaded smart account contract | +| `webauthnVerifierAddress` | Deployed WebAuthn verifier contract ID | + +Testnet defaults are in `demo/.env.example`. + +## Sub-Managers + +After initialization, all sub-managers are available as properties: + +| Property | Purpose | +|----------|---------| +| `kit.signers` | Add/remove signers on context rules | +| `kit.rules` | CRUD context rules | +| `kit.policies` | Attach/detach policies | +| `kit.credentials` | Local credential lifecycle | +| `kit.multiSigners` | Multi-signer transaction flows | +| `kit.externalSigners` | G-address / wallet signers | +| `kit.indexer` | Contract discovery by credential or address | +| `kit.relayer` | Fee-sponsored submission | +| `kit.events` | Lifecycle event subscriptions | +| `kit.wallet` | Raw contract client (available after connect) | + +## Key Workflows + +- **Create wallet** - see [create-stellar-wallet](../create-stellar-wallet/SKILL.md) +- **Sign and submit transactions** - see [sign-stellar-transactions](../sign-stellar-transactions/SKILL.md) +- **Add/remove signers and rules** - see [manage-signers](../manage-signers/SKILL.md) +- **Set up multisig policies** - see [setup-policies](../setup-policies/SKILL.md) + +## On-Page Load Pattern + +```typescript +// 1. Try silent session restore +const session = await kit.connectWallet(); + +if (session) { + // Already connected - proceed + console.log('Restored:', session.contractId); +} else { + // Show connect/create UI +} +``` + +## Environment Variables (Vite) + +```bash +VITE_RPC_URL=https://soroban-testnet.stellar.org +VITE_NETWORK_PASSPHRASE=Test SDF Network ; September 2015 +VITE_ACCOUNT_WASM_HASH= +VITE_WEBAUTHN_VERIFIER_ADDRESS= +VITE_RELAYER_URL=https://my-relayer.example.com # optional +``` + +## Error Handling + +All SDK operations throw typed errors: + +```typescript +import { + WalletNotConnectedError, + WebAuthnError, + SimulationError, + SubmissionError, +} from 'smart-account-kit'; + +try { + await kit.transfer(token, recipient, amount); +} catch (err) { + if (err instanceof WebAuthnError) { + // User cancelled passkey prompt - expected, not a bug + } else if (err instanceof WalletNotConnectedError) { + // Need to connect first + } else if (err instanceof SimulationError) { + // Contract simulation rejected the tx + } +} +``` diff --git a/docs/.well-known/agent-skills/index.json b/docs/.well-known/agent-skills/index.json new file mode 100644 index 0000000..63f8e2a --- /dev/null +++ b/docs/.well-known/agent-skills/index.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json", + "skills": [ + { + "name": "smart-account-kit", + "description": "Initialize, configure, and use the smart-account-kit TypeScript SDK for deploying WebAuthn passkey-secured smart accounts on Stellar/Soroban. Use when a developer asks about smart-account-kit, passkey wallets on Stellar, or smart account integration.", + "url": "/.mintlify/skills/smart-account-kit/SKILL.md" + }, + { + "name": "create-stellar-wallet", + "description": "Create and deploy a WebAuthn passkey-secured smart account wallet on Stellar using smart-account-kit. Use when a user wants to create a new wallet, register a passkey, deploy a smart account, or set up a new account on Stellar/Soroban.", + "url": "/.mintlify/skills/create-stellar-wallet/SKILL.md" + }, + { + "name": "sign-stellar-transactions", + "description": "Sign and submit Stellar transactions using a smart account passkey via smart-account-kit. Use when a developer needs to sign transactions, submit operations, execute contract calls, or transfer tokens on Stellar/Soroban using a connected smart account.", + "url": "/.mintlify/skills/sign-stellar-transactions/SKILL.md" + }, + { + "name": "manage-signers", + "description": "Add, remove, and manage signers and context rules on a Stellar smart account using smart-account-kit. Use when a developer wants to add a passkey signer, add a G-address signer, remove a signer, create context rules, update rules, or manage who can authorize transactions on a smart account.", + "url": "/.mintlify/skills/manage-signers/SKILL.md" + }, + { + "name": "setup-policies", + "description": "Attach and configure authorization policies on Stellar smart account context rules using smart-account-kit. Use when a developer wants to set up threshold multisig, spending limits, weighted voting, M-of-N signatures, or any policy-enforced authorization on a Stellar smart account.", + "url": "/.mintlify/skills/setup-policies/SKILL.md" + } + ] +} diff --git a/docs/ai/skills.mdx b/docs/ai/skills.mdx new file mode 100644 index 0000000..ac774c1 --- /dev/null +++ b/docs/ai/skills.mdx @@ -0,0 +1,124 @@ +--- +title: Agent Skills +description: Use Smart Account Kit skills to let AI agents (Claude Code, Cursor, etc.) understand and work with the SDK in your project. +--- + +## What Are Agent Skills? + +Agent skills are structured files ([agentskills.io spec](https://agentskills.io/specification)) that describe what an SDK can do and how to use it. When an AI agent discovers your project's skills, it can answer questions, write correct code, and follow best practices without requiring manual context or documentation links. + +Smart Account Kit ships five skills covering its core workflows. + +## Available Skills + +| Skill | Description | +|-------|-------------| +| `smart-account-kit` | SDK initialization, config, sub-managers, and error handling | +| `create-stellar-wallet` | Create and deploy passkey-secured smart accounts | +| `sign-stellar-transactions` | Sign and submit transactions through a smart account | +| `manage-signers` | Add/remove signers, create and update context rules | +| `setup-policies` | Threshold multisig, spending limits, weighted voting | + +## Using Skills with Claude Code + +### Option 1 - Mintlify MCP (Recommended) + +If you have the [Mintlify MCP server](/ai/mcp) configured in Claude Code, skills are discovered automatically when you reference the docs. + +Add to your `~/.claude/mcp.json`: + +```json +{ + "servers": { + "Mintlify": { + "type": "http", + "url": "https://mintlify.com/docs/mcp" + } + } +} +``` + +Then ask Claude Code questions and it will pull the relevant skill automatically: + +``` +How do I add a 2-of-3 multisig policy to my smart account? +``` + +### Option 2 - Direct Skill Reference + +Reference a skill file directly in your prompt or `CLAUDE.md`: + +``` +@docs/.mintlify/skills/create-stellar-wallet/SKILL.md + +Set up wallet creation in my React app. +``` + +### Option 3 - Add to CLAUDE.md + +Add a skills reference block to your project's `CLAUDE.md` so Claude Code loads the relevant skill context automatically: + +```markdown +## Smart Account Kit + +This project uses smart-account-kit for Stellar smart account management. + +Relevant skills: +- @docs/.mintlify/skills/smart-account-kit/SKILL.md +- @docs/.mintlify/skills/create-stellar-wallet/SKILL.md +- @docs/.mintlify/skills/sign-stellar-transactions/SKILL.md +``` + +## Discovery Endpoint + +AI agents that support the agentskills.io discovery protocol can find all skills automatically at: + +``` +GET /.well-known/agent-skills/index.json +``` + +This is served automatically by Mintlify when your docs are deployed. + +## Skill Files + +All skill files live under `docs/.mintlify/skills/`: + +``` +docs/ +└── .mintlify/ + └── skills/ + ├── smart-account-kit/ + │ └── SKILL.md + ├── create-stellar-wallet/ + │ └── SKILL.md + ├── sign-stellar-transactions/ + │ └── SKILL.md + ├── manage-signers/ + │ └── SKILL.md + └── setup-policies/ + └── SKILL.md +``` + +Each `SKILL.md` contains: +- **Frontmatter** - machine-readable metadata (`name`, `description`, `allowed-tools`) +- **Instructions** - step-by-step usage for AI agents +- **Code examples** - copy-paste ready snippets + +## Writing Custom Skills + +You can extend or override these skills for your own project. Create a `SKILL.md` following the [agentskills.io specification](https://agentskills.io/specification): + +```markdown +--- +name: my-custom-skill +description: "What this skill does and when to use it." +license: MIT +allowed-tools: Read Write Edit Bash(pnpm:*) +--- + +## Instructions + +Step-by-step instructions for the AI agent... +``` + +Place it in `.claude/commands/my-custom-skill.md` (project-level) or `~/.claude/commands/my-custom-skill.md` (user-level) for Claude Code to pick it up as a `/my-custom-skill` slash command. diff --git a/docs/mint.json b/docs/mint.json index 81b3e6e..63912c2 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -61,6 +61,12 @@ "advanced/wallet-adapters" ] }, + { + "group": "AI", + "pages": [ + "ai/skills" + ] + }, { "group": "Migration", "pages": [ From e647ee5aff62e93ab4ca64f18caf7f6f443fe1d0 Mon Sep 17 00:00:00 2001 From: Collins Ikechukwu Date: Sat, 11 Apr 2026 17:57:19 +0100 Subject: [PATCH 3/3] docs: update skills page with npx skills add CLI usage --- docs/ai/skills.mdx | 144 ++++++++++++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/docs/ai/skills.mdx b/docs/ai/skills.mdx index ac774c1..9fb53ff 100644 --- a/docs/ai/skills.mdx +++ b/docs/ai/skills.mdx @@ -5,10 +5,56 @@ description: Use Smart Account Kit skills to let AI agents (Claude Code, Cursor, ## What Are Agent Skills? -Agent skills are structured files ([agentskills.io spec](https://agentskills.io/specification)) that describe what an SDK can do and how to use it. When an AI agent discovers your project's skills, it can answer questions, write correct code, and follow best practices without requiring manual context or documentation links. +Agent skills are structured files ([agentskills.io spec](https://agentskills.io/specification)) that describe what an SDK can do and how to use it. When an AI agent discovers your project's skills, it can answer questions, write correct code, and follow best practices without needing manual context or documentation links. Smart Account Kit ships five skills covering its core workflows. +## Install Skills + +Use the [`skills` CLI](https://skills.sh) to install all Smart Account Kit skills into your project in one command: + + +```bash npx +npx skills add https://smart-account-kit.mintlify.app/ +``` + +```bash Global install +npx skills add https://smart-account-kit.mintlify.app/ -g +``` + +```bash Specific agents only +npx skills add https://smart-account-kit.mintlify.app/ --agent claude-code cursor +``` + +```bash Specific skills only +npx skills add https://smart-account-kit.mintlify.app/ --skill create-stellar-wallet sign-stellar-transactions +``` + + +This fetches the skills index from the docs site and installs the relevant `SKILL.md` files into the agent directories on your machine. + +## Manage Installed Skills + +```bash +# List installed skills in your project +npx skills list + +# List global skills +npx skills ls -g + +# Check for updates +npx skills check + +# Update all skills +npx skills update + +# Remove a skill +npx skills remove manage-signers + +# Remove all Smart Account Kit skills +npx skills remove --skill '*' -y +``` + ## Available Skills | Skill | Description | @@ -19,94 +65,88 @@ Smart Account Kit ships five skills covering its core workflows. | `manage-signers` | Add/remove signers, create and update context rules | | `setup-policies` | Threshold multisig, spending limits, weighted voting | -## Using Skills with Claude Code - -### Option 1 - Mintlify MCP (Recommended) +Preview available skills before installing: -If you have the [Mintlify MCP server](/ai/mcp) configured in Claude Code, skills are discovered automatically when you reference the docs. - -Add to your `~/.claude/mcp.json`: - -```json -{ - "servers": { - "Mintlify": { - "type": "http", - "url": "https://mintlify.com/docs/mcp" - } - } -} +```bash +npx skills add https://smart-account-kit.mintlify.app/ --list ``` -Then ask Claude Code questions and it will pull the relevant skill automatically: - -``` -How do I add a 2-of-3 multisig policy to my smart account? -``` +## Using Skills with Claude Code -### Option 2 - Direct Skill Reference +Once installed, Claude Code picks up skills automatically from the agent directory. You can also: -Reference a skill file directly in your prompt or `CLAUDE.md`: +### Reference a skill directly ``` -@docs/.mintlify/skills/create-stellar-wallet/SKILL.md +@.claude/commands/create-stellar-wallet.md Set up wallet creation in my React app. ``` -### Option 3 - Add to CLAUDE.md +### Add to CLAUDE.md -Add a skills reference block to your project's `CLAUDE.md` so Claude Code loads the relevant skill context automatically: +Pin the most relevant skills for your project in `CLAUDE.md` so they are always in context: ```markdown ## Smart Account Kit This project uses smart-account-kit for Stellar smart account management. -Relevant skills: -- @docs/.mintlify/skills/smart-account-kit/SKILL.md -- @docs/.mintlify/skills/create-stellar-wallet/SKILL.md -- @docs/.mintlify/skills/sign-stellar-transactions/SKILL.md +@.claude/commands/smart-account-kit.md +@.claude/commands/sign-stellar-transactions.md +``` + +### Use the Mintlify MCP + +If you have the Mintlify MCP server configured, skills are discovered automatically when you ask about the SDK: + +```json ~/.claude/mcp.json +{ + "servers": { + "Mintlify": { + "type": "http", + "url": "https://mintlify.com/docs/mcp" + } + } +} ``` ## Discovery Endpoint -AI agents that support the agentskills.io discovery protocol can find all skills automatically at: +AI agents that support the agentskills.io discovery protocol find all skills at: ``` GET /.well-known/agent-skills/index.json ``` -This is served automatically by Mintlify when your docs are deployed. +This endpoint is served automatically by Mintlify. ## Skill Files -All skill files live under `docs/.mintlify/skills/`: +All skill source files live in the repo under `docs/.mintlify/skills/`: ``` docs/ └── .mintlify/ └── skills/ - ├── smart-account-kit/ - │ └── SKILL.md - ├── create-stellar-wallet/ - │ └── SKILL.md - ├── sign-stellar-transactions/ - │ └── SKILL.md - ├── manage-signers/ - │ └── SKILL.md - └── setup-policies/ - └── SKILL.md -``` - -Each `SKILL.md` contains: -- **Frontmatter** - machine-readable metadata (`name`, `description`, `allowed-tools`) -- **Instructions** - step-by-step usage for AI agents -- **Code examples** - copy-paste ready snippets + ├── smart-account-kit/SKILL.md + ├── create-stellar-wallet/SKILL.md + ├── sign-stellar-transactions/SKILL.md + ├── manage-signers/SKILL.md + └── setup-policies/SKILL.md +``` + +Each file contains machine-readable frontmatter (`name`, `description`, `allowed-tools`) followed by step-by-step instructions and copy-paste code examples. ## Writing Custom Skills -You can extend or override these skills for your own project. Create a `SKILL.md` following the [agentskills.io specification](https://agentskills.io/specification): +Extend or create your own skills with `npx skills init`: + +```bash +npx skills init my-custom-skill +``` + +Then edit the generated `SKILL.md` following the [agentskills.io specification](https://agentskills.io/specification): ```markdown --- @@ -120,5 +160,3 @@ allowed-tools: Read Write Edit Bash(pnpm:*) Step-by-step instructions for the AI agent... ``` - -Place it in `.claude/commands/my-custom-skill.md` (project-level) or `~/.claude/commands/my-custom-skill.md` (user-level) for Claude Code to pick it up as a `/my-custom-skill` slash command.