> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rhinestone.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a session

> Grant scoped authority to an app-held session key, persist it, and execute without another wallet prompt.

export const CSS = ".rhinestone-1auth-demo .grant-demo {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n}\n.rhinestone-1auth-demo .grant-demo-header, .rhinestone-1auth-demo .grant-demo-row {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 1rem;\n}\n.rhinestone-1auth-demo .grant-demo-eyebrow {\n  margin: 0 0 0.25rem;\n  font-size: 0.75rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.06em;\n  color: light-dark(#71717a, #a1a1aa);\n}\n.rhinestone-1auth-demo .grant-demo-title {\n  margin: 0;\n  font-size: 1.05rem;\n  font-weight: 600;\n  color: light-dark(#18181b, #fafafa);\n}\n.rhinestone-1auth-demo .grant-demo-pill {\n  flex-shrink: 0;\n  border-radius: 999px;\n  padding: 0.25rem 0.625rem;\n  font-size: 0.75rem;\n  font-weight: 600;\n}\n.rhinestone-1auth-demo .grant-demo-pill-neutral {\n  background: light-dark(#e4e4e7, #27272a);\n  color: light-dark(#3f3f46, #d4d4d8);\n}\n.rhinestone-1auth-demo .grant-demo-pill-success {\n  background: light-dark(#dcfce7, #14532d);\n  color: light-dark(#166534, #bbf7d0);\n}\n.rhinestone-1auth-demo .grant-demo-pill-error {\n  background: light-dark(#fee2e2, #450a0a);\n  color: light-dark(#b91c1c, #fecaca);\n}\n.rhinestone-1auth-demo .grant-demo-scope {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));\n  gap: 0.5rem;\n}\n.rhinestone-1auth-demo .grant-demo-scope > div {\n  min-width: 0;\n  border: 1px solid light-dark(#e4e4e7, #3f3f46);\n  border-radius: 0.5rem;\n  padding: 0.625rem;\n  background: light-dark(#fafafa, #18181b);\n}\n.rhinestone-1auth-demo .grant-demo-scope span, .rhinestone-1auth-demo .grant-demo-label, .rhinestone-1auth-demo .grant-demo-result-grid dt {\n  display: block;\n  margin: 0 0 0.25rem;\n  font-size: 0.72rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  color: light-dark(#71717a, #a1a1aa);\n}\n.rhinestone-1auth-demo .grant-demo-scope strong, .rhinestone-1auth-demo .grant-demo-result-grid dd {\n  display: block;\n  margin: 0;\n  overflow-wrap: anywhere;\n  font-size: 0.875rem;\n  color: light-dark(#18181b, #fafafa);\n}\n.rhinestone-1auth-demo .grant-demo-mono {\n  margin: 0;\n  font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n  font-size: 0.875rem;\n  color: light-dark(#18181b, #fafafa);\n}\n.rhinestone-1auth-demo .grant-demo-secondary {\n  white-space: nowrap;\n}\n.rhinestone-1auth-demo .grant-demo-primary {\n  width: fit-content;\n}\n.rhinestone-1auth-demo .grant-demo-footnote {\n  margin: 0;\n  max-width: 62ch;\n  font-size: 0.8rem;\n  line-height: 1.45;\n  color: light-dark(#71717a, #a1a1aa);\n}\n.rhinestone-1auth-demo .grant-demo-result {\n  margin-top: 0;\n}\n.rhinestone-1auth-demo .grant-demo-result-grid {\n  display: grid;\n  grid-template-columns: repeat(2, minmax(0, 1fr));\n  gap: 0.75rem;\n  margin: 0;\n}\n.rhinestone-1auth-demo .grant-demo-mint-panel {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 1rem;\n  border: 1px solid light-dark(#bbf7d0, #166534);\n  border-radius: 0.5rem;\n  padding: 1rem;\n  background: light-dark(#f0fdf4, #052e16);\n}\n.rhinestone-1auth-demo .grant-demo-mint-title {\n  margin: 0 0 0.25rem;\n  font-size: 0.95rem;\n  font-weight: 600;\n  color: light-dark(#14532d, #dcfce7);\n}\n.rhinestone-1auth-demo .grant-demo-mint-copy {\n  margin: 0;\n  max-width: 48ch;\n  font-size: 0.8rem;\n  line-height: 1.45;\n  color: light-dark(#166534, #bbf7d0);\n}\n.rhinestone-1auth-demo .grant-demo-tx-link {\n  color: light-dark(#2563eb, #93c5fd);\n  font-weight: 600;\n  text-decoration: none;\n}\n.rhinestone-1auth-demo .grant-demo-tx-link:hover {\n  text-decoration: underline;\n}\n@media (max-width: 640px) {\n.rhinestone-1auth-demo .grant-demo-header, .rhinestone-1auth-demo .grant-demo-row, .rhinestone-1auth-demo .grant-demo-mint-panel {\n    align-items: stretch;\n    flex-direction: column;\n  }\n.rhinestone-1auth-demo .grant-demo-scope, .rhinestone-1auth-demo .grant-demo-result-grid {\n    grid-template-columns: 1fr;\n  }\n.rhinestone-1auth-demo .grant-demo-primary, .rhinestone-1auth-demo .grant-demo-secondary {\n    width: 100%;\n  }\n}\n.rhinestone-1auth-demo .oa-demo {\n  margin: 1.5rem 0;\n  border: 1px solid #202127;\n  border-radius: 16px;\n  background: #0b0c0e;\n  overflow: hidden;\n}\n.rhinestone-1auth-demo .oa-demo-head {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 0.75rem;\n  min-height: 39px;\n  padding: 0.625rem 1rem;\n  border-bottom: 1px solid #191a1e;\n}\n.rhinestone-1auth-demo .oa-demo-title {\n  font-size: 0.8125rem;\n  font-weight: 500;\n  letter-spacing: -0.01em;\n  color: #e4e4e9;\n}\n.rhinestone-1auth-demo .oa-demo-status {\n  display: flex;\n  align-items: center;\n  gap: 0.4375rem;\n  flex: 0 0 auto;\n  font-size: 0.75rem;\n  color: #5e5e68;\n}\n.rhinestone-1auth-demo .oa-demo-dot {\n  width: 6px;\n  height: 6px;\n  border-radius: 999px;\n  background: #3f3f48;\n}\n.rhinestone-1auth-demo .oa-demo-dot[data-state=\"on\"] {\n  background: #22c55e;\n  box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);\n}\n.rhinestone-1auth-demo .oa-demo-body {\n  display: flex;\n  flex-direction: column;\n  gap: 0.75rem;\n  padding: 1rem;\n}\n.rhinestone-1auth-demo .demo-container {\n  display: contents;\n}\n.rhinestone-1auth-demo .oa-demo-body > * > [style] {\n  margin: 0 !important;\n}\n.rhinestone-1auth-demo .oa-demo-body > * > div[style]:not([class]) {\n  padding: 0 !important;\n}\n.rhinestone-1auth-demo .oa-demo-body [style] {\n  text-align: left !important;\n}\n.rhinestone-1auth-demo .oa-demo-body [style*=\"justify-content: center\"] {\n  justify-content: flex-start !important;\n}\n.rhinestone-1auth-demo .demo-button {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  gap: 0.375rem;\n  height: 2.25rem;\n  padding: 0 0.875rem;\n  border: none;\n  border-radius: 10px;\n  background: #4f41ef;\n  color: #ffffff;\n  font-size: 0.875rem;\n  font-weight: 500;\n  cursor: pointer;\n  transition:\n    background 0.15s ease,\n    opacity 0.15s ease;\n}\n.rhinestone-1auth-demo .demo-button:hover:not(:disabled) {\n  background: #6355f5;\n}\n.rhinestone-1auth-demo .demo-button:disabled {\n  opacity: 0.6;\n  cursor: default;\n}\n.rhinestone-1auth-demo .demo-button-secondary {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  height: 2.25rem;\n  padding: 0 0.875rem;\n  border: 1px solid #2e2e36;\n  border-radius: 10px;\n  background: transparent;\n  color: #c8c8d0;\n  font-size: 0.875rem;\n  font-weight: 500;\n  cursor: pointer;\n  transition:\n    border-color 0.15s ease,\n    color 0.15s ease;\n}\n.rhinestone-1auth-demo .demo-button-secondary:hover:not(:disabled) {\n  border-color: #45454f;\n  color: #f2f2f5;\n}\n.rhinestone-1auth-demo .oa-demo-body > * > .demo-button, .rhinestone-1auth-demo .oa-demo-body > * > .demo-button-secondary {\n  align-self: flex-start;\n}\n.rhinestone-1auth-demo .oa-demo-body input[type=\"checkbox\"] {\n  accent-color: #4f41ef;\n}\n.rhinestone-1auth-demo .demo-result {\n  padding: 0.75rem 0.875rem;\n  border-radius: 10px;\n  background: #14151a;\n}\n.rhinestone-1auth-demo .demo-result-label {\n  font-size: 0.6875rem;\n  font-weight: 500;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  color: #6e6e78;\n}\n.rhinestone-1auth-demo .demo-result-value {\n  margin-top: 0.1875rem;\n  font-family: paperMono, ui-monospace, SFMono-Regular, Menlo, monospace;\n  font-size: 0.8125rem;\n  line-height: 1.5;\n  color: #e4e4e9;\n  overflow-wrap: anywhere;\n}\n.rhinestone-1auth-demo .demo-result-success .demo-result-label {\n  color: #4ade80;\n}\n.rhinestone-1auth-demo .demo-result-error .demo-result-label {\n  color: #f87171;\n}\n.rhinestone-1auth-demo .demo-result-error .demo-result-value {\n  color: #fca5a5;\n}\n.rhinestone-1auth-demo .demo-error {\n  margin: 0;\n  font-size: 0.8125rem;\n  color: #f87171;\n  overflow-wrap: anywhere;\n}\n.rhinestone-1auth-demo .oa-demo-skeleton {\n  display: block;\n  width: 7rem;\n  height: 2.25rem;\n  border-radius: 10px;\n  background: #16171c;\n  animation: rhinestone-1auth-demo-pulse 1.6s ease-in-out infinite;\n}\n@keyframes rhinestone-1auth-demo-pulse {\n  50% {\n    opacity: 0.45;\n  }\n}";

