Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3e505f4
Bump the minor-and-patch group with 18 updates
dependabot[bot] Sep 1, 2026
edf82e6
delegation params
TtheBC01 Sep 4, 2026
04f64e2
Section-scoped configure apply
TtheBC01 Sep 4, 2026
249897e
Update dependencies
Sep 4, 2026
abb8dfb
Merge pull request #58 from 1Shot-API/dependabot/npm_and_yarn/minor-a…
csibbach Sep 4, 2026
7d6b63c
Add CCTP bridge enhancements for improved user experience
Aug 31, 2026
69f5ce1
Fix potential race condition for analytics
Aug 31, 2026
ccef2d1
Fix bridge submission error not showing error.
Aug 31, 2026
fabe6d4
Add QuoteCountdown component and integrate into PaymentFeePicker and …
Aug 31, 2026
0183572
Fix disable delegations/credentials in configurators
Aug 31, 2026
df5dca3
Add support for optional asset icons in addAsset functionality
Aug 31, 2026
69da4ab
Enhance host chain management with weight-based sorting and interface…
Sep 1, 2026
4530a6e
Enhance ModalHost to support awaiting confirmation callback
Sep 1, 2026
92d78ce
Refactor relayer vault assertion handling in DelegationService and Tr…
Sep 1, 2026
9cbaf62
Implement relayer fee confirmation flow in modals
Sep 1, 2026
5302725
Create dependabot.yml
csibbach Aug 31, 2026
d5ea872
Update dependabot settings from 1Shot
Sep 1, 2026
8e5328b
Bump the minor-and-patch group with 18 updates
dependabot[bot] Sep 1, 2026
4fbd9e8
Update dependencies
Sep 4, 2026
231549f
Enhance LiFi integration with new permission types and modals
Sep 4, 2026
36aaf21
Merge branch 'develop' into feature/LiFi7715Support
Sep 4, 2026
1a96171
Merge pull request #66 from 1Shot-API/bug/DelegationParams
csibbach Sep 7, 2026
b79f62d
Merge branch 'develop' into feature/LiFi7715Support
Sep 7, 2026
cdeca1c
Update dependencies and enhance Bitcoin support
Sep 7, 2026
a60ae1d
Refactor chain switching logic and improve focused asset handling
Sep 7, 2026
cea630f
Bug fixes
Sep 7, 2026
558202f
Bug fixes
Sep 7, 2026
75f823e
Merge pull request #67 from 1Shot-API/feature/LiFi7715Support
csibbach Sep 7, 2026
b23f636
Support Bitcoin chains for changing chain from wallet.
Sep 7, 2026
f4bc72f
Merge pull request #72 from 1Shot-API/feature/Bitcoin
csibbach Sep 8, 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
60 changes: 55 additions & 5 deletions .agents/skills/oneshot-embedded-wallet/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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 configure / focusWallet / addAsset / createAccount / onramp / bridge for
or custom RPC such as configure / switchChain / focusWallet / addAsset / createAccount / onramp / bridge for
theming, host-driven focus mode, tracked assets, and first-party Safari create.
license: MIT
metadata:
Expand Down Expand Up @@ -190,6 +190,40 @@ Unknown keys are rejected (Zod `.strict()`).

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

## Custom RPC — `switchChain`

Switch the Branding Layer session chain. Accepts EVM hex ids **and** Bitcoin
sentinels (`"Bitcoin"` mainnet, `"BitcoinTestnet"` testnet). Prefer this over
EIP-1193 `wallet_switchEthereumChain` when the host catalog includes Bitcoin —
EIP-1193 params are hex-only and reject non-hex ids with `Invalid params`.

```ts
await proxy.rpc("switchChain", { chainId: "Bitcoin" });
await proxy.rpc("switchChain", { chainId: "0x2105" });
```

| Method | Params | Behavior |
|--------|--------|----------|
| `switchChain` | `{ chainId: \`0x…\` \| \`"Bitcoin"\` \| \`"BitcoinTestnet"\` }` | Bitcoin: session-only. EVM: same as `wallet_switchEthereumChain` via RpcHelper |

