1Shot API's free, permissionless embedded wallet. Includes passkey-based verification and OID4 credential management for shared KYC.
This repo is the OWS Branding Layer for the 1Shot Wallet — a frontend-only static app (React + Vite + Tailwind + shadcn/ui) hosted at wallet.1shotapi.com.
Host Layer (integrator dapp)
└── This app / Branding Layer (@1shotapi/ows-wallet-utils)
└── /signer/ Signing Layer (@1shotapi/ows-signer)
Safari create (first-party): /create/ → embeds Branding + createAccount RPC
| Path | Content |
|---|---|
/ |
React Branding Layer (Vite bundle) |
/signer/ |
Static @1shotapi/ows-signer ES modules |
/create/ |
First-party Host page for Safari passkey create |
extension/ |
MV3 Chrome/Firefox extension (MetaMask-style host) |
Production deliverable: a static nginx Docker image (no server-side runtime).
/assets/ never SPA-fallbacks (missing files return 404). That prevents CDNs from caching index.html under a hashed .js/.css URL, which Firefox surfaces as NS_ERROR_CORRUPTED_CONTENT. After a bad cache, purge Cloudflare for wallet.1shotapi.com/assets/* (or the whole zone) — redeploying alone will not clear poisoned entries until max-age expires.
npm install
cp .env.example .env # set NGROK_AUTHTOKEN (and optional NGROK_DOMAIN)npm run dev # Branding Layer + ngrok HTTPS tunnel
npm run dev:local # Branding Layer, local HTTP only
npm run dev:host # Test Host Layer (configure knobs + EIP-1193)
npm run dev:extension # Browser extension (side panel + EIP-1193 shim)| Service | Local URL |
|---|---|
Branding (/) |
http://localhost:5174/ |
Signing (/signer/) |
http://localhost:5174/signer/ |
Create (/create/) |
http://localhost:5174/create/ |
| Test host | http://localhost:5173 |
Passkeys need HTTPS — use the printed ngrok wallet URL as the host iframe source (NGROK_DOMAIN in .env is picked up by dev:host).
By default Vite uses published @1shotapi/ows-* from node_modules. To point at a sibling ../prf-wallet checkout, set OWS_LOCAL_PACKAGES=1 (Firefox often breaks on the resulting /@fs/C: module URLs — prefer Chrome, or leave the flag unset for ngrok).
Style testing: use the Style (configure RPC) panel on the test host (host/), not in-wallet debug UI. See host/README.md.
Production iframe URL: https://wallet.1shotapi.com/
npm install @1shotapi/ows-providerimport { OWSProxy } from "@1shotapi/ows-provider";
const proxy = await OWSProxy.create(container, "https://wallet.1shotapi.com/");
await proxy.rpc("configure", {
copy: { productName: "Acme Wallet", tagline: "Powered by 1Shot" },
theme: { primary: "oklch(0.45 0.18 250)" },
});
proxy.showWallet();The Branding Layer publishes product events over Postmate (ows:analytics). OWS types only the base fields (eventId, timestamp, hostDomain, name); this wallet adds rich fields. Hosts receive the full object:
proxy.analytics.on((event) => {
// switch (event.name) { case "PersonalSign": ... }
console.info("wallet analytics", event);
});name |
When | Notable fields |
|---|---|---|
AccountCreated / AccountCreateFailed / AccountCreateCancelled |
Passkey create | accountAddress, errorCode |
PersonalSign / …Failed / …Cancelled |
EIP-191 sign | accountAddress, messageLength, durationMs |
TypedSign / …Failed / …Cancelled |
EIP-712 sign | accountAddress, primaryType, durationMs |
TransactionSubmitted / …Failed / …Cancelled |
Send / host tx | accountAddress, chainId, to, txHash, methodId, durationMs |
CredentialIssued / …Failed / …Cancelled |
OID4VCI accept | issuerOrigin, durationMs |
CredentialPresented / …Failed / …Cancelled |
OID4VP present | verifierOrigin, durationMs |
DelegationCreated / …Failed / …Cancelled |
EIP-7715 grant | accountAddress, chainId, durationMs |
DelegationCancelled / …Failed / DelegationCancelAborted |
EIP-7715 revoke | accountAddress, chainId, txHash, durationMs |
The same rich payload is also POSTed fire-and-forget to the 1Shot relayer
POST /wallet/product-events. The local Host playground (host/) shows a live
Analytics panel fed by proxy.analytics.on — filter by name to inspect
outcomes while testing.
Additive merge of theme CSS variables, copy, feature flags, and optional
destinationUrl (status webhooks from the
1Shot Relayer).
Safe to call repeatedly. Schema is Zod-strict (unknown keys rejected). Full field
list: skills/oneshot-embedded-wallet/SKILL.md.
When destinationUrl is set, the wallet asks the 1Shot Relayer to send
transaction status update webhooks to that URL.
Integrators / coding agents:
npx skills add 1Shot-API/embedded-wallet@oneshot-embedded-wallet
# or from a sibling clone:
npx skills add ../embedded-wallet --skill oneshot-embedded-walletSource: skills/oneshot-embedded-wallet.
npm run build # dist/ (branding + create/) + dist/signer
npm run preview # preview production wallet builddocker build -t oneshot-wallet .
docker run --rm -p 8080:80 oneshot-wallet
# open http://localhost:8080/
# signer at http://localhost:8080/signer/| Script | Purpose |
|---|---|
dev / dev:local |
Dev server (± ngrok) |
dev:host |
Test Host Layer |
build |
Typecheck + Vite build + copy signer from node_modules |
clean |
Remove dist/ |
lint |
tsc --noEmit |
See roadmap.md (ShadCN + customization phases).
Functional Branding Layer (passkey unlock, EIP-1193, signing consent, recovery, credentials) with Phase 0–1 style foundations (configure + StyleProvider + shell). ShadCN UI migration in progress.