export const SOURCE_COMMIT = "e06ebe8add704e95f679eaddc38fd7ea88cb3de8";

export const SDK_VERSION = "0.10.1";

export const VIEM_VERSION = "2.55.0";

export const STORAGE_KEY = "rhinestone-docs-1auth-account";

export const PROD_BACKENDS = {
  auth: "https://passkey.1auth.app",
  sponsorship: "https://docs.1auth.app/api/sponsorship"
};

export const DEV_BACKENDS = {
  auth: "https://passkey.passkey-test.app",
  sponsorship: "https://docs.passkey-test.app/api/sponsorship"
};

export const PROD_DOCS_HOST = "docs.rhinestone.dev";

export const backends = () => {
  const host = typeof window === "undefined" ? "" : window.location.hostname;
  return host === PROD_DOCS_HOST ? PROD_BACKENDS : DEV_BACKENDS;
};

export const BACKEND_NOTE = "Only the production docs host talks to the production Rhinestone deployment. " + "Every other host — a Mintlify preview, a tunnel, localhost — uses dev, so " + "an unrecognised preview pattern fails towards dev rather than creating " + "passkey state against production.";

export const CDN = spec => "https://esm.sh/" + spec + (spec.startsWith("@rhinestone/1auth@") ? "?deps=viem@" + VIEM_VERSION + "&external=react" : "");

export const shareReact = () => {
  if (window.__OA_SHARED_REACT__) return;
  if (typeof HTMLScriptElement.supports !== "function" || !HTMLScriptElement.supports("importmap")) {
    throw new Error("This demo requires a browser with import-map support.");
  }
  window.__OA_REACT__ = React;
  const reserved = ["default", "import", "export", "class", "function", "const", "let", "var", "new", "delete", "typeof", "void", "in", "of", "do", "if", "else", "for", "while", "switch", "case", "break", "continue", "return", "this", "super", "throw", "try", "catch", "finally", "yield", "await", "enum", "null", "true", "false", "with", "debugger", "instanceof", "extends", "static"];
  const names = Object.keys(React).filter(name => (/^[A-Za-z_$][A-Za-z0-9_$]*$/).test(name) && !reserved.includes(name));
  const source = ["const R = globalThis.__OA_REACT__; export default R;", ...names.map(name => "export const " + name + " = R." + name + ";")].join(" ");
  const jsx = ["const R = globalThis.__OA_REACT__; export const Fragment = R.Fragment;", "export const jsx = (type, props, key) => R.createElement(type, key == null ? props : { ...props, key });", "export const jsxs = jsx; export const jsxDEV = jsx;"].join(" ");
  const dataUrl = value => "data:text/javascript," + encodeURIComponent(value);
  const script = document.createElement("script");
  script.type = "importmap";
  script.textContent = JSON.stringify({
    imports: {
      react: dataUrl(source),
      "react/jsx-runtime": dataUrl(jsx),
      "react/jsx-dev-runtime": dataUrl(jsx)
    }
  });
  document.head.appendChild(script);
  window.__OA_SHARED_REACT__ = true;
};

export const MODULE_SPECS = {
  sdk: () => CDN("@rhinestone/1auth@" + SDK_VERSION),
  sdkHeadless: () => CDN("@rhinestone/1auth@" + SDK_VERSION + "/headless"),
  viem: () => CDN("viem@" + VIEM_VERSION),
  viemChains: () => CDN("viem@" + VIEM_VERSION + "/chains"),
  viemAccounts: () => CDN("viem@" + VIEM_VERSION + "/accounts")
};

export const importModule = url => new Promise((resolve, reject) => {
  const key = "__oa_" + Math.random().toString(36).slice(2);
  window[key] = {
    resolve,
    reject
  };
  const script = document.createElement("script");
  script.type = "module";
  script.textContent = "import(" + JSON.stringify(url) + ").then(m => window[" + JSON.stringify(key) + "].resolve(m))" + ".catch(e => window[" + JSON.stringify(key) + "].reject(new Error(String(e))));";
  script.onerror = () => reject(new Error("failed to load " + url));
  document.head.appendChild(script);
  script.remove();
});

export const importModuleWithRetry = async url => {
  try {
    return await importModule(url);
  } catch (first) {
    await new Promise(r => setTimeout(r, 500));
    return importModule(url);
  }
};

export const loadDeps = async () => {
  shareReact();
  const specs = Object.entries(MODULE_SPECS).map(([name, spec]) => [name, spec()]);
  const cacheKey = "__OA_DEPS__" + specs.map(([, url]) => url).join("|");
  if (window[cacheKey]) return window[cacheKey];
  const loaded = await Promise.all(specs.map(([, url]) => importModuleWithRetry(url)));
  const deps = {
    React
  };
  specs.forEach(([name], i) => {
    deps[name] = loaded[i];
  });
  deps.motion = motionShim();
  window[cacheKey] = deps;
  return deps;
};

export const unreachable = call => "Sponsorship " + call + " never reached " + backends().sponsorship + ". The browser hides why: a rejected origin, a failed preflight and a network" + " error all look identical here. The failing request is in the console.";

export const sponsorship = {
  accessToken: async () => {
    let res;
    try {
      res = await fetch(backends().sponsorship + "/access-token", {
        method: "GET",
        credentials: "include"
      });
    } catch (e) {
      throw new Error(unreachable("access token"));
    }
    if (!res.ok) throw new Error("Sponsorship access token failed (" + res.status + ")");
    return (await res.json()).token;
  },
  getExtensionToken: async intentOp => {
    let res;
    try {
      res = await fetch(backends().sponsorship + "/extension-token", {
        method: "POST",
        credentials: "include",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          intentOp
        })
      });
    } catch (e) {
      throw new Error(unreachable("extension token"));
    }
    if (!res.ok) throw new Error("Sponsorship extension token failed (" + res.status + ")");
    return (await res.json()).token;
  }
};

export const motionShim = () => {
  const passthrough = Tag => React.forwardRef((props, ref) => {
    const rest = Object.fromEntries(Object.entries(props).filter(([k]) => !["initial", "animate", "exit", "transition", "variants", "whileHover", "whileTap", "layout", "layoutId"].includes(k)));
    return React.createElement(Tag, {
      ...rest,
      ref
    });
  });
  const cache = {};
  return {
    motion: new Proxy({}, {
      get: (_, tag) => {
        if (!cache[tag]) cache[tag] = passthrough(tag);
        return cache[tag];
      }
    }),
    AnimatePresence: ({children}) => React.createElement(React.Fragment, null, children),
    useReducedMotion: () => true
  };
};

export const AuthContext = React.createContext(null);

export const useAuthContext = () => React.useContext(AuthContext);

