Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
0e40647
Initial bootstrap.
Jul 15, 2026
727a94f
Refactor project structure to utilize @1shotapi packages for the Sign…
Jul 15, 2026
5db4c4f
Create roadmap for refactor and ShadCN-ification
Jul 15, 2026
3175ff7
Update dependencies and enhance styling capabilities
Jul 15, 2026
afdc0c6
Implement Test Host Layer for 1Shot Wallet
Jul 15, 2026
7431a44
Refactor 1Shot Wallet structure and update configurations
Jul 15, 2026
318fb66
Refactor modals and UI components for improved dialog handling
Jul 15, 2026
014be3c
Enhance connect modal functionality and styling
Jul 15, 2026
7c84185
Implement wallet setup modal and update related styles
Jul 15, 2026
3fc6b24
Complete passkey name modal integration and update related styles
Jul 15, 2026
7207078
Create CI/CD for publishing the wallet.
Jul 15, 2026
b5b7d45
Refactor signing modals with components and adjustable styles
Jul 15, 2026
a0dd15e
Credentials modals
Jul 15, 2026
c4b4809
Refactor backup components.
Jul 15, 2026
9052d31
Add Zustand for state management and integrate session and modal stores
Jul 15, 2026
6b88431
Refactor host application structure and enhance styling capabilities
Jul 15, 2026
7290417
Update package-lock and host configuration for inline wallet presenta…
Jul 15, 2026
f672942
Revert back to NPM published version of OWS package
Jul 15, 2026
07cf70a
Add react-colorful dependency and enhance WalletConfigurator with new…
Jul 15, 2026
d1e9f59
Reshape wallet to prepare for extra functionality.
Jul 16, 2026
c09e36a
Enhance credential management and UI components
Jul 16, 2026
76cd729
Bump package versions to correct passkey public key encoding mismatches.
Jul 16, 2026
f9b2c85
Fix race condition when doing initial wallet unlock with credential d…
Jul 16, 2026
9d42f1d
Update dependencies and enhance USDC transaction functionality
Jul 17, 2026
831b709
Fix copy button and width overflow styles
Jul 17, 2026
0e5dbf7
Refactor MainPanel and CreateBackupModal to use CopyableText component
Jul 17, 2026
77439b8
arc is new default network
TtheBC01 Jul 17, 2026
1705298
Merge pull request #2 from 1Shot-API/feature/defaultnetwork
TtheBC01 Jul 17, 2026
7dd7932
update login screen
TtheBC01 Jul 17, 2026
d3a702e
fix login button text
TtheBC01 Jul 17, 2026
219675d
Merge pull request #3 from 1Shot-API/feature/NewLogin
TtheBC01 Jul 17, 2026
c9610be
Add 1shotapi.com as a trusted issuer.
Jul 18, 2026
d3b1a38
Implement focus and unfocus wallet functionality
Jul 18, 2026
5ceed81
Enhance asset management features in wallet
Jul 20, 2026
68c0c65
Refactor asset management and introduce new repository implementations
Jul 20, 2026
c5adcd6
Enhance user interface for balances and receiving assets
Jul 21, 2026
90b87f2
Implement transfer functionality and enhance transaction handling
Jul 21, 2026
ad5d518
Enhance transaction feedback and form validation in wallet
Jul 21, 2026
ccca1c7
Add passkey ceremony overlays and related UI components
Jul 21, 2026
fce1b49
Remove chain column from AssetList
Jul 21, 2026
01d2aad
Purchase button
Jul 21, 2026
ab715e8
Other file for purchase
Jul 21, 2026
36c5451
Add passkey prompt titles and UI fields
Jul 21, 2026
e7a9072
Merge pull request #4 from 1Shot-API/feature/UIRefinements
csibbach Jul 21, 2026
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
239 changes: 239 additions & 0 deletions .agents/skills/oneshot-embedded-wallet/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
---
name: oneshot-embedded-wallet
description: >-
Integrate the 1Shot embedded wallet (OWS Host Layer) with @1shotapi/ows-provider.
Use when embedding wallet.1shotapi.com, wiring OWSProxy, EIP-1193, credentials,
or custom RPC such as setStyle / focusWallet / addAsset for theming, host-driven focus mode, and tracked assets on the 1Shot Branding Layer.
license: MIT
metadata:
author: 1Shot-API
version: "0.1.0"
repository: https://github.com/1Shot-API/embedded-wallet
---

