Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions docs/.mintlify/skills/create-stellar-wallet/SKILL.md
Original file line number Diff line number Diff line change
@@ -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.
160 changes: 160 additions & 0 deletions docs/.mintlify/skills/manage-signers/SKILL.md
Original file line number Diff line number Diff line change
@@ -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.
Loading