export const buildModules = deps => {
  const mods = {};
  mods.authctx = (() => {
    const DOCS_APP_ORIGIN_STORAGE_KEY = STORAGE_KEY;
    const useAuth = () => useAuthContext();
    return {
      DOCS_APP_ORIGIN_STORAGE_KEY,
      useAuth,
      clearDocsSession: () => {}
    };
  })();
  mods.constants = (() => {
    const DOCS_APP_ORIGIN_CLIENT_ID = "docs-app-origin";
    function docsAppOriginWebAuthn() {
      if (typeof window === "undefined" || window.location.protocol !== "https:") {
        return {
          mode: "app_origin"
        };
      }
      return {
        mode: "app_origin",
        rpId: window.location.hostname
      };
    }
    const DOCS_EXPERIMENTAL_CLEAR_SIGNING = true;
    const APP_ORIGIN_DEMO_CAPABILITIES = {
      payButton: true,
      eip1193Transactions: true,
      sendCalls: true,
      batchCalls: true,
      messageSigning: true,
      typedDataSigning: true,
      batchIntents: true,
      smartSessions: true,
      walletConnection: true,
      recovery: true
    };
    const DOCS_DIALOG_THEME = {
      mode: "dark",
      accent: "#ffffff",
      backdrop: {
        color: "#52525c",
        opacity: 0.5,
        blur: 12
      }
    };
    const MOCK_USD_ADDRESS = "0x2f6fdE5E2AeAB6335d8f978B4d8B2a9c1129AcFb";
    const RECIPIENT_ADDRESS = "0x180b791BD181DF1773E4e33cb0374b258936d186";
    const MOCK_RWA_ADDRESS = "0x5948C7E284174f6Df0f2505fb3CBFbd685154323";
    const MOCK_SWAP_ADDRESS = "0xf2DFDC35d46e62F6D026afa36Cc6e941174B67f1";
    const MOCK_NFT_V2_ADDRESS = "0x99CCb273351Cf13713F7Bf5A0716d7C2a3DD4712";
    const MOCK_NFT_LUIGI_ID = 1n;
    const NVDA_ICON_SVG = `<svg width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="250" height="250" fill="#76B900"/><path d="M99.5748 98.4439V86.5224C100.752 86.4488 101.93 86.3752 103.107 86.3752C135.781 85.345 157.195 114.486 157.195 114.486C157.195 114.486 134.088 146.571 109.289 146.571C105.977 146.571 102.739 146.056 99.6484 145.026V108.82C112.379 110.365 114.955 115.958 122.535 128.689L139.534 114.413C139.534 114.413 127.097 98.1495 106.198 98.1495C103.99 98.0759 101.782 98.2231 99.5748 98.4439ZM99.5748 59V76.8086L103.107 76.5878C148.512 75.0425 178.168 113.824 178.168 113.824C178.168 113.824 144.17 155.181 108.773 155.181C105.683 155.181 102.666 154.887 99.6484 154.372V165.41C102.15 165.705 104.726 165.925 107.228 165.925C140.196 165.925 164.039 149.073 187.146 129.204C190.973 132.295 206.647 139.727 209.885 142.965C187.956 161.363 136.811 176.154 107.817 176.154C105.02 176.154 102.371 176.007 99.722 175.713V191.24H224.971V59L99.5748 59ZM99.5748 145.026V154.445C69.1088 149 60.646 117.283 60.646 117.283C60.646 117.283 75.2903 101.093 99.5748 98.4439V108.746H99.5012C86.7702 107.201 76.7621 119.122 76.7621 119.122C76.7621 119.122 82.4285 139.212 99.5748 145.026ZM45.4866 115.958C45.4866 115.958 63.516 89.3188 99.6484 86.5224V76.8086C59.6158 80.0465 25.0288 113.898 25.0288 113.898C25.0288 113.898 44.6036 170.561 99.5748 175.713V165.41C59.2478 160.406 45.4866 115.958 45.4866 115.958Z" fill="white"/></svg>`;
    const NVDA_ICON_DATA_URL = `data:image/svg+xml;utf8,${encodeURIComponent(NVDA_ICON_SVG)}`;
    const LUIGI_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="none" viewBox="0 0 28 28"><rect width="28" height="28" fill="#32a742" rx="4"/><path fill="#101110" d="M13 5.4h.9a7 7 0 0 1 4 1.1q1.3 1 2 2.4l.3.5.5-.6L22 7.5q1-.9 2.2-1.3h.5l.4.3v.8a9 9 0 0 1-2.5 4.4l-.6.6q.6.7.2 1.7t-1.4 1.6l.2.8q.4 2-.5 3.6l-.4.5a8 8 0 0 1-5.7 2.1h-1l-1.8-.3-.5-.2-.9-.3q-1-.4-1.8-1.2-.6-.6-1-1.4a5 5 0 0 1 0-3.6q-.9-.3-1.3-.9-.5-.9-.1-2v-.1L3.8 10q-.9-1.3-1-2.8 0-.5.3-.6C4 6.6 6 8 6.8 8.8l.9.9q.4-1 1.1-2c1.2-1.5 2.3-2 4.2-2.3"/><path fill="#f7c30f" d="M8.5 14q1.1.7 2.4.5c1-.2 2.3-1 3.3-.9l1.6.4.3.1.6.2q1.6.4 2.8-.5l.2.2c.8 1.3 1.5 3.3 1 4.8l-.5 1c0 .2-.7.8-.9.9l-.3.2-.3.2q-.6.5-1.3.6a9 9 0 0 1-7.6-.4l-.5-.4-.6-.4-.6-.7-.4-.7c-.5-.8-.4-2.4-.1-3.2q.1-.8.6-1.4l.1-.2z"/><path fill="#fb9410" d="M17.7 20.8q0 .3.4.3h.6q-.6.5-1.3.6a9 9 0 0 1-7.6-.4q.5 0 .9-.3 3.4.9 6.7 0z"/><path fill="#e0051c" d="M17.6 20.2c.2-1.5 2.6-3 2.8-1.5h.3l-.5 1.1c0 .2-.7.8-.9.9l-.3.2-.3.2H18q-.3 0-.4-.3z"/><path fill="#870c0d" d="M17.6 20.2c.2-1.5 2.6-3 2.8-1.5h.3l-.5 1.1c0 .2-.7.8-.9.9l.4-.5q.6-.7.6-1.5l-.3-.1h-.8q-.6.4-1 1l-.4.4h-.1z"/><path fill="#fb99a0" d="m17.8 20 .3-.7c.4-.6 1.3-1.4 2-1l.2.4-.3-.1h-.8q-.6.4-1 1z"/><path fill="#fac550" d="M20.4 18.7h.3l-.5 1.1v-.2q.3-.5.2-.9"/><path fill="#870c0d" d="m17.6 20.2.1-.1q-.1.6.4.9h.6l.3-.1-.3.2H18q-.3 0-.4-.3z"/><path fill="#e0051c" d="M7.9 19q0-.7.7-.7c1 0 2.5 1.7 2 2.7q-.2.4-.8.3l-.5-.4-.6-.4-.6-.7-.4-.7z"/><path fill="#870c0d" d="M7.9 19q0-.7.7-.7c1 0 2.5 1.7 2 2.7q-.2.4-.8.3l-.5-.4c.4 0 .4.3 1 .2l.3-.3q0-.5-.2-.9l-.5-.6Q9 18.4 8 19l.2.9-.1-.2v.2l-.4-.7z"/><path fill="#fb99a0" d="M8 18.9q.2-.6.8-.5a3 3 0 0 1 1.7 1.5l-.6-.6Q9 18.4 8 19"/><path fill="#fac550" d="M7.7 19h.2q0 .4.2.6v.2z"/><path fill="#831218" d="m8 19.6.2.2q.4.3.5.7l-.6-.7z"/><path fill="#101110" d="M17.4 14.9h.7q1.2.8.6 2v.1q-.4.5-1 .6-.8-.2-1-.8-.3-.6 0-1.2t.7-.7"/><path fill="#f2f0ee" d="M17.3 15.2q.3 0 .5.2v.3q0 .3-.3.3-.4 0-.5-.2-.2-.5.3-.6"/><path fill="#fb9410" d="M8.5 14q1.1.7 2.4.5c1-.2 2.3-1 3.3-.9l1.6.4.3.1.6.2q1.6.4 2.8-.5l.2.2-.6.5-.7.3H18v.1h-.6c0-.2-.8-.2-1-.2q-1.3-.4-2.7-.6l-1.3.3c-1.8.5-2.3.7-4 0l-.1-.1z"/><path fill="#fac550" d="m8.3 14.3-.1.2h-.1z"/><path fill="#101110" d="m14.1 17.6.3.1a5 5 0 0 0 2.8.3l.6-.1.1.2c.1 1.7-1.6 2.1-3 1.8l-.8-.3-.4.1-.7.3-1.2-.1c-.7-.2-1.6-.9-1.3-1.7q0-.2.3 0 1.7.4 3.3-.6M10.2 15c1.6-.2 1.9 2.3.4 2.7l-.6-.1c-1.1-.5-1-2.4.2-2.6"/><path fill="#f2f0ee" d="M10.6 15.3q.3 0 .5.2v.4q0 .2-.3.2h-.2l-.3-.2q-.2-.5.3-.6"/><path fill="#101110" d="m14 17.2.3.1v.2q-.5 0-.4-.3"/><path fill="#008a4d" d="M8.4 10.7 8 10c-.1-.2.3-.9.4-1A6 6 0 0 1 11 6.3c1.5-.7 3.5-.8 5-.2q2.2.7 3.3 2.6.4.4.6 1l-.2.3-.5.8-.7 1.2-.7-.4 1 1.2-.6-.5a7 7 0 0 0-2.3-1q.7-1 .5-2t-1-1.5a3 3 0 0 0-4 .7c-.4.8-.4 2 .2 2.7l.2.2-1.5.6-.8.5v-.2l.3-.4.1-.3-.2.1-.4.4V12z"/><path fill="#80c491" d="M8.4 10.7 8 10c-.1-.2.3-.9.4-1A6 6 0 0 1 11 6.3c1.5-.7 3.5-.8 5-.2q2.2.7 3.3 2.6.4.4.6 1l-.2.3v-.2l-.7-.9q-.7-1-1.7-2a6 6 0 0 0-2.8-1 6 6 0 0 0-4.6 2c-.5.5-1.3 2-1.6 2.8"/><path fill="#064634" d="m19.7 9.8.1.2-.5.8-.7 1.2q-.4-.4-.7-.4l1 1.2-.6-.5-1-.5.1-.4q.3-.3 1-.4.9-.3 1.3-1.2"/><path fill="#80c491" d="M10 11.6h.3l-.4.3zm-.7.4.5-.3z"/><path fill="#008a4d" d="M9.6 12.9a7 7 0 0 1 6.8-1.2l1 .4a7 7 0 0 1 2 1.5c-.8.7-2 .5-2.8.3q-.6 0-1-.3c-1.4-.4-1.7-.5-3 0l-1.8.6a3 3 0 0 1-2.2-.5z"/><path fill="#064634" d="M17.3 12.1a7 7 0 0 1 2 1.5c-.7.7-1.8.5-2.7.3.8-.6 1-.7.7-1.8"/><path fill="#80c491" d="M9.6 12.9c.1.5-.1.5.4 1s2.3-.4 2.5-.3q-.8.4-1.7.6a3 3 0 0 1-2.2-.5z"/><path fill="#f2f0ee" d="M13.8 7.6q1 0 1.8.6 1.5 1.4.1 3a9 9 0 0 0-3.6.1q-.6-.5-.7-1.3.1-2.3 2.4-2.4"/><path fill="#008a4d" d="m13.6 8-.1 2h1.6v.8h-2.3V8z"/><path fill="#fb9410" d="m18.8 12.3.3-.7q1-1.7 2.4-3.2l.7-.7.2.3 1 2.4-3 2.9-.6.4-.8-1.3z"/><path fill="#f7c30f" d="m18.8 12.3.3-.7q1-1.7 2.4-3.2l.7-.7.2.3.3.8-.8.5-.7.5-1 1-.9.8z"/><path fill="#fbeca3" d="m18.8 12.3.3-.7q1-1.7 2.4-3.2l.7-.7.2.3-.3.3a14 14 0 0 0-2.5 2.9l-.3.4z"/><path fill="#fb9410" d="M5.4 8.1q2 1.5 3.3 3.6l.5.7-.1.2q-.6.5-.8 1.2l-.8-.5q-1.6-1.1-3-2.7l.7-2.1z"/><path fill="#f7c30f" d="M5.4 8.1q2 1.5 3.3 3.6l.5.7-.1.2H9l-.6-.8-1.5-1.6-.7-.5-1-.7H5z"/><path fill="#fbeca3" d="M5.4 8.1a14 14 0 0 1 3.4 3.7h-.1C8.2 11 6 8.9 5.3 8.5z"/><path fill="#fac550" d="m8.7 11.7.5.7-.1.2v-.3z"/><path fill="#064634" d="M21.7 12.5c.7 1.1 0 2.2-1 2.8l-.2-.3-.5-1zm-15.4.3 1.2.9.6.4-.5.9-.2.3q-.8-.3-1.2-1.1-.2-.8 0-1.4"/><path fill="#f2f0ee" d="M24.3 6.5h.4l-.5.4-1.5.9h-.2l-.1-.2a5 5 0 0 1 1.9-1.1M3.2 7a6 6 0 0 1 2 1v.2H5l-.8-.4L3 7.3z"/><path fill="#1f5e4c" d="m9.3 12.7.3-.4v.1z"/></svg>`;
    const LUIGI_ICON_DATA_URL = `data:image/svg+xml;utf8,${encodeURIComponent(LUIGI_ICON_SVG)}`;
    const MUSD_ICON_SVG = `<svg width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_4758_159829)"><g filter="url(#filter0_i_4758_159829)"><path d="M124.999 249.149C193.565 249.149 249.149 193.565 249.149 124.999C249.149 56.4325 193.565 0.848633 124.999 0.848633C56.4325 0.848633 0.848633 56.4325 0.848633 124.999C0.848633 193.565 56.4325 249.149 124.999 249.149Z" fill="white"/></g><path d="M101.7 211.35C101.7 214.25 99.3499 215.9 96.6499 215.05C59.0499 203 31.8999 167.85 31.8999 126.25C31.8999 84.7497 59.0499 49.5497 96.6999 37.4997C99.4999 36.6497 101.75 38.2997 101.75 41.1997V48.4497C101.75 50.3997 100.3 52.5997 98.4499 53.2997C68.6499 64.2497 47.4499 92.8497 47.4499 126.25C47.4499 159.7 68.6999 188.25 98.4499 199.1C100.3 199.8 101.75 202 101.75 203.95V211.35H101.7Z" fill="black"/><path d="M132.752 184.451C132.752 186.601 131.002 188.351 128.852 188.351H121.102C118.952 188.351 117.202 186.601 117.202 184.451V172.251C100.252 169.901 92.0016 160.501 89.7516 147.501C89.3516 145.251 91.1016 143.351 93.3516 143.351H102.202C104.052 143.351 105.602 144.701 106.002 146.451C107.652 154.101 112.102 160.051 125.702 160.051C135.702 160.051 142.852 154.401 142.852 146.101C142.852 137.751 138.702 134.551 123.952 132.151C102.202 129.251 91.9516 122.651 91.9516 105.651C91.9516 92.5512 101.952 82.2512 117.252 80.1512V68.1012C117.252 65.9512 119.002 64.2012 121.152 64.2012H128.902C131.052 64.2012 132.802 65.9512 132.802 68.1012V80.4012C145.302 82.6512 153.252 89.7012 155.902 101.551C156.402 103.801 154.652 105.801 152.302 105.801H144.152C142.402 105.801 140.952 104.651 140.452 103.001C138.202 95.5512 132.902 92.2512 123.552 92.2512C113.252 92.2512 107.952 97.2012 107.952 104.201C107.952 111.551 110.952 115.251 126.752 117.501C148.102 120.401 159.152 126.501 159.152 144.651C159.152 158.401 148.852 169.601 132.852 172.101V184.501H132.752V184.451Z" fill="black"/><path d="M153.3 214.997C150.5 215.847 148.25 214.197 148.25 211.297V204.047C148.25 201.897 149.5 199.897 151.55 199.197C181.25 188.347 202.55 159.697 202.55 126.347C202.55 92.897 181.3 64.347 151.55 53.497C149.7 52.797 148.25 50.597 148.25 48.647V41.397C148.25 38.497 150.6 36.847 153.3 37.697C190.95 49.547 218.1 84.747 218.1 126.247C218.1 167.847 190.95 202.997 153.3 214.997Z" fill="black"/></g><defs><filter id="filter0_i_4758_159829" x="0.848633" y="0.848633" width="248.3" height="248.3" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset/><feGaussianBlur stdDeviation="41.5"/><feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/><feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend mode="normal" in2="shape" result="effect1_innerShadow_4758_159829"/></filter><clipPath id="clip0_4758_159829"><rect width="250" height="250" fill="white"/></clipPath></defs></svg>`;
    const MUSD_ICON_DATA_URL = `data:image/svg+xml;utf8,${encodeURIComponent(MUSD_ICON_SVG)}`;
    const USDC_BASE_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
    const USDC_BASE_SEPOLIA_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
    const USDC_ARB_SEPOLIA_ADDRESS = "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d";
    const BASE_SEPOLIA_CHAIN_ID = 84532;
    const ARB_SEPOLIA_CHAIN_ID = 421614;
    const erc20Abi = [{
      name: "transfer",
      type: "function",
      stateMutability: "nonpayable",
      inputs: [{
        name: "to",
        type: "address"
      }, {
        name: "amount",
        type: "uint256"
      }],
      outputs: [{
        name: "",
        type: "bool"
      }]
    }, {
      name: "approve",
      type: "function",
      stateMutability: "nonpayable",
      inputs: [{
        name: "spender",
        type: "address"
      }, {
        name: "amount",
        type: "uint256"
      }],
      outputs: [{
        name: "",
        type: "bool"
      }]
    }, {
      name: "mint",
      type: "function",
      stateMutability: "nonpayable",
      inputs: [{
        name: "to",
        type: "address"
      }, {
        name: "amount",
        type: "uint256"
      }],
      outputs: []
    }, {
      name: "balanceOf",
      type: "function",
      inputs: [{
        name: "account",
        type: "address"
      }],
      outputs: [{
        name: "",
        type: "uint256"
      }],
      stateMutability: "view"
    }];
    const mockSwapAbi = [{
      name: "swap",
      type: "function",
      stateMutability: "nonpayable",
      inputs: [{
        name: "usdcIn",
        type: "uint256"
      }, {
        name: "minRwaOut",
        type: "uint256"
      }, {
        name: "recipient",
        type: "address"
      }],
      outputs: [{
        name: "rwaOut",
        type: "uint256"
      }]
    }, {
      name: "quote",
      type: "function",
      stateMutability: "view",
      inputs: [{
        name: "usdcIn",
        type: "uint256"
      }],
      outputs: [{
        name: "rwaOut",
        type: "uint256"
      }]
    }, {
      name: "getPrice",
      type: "function",
      stateMutability: "view",
      inputs: [],
      outputs: [{
        name: "price",
        type: "uint256"
      }]
    }, {
      name: "pricePerShare",
      type: "function",
      stateMutability: "view",
      inputs: [],
      outputs: [{
        name: "",
        type: "uint256"
      }]
    }, {
      name: "jitterRange",
      type: "function",
      stateMutability: "view",
      inputs: [],
      outputs: [{
        name: "",
        type: "uint256"
      }]
    }];
    const mockNftV2Abi = [{
      name: "buy",
      type: "function",
      stateMutability: "nonpayable",
      inputs: [{
        name: "id",
        type: "uint256"
      }, {
        name: "amount",
        type: "uint256"
      }, {
        name: "recipient",
        type: "address"
      }],
      outputs: [{
        name: "totalPrice",
        type: "uint256"
      }]
    }, {
      name: "priceOf",
      type: "function",
      stateMutability: "view",
      inputs: [{
        name: "id",
        type: "uint256"
      }],
      outputs: [{
        name: "",
        type: "uint256"
      }]
    }];
    return {
      docsAppOriginWebAuthn,
      DOCS_APP_ORIGIN_CLIENT_ID,
      DOCS_EXPERIMENTAL_CLEAR_SIGNING,
      APP_ORIGIN_DEMO_CAPABILITIES,
      DOCS_DIALOG_THEME,
      MOCK_USD_ADDRESS,
      RECIPIENT_ADDRESS,
      MOCK_RWA_ADDRESS,
      MOCK_SWAP_ADDRESS,
      MOCK_NFT_V2_ADDRESS,
      MOCK_NFT_LUIGI_ID,
      NVDA_ICON_DATA_URL,
      LUIGI_ICON_DATA_URL,
      MUSD_ICON_DATA_URL,
      USDC_BASE_ADDRESS,
      USDC_BASE_SEPOLIA_ADDRESS,
      USDC_ARB_SEPOLIA_ADDRESS,
      BASE_SEPOLIA_CHAIN_ID,
      ARB_SEPOLIA_CHAIN_ID,
      erc20Abi,
      mockSwapAbi,
      mockNftV2Abi
    };
  })();
  mods.waitForHeadlessIntent = (() => {
    const DEFAULT_POLL_ATTEMPTS = 8;
    const DEFAULT_POLL_INTERVAL_MS = 2500;
    const SUCCESS_STATUSES = {
      preconfirmed: true,
      filled: true,
      completed: true
    };
    async function waitForHeadlessIntent(client, submitted, polling = {}) {
      if (submitted.transactionHash) return submitted;
      const attempts = polling.attempts ?? DEFAULT_POLL_ATTEMPTS;
      const intervalMs = polling.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
      let latestStatus = submitted.status;
      let latestHash;
      for (let attempt = 0; !latestHash && attempt < attempts; attempt += 1) {
        const {promise, resolve} = Promise.withResolvers();
        setTimeout(resolve, intervalMs);
        await promise;
        const statusResult = await client.getIntentStatus(submitted.intentId);
        latestStatus = statusResult.status;
        latestHash = statusResult.transactionHash;
        if (SUCCESS_STATUSES[latestStatus.toLowerCase()]) break;
      }
      if (!latestHash && !SUCCESS_STATUSES[latestStatus.toLowerCase()]) {
        throw new Error(latestStatus.toLowerCase() === "failed" ? "Headless intent failed before completion" : `Headless intent did not complete after ${attempts} status checks`);
      }
      return {
        ...submitted,
        status: latestStatus,
        transactionHash: latestHash
      };
    }
    return {
      waitForHeadlessIntent
    };
  })();
  mods.grantPermissionScope = (() => {
    const {createCrossChainPermission, definePermissions} = deps.sdk;
    const {parseUnits} = deps.viem;
    const {arbitrumSepolia, baseSepolia} = deps.viemChains;
    const {ARB_SEPOLIA_CHAIN_ID, BASE_SEPOLIA_CHAIN_ID, MOCK_SWAP_ADDRESS, MOCK_USD_ADDRESS, USDC_ARB_SEPOLIA_ADDRESS, erc20Abi, mockSwapAbi} = mods.constants;
    const GRANT_PERMISSION_CHAIN_ID = BASE_SEPOLIA_CHAIN_ID;
    const GRANT_PERMISSION_MINT_AMOUNT = parseUnits("0.1", 6);
    const GRANT_PERMISSION_USAGE_LIMIT = 25;
    function buildGrantPermissionScope(accountAddress, nowSeconds = Math.floor(Date.now() / 1e3)) {
      const validUntil = nowSeconds + 24 * 60 * 60;
      const validUntilDate = new Date(validUntil * 1e3);
      const validAfterDate = new Date(nowSeconds * 1e3);
      const permissions = definePermissions({
        address: MOCK_USD_ADDRESS,
        name: "mUSD",
        abi: erc20Abi,
        functions: {
          mint: {
            maxUses: BigInt(GRANT_PERMISSION_USAGE_LIMIT),
            validUntil: validUntilDate,
            validAfter: validAfterDate,
            params: {
              to: {
                condition: "equal",
                value: accountAddress
              },
              amount: {
                condition: "equal",
                value: GRANT_PERMISSION_MINT_AMOUNT
              }
            }
          }
        }
      });
      return {
        ...permissions,
        validUntil,
        validAfter: nowSeconds,
        maxUses: GRANT_PERMISSION_USAGE_LIMIT
      };
    }
    const NVDANON_GRANT_CHAIN_ID = ARB_SEPOLIA_CHAIN_ID;
    const NVDANON_SOURCE_CHAIN_ID = BASE_SEPOLIA_CHAIN_ID;
    const NVDANON_USDC_PER_BUY = parseUnits("0.1", 6);
    const NVDANON_USAGE_LIMIT = 25;
    const NVDANON_TIME_LIMIT_MINUTES = 60;
    function buildNvdanonGrantScope(accountAddress, nowSeconds = Math.floor(Date.now() / 1e3), config = {}) {
      const timeLimitMinutes = config.timeLimitMinutes ?? NVDANON_TIME_LIMIT_MINUTES;
      const usageLimit = config.usageLimit ?? NVDANON_USAGE_LIMIT;
      const usdcPerBuy = config.usdcPerBuy ?? NVDANON_USDC_PER_BUY;
      const validUntil = nowSeconds + timeLimitMinutes * 60;
      const validUntilDate = new Date(validUntil * 1e3);
      const validAfterDate = new Date(nowSeconds * 1e3);
      const maxUses = BigInt(usageLimit);
      const usdcAddress = USDC_ARB_SEPOLIA_ADDRESS;
      const approve = definePermissions({
        address: usdcAddress,
        name: "USDC",
        abi: erc20Abi,
        functions: {
          approve: {
            maxUses,
            validUntil: validUntilDate,
            validAfter: validAfterDate,
            params: {
              spender: {
                condition: "equal",
                value: MOCK_SWAP_ADDRESS
              },
              amount: {
                condition: "equal",
                value: usdcPerBuy
              }
            }
          }
        }
      });
      const swap = definePermissions({
        address: MOCK_SWAP_ADDRESS,
        name: "RWASwap",
        abi: mockSwapAbi,
        functions: {
          swap: {
            maxUses,
            validUntil: validUntilDate,
            validAfter: validAfterDate,
            params: {
              usdcIn: {
                condition: "equal",
                value: usdcPerBuy
              },
              recipient: {
                condition: "equal",
                value: accountAddress
              }
            }
          }
        }
      });
      const bridge = createCrossChainPermission({
        from: {
          chain: baseSepolia,
          token: MOCK_USD_ADDRESS,
          maxAmount: usdcPerBuy
        },
        to: {
          chain: arbitrumSepolia,
          token: usdcAddress,
          recipient: accountAddress
        },
        validUntil: BigInt(validUntil),
        validAfter: BigInt(nowSeconds),
        settlementLayers: ["ACROSS"]
      });
      return {
        permissions: [...approve.permissions, ...swap.permissions],
        crossChainPermits: [bridge],
        contracts: [...approve.contracts, ...swap.contracts],
        validUntil,
        validAfter: nowSeconds,
        maxUses: usageLimit,
        usdcAddress
      };
    }
    function shortHex(value, prefix = 6, suffix = 4) {
      if (!value) return "";
      if (value.length <= prefix + suffix + 3) return value;
      return `${value.slice(0, prefix)}...${value.slice(-suffix)}`;
    }
    function baseSepoliaTxUrl(hash) {
      if (!hash || !(/^0x[0-9a-fA-F]{64}$/).test(hash)) return null;
      return `https://sepolia.basescan.org/tx/${hash}`;
    }
    function arbSepoliaTxUrl(hash) {
      if (!hash || !(/^0x[0-9a-fA-F]{64}$/).test(hash)) return null;
      return `https://sepolia.arbiscan.io/tx/${hash}`;
    }
    function asAddress(value) {
      return value?.startsWith("0x") && value.length === 42 ? value : void 0;
    }
    return {
      buildGrantPermissionScope,
      buildNvdanonGrantScope,
      shortHex,
      baseSepoliaTxUrl,
      arbSepoliaTxUrl,
      asAddress,
      GRANT_PERMISSION_CHAIN_ID,
      GRANT_PERMISSION_MINT_AMOUNT,
      GRANT_PERMISSION_USAGE_LIMIT,
      NVDANON_GRANT_CHAIN_ID,
      NVDANON_SOURCE_CHAIN_ID,
      NVDANON_USDC_PER_BUY,
      NVDANON_USAGE_LIMIT,
      NVDANON_TIME_LIMIT_MINUTES
    };
  })();
  mods.grantPermissionDemo = (() => {
    const {useCallback, useEffect, useMemo, useRef, useState} = deps.React;
    const {encodeFunctionData, formatUnits} = deps.viem;
    const {generatePrivateKey, privateKeyToAccount} = deps.viemAccounts;
    const {buildSmartSessionHeadlessSignatures, OneAuthHeadlessClient} = deps.sdkHeadless;
    const {waitForHeadlessIntent} = mods.waitForHeadlessIntent;
    const {DOCS_APP_ORIGIN_STORAGE_KEY, useAuth} = mods.authctx;
    const {MOCK_SWAP_ADDRESS, MOCK_USD_ADDRESS, USDC_ARB_SEPOLIA_ADDRESS, erc20Abi, mockSwapAbi} = mods.constants;
    const {GRANT_PERMISSION_CHAIN_ID, GRANT_PERMISSION_MINT_AMOUNT, GRANT_PERMISSION_USAGE_LIMIT, NVDANON_GRANT_CHAIN_ID, NVDANON_SOURCE_CHAIN_ID, NVDANON_USDC_PER_BUY, asAddress, baseSepoliaTxUrl, buildGrantPermissionScope, buildNvdanonGrantScope, shortHex} = mods.grantPermissionScope;
    const GRANT_PERMISSION_STORAGE_KEY = {
      "mockusd-mint": "1auth:docs:grant-permission-demo:v2",
      "nvdanon-swap": "1auth:docs:grant-permission-demo:nvdanon:v2"
    };
    function readStoredAccountAddress() {
      if (typeof window === "undefined") return void 0;
      try {
        const saved = localStorage.getItem(DOCS_APP_ORIGIN_STORAGE_KEY);
        if (!saved) return void 0;
        const parsed = JSON.parse(saved);
        return asAddress(parsed.address ?? parsed.accountAddress);
      } catch {
        return void 0;
      }
    }
    function stringifyGrantStorage(value) {
      return JSON.stringify(value, (_key, entry) => typeof entry === "bigint" ? entry.toString() : entry);
    }
    function readStoredGrantDemo(storageKey, activeAccountAddress) {
      if (typeof window === "undefined") return null;
      try {
        const saved = sessionStorage.getItem(storageKey);
        if (!saved) return null;
        const parsed = JSON.parse(saved);
        const sessionKeyAddress = asAddress(parsed.sessionKeyAddress);
        const accountAddress = asAddress(parsed.accountAddress);
        const sessionPrivateKey = parsed.sessionPrivateKey;
        if (!sessionKeyAddress || !sessionPrivateKey?.startsWith("0x")) {
          sessionStorage.removeItem(storageKey);
          return null;
        }
        if (activeAccountAddress && accountAddress && accountAddress.toLowerCase() !== activeAccountAddress.toLowerCase()) {
          return null;
        }
        return {
          accountAddress,
          sessionPrivateKey,
          sessionKeyAddress,
          result: parsed.result,
          mintResult: parsed.mintResult,
          usdcPerBuy: parsed.usdcPerBuy != null ? BigInt(parsed.usdcPerBuy) : void 0,
          savedAt: parsed.savedAt ?? Date.now()
        };
      } catch {
        sessionStorage.removeItem(storageKey);
        return null;
      }
    }
    function writeStoredGrantDemo(storageKey, value) {
      if (typeof window === "undefined") return;
      sessionStorage.setItem(storageKey, stringifyGrantStorage(value));
    }
    function clearStoredGrantDemo(storageKey) {
      if (typeof window === "undefined") return;
      sessionStorage.removeItem(storageKey);
    }
    function isCanceledGrant(result) {
      const text = `${result.error?.code ?? ""} ${result.error?.message ?? ""}`.toLowerCase();
      return text.includes("cancel") || text.includes("reject") || text.includes("dismiss");
    }
    function hasGrantChainCoverage(result, targetChainIds) {
      const handle = result?.sessionKeyHandle;
      if (!result?.success || !handle) return false;
      const chainIds = new Set(handle.chainIds ?? [handle.chainId]);
      return targetChainIds.every(chainId => {
        if (!chainIds.has(chainId)) return false;
        return targetChainIds.length === 1 || !!handle.permissionIdsByChain?.[chainId];
      });
    }
    function isStoredGrantCompatible(stored, targetChainIds) {
      return !stored.result?.success || hasGrantChainCoverage(stored.result, targetChainIds);
    }
    function buildMockUsdPrepareOptions(accountAddress) {
      return {
        targetChain: GRANT_PERMISSION_CHAIN_ID,
        calls: [{
          to: MOCK_USD_ADDRESS,
          data: encodeFunctionData({
            abi: erc20Abi,
            functionName: "mint",
            args: [accountAddress, GRANT_PERMISSION_MINT_AMOUNT]
          }),
          label: "Mint mUSD",
          sublabel: "0.1 mUSD to your account"
        }]
      };
    }
    function buildNvdanonPrepareOptions(accountAddress, usdcPerBuy = NVDANON_USDC_PER_BUY) {
      const usdcAddress = USDC_ARB_SEPOLIA_ADDRESS;
      const usdcLabel = `${formatUnits(usdcPerBuy, 6)} USDC`;
      return {
        targetChain: NVDANON_GRANT_CHAIN_ID,
        calls: [{
          to: usdcAddress,
          data: encodeFunctionData({
            abi: erc20Abi,
            functionName: "approve",
            args: [MOCK_SWAP_ADDRESS, usdcPerBuy]
          }),
          label: "Approve USDC",
          sublabel: `${usdcLabel} to RWASwap`
        }, {
          to: MOCK_SWAP_ADDRESS,
          data: encodeFunctionData({
            abi: mockSwapAbi,
            functionName: "swap",
            args: [usdcPerBuy, 0n, accountAddress]
          }),
          label: "Buy NVDAnon",
          sublabel: `${usdcLabel} \u2192 NVDAnon`
        }],
        tokenRequests: [{
          token: usdcAddress,
          amount: usdcPerBuy
        }],
        sourceAssets: [MOCK_USD_ADDRESS],
        sourceChainId: NVDANON_SOURCE_CHAIN_ID
      };
    }
    function useGrantPermissionFlow(options = {}) {
      const variant = options.variant ?? "mockusd-mint";
      const enabled = options.enabled ?? true;
      const storageKey = GRANT_PERMISSION_STORAGE_KEY[variant];
      const {client, login, isAuthenticated, address, sponsorship} = useAuth();
      const sessionPrivateKeyRef = useRef(null);
      const grantUsdcPerBuyRef = useRef(NVDANON_USDC_PER_BUY);
      const [sessionKeyAddress, setSessionKeyAddress] = useState(null);
      const [status, setStatus] = useState("idle");
      const [result, setResult] = useState(null);
      const [mintStatus, setMintStatus] = useState("idle");
      const [mintResult, setMintResult] = useState(null);
      const [error, setError] = useState(null);
      const [mintError, setMintError] = useState(null);
      const targetChainIds = useMemo(() => variant === "nvdanon-swap" ? [NVDANON_GRANT_CHAIN_ID] : [GRANT_PERMISSION_CHAIN_ID], [variant]);
      const sourceChainIds = useMemo(() => variant === "nvdanon-swap" ? [NVDANON_SOURCE_CHAIN_ID] : [], [variant]);
      const requiredSessionChainIds = useMemo(() => Array.from(new Set([...targetChainIds, ...sourceChainIds])), [sourceChainIds, targetChainIds]);
      const generateSessionKey = useCallback(() => {
        const privateKey = generatePrivateKey();
        const account = privateKeyToAccount(privateKey);
        const activeAccountAddress = asAddress(address) ?? readStoredAccountAddress();
        sessionPrivateKeyRef.current = privateKey;
        setSessionKeyAddress(account.address);
        setResult(null);
        setError(null);
        setMintResult(null);
        setMintError(null);
        setMintStatus("idle");
        setStatus("ready");
        writeStoredGrantDemo(storageKey, {
          accountAddress: activeAccountAddress,
          sessionPrivateKey: privateKey,
          sessionKeyAddress: account.address,
          savedAt: Date.now()
        });
        return account.address;
      }, [address, storageKey]);
      const reset = useCallback(() => {
        clearStoredGrantDemo(storageKey);
        generateSessionKey();
      }, [generateSessionKey, storageKey]);
      const resetMint = useCallback(() => {
        setMintStatus("idle");
        setMintResult(null);
        setMintError(null);
      }, []);
      const grantPermission = useCallback(async config => {
        setStatus("granting");
        setError(null);
        setResult(null);
        setMintResult(null);
        setMintError(null);
        setMintStatus("idle");
        try {
          let activeAccountAddress = asAddress(address);
          if (!activeAccountAddress) {
            activeAccountAddress = await login() ?? void 0;
          }
          if (!activeAccountAddress) {
            throw new Error("Sign in before granting a session permission.");
          }
          const activeSessionKey = sessionKeyAddress ?? generateSessionKey();
          grantUsdcPerBuyRef.current = config?.usdcPerBuy ?? NVDANON_USDC_PER_BUY;
          const permissions = variant === "nvdanon-swap" ? buildNvdanonGrantScope(activeAccountAddress, void 0, config) : buildGrantPermissionScope(activeAccountAddress);
          const grantResult = await client.grantPermissions({
            accountAddress: activeAccountAddress,
            targetChains: targetChainIds,
            sourceChains: sourceChainIds,
            sessionKeyAddress: activeSessionKey,
            ...permissions
          });
          setResult(grantResult);
          if (grantResult.success) {
            writeStoredGrantDemo(storageKey, {
              accountAddress: activeAccountAddress,
              sessionPrivateKey: sessionPrivateKeyRef.current,
              sessionKeyAddress: activeSessionKey,
              result: grantResult,
              usdcPerBuy: grantUsdcPerBuyRef.current,
              savedAt: Date.now()
            });
            setStatus("granted");
            return;
          }
          if (isCanceledGrant(grantResult)) {
            setStatus("canceled");
            return;
          }
          setStatus("error");
          setError(grantResult.error?.message ?? "Permission grant failed");
        } catch (err) {
          setStatus("error");
          setError(err instanceof Error ? err.message : "Permission grant failed");
        }
      }, [address, client, generateSessionKey, login, sessionKeyAddress, sourceChainIds, storageKey, targetChainIds, variant]);
      const mintWithoutSignature = useCallback(async () => {
        const grantHandle = result?.sessionKeyHandle;
        const privateKey = sessionPrivateKeyRef.current;
        const activeAccountAddress = asAddress(address) ?? readStoredAccountAddress();
        if (!result?.success || !grantHandle || !privateKey || !activeAccountAddress || !hasGrantChainCoverage(result, requiredSessionChainIds)) {
          setMintStatus("error");
          setMintError(variant === "nvdanon-swap" ? "Grant the NVDAnon buy permission before swapping headlessly." : "Grant the mUSD mint permission before minting headlessly.");
          return;
        }
        setMintStatus("minting");
        setMintError(null);
        setMintResult(null);
        try {
          const headlessClient = new OneAuthHeadlessClient({
            providerUrl: client.getProviderUrl(),
            clientId: client.getClientId(),
            webauthn: client.getWebAuthnMode() === "app_origin" ? {
              mode: "app_origin",
              ...client.getWebAuthnRpId() ? {
                rpId: client.getWebAuthnRpId()
              } : {}
            } : {
              mode: "experimental_cross_origin"
            },
            sponsorship
          });
          const prepareOptions = variant === "nvdanon-swap" ? buildNvdanonPrepareOptions(activeAccountAddress, grantUsdcPerBuyRef.current) : buildMockUsdPrepareOptions(activeAccountAddress);
          const prepared = await headlessClient.prepareIntent({
            accountAddress: activeAccountAddress,
            sessionKeyHandle: grantHandle,
            ...prepareOptions
          });
          const signatures = await buildSmartSessionHeadlessSignatures({
            privateKey,
            accountAddress: prepared.accountAddress,
            sessionKeyHandle: grantHandle,
            prepared
          });
          const submitted = await headlessClient.submitIntent({
            intentOp: prepared.intentOp,
            digestResult: prepared.digestResult,
            binding: prepared.binding,
            accountAddress: prepared.accountAddress,
            targetChain: prepared.targetChain,
            calls: prepared.calls,
            expiresAt: prepared.expiresAt,
            sponsorshipMode: prepared.sponsorshipMode ?? "self-funded",
            ...signatures
          });
          const completed = await waitForHeadlessIntent(headlessClient, submitted);
          setMintResult(completed);
          writeStoredGrantDemo(storageKey, {
            accountAddress: activeAccountAddress,
            sessionPrivateKey: privateKey,
            sessionKeyAddress: grantHandle.sessionKeyAddress,
            result,
            mintResult: completed,
            usdcPerBuy: grantUsdcPerBuyRef.current,
            savedAt: Date.now()
          });
          setMintStatus("minted");
        } catch (err) {
          setMintStatus("error");
          setMintError(err instanceof Error ? err.message : variant === "nvdanon-swap" ? "Headless NVDAnon buy failed" : "Headless mint failed");
        }
      }, [address, client, requiredSessionChainIds, result, sponsorship, storageKey, variant]);
      useEffect(() => {
        if (!enabled) return;
        if (sessionKeyAddress) return;
        const activeAccountAddress = asAddress(address) ?? readStoredAccountAddress();
        const stored = readStoredGrantDemo(storageKey, activeAccountAddress);
        if (stored && isStoredGrantCompatible(stored, requiredSessionChainIds)) {
          sessionPrivateKeyRef.current = stored.sessionPrivateKey;
          if (stored.usdcPerBuy != null) grantUsdcPerBuyRef.current = stored.usdcPerBuy;
          setSessionKeyAddress(stored.sessionKeyAddress);
          setResult(stored.result ?? null);
          setMintResult(stored.mintResult ?? null);
          setMintStatus(stored.mintResult ? "minted" : "idle");
          setStatus(stored.result?.success ? "granted" : "ready");
          return;
        }
        if (stored) {
          clearStoredGrantDemo(storageKey);
        }
        generateSessionKey();
      }, [address, enabled, generateSessionKey, requiredSessionChainIds, sessionKeyAddress, storageKey]);
      useEffect(() => {
        const activeAccountAddress = asAddress(address);
        if (!activeAccountAddress) return;
        const stored = readStoredGrantDemo(storageKey);
        if (stored?.accountAddress && stored.accountAddress.toLowerCase() !== activeAccountAddress.toLowerCase()) {
          clearStoredGrantDemo(storageKey);
          sessionPrivateKeyRef.current = null;
          setSessionKeyAddress(null);
          setResult(null);
          setMintResult(null);
          setMintStatus("idle");
          setError(null);
          setMintError(null);
          setStatus("idle");
        }
      }, [address, storageKey]);
      return {
        isAuthenticated,
        sessionKeyAddress,
        status,
        result,
        mintStatus,
        mintResult,
        error,
        mintError,
        generateSessionKey,
        grantPermission,
        mintWithoutSignature,
        resetMint,
        reset
      };
    }
    function GrantPermissionDemo() {
      const flow = useGrantPermissionFlow();
      const isBusy = flow.status === "granting";
      const mintTxUrl = baseSepoliaTxUrl(flow.mintResult?.transactionHash);
      const statusTone = flow.status === "granted" ? "success" : flow.status === "error" || flow.status === "canceled" ? "error" : "neutral";
      return React.createElement("div", {
        className: "demo-container grant-demo"
      }, React.createElement("div", {
        className: "grant-demo-header"
      }, React.createElement("div", null, React.createElement("p", {
        className: "grant-demo-eyebrow"
      }, "SmartSession permission"), React.createElement("h3", {
        className: "grant-demo-title"
      }, "Grant scoped mUSD minting")), React.createElement("span", {
        className: `grant-demo-pill grant-demo-pill-${statusTone}`
      }, flow.status === "granting" ? "Granting" : flow.status === "granted" ? "Granted" : flow.status === "canceled" ? "Canceled" : flow.status === "error" ? "Needs attention" : "Ready")), React.createElement("div", {
        className: "grant-demo-scope",
        "aria-label": "Permission scope"
      }, React.createElement("div", null, React.createElement("span", null, "Contract"), React.createElement("strong", null, "mUSD")), React.createElement("div", null, React.createElement("span", null, "Action"), React.createElement("strong", null, "mint")), React.createElement("div", null, React.createElement("span", null, "Amount"), React.createElement("strong", null, "0.1 mUSD")), React.createElement("div", null, React.createElement("span", null, "Recipient"), React.createElement("strong", null, "Account")), React.createElement("div", null, React.createElement("span", null, "Uses"), React.createElement("strong", null, GRANT_PERMISSION_USAGE_LIMIT))), React.createElement("div", {
        className: "grant-demo-row"
      }, React.createElement("div", null, React.createElement("p", {
        className: "grant-demo-label"
      }, "Session key"), React.createElement("p", {
        className: "grant-demo-mono"
      }, flow.sessionKeyAddress ? shortHex(flow.sessionKeyAddress, 8, 6) : "Generating...")), React.createElement("button", {
        type: "button",
        className: "demo-button-secondary grant-demo-secondary",
        onClick: flow.generateSessionKey,
        disabled: isBusy
      }, "New key")), React.createElement("button", {
        type: "button",
        className: "demo-button grant-demo-primary",
        onClick: () => flow.grantPermission(),
        disabled: isBusy || !flow.sessionKeyAddress
      }, isBusy ? "Opening permission review..." : flow.isAuthenticated ? "Grant permission" : "Sign in to grant permission"), React.createElement("p", {
        className: "grant-demo-footnote"
      }, "This demo sends only the public session-key address to Rhinestone. The private key is stored in this tab's sessionStorage so the guide can survive a reload."), flow.result?.success && React.createElement("div", {
        className: "demo-result demo-result-success grant-demo-result"
      }, React.createElement("p", {
        className: "demo-result-label"
      }, "Grant result"), React.createElement("dl", {
        className: "grant-demo-result-grid"
      }, React.createElement("div", null, React.createElement("dt", null, "Grant ID"), React.createElement("dd", null, flow.result.grantId ?? "Created")), React.createElement("div", null, React.createElement("dt", null, "Permission ID"), React.createElement("dd", null, shortHex(flow.result.permissionId ?? flow.result.sessionKeyHandle?.permissionId, 10, 8))), React.createElement("div", null, React.createElement("dt", null, "Chain"), React.createElement("dd", null, GRANT_PERMISSION_CHAIN_ID)), React.createElement("div", null, React.createElement("dt", null, "Status"), React.createElement("dd", null, (flow.result.status ?? flow.result.chainResults?.[0]?.status) ?? "enabled")), (flow.result.transactionHash || flow.result.chainResults?.[0]?.transactionHash) && React.createElement("div", null, React.createElement("dt", null, "Transaction"), React.createElement("dd", null, shortHex(flow.result.transactionHash ?? flow.result.chainResults?.[0]?.transactionHash, 10, 8))))), flow.result?.success && React.createElement("div", {
        className: "grant-demo-mint-panel"
      }, React.createElement("div", null, React.createElement("p", {
        className: "grant-demo-mint-title"
      }, "Mint with session key"), React.createElement("p", {
        className: "grant-demo-mint-copy"
      }, "No new user signature. The SmartSession permission enforces 0.1 mUSD to this account.")), React.createElement("button", {
        type: "button",
        className: "demo-button grant-demo-primary",
        onClick: flow.mintWithoutSignature,
        disabled: flow.mintStatus === "minting"
      }, flow.mintStatus === "minting" ? "Minting..." : "Mint 0.1 mUSD")), flow.mintResult && React.createElement("div", {
        className: "demo-result demo-result-success grant-demo-result"
      }, React.createElement("p", {
        className: "demo-result-label"
      }, "Headless mint"), React.createElement("dl", {
        className: "grant-demo-result-grid"
      }, React.createElement("div", null, React.createElement("dt", null, "Intent"), React.createElement("dd", null, shortHex(flow.mintResult.intentId, 10, 8))), React.createElement("div", null, React.createElement("dt", null, "Status"), React.createElement("dd", null, flow.mintResult.status)), flow.mintResult.transactionHash ? React.createElement("div", null, React.createElement("dt", null, "Transaction"), React.createElement("dd", null, mintTxUrl ? React.createElement("a", {
        className: "grant-demo-tx-link",
        href: mintTxUrl,
        target: "_blank",
        rel: "noreferrer"
      }, shortHex(flow.mintResult.transactionHash, 10, 8)) : shortHex(flow.mintResult.transactionHash, 10, 8))) : React.createElement("div", null, React.createElement("dt", null, "Transaction"), React.createElement("dd", null, "Confirmed; explorer hash pending")))), (flow.error || flow.status === "canceled") && React.createElement("div", {
        className: "demo-result demo-result-error grant-demo-result"
      }, React.createElement("p", {
        className: "demo-result-label"
      }, flow.status === "canceled" ? "Canceled" : "Error"), React.createElement("p", {
        className: "demo-result-value"
      }, flow.status === "canceled" ? "Permission grant was canceled before completion." : flow.error)), flow.mintError && React.createElement("div", {
        className: "demo-result demo-result-error grant-demo-result"
      }, React.createElement("p", {
        className: "demo-result-label"
      }, "Headless mint failed"), React.createElement("p", {
        className: "demo-result-value"
      }, flow.mintError)));
    }
    return {
      useGrantPermissionFlow,
      GrantPermissionDemo
    };
  })();
  return mods;
};