# 1Shot Embedded Wallet (Host integration)

Teach an agent how to embed the **1Shot Wallet** Branding Layer from a Host Layer app using `@1shotapi/ows-provider`.

```
Host (your dapp) @1shotapi/ows-provider → OWSProxy
└── Branding iframe https://wallet.1shotapi.com/
└── Signing https://wallet.1shotapi.com/signer/ (same origin)
```

## Install

```bash
npm install @1shotapi/ows-provider @1shotapi/ows-types
```

## Minimal setup

```typescript
import { OWSProxy } from "@1shotapi/ows-provider";

const WALLET_URL = "https://wallet.1shotapi.com/";

const container = document.getElementById("wallet-container")!;
const proxy = await OWSProxy.create(container, WALLET_URL);

// Optional: theme / copy before showing the flyout
await proxy.rpc("setStyle", {
copy: { productName: "Acme Wallet", tagline: "Powered by 1Shot" },
theme: { primary: "oklch(0.45 0.18 250)" },
});

proxy.showWallet();

// EIP-1193
const accounts = await proxy.ethereum.request({ method: "eth_requestAccounts" });
```

### Local / HTTPS notes

- Passkeys require a **secure-context ancestor chain**. The Host page must be HTTPS (or `localhost`) when the wallet iframe is HTTPS.
- Dev wallet URL: your ngrok or local Vite origin root (e.g. `https://….ngrok-free.app/`).
- Production wallet URL: **`https://wallet.1shotapi.com/`** (Signing Layer at `/signer/` on the same origin — do not embed `/signer/` from the host).

## Custom RPC — `setStyle`

1Shot-specific method registered on the Branding Layer. Call via:

```typescript
await proxy.rpc("setStyle", options);
```

`options` is a partial merge (safe to call repeatedly):