Returns `{ ok: true, chainId }`. Bitcoin switches also emit EIP-1193
`chainChanged` with the Bitcoin sentinel so hosts stay in sync.

## Custom RPC — `getChainId`

Read the Branding Layer **session** chain id (EVM hex or Bitcoin sentinel).
Prefer this over EIP-1193 `eth_chainId` when the host catalog includes Bitcoin —
`eth_chainId` only reflects the last EVM RpcHelper chain.

```ts
const { chainId } = await proxy.rpc("getChainId");
// "0x2105" | "Bitcoin" | "BitcoinTestnet" | …
```

| Method | Params | Behavior |
|--------|--------|----------|
| `getChainId` | none | Returns `{ chainId }` from the wallet session store |

## 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).
Expand Down Expand Up @@ -305,16 +339,14 @@ Product analytics: `BridgeOpened`, `BridgeCompleted`, `BridgeFailed`, `BridgeCan
| `proxy.ethereum.on` / `removeListener` | Branding→Host EIP-1193 notifications (`chainChanged`, `accountsChanged` via `ows:eip1193`) |
| `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`, `createAccount`, `onramp`, `bridge`, …) |
| `proxy.rpc(method, params)` | Custom Branding RPC (`configure`, `switchChain`, `getChainId`, `focusWallet`, `unfocusWallet`, `addAsset`, `createAccount`, `onramp`, `bridge`, …) |
| `proxy.analytics.on(listener)` / `.on(name, listener)` / `.off(listener)` | Branding→Host product analytics (`ows:analytics`) |
| `proxy.showWallet()` / `hideWallet()` | Host-driven flyout without an EIP-1193 call |
| `proxy.rpc(method, params)` | Custom Branding RPC (`configure`, `focusWallet`, `unfocusWallet`, `addAsset`, `createAccount`, …) |

Subscribe so in-wallet chain/account changes update host UI without polling:

```typescript
proxy.ethereum.on("chainChanged", (chainId) => {
// hex chain id string
// EVM hex (`0x…`) or Bitcoin sentinel (`Bitcoin` / `BitcoinTestnet`)
});
proxy.ethereum.on("accountsChanged", (accounts) => {
// EVM address array
Expand Down Expand Up @@ -350,6 +382,24 @@ The same rich payload is POSTed fire-and-forget to `POST /wallet/product-events`
1Shot relayer. The local Host (`host/`) and marketing [wallet playground](https://www.1shotapi.com/playground)
include a live Analytics panel fed by `proxy.analytics.on` (filter by `name`).

EIP-7715 host RPCs: `wallet_requestExecutionPermissions`, `wallet_revokeExecutionPermission`, `wallet_getSupportedExecutionPermissions`, `wallet_getGrantedExecutionPermissions` (grant consent and on-chain revoke are wallet-driven).

### Supported permission types

| `permission.type` | Chains (v1) | Purpose |
|-------------------|-------------|---------|
| `erc20-token-periodic` | All relayer chains | Periodic ERC-20 `transfer` budget (`ScopeType.Erc20PeriodTransfer`) |
| `lifi-swap-approve` | Base (`0x2105`) | One-time `approve(inputToken → LiFi Diamond)` onboarding |
| `lifi-swap-periodic` | Base (`0x2105`) | Periodic LiFi swap via `LiFiSwapEnforcer` (`0x47472E8AA7012D1c23336aa28514AE94389318f5`) |

**`erc20-token-periodic` / `lifi-swap-periodic` amounts:** Hosts should pass `periodAmount` (hex atoms) to prefill the grant form. Playground demos default to **10 USDC** (`0x989680`).

**`lifi-swap-approve` data:** `tokenAddress`, `spender` (LiFi Diamond). Amount is not pinned — the delegate may approve `maxUint256`.

**`lifi-swap-periodic` data:** `lifiDiamond`, `tokenAddress` (input ERC-20), `outputAssetId` (`bytes32` hex), `outputRecipient` (`bytes32` hex), `destinationChainId` (number or decimal string), `quoteSigner`, `periodAmount` (hex atoms), `periodDuration` (seconds). Optional: `startDate`, `slippageBps` (default `50`, must be `< 10000`). Grant response echoes attenuated fields plus `delegationHash` on `permission.data`.

Hosts that need both approve and swap should send **two** items in one `wallet_requestExecutionPermissions` batch. The wallet walks consent as a wizard (**Next** on intermediate forms, **Grant** on the last), then signs every delegation in **one** passkey ceremony and encrypts/uploads all vault rows in a **second** passkey ceremony. Each permission still gets its own vault row and `context`. **Do not** encode approve + swap as one `Delegation[]` chain (that would be treated as parent/child). Quote signing at redemption stays with the host/`quoteSigner` backend — the wallet only grants and signs the delegation.

## Hard rules

- Never embed the Signing Layer iframe from the Host — always Host → Branding → Signing.
Expand Down
5 changes: 4 additions & 1 deletion .agents/skills/ows-branding-layer/references/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ new RpcHelper(providers, wallet, signer, {
getSupportedExecutionPermissions: async () => ({
"erc20-token-periodic": {
chainIds: [...rpcHelper.getConfiguredChainIds()],
ruleTypes: [],
ruleTypes: ["expiry"],
},
// 1Shot embedded-wallet also supports LiFi on Base:
// "lifi-swap-approve" / "lifi-swap-periodic" with LiFiSwapEnforcer.
// Request them as separate batch items — never one Delegation[] chain.
}),
getGrantedExecutionPermissions: async () => { /* map stored grants */ },
},
Expand Down
8 changes: 4 additions & 4 deletions extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
"postinstall": "wxt prepare"
},
"dependencies": {
"@1shotapi/ows-provider": "^0.4.2",
"@1shotapi/ows-types": "^0.5.1"
"@1shotapi/ows-provider": "^0.5.1",
"@1shotapi/ows-types": "^0.8.0"
},
"devDependencies": {
"@types/chrome": "^0.0.287",
"@types/chrome": "^0.2.7",
"typescript": "~5.8.0",
"wxt": "^0.21.3"
"wxt": "^0.21.4"
}
}
20 changes: 10 additions & 10 deletions host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,24 @@
"preview": "vite preview"
},
"dependencies": {
"@1shotapi/ows-provider": "^0.4.2",
"@1shotapi/ows-types": "^0.5.1",
"@1shotapi/ows-provider": "^0.5.1",
"@1shotapi/ows-types": "^0.8.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.24.0",
"radix-ui": "^1.6.2",
"react": "^19.2.7",
"radix-ui": "^1.6.7",
"react": "^19.2.8",
"react-colorful": "^5.8.0",
"react-dom": "^19.2.7",
"shadcn": "^4.13.0",
"react-dom": "^19.2.8",
"shadcn": "^4.19.0",
"tailwind-merge": "^3.6.0",
"viem": "^2.55.2"
"viem": "^2.56.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.3.2",
"@tailwindcss/vite": "^4.3.3",
"@types/node": "^22.20.1",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.5",
"@vitejs/plugin-react": "^4.7.0",
"dotenv": "^16.4.7",
"tailwindcss": "^4.3.2",
Expand Down
16 changes: 8 additions & 8 deletions host/src/components/DesignPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export function DesignPanel({
previewMountRef,
}: IDesignPanelProps) {
return (
<div className="grid h-[calc(100svh-3.5rem)] grid-cols-1 lg:grid-cols-[minmax(22rem,26rem)_1fr]">
<aside className="border-border bg-background overflow-y-auto border-r">
<div className="grid h-[calc(100svh-3.5rem)] min-h-0 grid-cols-1 lg:grid-cols-[minmax(22rem,26rem)_1fr]">
<aside className="border-border bg-background min-h-0 overflow-y-auto border-r">
<div className="border-border border-b px-5 py-4">
<h2 className="font-heading text-lg font-bold tracking-tight">
Configure
Expand All @@ -34,10 +34,10 @@ export function DesignPanel({
</aside>

<section
className="relative flex min-h-[28rem] flex-col bg-[#f8fafc] p-6"
className="relative flex min-h-0 flex-1 flex-col bg-[#f8fafc] p-6"
aria-label="Wallet preview"
>
<div className="mb-4 flex items-start justify-between gap-3">
<div className="mb-4 flex shrink-0 items-start justify-between gap-3">
<div>
<h2 className="font-heading text-lg font-bold tracking-tight">
Preview
Expand All @@ -52,8 +52,8 @@ export function DesignPanel({
</span>
</div>

<div className="relative flex flex-1 items-center justify-center overflow-auto rounded-2xl border-2 border-dashed border-slate-300/80 bg-white shadow-sm">
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 flex h-9 items-center gap-1.5 border-b border-slate-100 bg-slate-50/90 px-3">
<div className="relative flex min-h-0 flex-1 flex-col overflow-hidden rounded-2xl border-2 border-dashed border-slate-300/80 bg-white shadow-sm">
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 flex h-9 shrink-0 items-center gap-1.5 border-b border-slate-100 bg-slate-50/90 px-3">
<span className="size-2.5 rounded-full bg-red-300" />
<span className="size-2.5 rounded-full bg-amber-300" />
<span className="size-2.5 rounded-full bg-emerald-300" />
Expand All @@ -62,9 +62,9 @@ export function DesignPanel({
</span>
</div>

<div className="flex min-h-[36rem] w-full items-center justify-center px-6 pt-12 pb-8">
<div className="grid min-h-0 flex-1 place-items-center overflow-auto px-6 pt-12 pb-6">
<div
className="relative h-[600px] w-[360px] shrink-0 overflow-hidden rounded-xl border border-slate-200 bg-white shadow-md"
className="relative aspect-[360/600] max-h-full w-auto max-w-[min(360px,100%)] shrink-0 overflow-hidden rounded-xl border border-slate-200 bg-white shadow-md [height:min(600px,100%)]"
aria-label="Wallet inline mount"
>
<div ref={previewMountRef} className="absolute inset-0" />
Expand Down
13 changes: 12 additions & 1 deletion host/src/components/WalletActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export interface IWalletActionsProps {
}>;
delegationsOutput: string | null;
onRequestDelegation: () => void;
onRequestLiFiDelegation: () => void;
onCancelDelegation: (id: string) => void;
onGetSupportedPermissions: () => void;
onGetGrantedPermissions: () => void;
Expand Down Expand Up @@ -144,6 +145,7 @@ export function WalletActions({
sessionGrants,
delegationsOutput,
onRequestDelegation,
onRequestLiFiDelegation,
onCancelDelegation,
onGetSupportedPermissions,
onGetGrantedPermissions,
Expand Down Expand Up @@ -653,7 +655,8 @@ export function WalletActions({
<Label>Delegations (EIP-7715)</Label>
<p className="text-muted-foreground text-xs">
Request a periodic USDC spending permission for the current chain
(session key held in memory only). Relayer-enabled chains only.
(session key held in memory only). Relayer-enabled chains only. LiFi
approve + swap batch requires Base.
</p>
<div className="flex flex-wrap gap-2">
<Button
Expand All @@ -664,6 +667,14 @@ export function WalletActions({
>
Request permission
</Button>
<Button
type="button"
variant="outline"
disabled={!ready || busy || chainId.toLowerCase() !== "0x2105"}
onClick={onRequestLiFiDelegation}
>
Request LiFi (Base)
</Button>
<Button
type="button"
variant="outline"
Expand Down
45 changes: 36 additions & 9 deletions host/src/components/WalletConfigurator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ import {
CATALOG_CHAIN_OPTIONS,
DEFAULTS_PRESET,
OCEAN_PRESET,
buildConfigurePayload,
buildConfigurePayloadForSection,
type ConfigurePayloadSection,
type IStyleFormState,
} from "../styleForm";

export interface IWalletConfiguratorProps {
/** When false, apply/presets are disabled (wallet not connected yet). */
ready: boolean;
onApply: (options: Record<string, unknown>) => Promise<void>;
onApply: (
options: Record<string, unknown>,
applyOptions?: { replace?: boolean },
) => Promise<void>;
}

/**
Expand All @@ -30,6 +34,7 @@ export function WalletConfigurator({
onApply,
}: IWalletConfiguratorProps) {
const [form, setForm] = useState<IStyleFormState>(ACME_PRESET);
const [activeTab, setActiveTab] = useState("basic");
const [status, setStatus] = useState<string>("");
const [isError, setIsError] = useState(false);
const [busy, setBusy] = useState(false);
Expand All @@ -41,15 +46,37 @@ export function WalletConfigurator({
setForm((current) => ({ ...current, [key]: value }));
};

const apply = async (next?: IStyleFormState) => {
const apply = async (
next?: IStyleFormState,
section: ConfigurePayloadSection = activeTab === "style"
? "theme"
: activeTab === "text"
? "copy"
: "basic",
applyOptions?: { replace?: boolean },
) => {
const payloadForm = next ?? form;
if (next) setForm(next);
setBusy(true);
setIsError(false);
setStatus("Calling configure…");
try {
await onApply(buildConfigurePayload(payloadForm));
setStatus("Configuration applied.");
const payload =
applyOptions?.replace || section === "all"
? buildConfigurePayloadForSection(payloadForm, "all")
: buildConfigurePayloadForSection(payloadForm, section);
await onApply(payload, applyOptions);
const sectionLabel =
section === "all"
? "full configuration"
: section === "basic"
? "Basic tab"
: section === "theme"
? "Style tab"
: section === "copy"
? "Text tab"
: "features";
setStatus(`Applied ${sectionLabel}.`);
} catch (error) {
setIsError(true);
setStatus(error instanceof Error ? error.message : "Configuration failed");
Expand All @@ -60,7 +87,7 @@ export function WalletConfigurator({

return (
<section className="flex flex-col gap-4" aria-label="Style configuration">
<Tabs defaultValue="basic">
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList variant="line" className="w-full justify-start">
<TabsTrigger value="basic">Basic</TabsTrigger>
<TabsTrigger value="style">Style</TabsTrigger>
Expand Down Expand Up @@ -300,14 +327,14 @@ export function WalletConfigurator({
void apply();
}}
>
Apply configuration
Apply current tab
</Button>
<Button
type="button"
variant="outline"
disabled={!ready || busy}
onClick={() => {
void apply(OCEAN_PRESET);
void apply(OCEAN_PRESET, "all", { replace: true });
}}
>
Preset: ocean
Expand All @@ -317,7 +344,7 @@ export function WalletConfigurator({
variant="outline"
disabled={!ready || busy}
onClick={() => {
void apply(DEFAULTS_PRESET);
void apply(DEFAULTS_PRESET, "all", { replace: true });
}}
>
Preset: defaults
Expand Down
30 changes: 30 additions & 0 deletions host/src/components/hostChains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ const ARC_TESTNET: IHostChainMeta = {
};

const HOST_CHAIN_SEED: readonly IHostChainMeta[] = [
{
value: "Bitcoin",
label: "Bitcoin",
usdc: "",
tokenSymbol: "BTC",
blockExplorerUrl: "https://mempool.space",
isTestnet: false,
weight: 85,
},
{
value: "BitcoinTestnet",
label: "Bitcoin Testnet",
usdc: "",
tokenSymbol: "tBTC",
blockExplorerUrl: "https://mempool.space/testnet",
isTestnet: true,
weight: 75,
},
...(EnableArcMainnet ? [ARC_MAINNET] : []),
ARC_TESTNET,
{
Expand Down Expand Up @@ -121,6 +139,18 @@ export const DEMO_ADD_ASSET_ICON_URL =
export const DEMO_EXECUTION_DELEGATEE =
"0x1111111111111111111111111111111111111111" as const;

/** Base mainnet LiFi Diamond (common production address). */
export const DEMO_LIFI_DIAMOND_BASE =
"0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE" as const;

/** Base WETH — used as same-chain LiFi swap output in the playground. */
export const DEMO_WETH_BASE =
"0x4200000000000000000000000000000000000006" as const;

/** Demo quote signer for LiFi playground grants (not a production key). */
export const DEMO_LIFI_QUOTE_SIGNER =
"0x2222222222222222222222222222222222222222" as const;

export type UsdcMode = "balance" | "send";

export function hostChainMeta(chainId: string) {
Expand Down
Loading
Loading