name: "@1sat/connect SDK Development" description: >- This skill should be used when the user asks to "add a feature to @1sat/connect", "update the connect SDK", "add a new RPC method", "fix popup handling", "update wallet types", "modify the popup protocol", "add error handling", or needs guidance on developing, testing, or publishing the @1sat/connect popup-based wallet SDK package.
@1sat/connect SDK Development Guide
This skill provides guidance for developing the @1sat/connect package — a popup-based
wallet SDK that enables dApps to communicate with the 1Sat Wallet at www.1satwallet.com.
Package Architecture
packages/connect/src/
index.ts — Public API: exports, createOneSat(), detection helpers
provider.ts — OneSatBrowserProvider: popup-based provider implementation
popup.ts — PopupManager: opens popups, tracks pending requests, handles timeouts
messages.ts — Protocol message format: request/response creation, validation
types.ts — All TypeScript interfaces and type exports
errors.ts — Typed error classes with numeric codes (4001-4014)
storage.ts — localStorage persistence for connection state
wallet.ts — Wallet-side utilities (sendResponse, rejectRequest, parsePopupParams)
transport.ts — CWI transport layer (embed/redirect for BRC-100 operations)
Popup Communication Protocol
Request Flow
- dApp calls a provider method (e.g.
connect(),signMessage()) OneSatBrowserProvider.sendRequest()generates arequestIdviacrypto.randomUUID()PopupManager.openPopup()builds a URL:{popupUrl}/connect?method=X&requestId=Y&origin=Z¶ms={...}window.open()opens the popup; returnsnullif blocked →PopupBlockedError- A
PendingRequestis stored withresolve/rejectcallbacks, timeout, and close-check interval OneSatBrowserProvider.handleMessage()listens forwindow.postMessagefrom the popup origin- On match: resolves or rejects the pending request, cleans up timers
Response Flow (Wallet Side)
- Popup page calls
parsePopupParams(searchParams)to extractrequestId,origin,appName,params - User approves →
sendResponse(requestId, result, origin)which callswindow.opener.postMessage() - User rejects →
rejectRequest(requestId, origin)sends error code 4001 - Popup closes via
window.close()
Message Format
interface ProtocolMessage {
type: 'onesat'
version: 1
requestId: string
method?: string // Present in requests
params?: unknown // Present in requests with params
result?: unknown // Present in success responses
error?: { // Present in error responses
code: number
message: string
data?: unknown
}
}
Adding New RPC Methods
To add a new wallet operation:
1. Define Types in types.ts
// Add request/result interfaces
export interface NewMethodRequest {
someParam: string
}
export interface NewMethodResult {
txid: string
}
// Add to RpcMethods object
export const RpcMethods = {
// ... existing methods
NEW_METHOD: 'newMethod',
} as const
2. Add Provider Method in provider.ts
async newMethod(request: NewMethodRequest): Promise<NewMethodResult> {
this.requireConnection() // Ensure connected first
return this.sendRequest<NewMethodResult>(RpcMethods.NEW_METHOD, request)
}
3. Update Provider Interface in types.ts
export interface OneSatProvider {
// ... existing methods
newMethod(request: NewMethodRequest): Promise<NewMethodResult>
}
4. Export from index.ts
Add the new types to the type exports block.
5. Handle in Wallet Popup
In the 1sat-web repo, handle the new method in the appropriate popup page.
Error System
All errors extend OneSatError with a numeric code:
| Code | Class | When |
|---|---|---|
| 4001 | UserRejectedError |
User rejected request |
| 4002 | WalletLockedError |
Wallet is locked |
| 4003 | WalletNotConnectedError |
Not connected |
| 4004 | InsufficientFundsError |
Not enough BSV |
| 4006 | PopupBlockedError |
Browser blocked popup |
| 4007 | PopupClosedError |
Popup closed unexpectedly |
| 4008 | TimeoutError |
Request timed out |
To add a new error:
- Add code to
ErrorCodesinerrors.ts - Create error class extending
OneSatError - Add case to
fromErrorResponse()switch - Export from
index.ts
ConnectParams Challenge Pattern
The connect() method accepts optional ConnectParams with a challenge string.
When provided, the wallet popup signs the challenge with BSM during the same user
interaction, returning the signature in ConnectResult.signedMessage. This avoids
a second window.open() which browsers block.
Critical: This is the recommended pattern for any dApp that needs to verify wallet
ownership after connecting. Without it, signMessage() requires a second popup that
will be blocked in most browsers.
Build & Publish
# Build
bun run build # Bundles to dist/ + generates .d.ts
# Publish (use npm-publish skill)
# Current version: check package.json
# npm registry: npm view @1sat/connect version
The build uses bun build for the JS bundle and tsc --emitDeclarationOnly for type declarations.
Key Design Decisions
- DEFAULT_POPUP_URL:
https://www.1satwallet.com— must includewwwdue to 307 redirect - Timeout: 5 minutes (300,000ms) — wallet operations can take time
- Popup check interval: 500ms — checks if popup was closed
- Storage: localStorage for connection persistence across page loads
- CWI Transport: Separate from popup flow — used for BRC-100 operations via embed/redirect
Additional Resources
Reference Files
references/protocol-spec.md— Detailed protocol message specification- Root README.md — SDK overview and quick start
packages/extension/README.md— Browser extension development guide
Related Repos
- 1sat-web (
~/code/1sat-web) — Wallet popup pages that handle SDK requests - sigma-auth (
~/code/sigma-auth) — Reference consumer using challenge signing