| Field | Type | Purpose |
|-------|------|---------|
| `theme.primary` | string (CSS color) | `--primary` |
| `theme.primaryForeground` | string | `--primary-foreground` |
| `theme.background` / `foreground` | string | page colors |
| `theme.muted` / `mutedForeground` | string | secondary text |
| `theme.border` / `accent` / `accentForeground` | string | chrome |
| `theme.radius` | string | `--radius` (e.g. `"0.625rem"`) |
| `theme.fontSans` | string | `--font-sans` |
| `copy.productName` | string | titles / chrome |
| `copy.tagline` | string | supporting line |
| `copy.connect.title` | string | connect modal title |
| `copy.connect.body` | string | connect modal body |
| `copy.connect.rejectLabel` | string | Reject button |
| `copy.connect.continueLabel` | string | Continue button |
| `copy.walletSetup.title` | string | setup modal title |
| `copy.walletSetup.body` | string | setup modal body |
| `copy.walletSetup.cancelLabel` | string | Cancel button |
| `copy.walletSetup.loginLabel` | string | Login with passkey |
| `copy.walletSetup.createLabel` | string | Create account |
| `copy.passkeyName.title` | string | passkey name modal title |
| `copy.passkeyName.body` | string | passkey name modal body |
| `copy.passkeyName.fieldLabel` | string | input label |
| `copy.passkeyName.placeholder` | string | input placeholder |
| `copy.passkeyName.emptyError` | string | empty-name validation error |
| `copy.passkeyName.cancelLabel` | string | Cancel button |
| `copy.passkeyName.continueLabel` | string | Continue button |
| `copy.personalSign.title` | string | personal_sign modal title |
| `copy.personalSign.accountLabel` | string | Account field label |
| `copy.personalSign.messageLabel` | string | Message field label |
| `copy.personalSign.rejectLabel` | string | Reject button |
| `copy.personalSign.signLabel` | string | Sign button |
| `copy.typedData.title` | string | EIP-712 modal title |
| `copy.typedData.accountLabel` | string | Account field label |
| `copy.typedData.primaryTypeLabel` | string | Primary type label |
| `copy.typedData.domainLabel` | string | Domain label |
| `copy.typedData.messageLabel` | string | Message label |
| `copy.typedData.rejectLabel` | string | Reject button |
| `copy.typedData.signLabel` | string | Sign button |
| `copy.credentialOffer.title` | string | offer modal title |
| `copy.credentialOffer.body` | string | supports `{issuerName}` `{issuerId}` |
| `copy.credentialOffer.offeredHeading` | string | offered list heading |
| `copy.credentialOffer.passkeyNote` | string | passkey hint |
| `copy.credentialOffer.rejectLabel` | string | Reject button |
| `copy.credentialOffer.acceptLabel` | string | Accept button |
| `copy.credentialPresentation.title` | string | presentation modal title |
| `copy.credentialPresentation.body` | string | supports `{verifierName}` `{verifierId}` |
| `copy.credentialPresentation.credentialDetail` | string | supports `{credentialType}` `{credentialIssuer}` |
| `copy.credentialPresentation.claimsHeading` | string | claims list heading |
| `copy.credentialPresentation.passkeyNote` | string | passkey hint |
| `copy.credentialPresentation.rejectLabel` | string | Reject button |
| `copy.credentialPresentation.shareLabel` | string | Share button |
| `copy.credentials.tabLabel` | string | Credentials tab label |
| `copy.credentials.emptyCountLabel` | string | zero-count summary |
| `copy.credentials.countLabel` | string | supports `{count}` |
| `copy.credentials.refreshLabel` | string | Refresh button |
| `copy.credentials.loadingBody` | string | loading state |
| `copy.credentials.emptyBody` | string | empty-state text |
| `copy.credentials.loadFailedError` | string | list load failure |
| `copy.credentials.refreshFailedError` | string | relayer refresh failure |
| `copy.credentials.notFoundError` | string | detail not in cache |
| `copy.credentials.openFailedError` | string | detail open failure |
| `copy.credentials.typeColumn` | string | Type column header |
| `copy.credentials.issuerColumn` | string | Issuer column header |
| `copy.credentials.issuedColumn` | string | Issued column header |
| `copy.credentials.viewLabel` | string | View button |
| `copy.credentials.detailFallbackTitle` | string | detail title fallback |
| `copy.credentials.detailDescription` | string | detail dialog description |
| `copy.credentials.issuerLabel` | string | Issuer field label |
| `copy.credentials.formatLabel` | string | Format field label |
| `copy.credentials.issuedLabel` | string | Issued field label |
| `copy.credentials.validUntilLabel` | string | Valid until label |
| `copy.credentials.idLabel` | string | Id field label |
| `copy.credentials.claimsHeading` | string | Claims section heading |
| `copy.credentials.claimsLoading` | string | claims loading text |
| `copy.credentials.claimsEmpty` | string | no claims text |
| `copy.credentials.closeLabel` | string | Close button |
| `copy.createBackup.title` | string | create backup modal title |
| `copy.createBackup.body` | string | supports `{minLength}` |
| `copy.createBackup.passphrasePrompt` | string | Signing Layer passphrase label (`{minLength}`) |
| `copy.createBackup.continueLabel` | string | Signing Layer continue |
| `copy.createBackup.cancelLabel` | string | Cancel button |
| `copy.createBackup.closeLabel` | string | Close button |
| `copy.createBackup.copyLabel` | string | Copy button |
| `copy.createBackup.copiedLabel` | string | after successful copy |
| `copy.createBackup.copyFailedLabel` | string | copy failure |
| `copy.createBackup.doneLabel` | string | Done button |
| `copy.createBackup.encryptedLabel` | string | result ciphertext label |
| `copy.createBackup.passwordTooShortError` | string | short passphrase error |
| `copy.createBackup.cancelledError` | string | passkey cancelled |
| `copy.createBackup.failedError` | string | generic failure |
| `copy.restoreBackup.title` | string | restore backup modal title |
| `copy.restoreBackup.body` | string | restore prompt body |
| `copy.restoreBackup.passphraseLabel` | string | Signing Layer passphrase label |
| `copy.restoreBackup.restoreLabel` | string | Signing Layer restore button |
| `copy.restoreBackup.cancelLabel` | string | Cancel button |
| `copy.restoreBackup.closeLabel` | string | Close button |
| `copy.restoreBackup.doneLabel` | string | Done button |
| `copy.restoreBackup.successBody` | string | success message |
| `copy.restoreBackup.decryptFailedError` | string | bad passphrase error |
| `copy.restoreBackup.cancelledError` | string | passkey cancelled |
| `copy.restoreBackup.failedError` | string | generic failure |
| `dark` | boolean | toggles `html.dark` |