export const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;

export const readStoredAddressFromJsonEnvelope = () => {
  try {
    const raw = window.localStorage.getItem(STORAGE_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw);
    const address = parsed && typeof parsed === "object" ? parsed.address : null;
    return typeof address === "string" && ADDRESS_RE.test(address) ? address : null;
  } catch {
    return null;
  }
};

export const writeStoredAddressAsJsonEnvelope = address => {
  window.localStorage.setItem(STORAGE_KEY, JSON.stringify({
    address
  }));
};

export const useOneAuth = deps => {
  const [address, setAddress] = React.useState(null);
  const [client, setClient] = React.useState(null);
  const mounted = React.useRef(false);
  const sessionGeneration = React.useRef(0);
  React.useEffect(() => {
    if (!deps) return;
    mounted.current = true;
    const restoreGeneration = ++sessionGeneration.current;
    const {OneAuthClient} = deps.sdk;
    const nextClient = new OneAuthClient({
      providerUrl: backends().auth,
      clientId: "docs-app-origin",
      webauthn: {
        mode: "app_origin",
        rpId: window.location.hostname
      },
      theme: {
        mode: "dark",
        accent: "#ffffff",
        backdrop: {
          color: "#52525c",
          opacity: 0.5,
          blur: 12
        }
      },
      experimental_clear_signing: true,
      recovery: {
        fallback: {
          type: "verified-identity"
        }
      },
      testnets: true,
      prewarm: true,
      sponsorship,
      onDisconnect: () => {
        if (!mounted.current) return;
        sessionGeneration.current += 1;
        window.localStorage.removeItem(STORAGE_KEY);
        setAddress(null);
      }
    });
    setClient(nextClient);
    const stored = readStoredAddressFromJsonEnvelope();
    if (stored) {
      nextClient.getSession().then(result => {
        if (!mounted.current || sessionGeneration.current !== restoreGeneration) return;
        const account = result.success ? result.session.accountAddress : null;
        if (account && ADDRESS_RE.test(account)) {
          writeStoredAddressAsJsonEnvelope(account);
          setAddress(account);
        } else {
          window.localStorage.removeItem(STORAGE_KEY);
          setAddress(null);
        }
      }).catch(() => {
        if (!mounted.current || sessionGeneration.current !== restoreGeneration) return;
        window.localStorage.removeItem(STORAGE_KEY);
        setAddress(null);
      });
    }
    return () => {
      mounted.current = false;
      sessionGeneration.current += 1;
    };
  }, [deps]);
  const login = React.useCallback(async () => {
    if (!client) return null;
    const generation = ++sessionGeneration.current;
    const result = await client.authenticate({
      oauthEnabled: false
    });
    if (!result.success) {
      if (result.error?.code === "USER_CANCELLED") return null;
      throw new Error(result.error?.message ?? "Authentication failed");
    }
    const account = result.session.accountAddress;
    if (!ADDRESS_RE.test(account)) throw new Error("Authentication returned an invalid account address");
    if (!mounted.current || sessionGeneration.current !== generation) return null;
    writeStoredAddressAsJsonEnvelope(account);
    setAddress(account);
    return account;
  }, [client]);
  const logout = React.useCallback(() => {
    sessionGeneration.current += 1;
    window.localStorage.removeItem(STORAGE_KEY);
    if (mounted.current) setAddress(null);
    if (client?.disconnect) client.disconnect();
  }, [client]);
  return React.useMemo(() => ({
    address,
    isAuthenticated: !!address,
    client,
    sponsorship,
    login,
    logout
  }), [address, client, login, logout]);
};

export const Frame = ({children, connected}) => <div className="oa-demo">
    <div className="oa-demo-head">
      <span className="oa-demo-title">Grant a scoped session</span>
      <span className="oa-demo-status">
        <span className="oa-demo-dot" data-state={connected ? "on" : "off"} />
        {connected ? "Signed in" : "Not connected"}
      </span>
    </div>
    <div className="oa-demo-body">{children}</div>
  </div>;

export const GrantPermissionDemo = () => {
  const [deps, setDeps] = React.useState(null);
  const [loadError, setLoadError] = React.useState("");
  const [connecting, setConnecting] = React.useState(false);
  React.useEffect(() => {
    let cancelled = false;
    loadDeps().then(d => !cancelled && setDeps(d)).catch(e => !cancelled && setLoadError(String(e.message ?? e)));
    return () => {
      cancelled = true;
    };
  }, []);
  const mods = React.useMemo(() => deps ? buildModules(deps) : null, [deps]);
  const auth = useOneAuth(deps);
  return <div className="not-prose rhinestone-1auth-demo" data-demo="grant-permission">
      <style dangerouslySetInnerHTML={{
    __html: CSS
  }} />
      <AuthContext.Provider value={auth}>
        <Frame connected={auth.isAuthenticated}>
          {mods && auth.client && false && !auth.isAuthenticated ? <div>
              <button className="demo-button" disabled={connecting} onClick={async () => {
    setConnecting(true);
    setLoadError("");
    try {
      await auth.login();
    } catch (error) {
      setLoadError(String(error.message ?? error));
    } finally {
      setConnecting(false);
    }
  }}>{connecting ? "Signing in..." : "Sign in to try it"}</button>
              {loadError ? <p className="demo-error">{loadError}</p> : null}
            </div> : mods && (auth.client || false) ? React.createElement(mods.grantPermissionDemo.GrantPermissionDemo, {
    key: auth.address
  }) : loadError ? <p className="demo-error">{loadError}</p> : <span className="oa-demo-skeleton" />}
        </Frame>
      </AuthContext.Provider>
    </div>;
};