Returns `{ ok: true, productName: string }` with the resolved product name after merge.

Unknown keys are rejected (Zod `.strict()`).

See also [README.md](../../README.md) in this repository.

## Custom RPC — `focusWallet` / `unfocusWallet`

Host-controlled shell modes. Callers (not end users) switch between **General** (multi-chain tabs) and **Focused** (single chain + asset detail view).

```typescript
// Lock to one chain + ERC-20 (or other) asset
await proxy.rpc("focusWallet", {
chainId: "0x4cef52", // Arc Testnet
assetAddress: "0x3600000000000000000000000000000000000000", // USDC
});
proxy.showWallet();

// Restore general mode (keeps the current chain)
await proxy.rpc("unfocusWallet");
```

| Method | Params | Effect |
|--------|--------|--------|
| `focusWallet` | `{ chainId: \`0x…\`, assetAddress: \`0x…\` }` | Switches active chain, sets focused asset, shows Asset Details shell |
| `unfocusWallet` | none | Clears focus; returns to network selector + tabs |

`focusWallet` returns `{ ok: true, mode: "focused", chainId, assetAddress }`.
`unfocusWallet` returns `{ ok: true, mode: "general" }`.

Unlike `addAsset`, **`focusWallet` does not ask the user for confirmation** — hosts may temporarily lock the shell to any asset.

## Custom RPC — `addAsset`

Propose a tracked **ERC-20** for the Balances tab. The wallet resolves the token (known catalog, or on-chain `getCode` + `name`/`symbol`/`decimals`) **before** showing the confirm modal. Non-ERC-20 addresses are rejected. **Always requires user confirmation** (Reject / Add). On approval the asset is persisted; on rejection the RPC throws a user-rejected error.

```typescript
await proxy.rpc("addAsset", {
chainId: "0x4cef52", // Arc Testnet
assetAddress: "0x3600000000000000000000000000000000000000", // USDC
});
proxy.showWallet();
```

| Method | Params | Effect |
|--------|--------|--------|
| `addAsset` | `{ chainId: \`0x…\`, assetAddress: \`0x…\` }` | Probes ERC-20, shows confirm modal; on accept, adds to tracked assets |

Returns `{ ok: true, chainId, assetAddress }` when the user accepts.

Users can also add assets from the Balances tab without a host RPC. The Balances list shows tracked assets for the currently selected network only (USDC is always tracked per supported chain).

## Other Host APIs

| API | Use |
|-----|-----|
| `proxy.ethereum.request(...)` | EIP-1193 (accounts, sign, chain, …) |
| `proxy.credentials.*` | OID4 offer / present (when enabled in wallet) |
| `proxy.showWallet()` / `hideWallet()` | Host-driven flyout without an EIP-1193 call |
| `proxy.rpc(method, params)` | Custom Branding RPC (`setStyle`, `focusWallet`, `unfocusWallet`, `addAsset`, …) |

## Hard rules

- Never embed the Signing Layer iframe from the Host — always Host → Branding → Signing.
- Prefer the published wallet URL in production; point at a local Branding origin only while developing this repo.
- Theme with `setStyle`; do not ask integrators to fork CSS for basic brand colors / product name.
- Use `focusWallet` / `unfocusWallet` for host-driven single-asset flows; do not expose mode switching in the wallet UI.
- Use `addAsset` when the host wants a lasting Balances entry; expect a confirm modal (contrast with `focusWallet`).
Loading