A session lets your application execute approved calls without asking for a passkey signature each time. The user grants the authority once; your application keeps the session signer and uses the embedded wallet to prepare and submit later intents.

Start with the shared [embedded wallet setup](/wallets/embedded-wallets/accounts#initialize-the-client), authenticate the user, and keep the returned `accountAddress`. Configure the same [sponsorship endpoints](/transactions/sponsorship/set-up#embedded-wallets-backend) before granting or executing a session.

The examples below use the public `@rhinestone/1auth` `0.10.1` release and its `viem` `^2.55.0` peer dependency.

<Steps>
  <Step title="Create a session signer">
    Generate an ECDSA key for the session. Only send its public address to Rhinestone.

    ```ts theme={null}
    import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";

    const sessionPrivateKey = generatePrivateKey();
    const sessionSigner = privateKeyToAccount(sessionPrivateKey);
    const sessionKeyAddress = sessionSigner.address;
    ```

    This flow uses `buildSmartSessionHeadlessSignatures()`, which requires a locally accessible `Hex` private key. Keep it in storage appropriate to its value and environment. A non-exportable KMS or HSM key requires a custom signer and session-key signature implementation, which this guide does not cover.
  </Step>

  <Step title="Define the authority">
    Grant one ERC-20 `transfer` action on Base Sepolia. The recipient is fixed, each transfer is capped at 10 tokens, and that function can execute up to 25 times during the 24-hour window.

    ```ts {12,19,21-22} theme={null}
    import { definePermissions } from "@rhinestone/1auth";
    import { erc20Abi, parseUnits, type Address } from "viem";

    const targetChain = 84532;
    // Replace these placeholders with addresses on the target chain.
    const token = "0x..." as Address;
    const recipient = "0x..." as Address;
    const maxAmountPerTransfer = parseUnits("10", 6);
    const validAfter = Math.floor(Date.now() / 1000);
    const validUntil = validAfter + 24 * 60 * 60;

    const transferPermission = definePermissions({
      address: token,
      name: "Token",
      abi: erc20Abi,
      functions: {
        transfer: {
          params: {
            recipient: { condition: "equal", value: recipient },
            amount: {
              condition: "lessThanOrEqual",
              value: maxAmountPerTransfer,
            },
          },
        },
      },
    });
    ```

    `definePermissions()` derives the selector and calldata constraints from the ABI. The name and ABI make the review readable, but they are app-supplied metadata. The contract address, selector, parameter rules, validity window, and usage limit define the authority.

    See [Session limits](/wallets/session-keys/session-limits) before granting broader actions or using assets from another chain.
  </Step>

  <Step title="Ask the user to grant the session" id="ask-the-user-to-grant-the-session">
    Use the initialized `oneAuth` client and authenticated account from the embedded wallet setup:

    ```ts {3,9-10} theme={null}
    import { oneAuth } from "./oneauth";

    const grant = await oneAuth.grantPermissions({
      accountAddress,
      targetChains: [targetChain],
      sessionKeyAddress,
      validAfter,
      validUntil,
      maxUses: 25,
      ...transferPermission,
    });

    if (!grant.success || !grant.grantId || !grant.sessionKeyHandle) {
      throw new Error(grant.error?.message ?? "Permission grant failed");
    }
    ```

    The embedded wallet shows the scope, collects the owner's passkey authorization, and installs or enables the session-key configuration. No target chain is selected implicitly.
  </Step>

  <Step title="Persist the signer and handle">
    Store both halves before starting headless execution. The signer is the capability; the handle describes the approved onchain configuration.

    ```ts theme={null}
    const accountNamespace = accountAddress.toLowerCase();
    const storedSession = {
      grantId: grant.grantId,
      accountAddress,
      sessionKeyAddress,
      privateKey: sessionPrivateKey,
      handle: grant.sessionKeyHandle,
    };

    sessionStorage.setItem(
      `session:${accountNamespace}`,
      JSON.stringify(storedSession, (_key, value) =>
        typeof value === "bigint" ? value.toString() : value,
      ),
    );
    ```

    The stable, account-scoped key remains available after the in-memory `grant` result is gone. If an account has multiple grants, store a grant map and selected `grantId` under that account namespace instead of overwriting this record.

    `sessionStorage` survives a page refresh but not a tab close. Use encrypted application storage or a protected backend secret store when the session must last longer. Never send the private key to Rhinestone.

    Rhinestone separately stores public grant metadata so the handle can be recovered from the same account namespace. It cannot recover the private signer.
  </Step>

  <Step title="Prepare, sign, and submit">
    Create a headless client with the same provider, app identity, WebAuthn namespace, and sponsorship callbacks as your embedded wallet client. Reuse your integration's configuration rather than creating another app registration. The `sponsorship` import below is the URL or callback configuration already passed to `OneAuthClient`; export it from your existing integration config.

    ```ts {53,67,70,77} theme={null}
    import {
      buildSmartSessionHeadlessSignatures,
      OneAuthHeadlessClient,
      type SessionKeyHandle,
    } from "@rhinestone/1auth/headless";
    import {
      encodeFunctionData,
      erc20Abi,
      parseUnits,
      type Address,
      type Hex,
    } from "viem";
    import { privateKeyToAccount } from "viem/accounts";
    import { sponsorship } from "./sponsorship";

    const rawSession = sessionStorage.getItem(
      `session:${accountAddress.toLowerCase()}`,
    );
    if (!rawSession) throw new Error("No session stored for this account");

    const saved = JSON.parse(rawSession) as {
      grantId: string;
      accountAddress: Address;
      sessionKeyAddress: Address;
      privateKey: Hex;
      handle: SessionKeyHandle;
    };

    const sameAddress = (left: Address, right: Address) =>
      left.toLowerCase() === right.toLowerCase();
    const signerAddress = privateKeyToAccount(saved.privateKey).address;

    if (
      !sameAddress(saved.accountAddress, accountAddress) ||
      !sameAddress(saved.handle.accountAddress, accountAddress) ||
      !sameAddress(saved.sessionKeyAddress, signerAddress) ||
      !sameAddress(saved.handle.sessionKeyAddress, signerAddress)
    ) {
      throw new Error("Stored session does not match this account and signer");
    }

    const rpId = oneAuth.getWebAuthnRpId();
    const headless = new OneAuthHeadlessClient({
      providerUrl: oneAuth.getProviderUrl(),
      clientId: oneAuth.getClientId(),
      webauthn: {
        mode: "app_origin",
        ...(rpId ? { rpId } : {}),
      },
      sponsorship,
    });

    const prepared = await headless.prepareIntent({
      accountAddress,
      targetChain,
      calls: [
        {
          to: token,
          value: 0n,
          data: encodeFunctionData({
            abi: erc20Abi,
            functionName: "transfer",
            args: [recipient, parseUnits("5", 6)],
          }),
        },
      ],
      sessionKeyHandle: saved.handle,
    });

    const signatures = await buildSmartSessionHeadlessSignatures({
      privateKey: saved.privateKey,
      accountAddress: prepared.accountAddress,
      sessionKeyHandle: saved.handle,
      prepared,
    });

    const submitted = await headless.submitIntent({
      intentOp: prepared.intentOp,
      digestResult: prepared.digestResult,
      binding: prepared.binding,
      accountAddress: prepared.accountAddress,
      targetChain: prepared.targetChain,
      calls: prepared.calls,
      expiresAt: prepared.expiresAt,
      sponsorshipMode: prepared.sponsorshipMode ?? "self-funded",
      ...signatures,
    });

    if (!submitted.success) {
      throw new Error("Headless submission failed");
    }

    const final = await headless.waitForIntent(
      submitted.intentId,
      submitted.transactionResult,
    );
    ```

    `prepareIntent()` fixes the quote and returns the data the session signer must sign. `buildSmartSessionHeadlessSignatures()` rebuilds the per-chain session-key signatures from the saved handle. `submitIntent()` forwards those signatures without another passkey dialog, and the onchain validator rejects calls outside the grant.
  </Step>
</Steps>

## Try a scoped session

This testnet demo grants a narrower action than the transfer example above: mint 0.1 mUSD to your own account on Base Sepolia, up to 25 times within 24 hours. Grant the permission once, then mint without another passkey prompt. Fees are sponsored, and the session key stays in this tab's `sessionStorage`.

<GrantPermissionDemo />
