# JAW Core Documentation > Official documentation for @jaw.id/core package ## Supported Networks ### Mainnets | Network | Chain ID | Chain ID (Hex) | | ------------ | -------- | -------------- | | Ethereum | `1` | `0x1` | | Optimism | `10` | `0xa` | | Base | `8453` | `0x2105` | | Arbitrum One | `42161` | `0xa4b1` | | Linea | `59144` | `0xe708` | ### Testnets Testnets are available when `preference.showTestnets` is enabled: ```typescript const jaw = JAW.create({ apiKey: 'your-api-key', preference: { showTestnets: true, }, }); ``` | Network | Chain ID | Chain ID (Hex) | | ---------------- | ---------- | -------------- | | Ethereum Sepolia | `11155111` | `0xaa36a7` | | Optimism Sepolia | `11155420` | `0xaa37dc` | | Base Sepolia | `84532` | `0x14a34` | | Arbitrum Sepolia | `421614` | `0x66eee` | | Linea Sepolia | `59141` | `0xe705` | ### Related * [defaultChainId](/configuration/defaultChainId) - Set the default network * [wallet\_switchEthereumChain](/api-reference/wallet_switchEthereumChain) - Switch networks programmatically ## Wagmi Integration The `@jaw.id/wagmi` package provides a Wagmi-compatible connector and React hooks for integrating JAW smart accounts into your dApp. ### Installation :::code-group ```bash [npm] npm install @jaw.id/wagmi wagmi viem @tanstack/react-query ``` ```bash [pnpm] pnpm add @jaw.id/wagmi wagmi viem @tanstack/react-query ``` ```bash [yarn] yarn add @jaw.id/wagmi wagmi viem @tanstack/react-query ``` ```bash [bun] bun add @jaw.id/wagmi wagmi viem @tanstack/react-query ``` ::: ### Quick Start #### 1. Configure the Connector ```typescript import { createConfig, http } from 'wagmi'; import { mainnet, base, baseSepolia } from 'wagmi/chains'; import { jaw } from '@jaw.id/wagmi'; export const config = createConfig({ chains: [mainnet, base, baseSepolia], connectors: [ jaw({ apiKey: 'YOUR_API_KEY', appName: 'My App', appLogoUrl: 'https://example.com/logo.png', }), ], transports: { [mainnet.id]: http(), [base.id]: http(), [baseSepolia.id]: http(), }, }); ``` #### 2. Set Up Providers ```tsx import { WagmiProvider } from 'wagmi'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient(); function App({ children }) { return ( {children} ); } ``` #### 3. Use the Hooks ```tsx import { useAccount, useSendTransaction } from 'wagmi'; import { useConnect, useDisconnect } from '@jaw.id/wagmi'; function WalletButton() { const { address, isConnected } = useAccount(); const { mutate: connect, isPending } = useConnect(); const { mutate: disconnect } = useDisconnect(); if (isConnected) { return (

Connected: {address}

); } return ( ); } ``` ### Standard Wagmi Hooks All standard wagmi hooks work with the JAW connector. Use them directly from `wagmi`: ```typescript import { useAccount, useSendTransaction, useSignMessage, useSignTypedData, useSwitchChain, useSendCalls, useWriteContract, useBalance, } from 'wagmi'; ``` | Hook | Description | | -------------------- | ----------------------------------------- | | `useAccount` | Get connected account address and status | | `useSendTransaction` | Send a single transaction | | `useSignMessage` | Sign a personal message (EIP-191) | | `useSignTypedData` | Sign typed data (EIP-712) | | `useSwitchChain` | Switch to a different chain | | `useSendCalls` | Send atomic batch transactions (EIP-5792) | | `useWriteContract` | Call a contract write function | | `useBalance` | Get ETH or token balance | :::info When a paymaster is configured, transactions sent via `useSendTransaction`, `useSendCalls`, and `useWriteContract` are automatically sponsored (gasless). ::: ### JAW Hooks For JAW-specific features, import hooks directly from `@jaw.id/wagmi`: ```typescript // Direct import (recommended) import { useConnect, useDisconnect, useGrantPermissions } from '@jaw.id/wagmi'; // Alternative: namespace import import { Hooks } from '@jaw.id/wagmi'; const { mutate } = Hooks.useConnect(); ``` | Hook | Description | | --------------------------------------------------- | --------------------------------------------------- | | [useConnect](/wagmi/useConnect) | Connect with optional capabilities (SIWE, subnames) | | [useDisconnect](/wagmi/useDisconnect) | Disconnect and clean up session | | [useGrantPermissions](/wagmi/useGrantPermissions) | Grant permissions to a spender | | [usePermissions](/wagmi/usePermissions) | Query current permissions | | [useRevokePermissions](/wagmi/useRevokePermissions) | Revoke a permission | | [useGetAssets](/wagmi/useGetAssets) | Query token balances across chains | ### Connector | Function | Description | | ------------------- | -------------------------------- | | [jaw()](/wagmi/jaw) | Create a JAW connector for Wagmi | ### Why Use JAW Hooks? #### useConnect vs wagmi's useConnect | Feature | `wagmi.useConnect` | `@jaw.id/wagmi.useConnect` | | ------------------------- | ------------------ | -------------------------- | | Basic connection | Yes | Yes | | Request SIWE signature | No | Yes | | Request subname issuance | No | Yes | | Uses `wallet_connect` RPC | No | When capabilities provided | Use `@jaw.id/wagmi`'s `useConnect` when you need capabilities like Sign-In with Ethereum or ENS subnames during connection. #### useDisconnect vs wagmi's useDisconnect Both work the same way. `@jaw.id/wagmi`'s version is provided for consistency. ### Actions (Non-React) For vanilla JavaScript or other frameworks, use actions directly: ```typescript import { connect, disconnect, grantPermissions } from '@jaw.id/wagmi'; // Use with your wagmi config await connect(config, { connector: jawConnector }); ``` ### Related * [Configuration](/configuration) - Full configuration options * [Provider API](/api-reference) - Direct provider methods * [Account API](/account) - Low-level Account class ## Connector Create a JAW connector for your [Wagmi Config](https://wagmi.sh/core/api/createConfig). ```typescript import { createConfig, http } from 'wagmi'; import { mainnet } from 'wagmi/chains'; import { jaw } from '@jaw.id/wagmi'; const config = createConfig({ chains: [mainnet], connectors: [ jaw({ apiKey: 'YOUR_API_KEY' }), ], transports: { [mainnet.id]: http(), }, }); ``` ### Signature ```typescript function jaw(parameters: JawParameters): Connector ``` ### Parameters #### apiKey Type: `string` (required) Your JAW API key. Get one at [JAW Dashboard](https://dashboard.jaw.id/). #### appName Type: `string` Display name for your application. Shown in the authentication UI. #### appLogoUrl Type: `string | null` URL to your application's logo. Used in authentication UI. #### defaultChainId Type: `number` Default chain ID to use on first connection. #### ens Type: `string` ENS domain for issuing subnames (e.g., `'myapp.eth'`). When configured, users can receive a subname during account creation. #### preference Type: `JawProviderPreference` Configuration for authentication behavior. ```typescript interface JawProviderPreference { /** Authentication mode (default: Mode.CrossPlatform) */ mode?: Mode.CrossPlatform | Mode.AppSpecific; /** Whether to show testnet chains (default: false) */ showTestnets?: boolean; /** UI handler for app-specific mode */ uiHandler?: UIHandler; } ``` #### paymasters Type: `Record }>` Custom paymaster configuration by chain ID for gas sponsorship. See [paymasters](/configuration/paymasters) for details. ### Returns `Connector` - A Wagmi-compatible connector instance. ### Connector Properties The returned connector has these Wagmi-standard properties: | Property | Value | | -------- | --------------- | | `id` | `'jaw'` | | `name` | `'JAW'` | | `type` | `'jaw'` | | `rdns` | `'keys.jaw.id'` | ### Example ```typescript import { createConfig, http } from 'wagmi'; import { mainnet, base, baseSepolia } from 'wagmi/chains'; import { jaw } from '@jaw.id/wagmi'; const config = createConfig({ chains: [mainnet, base, baseSepolia], connectors: [ jaw({ apiKey: 'YOUR_API_KEY', appName: 'My DApp', appLogoUrl: 'https://example.com/logo.png', defaultChainId: 8453, // Base ens: 'myapp.eth', preference: { showTestnets: true, }, paymasters: { 8453: { url: 'https://my-paymaster.com/base' }, 84532: { url: 'https://my-paymaster.com/base-sepolia' }, }, }), ], transports: { [mainnet.id]: http(), [base.id]: http(), [baseSepolia.id]: http(), }, }); ``` ### Related * [Configuration](/configuration) - Full configuration reference * [useConnect](/wagmi/useConnect) - Connect with capabilities * [preference](/configuration/preference) - Mode and UI handler options ## useConnect Hook to connect to the wallet with optional capabilities. **Type:** `hook` ### Import ```typescript import { useConnect } from '@jaw.id/wagmi'; ``` ### Signature ```typescript function useConnect(parameters?: { config?: Config; mutation?: UseMutationParameters; }): UseMutationResult ``` ### Parameters #### config Type: `Config` (optional) Wagmi config. If not provided, uses the config from `WagmiProvider`. #### mutation Type: `UseMutationParameters` (optional) TanStack React Query mutation options. ### Returns Returns a TanStack React Query mutation result: | Property | Type | Description | | ------------- | ----------------------- | --------------------------------- | | `mutate` | `function` | Function to trigger connection | | `mutateAsync` | `function` | Async version of mutate | | `data` | `{ accounts, chainId }` | Connection result | | `isPending` | `boolean` | Whether connection is in progress | | `isSuccess` | `boolean` | Whether connection succeeded | | `isError` | `boolean` | Whether connection failed | | `error` | `Error` | Error if connection failed | #### data When successful, `data` contains: ```typescript { accounts: Address[] | AccountWithCapabilities[]; chainId: number; } ``` If capabilities were requested, accounts include capability information. ### Mutation Variables When calling `mutate()`, you can pass: #### connector Type: `Connector` (required) The connector to use for connection. #### chainId Type: `number` (optional) Chain ID to connect to. #### capabilities Type: `WalletConnectCapabilities` (optional) Capabilities to request during connection. When provided, uses `wallet_connect` instead of `eth_requestAccounts`. ```typescript interface WalletConnectCapabilities { /** SIWE parameters for authentication */ signInWithEthereum?: { nonce: string; chainId: string; domain?: string; uri?: string; statement?: string; }; /** Text records for subname issuance */ subnameTextRecords?: { key: string; value: string }[]; } ``` ### Examples #### Basic Connection ```tsx import { useAccount } from 'wagmi'; import { useConnect, jaw } from '@jaw.id/wagmi'; function ConnectButton() { const { isConnected } = useAccount(); const { mutate: connect, isPending } = useConnect(); if (isConnected) return

Connected!

; return ( ); } ``` #### Using Config Connector ```tsx import { useConnect } from '@jaw.id/wagmi'; import { config } from './config'; function ConnectButton() { const { mutate: connect, isPending } = useConnect(); return ( ); } ``` #### Connection with SIWE Request a Sign-In with Ethereum signature during connection: ```tsx import { useConnect, jaw } from '@jaw.id/wagmi'; function ConnectWithSIWE() { const { mutate: connect, data, isPending } = useConnect(); const handleConnect = () => { connect({ connector: jaw({ apiKey: '...' }), capabilities: { signInWithEthereum: { nonce: crypto.randomUUID(), chainId: '0x1', domain: window.location.host, uri: window.location.origin, statement: 'Sign in to My DApp', }, }, }); }; return (
{data && (

Connected: {data.accounts[0].address}

SIWE Signature: {data.accounts[0].capabilities?.signInWithEthereum?.signature}

)}
); } ``` #### Connection with Subname Issue an ENS subname during connection (requires `ens` config option): ```tsx import { useConnect, jaw } from '@jaw.id/wagmi'; function ConnectWithSubname() { const { mutate: connect, isPending } = useConnect(); const handleConnect = () => { connect({ connector: jaw({ apiKey: '...', ens: 'myapp.eth' }), capabilities: { subnameTextRecords: [ { key: 'avatar', value: 'https://example.com/avatar.png' }, { key: 'description', value: 'My profile' }, ], }, }); }; return ( ); } ``` #### With Mutation Callbacks ```tsx import { useConnect } from '@jaw.id/wagmi'; function ConnectWithCallbacks() { const { mutate: connect } = useConnect({ mutation: { onSuccess: (data) => { console.log('Connected:', data.accounts); }, onError: (error) => { console.error('Connection failed:', error); }, }, }); return ; } ``` ### Difference from wagmi useConnect | Feature | `wagmi.useConnect` | `@jaw.id/wagmi.useConnect` | | ---------------------------- | ------------------ | -------------------------- | | Basic connection | Yes | Yes | | Capabilities parameter | No | Yes | | Uses `wallet_connect` | No | When capabilities provided | | Returns account capabilities | No | Yes (when requested) | Use `@jaw.id/wagmi`'s `useConnect` when you need to request capabilities during connection. For basic connections without capabilities, either hook works. ### Related * [useDisconnect](/wagmi/useDisconnect) - Disconnect from wallet * [jaw()](/wagmi/jaw) - Create the connector * [wallet\_connect](/api-reference/wallet_connect) - RPC method reference ## useDisconnect Hook to disconnect from the wallet. **Type:** `hook` ### Import ```typescript import { useDisconnect } from '@jaw.id/wagmi'; ``` ### Signature ```typescript function useDisconnect(parameters?: { config?: Config; mutation?: UseMutationParameters; }): UseMutationResult ``` ### Parameters #### config Type: `Config` (optional) Wagmi config. If not provided, uses the config from `WagmiProvider`. #### mutation Type: `UseMutationParameters` (optional) TanStack React Query mutation options. ### Returns Returns a TanStack React Query mutation result: | Property | Type | Description | | ------------- | ---------- | ------------------------------------ | | `mutate` | `function` | Function to trigger disconnection | | `mutateAsync` | `function` | Async version of mutate | | `isPending` | `boolean` | Whether disconnection is in progress | | `isSuccess` | `boolean` | Whether disconnection succeeded | | `isError` | `boolean` | Whether disconnection failed | | `error` | `Error` | Error if disconnection failed | ### Mutation Variables When calling `mutate()`, you can optionally pass: #### connector Type: `Connector` (optional) Specific connector to disconnect from. If omitted, disconnects the active connector. ### Examples #### Basic Usage ```tsx import { useAccount } from 'wagmi'; import { useDisconnect } from '@jaw.id/wagmi'; function DisconnectButton() { const { isConnected } = useAccount(); const { mutate: disconnect, isPending } = useDisconnect(); if (!isConnected) return null; return ( ); } ``` #### With Callback ```tsx import { useDisconnect } from '@jaw.id/wagmi'; function DisconnectButton() { const { mutate: disconnect } = useDisconnect({ mutation: { onSuccess: () => { console.log('Disconnected successfully'); // Redirect to login page, clear local state, etc. }, }, }); return ; } ``` #### Disconnect Specific Connector ```tsx import { useDisconnect, jaw } from '@jaw.id/wagmi'; function DisconnectButton() { const { mutate: disconnect } = useDisconnect(); const connector = jaw({ apiKey: '...' }); // Disconnect only this specific connector return ( ); } ``` #### Full Wallet Component ```tsx import { useAccount } from 'wagmi'; import { useConnect, useDisconnect, jaw } from '@jaw.id/wagmi'; function WalletButton() { const { address, isConnected } = useAccount(); const { mutate: connect, isPending: isConnecting } = useConnect(); const { mutate: disconnect, isPending: isDisconnecting } = useDisconnect(); if (isConnected) { return (
{address?.slice(0, 6)}...{address?.slice(-4)}
); } return ( ); } ``` ### Related * [useConnect](/wagmi/useConnect) - Connect to wallet * [jaw()](/wagmi/jaw) - Create the connector ## useGetAssets Hook to get the assets for the connected account. Fetches token balances across supported chains. **Type:** `hook` ### Import ```typescript import { useGetAssets } from '@jaw.id/wagmi'; ``` ### Signature ```typescript function useGetAssets(parameters?: { address?: Address; chainId?: number; connector?: Connector; chainFilter?: string[]; assetTypeFilter?: AssetType[]; assetFilter?: string[]; config?: Config; query?: UseQueryParameters; }): UseQueryResult ``` ### Parameters #### address Type: `Address` (optional) Specific account address to get assets for. Defaults to connected account. #### chainId Type: `number` (optional) Specific chain ID. Defaults to current chain. #### connector Type: `Connector` (optional) Specific connector to use. Defaults to active connector. #### chainFilter Type: `string[]` (optional) Array of chain IDs in hex format (e.g., `['0x1', '0xa']`) to filter results. If not provided, returns assets from all supported chains. #### assetTypeFilter Type: `AssetType[]` (optional) Filter by asset type. Options: `'native'`, `'erc20'`. ```typescript // Only get ERC-20 tokens useGetAssets({ assetTypeFilter: ['erc20'] }); // Only get native tokens (ETH, etc.) useGetAssets({ assetTypeFilter: ['native'] }); ``` #### assetFilter Type: `AssetFilter` (optional) Filter by specific assets per chain. Maps chain ID (hex) to an array of assets to filter by. ```typescript type AssetFilter = Record<`0x${string}`, AssetFilterEntry[]>; type AssetFilterEntry = { address: `0x${string}`; type: 'native' | 'erc20'; }; // Example: Get only USDC on mainnet and Base useGetAssets({ assetFilter: { '0x1': [{ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', type: 'erc20' }], '0x2105': [{ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', type: 'erc20' }], }, }); ``` #### config Type: `Config` (optional) Wagmi config. If not provided, uses the config from `WagmiProvider`. #### query Type: `UseQueryParameters` (optional) TanStack React Query options (excluding `gcTime` and `staleTime` which are managed internally). ### Returns Returns a TanStack React Query result: | Property | Type | Description | | ------------ | ------------------------- | ----------------------------------- | | `data` | `WalletGetAssetsResponse` | Assets grouped by chain ID | | `isLoading` | `boolean` | Whether initial load is in progress | | `isFetching` | `boolean` | Whether any fetch is in progress | | `isSuccess` | `boolean` | Whether query succeeded | | `isError` | `boolean` | Whether query failed | | `error` | `Error` | Error if query failed | | `refetch` | `function` | Manually refetch assets | #### data When successful, `data` is an object mapping chain IDs to asset arrays: ```typescript type WalletGetAssetsResponse = { [chainId: string]: Asset[]; }; interface Asset { /** Token contract address, null for native tokens */ address: string | null; /** Balance in hex format */ balance: string; /** Asset metadata */ metadata: { decimals: number; name: string; symbol: string; } | null; /** Asset type: 'native' or 'erc20' */ type: 'native' | 'erc20'; } ``` ### Behavior 1. **Auto-enables** when wallet is connected 2. **Listens for changes** via `assetsChanged` event 3. **Auto-refetches** when assets change (e.g., after transactions) 4. **Caches for 30 seconds** before becoming stale ### Examples #### Basic Usage ```tsx import { useAccount } from 'wagmi'; import { useGetAssets } from '@jaw.id/wagmi'; import { formatUnits } from 'viem'; function Portfolio() { const { isConnected } = useAccount(); const { data: assets, isLoading } = useGetAssets(); if (!isConnected) return

Connect wallet to view assets

; if (isLoading) return

Loading assets...

; return (
{Object.entries(assets || {}).map(([chainId, chainAssets]) => (

Chain {parseInt(chainId, 16)}

    {chainAssets.map((asset, i) => (
  • {asset.metadata?.symbol || 'Unknown'}: {' '} {formatUnits( BigInt(asset.balance), asset.metadata?.decimals || 18 )}
  • ))}
))}
); } ``` #### Filter by Chain ```tsx import { useGetAssets } from '@jaw.id/wagmi'; function MainnetAssets() { const { data: assets } = useGetAssets({ chainFilter: ['0x1'], // Mainnet only }); // ... } ``` #### Filter by Asset Type ```tsx import { useGetAssets } from '@jaw.id/wagmi'; function TokenBalances() { // Only get ERC-20 tokens, exclude native ETH const { data: assets } = useGetAssets({ assetTypeFilter: ['erc20'], }); // ... } ``` #### Manual Refresh ```tsx import { useGetAssets } from '@jaw.id/wagmi'; function RefreshablePortfolio() { const { data, refetch, isFetching } = useGetAssets(); return (
{/* Render assets */}
); } ``` #### Disable Auto-Fetch ```tsx import { useGetAssets } from '@jaw.id/wagmi'; function ManualAssets() { const { data, refetch } = useGetAssets({ query: { enabled: false, // Don't fetch automatically }, }); return (
{data &&

{Object.keys(data).length} chains with assets

}
); } ``` ### Related * [wallet\_getAssets](/api-reference/wallet_getAssets) - RPC method reference * [usePermissions](/wagmi/usePermissions) - Query permissions ## useGrantPermissions Hook to grant permissions to a spender address. **Type:** `hook` ### Import ```typescript import { useGrantPermissions } from '@jaw.id/wagmi'; ``` ### Signature ```typescript function useGrantPermissions(parameters?: { config?: Config; mutation?: UseMutationParameters; }): UseMutationResult ``` ### Parameters #### config Type: `Config` (optional) Wagmi config. If not provided, uses the config from `WagmiProvider`. #### mutation Type: `UseMutationParameters` (optional) TanStack React Query mutation options. ### Returns Returns a TanStack React Query mutation result: | Property | Type | Description | | ------------- | -------------------------------- | ------------------------------- | | `mutate` | `function` | Function to grant permissions | | `mutateAsync` | `function` | Async version of mutate | | `data` | `WalletGrantPermissionsResponse` | Granted permission details | | `isPending` | `boolean` | Whether granting is in progress | | `isSuccess` | `boolean` | Whether granting succeeded | | `isError` | `boolean` | Whether granting failed | | `error` | `Error` | Error if granting failed | #### data When successful, `data` contains: ```typescript interface WalletGrantPermissionsResponse { /** Smart account this permission is valid for */ account: Address; /** Entity that can use this permission */ spender: Address; /** Timestamp when permission becomes valid */ start: number; /** Timestamp when permission expires */ end: number; /** Salt for uniqueness */ salt: Hex; /** Array of call permissions */ calls: CallPermissionDetail[]; /** Array of spend permissions */ spends: SpendPermissionDetail[]; /** Permission identifier (hash) */ permissionId: Hex; /** Chain ID in hex */ chainId: Hex; } ``` ### Mutation Variables When calling `mutate()`, pass: #### expiry Type: `number` (required) Unix timestamp (in seconds) when the permission expires. #### spender Type: `Address` (required) The address that can use this permission to execute calls on behalf of the account. #### permissions Type: `PermissionsDetail` (required) The permissions to grant: ```typescript interface PermissionsDetail { /** Call permissions - which contracts and functions can be called */ calls?: CallPermissionDetail[]; /** Spend permissions - token spending limits */ spends?: SpendPermissionDetail[]; } interface CallPermissionDetail { /** Target contract address */ target: Address; /** Function selector (4 bytes) or signature */ selector: Hex; } interface SpendPermissionDetail { /** Token address (use 0xEee...EEeE for native ETH) */ token: Address; /** Maximum amount per period */ limit: string; /** Time period for the limit */ period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; } ``` #### Optional Parameters | Parameter | Type | Description | | ----------- | ----------- | ------------------------ | | `address` | `Address` | Specific account address | | `chainId` | `number` | Specific chain ID | | `connector` | `Connector` | Specific connector | ### Examples #### Basic Usage ```tsx import { useGrantPermissions } from '@jaw.id/wagmi'; function GrantPermissionButton() { const { mutate: grant, isPending, data } = useGrantPermissions(); const handleGrant = () => { grant({ expiry: Math.floor(Date.now() / 1000) + 86400 * 7, // 1 week spender: '0xAutomatedBot...', permissions: { calls: [ { target: ROUTER_ADDRESS, selector: '0x38ed1739', // swapExactTokensForTokens }, ], spends: [ { token: USDC_ADDRESS, limit: '500000000', // 500 USDC period: 'week', }, ], }, }); }; return (
{data &&

Permission ID: {data.permissionId}

}
); } ``` #### With Callbacks ```tsx import { useGrantPermissions } from '@jaw.id/wagmi'; function GrantWithCallbacks() { const { mutate: grant } = useGrantPermissions({ mutation: { onSuccess: (data) => { console.log('Permission granted:', data.permissionId); // Store permission ID for later revocation }, onError: (error) => { console.error('Failed to grant permission:', error); }, }, }); // ... } ``` #### Daily Spending Limit ```tsx import { useGrantPermissions } from '@jaw.id/wagmi'; import { parseUnits } from 'viem'; function DailySpendingLimit() { const { mutate: grant, isPending } = useGrantPermissions(); const handleGrant = () => { grant({ expiry: Math.floor(Date.now() / 1000) + 86400 * 30, // 30 days spender: '0xSubscriptionService...', permissions: { calls: [ { target: USDC_ADDRESS, selector: '0xa9059cbb', // transfer }, ], spends: [ { token: USDC_ADDRESS, limit: parseUnits('100', 6).toString(), // 100 USDC per day period: 'day', }, ], }, }); }; return ( ); } ``` ### Use Cases * **Session Keys** - Allow a temporary key to perform specific actions * **Subscription Services** - Grant recurring spending permissions * **Automated Trading** - Allow a bot to execute specific trades * **Gaming** - Let a game contract execute moves without prompts * **DeFi Automation** - Enable auto-compounding or limit orders ### Related * [usePermissions](/wagmi/usePermissions) - Get current permissions * [useRevokePermissions](/wagmi/useRevokePermissions) - Revoke a permission * [wallet\_grantPermissions](/api-reference/wallet_grantPermissions) - RPC method reference ## usePermissions Hook to get the current permissions for the connected account. Automatically updates when permissions change. **Type:** `hook` ### Import ```typescript import { usePermissions } from '@jaw.id/wagmi'; ``` ### Signature ```typescript function usePermissions(parameters?: { address?: Address; chainId?: number; connector?: Connector; config?: Config; query?: UseQueryParameters; }): UseQueryResult ``` ### Parameters #### address Type: `Address` (optional) Specific account address to get permissions for. Defaults to connected account. #### chainId Type: `number` (optional) Specific chain ID. Defaults to current chain. #### connector Type: `Connector` (optional) Specific connector to use. Defaults to active connector. #### config Type: `Config` (optional) Wagmi config. If not provided, uses the config from `WagmiProvider`. #### query Type: `UseQueryParameters` (optional) TanStack React Query options (excluding `gcTime` and `staleTime` which are managed internally). ### Returns Returns a TanStack React Query result: | Property | Type | Description | | ------------ | ------------------------------ | ----------------------------------- | | `data` | `WalletGetPermissionsResponse` | Array of permissions | | `isLoading` | `boolean` | Whether initial load is in progress | | `isFetching` | `boolean` | Whether any fetch is in progress | | `isSuccess` | `boolean` | Whether query succeeded | | `isError` | `boolean` | Whether query failed | | `error` | `Error` | Error if query failed | | `refetch` | `function` | Manually refetch permissions | #### data When successful, `data` is an array of permissions: ```typescript type WalletGetPermissionsResponse = Permission[]; interface Permission { /** Permission identifier (hash) */ id: Hex; /** Smart account address */ account: Address; /** Spender address */ spender: Address; /** Start timestamp */ start: number; /** Expiry timestamp */ expiry: number; /** Call permissions */ calls: CallPermissionDetail[]; /** Spend permissions */ spends: SpendPermissionDetail[]; } ``` ### Behavior 1. **Auto-enables** when wallet is connected 2. **Listens for changes** via `permissionsChanged` event 3. **Auto-refetches** when permissions change on-chain 4. **Caches indefinitely** until invalidated by events ### Examples #### Basic Usage ```tsx import { useAccount } from 'wagmi'; import { usePermissions } from '@jaw.id/wagmi'; function PermissionsList() { const { isConnected } = useAccount(); const { data: permissions, isLoading } = usePermissions(); if (!isConnected) return

Connect wallet to view permissions

; if (isLoading) return

Loading permissions...

; if (!permissions?.length) { return

No active permissions

; } return ( ); } ``` #### For Specific Address ```tsx import { usePermissions } from '@jaw.id/wagmi'; function PermissionsForAddress({ address }: { address: `0x${string}` }) { const { data: permissions } = usePermissions({ address }); // ... } ``` #### Disable Auto-Fetch ```tsx import { usePermissions } from '@jaw.id/wagmi'; function ManualPermissions() { const { data, refetch } = usePermissions({ query: { enabled: false, // Don't fetch automatically }, }); return (
{data &&

{data.length} permissions found

}
); } ``` ### Related * [useGrantPermissions](/wagmi/useGrantPermissions) - Grant new permissions * [useRevokePermissions](/wagmi/useRevokePermissions) - Revoke a permission * [wallet\_getPermissions](/api-reference/wallet_getPermissions) - RPC method reference ## useRevokePermissions Hook to revoke a permission by its ID. **Type:** `hook` ### Import ```typescript import { useRevokePermissions } from '@jaw.id/wagmi'; ``` ### Signature ```typescript function useRevokePermissions(parameters?: { config?: Config; mutation?: UseMutationParameters; }): UseMutationResult ``` ### Parameters #### config Type: `Config` (optional) Wagmi config. If not provided, uses the config from `WagmiProvider`. #### mutation Type: `UseMutationParameters` (optional) TanStack React Query mutation options. ### Returns Returns a TanStack React Query mutation result: | Property | Type | Description | | ------------- | ----------------------------- | --------------------------------- | | `mutate` | `function` | Function to revoke permission | | `mutateAsync` | `function` | Async version of mutate | | `data` | `RevokePermissionApiResponse` | Revocation result | | `isPending` | `boolean` | Whether revocation is in progress | | `isSuccess` | `boolean` | Whether revocation succeeded | | `isError` | `boolean` | Whether revocation failed | | `error` | `Error` | Error if revocation failed | ### Mutation Variables When calling `mutate()`, pass: #### id Type: `Hex` (required) The permission ID (hash) to revoke. This is the `permissionId` returned when granting a permission, or the `id` field from `usePermissions()`. #### Optional Parameters | Parameter | Type | Description | | ----------- | ----------- | ------------------------ | | `address` | `Address` | Specific account address | | `chainId` | `number` | Specific chain ID | | `connector` | `Connector` | Specific connector | ### Examples #### Basic Usage ```tsx import { useRevokePermissions } from '@jaw.id/wagmi'; function RevokeButton({ permissionId }: { permissionId: `0x${string}` }) { const { mutate: revoke, isPending } = useRevokePermissions(); return ( ); } ``` #### With Confirmation Dialog ```tsx import { useState } from 'react'; import { useRevokePermissions } from '@jaw.id/wagmi'; function RevokeWithConfirmation({ permission }) { const { mutate: revoke, isPending } = useRevokePermissions(); const [showConfirm, setShowConfirm] = useState(false); const handleRevoke = () => { revoke( { id: permission.id }, { onSuccess: () => { setShowConfirm(false); // Permission list will auto-update via usePermissions }, } ); }; if (showConfirm) { return (

Revoke permission for {permission.spender}?

); } return ; } ``` #### With Callbacks ```tsx import { useRevokePermissions } from '@jaw.id/wagmi'; function RevokeWithCallbacks() { const { mutate: revoke } = useRevokePermissions({ mutation: { onSuccess: () => { console.log('Permission revoked successfully'); // Show success toast, update UI, etc. }, onError: (error) => { console.error('Failed to revoke:', error); // Show error message }, }, }); // ... } ``` #### Full Permissions Manager ```tsx import { usePermissions, useRevokePermissions } from '@jaw.id/wagmi'; function PermissionsManager() { const { data: permissions, isLoading } = usePermissions(); const { mutate: revoke, isPending } = useRevokePermissions(); if (isLoading) return

Loading permissions...

; if (!permissions?.length) return

No active permissions

; return (

Active Permissions

{permissions.map((permission) => (

Spender: {permission.spender}

Expires: {new Date(permission.expiry * 1000).toLocaleDateString()}

Calls: {permission.calls.length} allowed

Spends: {permission.spends.length} limits

))}
); } ``` ### Related * [usePermissions](/wagmi/usePermissions) - Get current permissions * [useGrantPermissions](/wagmi/useGrantPermissions) - Grant new permissions * [wallet\_revokePermissions](/api-reference/wallet_revokePermissions) - RPC method reference ## Subscription Payments with Permissions Learn how to implement recurring subscription payments using JAW's permission system. This enables services like Netflix, Spotify, or any SaaS to charge users automatically without requiring approval for each payment. ### The Problem with Traditional Crypto Payments In traditional crypto payments, users must approve **every single transaction**. This creates a poor user experience for recurring payments: * User subscribes to Service * Every month, Service sends a payment request * User must open their wallet and approve * If user misses the approval, subscription lapses This friction makes crypto unsuitable for subscription businesses. ### How Permissions Solve This JAW's permission system (based on ERC-7715) allows users to grant **time-limited, amount-capped permissions** to service providers. The key insight is that permissions are **scoped** - they can only be used for specific actions you define. **Flow:** 1. **User Subscribes** - User grants permission: $10 USDC/month to Service 2. **Service Stores Permission ID** - Service stores permissionId in their database 3. **Monthly Charge** - Service transfers USDC using permissionId (no user interaction needed!) 4. **User Can Cancel Anytime** - User revokes permission, subscription ends immediately ### Implementation #### Installation **Wagmi:** :::code-group ```bash [npm] npm install @jaw.id/wagmi wagmi viem @tanstack/react-query ``` ```bash [pnpm] pnpm add @jaw.id/wagmi wagmi viem @tanstack/react-query ``` ```bash [yarn] yarn add @jaw.id/wagmi wagmi viem @tanstack/react-query ``` ```bash [bun] bun add @jaw.id/wagmi wagmi viem @tanstack/react-query ``` ::: **Core SDK:** :::code-group ```bash [npm] npm install @jaw.id/core viem ``` ```bash [pnpm] pnpm add @jaw.id/core viem ``` ```bash [yarn] yarn add @jaw.id/core viem ``` ```bash [bun] bun add @jaw.id/core viem ``` ::: #### Setup :::code-group ```typescript [Wagmi] import { createConfig, http } from 'wagmi'; import { base } from 'wagmi/chains'; import { jaw } from '@jaw.id/wagmi'; export const config = createConfig({ chains: [base], connectors: [ jaw({ apiKey: 'YOUR_API_KEY', appName: 'My Streaming Service', appLogoUrl: 'https://myservice.com/logo.png', }), ], transports: { [base.id]: http(), }, }); ``` ```typescript [Core SDK] import { JAW, Mode } from '@jaw.id/core'; const jaw = JAW.create({ appName: 'My Streaming Service', appLogoUrl: 'https://myservice.com/logo.png', defaultChainId: 8453, apiKey: 'YOUR_API_KEY', preference: { mode: Mode.AppSpecific }, }); ``` ::: #### 1. Grant Subscription Permission When a user clicks "Subscribe", request a permission that allows your service to charge them periodically. :::code-group ```tsx [Wagmi] import { useGrantPermissions } from '@jaw.id/wagmi'; import { parseUnits, type Address } from 'viem'; const SERVICE_SPENDER: Address = '0x...'; // Your backend wallet const USDC_ADDRESS: Address = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base function SubscribeButton({ planPrice }: { planPrice: string }) { const { mutate: grantPermission, isPending } = useGrantPermissions(); const handleSubscribe = () => { grantPermission({ expiry: Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60, // 1 year spender: SERVICE_SPENDER, permissions: { spends: [{ token: USDC_ADDRESS, allowance: parseUnits(planPrice, 6).toString(), unit: 'month', multiplier: 1, }], calls: [{ target: USDC_ADDRESS, functionSignature: 'transfer(address,uint256)', }], }, }, { onSuccess: (data) => { // Store data.permissionId in your backend! console.log('Permission ID:', data.permissionId); }, }); }; return ( ); } ``` ```typescript [Core SDK] import { parseUnits, type Address } from 'viem'; const SERVICE_SPENDER: Address = '0x...'; // Your backend wallet const USDC_ADDRESS: Address = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base async function subscribe(userAddress: Address, planPrice: string) { const allowance = parseUnits(planPrice, 6); const result = await jaw.provider.request({ method: 'wallet_grantPermissions', params: [{ address: userAddress, expiry: Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60, spender: SERVICE_SPENDER, permissions: { spends: [{ token: USDC_ADDRESS, allowance: `0x${allowance.toString(16)}`, unit: 'month', multiplier: 1, }], calls: [{ target: USDC_ADDRESS, functionSignature: 'transfer(address,uint256)', }], }, }], }); const permissionId = (result as { permissionId: string }).permissionId; // Store permissionId in your backend! return permissionId; } ``` ::: :::info **Store the Permission ID**: The `permissionId` returned is required to execute charges. Save it to your backend database. ::: #### 2. Execute Charge (Server Side) Your backend service charges users using their stored permission IDs. This typically runs as a cron job or scheduled task - no user approval needed since the permission authorizes the charge. ```typescript import { Account } from '@jaw.id/core'; import { privateKeyToAccount } from 'viem/accounts'; import { encodeFunctionData, parseUnits, type Address } from 'viem'; const USDC_ADDRESS: Address = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base const SERVICE_TREASURY: Address = '0x...'; // Your treasury address // Load the spender's private key from environment const spenderAccount = privateKeyToAccount(process.env.SPENDER_PRIVATE_KEY as `0x${string}`); async function chargeSubscription(permissionId: string, amount: string) { // Create account instance from the spender's private key const account = await Account.fromLocalAccount( { chainId: 8453, apiKey: process.env.JAW_API_KEY! }, spenderAccount ); // Transfer USDC from user to your treasury const { id } = await account.sendCalls( [{ to: USDC_ADDRESS, data: encodeFunctionData({ abi: [{ name: 'transfer', type: 'function', inputs: [ { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' } ], outputs: [{ type: 'bool' }] }], functionName: 'transfer', args: [SERVICE_TREASURY, parseUnits(amount, 6)], }), }], { permissionId: permissionId as `0x${string}` } ); return id; // user operation ID } ``` #### 3. Cancel Subscription Users can cancel anytime by revoking the permission. Once revoked, the spender can no longer execute charges. :::code-group ```tsx [Wagmi] import { useRevokePermissions } from '@jaw.id/wagmi'; function CancelButton({ permissionId }: { permissionId: string }) { const { mutate: revokePermission, isPending } = useRevokePermissions(); return ( ); } ``` ```typescript [Core SDK] async function cancelSubscription(userAddress: Address, permissionId: string) { await jaw.provider.request({ method: 'wallet_revokePermissions', params: [{ address: userAddress, id: permissionId as `0x${string}`, }], }); } ``` ::: :::info **Listing Permissions**: Use `usePermissions()` (Wagmi) or `wallet_getPermissions` (Core SDK) to retrieve all active permissions for a user. ::: ### Permission Parameters Reference | Parameter | Purpose | Example | | ------------------------------------- | ----------------------------------------- | --------------------------- | | `spender` | Address authorized to use this permission | Your backend wallet | | `expiry` | Unix timestamp when permission expires | 1 year from now | | `permissions.spends.token` | Token address to spend | USDC address | | `permissions.spends.allowance` | Max amount per period | $10 (10000000 for USDC) | | `permissions.spends.unit` | Time period | `month` | | `permissions.calls.target` | Contract that can be called | USDC contract address | | `permissions.calls.functionSignature` | Function that can be called | `transfer(address,uint256)` | #### Time Units | Unit | Description | | -------- | ---------------------- | | `minute` | Resets every minute | | `hour` | Resets every hour | | `day` | Resets every 24 hours | | `week` | Resets every 7 days | | `month` | Resets every \~30 days | | `year` | Resets every 365 days | ### Related * [useGrantPermissions](/wagmi/useGrantPermissions) - Wagmi hook reference * [useRevokePermissions](/wagmi/useRevokePermissions) - Cancel subscriptions * [wallet\_grantPermissions](/api-reference/wallet_grantPermissions) - RPC method reference ## apiKey Your JAW API key for authentication with JAW services. **Type:** `string` **Required:** Yes ### Usage ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key-here', }); ``` ```typescript // EIP-1193 Provider import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key-here', }); ``` ### How to Get an API Key 1. Visit the [JAW Dashboard](https://dashboard.jaw.id/) 2. Create a new project or select an existing one 3. Navigate to the API Keys section 4. Generate a new API key ### Setting Up Allowed Domains For security, you must configure which domains are allowed to use your API key: 1. In the [JAW Dashboard](https://dashboard.jaw.id/), go to your api-key settings 2. Find the **Allowed Domains** section 3. Add the domains where your application will run: * `localhost` for local development * `yourdomain.com` for production * `staging.yourdomain.com` for staging environments Requests from domains not in your allowed list will be rejected. ### Related Configuration * [appName](/configuration/appName) - Application name * [preference](/configuration/preference) - Advanced options ## appLogoUrl URL to your application's logo image. Displayed alongside your app name in the authentication interface. **Type:** `string | null` **Required:** No **Default:** `null` (uses default icon) ### Usage ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', appName: 'My DApp', appLogoUrl: 'https://my-dapp.com/logo.png', }); ``` ```typescript // EIP-1193 Provider import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', appName: 'My DApp', appLogoUrl: 'https://my-dapp.com/logo.png', }); ``` ### Where It Appears Your app logo is shown to users in: * Authentication popup header * Transaction approval screens * Permission grant dialogs ### Image Requirements * **Format:** PNG, JPG, or SVG * **Size:** 200x200px minimum (square recommended) * **Protocol:** Must use HTTPS ### Related Configuration * [appName](/configuration/appName) - Application name * [apiKey](/configuration/apiKey) - API key for authentication ## appName The name of your application, displayed to users during authentication and transaction signing. **Type:** `string` **Required:** No **Default:** `'DApp'` ### Usage ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', appName: 'My Awesome DApp', }); ``` ```typescript // EIP-1193 Provider import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', appName: 'My Awesome DApp', }); ``` ### Where It Appears Your app name is shown to users in: * Authentication popup header * Transaction approval screens * Permission grant dialogs ### Related Configuration * [appLogoUrl](/configuration/appLogoUrl) - Application logo * [apiKey](/configuration/apiKey) - API key for authentication ## defaultChainId The default blockchain network to connect to when the SDK initializes. **Type:** `number` **Required:** No **Default:** `1` (Ethereum Mainnet) ### Usage ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', defaultChainId: 1, // Mainnet }); ``` ```typescript // EIP-1193 Provider import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', defaultChainId: 1, // Mainnet }); ``` ### Using Testnets To use a testnet as the default chain, enable `showTestnets` in preferences: ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', defaultChainId: 84532, // Base Sepolia preference: { showTestnets: true, }, }); ``` ```typescript // EIP-1193 Provider import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', defaultChainId: 84532, // Base Sepolia preference: { showTestnets: true, }, }); ``` ### Related * [Supported Networks](/supported-networks) - Full list of supported chains * [preference](/configuration/preference) - Advanced options including testnet visibility * [wallet\_switchEthereumChain](/api-reference/wallet_switchEthereumChain) - Switch networks programmatically ## ens ENS domain used to issue subnames to users during authentication. **Type:** `string` **Required:** No :::warning **Important:** Configuring your ENS at the [JAW Dashboard](https://dashboard.jaw.id/) is a pre-requirement for subname issuance and management. ::: ### Usage ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', ens: 'myapp.eth', }); ``` ```typescript // EIP-1193 Provider import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', ens: 'myapp.eth', }); ``` ### Description When configured, users can receive a subname under your ENS domain during the authentication flow. For example, if your `ens` is set to `myapp.eth`, users can get subnames like `alice.myapp.eth`. By default, subname will be active on all supported networks. To attach text records on creation, please check the [wallet\_connect](/api-reference/wallet_connect) `subnameTextRecords` capability. ### Related Configuration * [apiKey](/configuration/apiKey) - API key for authentication * [wallet\_connect](/api-reference/wallet_connect) - Add subname text records using wallet\_connect capability ## Configuration Configuration options for the JAW SDK. These options apply to both the **Wagmi connector** and the **direct provider**. ### Usage #### With Wagmi (Recommended) ```typescript import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', appName: 'My DApp', appLogoUrl: 'https://my-dapp.com/logo.png', defaultChainId: 1, ens: 'myapp.eth', preference: { showTestnets: true, }, paymasters: { 1: { url: 'https://paymaster.example.com/mainnet' }, }, }); ``` #### With Provider Directly ```typescript import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', appName: 'My DApp', appLogoUrl: 'https://my-dapp.com/logo.png', defaultChainId: 1, ens: 'myapp.eth', preference: { showTestnets: true, }, paymasters: { 1: { url: 'https://paymaster.example.com/mainnet' }, }, }); ``` ### Configuration Options #### Core Parameters | Parameter | Type | Required | Description | | ----------------------------------------------- | -------------------------------------------------------------------- | -------- | ---------------------------------------- | | [apiKey](/configuration/apiKey) | `string` | Yes | API key for JAW services authentication | | [appName](/configuration/appName) | `string` | No | Application name displayed to users | | [appLogoUrl](/configuration/appLogoUrl) | `string \| null` | No | URL to application logo image | | [ens](/configuration/ens) | `string` | No | ENS domain for issuing subnames | | [defaultChainId](/configuration/defaultChainId) | `number` | No | Default blockchain network | | [preference](/configuration/preference) | `object` | No | Advanced SDK behavior options | | [paymasters](/configuration/paymasters) | `Record }>` | No | Custom paymaster configuration per chain | ### Preference Options The `preference` object contains advanced configuration: | Option | Type | Default | Description | | ------------ | ------------------------------------------ | -------------------- | --------------------------------------- | | mode | `Mode.CrossPlatform` \| `Mode.AppSpecific` | `Mode.CrossPlatform` | Authentication mode | | serverUrl | `string` | JAW Server | Custom passkey server URL | | showTestnets | `boolean` | `false` | Include testnet networks | | uiHandler | `UIHandler` | `undefined` | Custom UI handler for app-specific mode | [See all preference options →](/configuration/preference) ### Return Value #### Wagmi Connector The `jaw()` function returns a Wagmi `Connector` that can be used in your wagmi config. ```typescript import { createConfig } from 'wagmi'; import { jaw } from '@jaw.id/wagmi'; const config = createConfig({ connectors: [jaw({ apiKey: 'your-api-key' })], // ... }); ``` #### Provider The `JAW.create()` function returns an object with: ##### provider **Type:** `ProviderInterface` An EIP-1193 compatible Ethereum provider for making RPC requests. ```typescript const jaw = JAW.create({ apiKey: 'your-api-key' }); const accounts = await jaw.provider.request({ method: 'wallet_connect', }); ``` ##### disconnect() **Type:** `() => Promise` Method to disconnect the current session and clean up resources. ```typescript await jaw.disconnect(); ``` ### Minimal Configuration The only required option is `apiKey`: ```typescript // Wagmi const connector = jaw({ apiKey: 'your-api-key' }); // Provider const jaw = JAW.create({ apiKey: 'your-api-key' }); ``` ### Related * [Wagmi Integration](/wagmi) - Using with Wagmi * [Provider API](/api-reference) - Direct provider methods * [Supported Networks](/supported-networks) - Available chains ## paymasters Custom paymaster configuration for sponsoring gas fees on different blockchain networks. **Type:** `Record }>` **Required:** No **Default:** `undefined` ### Usage ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', paymasters: { 1: { url: 'https://paymaster.example.com/mainnet' }, 8453: { url: 'https://paymaster.example.com/base', context: { sponsorshipPolicyId: 'sp_my_policy' } }, }, }); ``` ```typescript // EIP-1193 Provider import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', paymasters: { 1: { url: 'https://paymaster.example.com/mainnet' }, 8453: { url: 'https://paymaster.example.com/base', context: { sponsorshipPolicyId: 'sp_my_policy' } }, }, }); ``` ### Description Paymasters are services that sponsor transaction gas fees, enabling gasless transactions for your users. The `paymasters` configuration allows you to specify custom paymaster endpoints and context for each supported network. **Benefits:** * **Gasless Transactions**: Users don't need native tokens to pay gas * **Better UX**: Remove friction from onboarding * **Flexible Sponsorship**: Control which transactions to sponsor via context/policies * **Cost Management**: Use your own paymaster infrastructure ### Important Requirements #### EntryPoint Version JAW SDK **only supports EntryPoint v0.8** (ERC-4337 v0.8). Your paymaster service must be compatible with: * [EntryPoint v0.8](https://github.com/eth-infinitism/account-abstraction/releases/tag/v0.8.0) * [EIP-7677](https://eips.ethereum.org/EIPS/eip-7677) Paymaster Web Service Capability #### Compatible Paymaster Providers Works with any EIP-7677 compliant paymaster that supports EntryPoint v0.8: * **[Pimlico](https://dashboard.pimlico.io/)** * **[Etherspot](https://developer.etherspot.io/dashboard)** ### Configuration Format ```typescript paymasters: { [chainId: number]: { url: string; // Paymaster service URL (required) context?: Record; // Additional context sent with requests (optional) } } ``` #### Properties | Property | Type | Required | Description | | --------- | ------------------------- | -------- | ------------------------------------------------------------------------ | | `url` | `string` | Yes | The paymaster service endpoint URL | | `context` | `Record` | No | Additional context passed to the paymaster (e.g., sponsorship policy ID) | ### Examples #### Basic Configuration ```typescript import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', paymasters: { 8453: { url: 'https://paymaster.etherspot.io/base' }, }, }); ``` #### With Sponsorship Policy ```typescript import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', paymasters: { 8453: { url: 'https://paymaster.etherspot.io/base', context: { sponsorshipPolicyId: 'sp_my_policy_id' } }, }, }); ``` #### Multiple Networks ```typescript import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', paymasters: { 1: { url: 'https://pm.etherspot.io/eth' }, // Ethereum Mainnet 10: { url: 'https://pm.etherspot.io/op' }, // Optimism 8453: { url: 'https://pm.etherspot.io/base' }, // Base 42161: { url: 'https://pm.etherspot.io/arb' }, // Arbitrum 84532: { url: 'https://api.pimlico.io/v2/84532/rpc?apikey=YOUR_API_KEY', // Base Sepolia context: { sponsorshipPolicyId: 'sp_test_policy' } }, }, }); ``` ### Paymaster Services #### Recommended Provider * **[Etherspot](https://developer.etherspot.io/dashboard)** * **[Pimlico](https://dashboard.pimlico.io/)** #### Other Compatible Providers Ensure any provider you use supports: * EntryPoint v0.8 * EIP-7677 Paymaster Web Service Capability ### Related * [eth\_sendTransaction](/api-reference/eth_sendTransaction) - Sponsor Transactions * [wallet\_sendCalls](/api-reference/wallet_sendCalls) - Sponsor Calls Bundle ## preference Advanced configuration options to customize SDK behavior and authentication mode. **Type:** `object` **Required:** No **Default:** See individual options below ### Usage ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; import { Mode } from '@jaw.id/core'; import { ReactUIHandler } from '@jaw.id/ui'; const connector = jaw({ apiKey: 'your-api-key', preference: { mode: Mode.AppSpecific, uiHandler: new ReactUIHandler(), showTestnets: true, }, }); ``` ```typescript // EIP-1193 Provider import { JAW, Mode } from '@jaw.id/core'; import { ReactUIHandler } from '@jaw.id/ui'; const jaw = JAW.create({ apiKey: 'your-api-key', preference: { mode: Mode.AppSpecific, uiHandler: new ReactUIHandler(), showTestnets: true, }, }); ``` ### Options #### mode Authentication mode that determines where passkey operations occur. **Type:** `Mode.CrossPlatform` | `Mode.AppSpecific` **Default:** `Mode.CrossPlatform` ##### Mode Comparison | Feature | Mode.CrossPlatform | Mode.AppSpecific | | ---------------------- | ------------------------ | ------------------- | | **Passkey operations** | On `keys.jaw.id` (popup) | Within your dApp | | **User experience** | Redirects to popup | Stays in your app | | **Wallet reuse** | ✅ Universal | ❌ App-specific only | | **Origin binding** | `keys.jaw.id` | Your domain | | **Branding** | JAW interface | Can use custom UI | :::warning **Important:** Passkeys are always stored by the browser/device (not on any server). The mode determines where passkey operations happen and whether wallets can be reused across applications. ::: ##### CrossPlatform Mode Passkey operations redirect to `keys.jaw.id` in a popup, enabling wallet reuse across multiple applications. ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; import { Mode } from '@jaw.id/core'; const connector = jaw({ apiKey: 'your-api-key', preference: { mode: Mode.CrossPlatform, }, }); ``` ```typescript // EIP-1193 Provider import { JAW, Mode } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', preference: { mode: Mode.CrossPlatform, }, }); ``` **How it works:** * User clicks "Connect" → Popup opens to `keys.jaw.id` * Passkey operations happen on `keys.jaw.id` origin * Wallet can be reused across any app using CrossPlatform mode * Users see consistent experience across applications **Benefits:** * Single wallet across multiple dApps * Seamless cross-application experience * Users don't recreate wallets for each app * Consistent authentication flow ##### AppSpecific Mode Passkey operations stay within your dApp (no redirect). Wallets are specific to your application's origin. **How it works:** * User clicks "Connect" → Modal opens within your dApp * Passkey operations happen on your dApp's origin * Passkeys are tied to your domain * Users never leave your application **Benefits:** * Users never leave your dApp * Full control over UI/UX * White-label experience * No external redirects ##### Using ReactUIHandler (Recommended for React) For React applications, install the `@jaw.id/ui` package which provides pre-built UI dialogs: :::code-group ```bash [npm] npm install @jaw.id/ui @jaw.id/core ``` ```bash [pnpm] pnpm add @jaw.id/ui @jaw.id/core ``` ```bash [yarn] yarn add @jaw.id/ui @jaw.id/core ``` ```bash [bun] bun add @jaw.id/ui @jaw.id/core ``` ::: Then configure the SDK with `ReactUIHandler`: ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; import { Mode } from '@jaw.id/core'; import { ReactUIHandler } from '@jaw.id/ui'; const connector = jaw({ apiKey: 'your-api-key', preference: { mode: Mode.AppSpecific, uiHandler: new ReactUIHandler(), }, }); ``` ```typescript // EIP-1193 Provider import { JAW, Mode } from '@jaw.id/core'; import { ReactUIHandler } from '@jaw.id/ui'; const jaw = JAW.create({ apiKey: 'your-api-key', preference: { mode: Mode.AppSpecific, uiHandler: new ReactUIHandler(), }, }); ``` ##### Custom UI Implementation For non-React apps or fully custom UI, see the [Custom UI Handler](/advanced/custom-ui-handler) documentation. #### serverUrl URL to a passkey metadata server for managing passkey credentials (credential IDs, public keys, display names). **Type:** `string` **Default:** `'https://api.justaname.id/wallet/v2/passkeys'` **Only works with:** `Mode.AppSpecific` ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; import { Mode } from '@jaw.id/core'; import { ReactUIHandler } from '@jaw.id/ui'; const connector = jaw({ apiKey: 'your-api-key', preference: { mode: Mode.AppSpecific, uiHandler: new ReactUIHandler(), serverUrl: 'https://custom-backend.example.com/passkeys', }, }); ``` ```typescript // EIP-1193 Provider import { JAW, Mode } from '@jaw.id/core'; import { ReactUIHandler } from '@jaw.id/ui'; const jaw = JAW.create({ apiKey: 'your-api-key', preference: { mode: Mode.AppSpecific, uiHandler: new ReactUIHandler(), serverUrl: 'https://custom-backend.example.com/passkeys', }, }); ``` :::warning **Important:** This is NOT for storing passkeys themselves (passkeys are always stored by the browser/device). The server stores metadata about passkeys to enable passkey lookups and management. ::: For details on running your own passkey server, see the [Custom Passkey Server](/advanced/passkey-server) documentation. #### showTestnets Whether to include testnet networks in the available chains list. **Type:** `boolean` **Default:** `false` ```typescript // Wagmi import { jaw } from '@jaw.id/wagmi'; const connector = jaw({ apiKey: 'your-api-key', preference: { showTestnets: true, }, }); ``` ```typescript // EIP-1193 Provider import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', preference: { showTestnets: true, }, }); ``` #### uiHandler Custom UI handler for rendering approval dialogs in app-specific mode. When provided, the SDK will use this handler instead of redirecting to external popups. **Type:** `UIHandler` **Default:** `undefined` **Only works with:** `Mode.AppSpecific` :::info For React applications, use `ReactUIHandler` from `@jaw.id/ui`. See the [AppSpecific Mode](#appspecific-mode) section above for setup instructions. ::: For custom UI implementations (non-React or advanced use cases), see the [Custom UI Handler](/advanced/custom-ui-handler) documentation. ### Related Configuration * [apiKey](/configuration/apiKey) - API key for authentication * [defaultChainId](/configuration/defaultChainId) - Default network selection ## eth\_accounts Get currently connected accounts without triggering authentication. Returns an empty array if no accounts are connected. **Authentication Required:** No ### Request ```typescript await jaw.provider.request({ method: 'eth_accounts', }); ``` #### Parameters None ### Response Returns an array of connected account addresses. Returns an empty array if not authenticated. **Type:** `0x${string}[]` #### Example (Connected) ```typescript ["0x1234567890123456789012345678901234567890"] ``` #### Example (Not Connected) ```typescript [] ``` ### Behavior * Returns empty array if not authenticated * Returns cached accounts if already connected * Does not trigger authentication popup ### Example ```typescript import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', }); const accounts = await jaw.provider.request({ method: 'eth_accounts', }); ``` ### Related Methods * [eth\_requestAccounts](/api-reference/eth_requestAccounts) - Request authentication ## eth\_chainId Get the current chain ID in hexadecimal format. **Authentication Required:** No (returns default chain if not authenticated) ### Request ```typescript await jaw.provider.request({ method: 'eth_chainId', }); ``` #### Parameters None ### Response Returns the chain ID as a hexadecimal string. **Type:** `0x${string}` #### Example ```json "0x1" ``` ### Behavior * Returns default chain ID if not authenticated * Returns current active chain ID if authenticated * Updates when chain is switched via `wallet_switchEthereumChain` ### Example Usage ```typescript import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', }); const chainId = await jaw.provider.request({ method: 'eth_chainId', }); ``` ### Related Methods * [wallet\_switchEthereumChain](/api-reference/wallet_switchEthereumChain) - Switch chains ## eth\_requestAccounts Request user authentication and account access. **Authentication Required:** No (triggers authentication) ### Request ```typescript await jaw.provider.request({ method: 'eth_requestAccounts', }); ``` #### Parameters None ### Response Returns an array of account addresses. **Type:** `0x${string}[]` #### Example ```typescript ["0x1234567890123456789012345678901234567890"] ``` ### Behavior * Opens authentication popup (in CrossPlatform mode) * User creates a new account or signs in with an existing one * Returns array with single account address * Emits `accountsChanged` event with the connected accounts * Subsequent calls return cached accounts without opening popup ### Errors | Code | Description | | ---- | ------------------------------------ | | 4001 | User rejected the request | | 4100 | Unauthorized (authentication failed) | ### Example ```typescript import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'your-api-key', }); const accounts = await jaw.provider.request({ method: 'eth_requestAccounts', }); ``` ### Related Methods * [eth\_accounts](/api-reference/eth_accounts) - Get accounts without triggering authentication * [wallet\_connect](/api-reference/wallet_connect) - Connect with advanced capabilities (SIWE, subnames) ## eth\_sendTransaction Broadcast a transaction to the network. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'eth_sendTransaction', params: [{ to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', value: '0x9184e72a000', data: '0x', }], }); ``` #### Parameters | Name | Type | Required | Description | | --------- | ------------- | -------- | --------------------------------------------- | | `to` | `0x${string}` | Yes | Recipient address | | `value` | `0x${string}` | No | Amount in wei (hexadecimal) | | `data` | `0x${string}` | No | Transaction data (hexadecimal) | | `chainId` | `0x${string}` | No | Target chain ID (defaults to connected chain) | #### Example ```typescript [{ "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "value": "0x9184e72a000", "data": "0x" }] ``` ### Response Returns the transaction hash after the transaction is submitted. **Type:** `0x${string}` #### Example ```typescript "0xabc123def456..." ``` ### Behavior * Opens popup for user approval * Uses ERC-4337 UserOperation under the hood * Transaction is sponsored by paymaster if configured * Returns transaction hash after submission ### Errors | Code | Description | | ------ | -------------------------------- | | 4001 | User rejected the request | | 4100 | Unauthorized (not authenticated) | | -32602 | Invalid params | ### Example Usage #### Send Native Token (ETH) ```typescript const txHash = await jaw.provider.request({ method: 'eth_sendTransaction', params: [{ to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', value: '0xde0b6b3a7640000', // 1 ETH in wei (hex) }], }); console.log('Transaction hash:', txHash); ``` #### Call Contract Function ```typescript import { encodeFunctionData } from 'viem'; // Encode ERC-20 transfer function const data = encodeFunctionData({ abi: [{ name: 'transfer', type: 'function', inputs: [ { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }, ], }], functionName: 'transfer', args: ['0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', 1000000n], }); const txHash = await jaw.provider.request({ method: 'eth_sendTransaction', params: [{ to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', data, }], }); ``` ### Related Methods * [wallet\_sendCalls](/api-reference/wallet_sendCalls) - Send multiple transactions atomically ## eth\_signTypedData\_v4 Sign structured typed data via EIP-712. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'eth_signTypedData_v4', params: [ '0x1234...', JSON.stringify(typedData), ], }); ``` #### Parameters | Name | Type | Required | Description | | ----------- | ------------- | -------- | ----------------------------------- | | `address` | `0x${string}` | Yes | Address to sign with | | `typedData` | `string` | Yes | JSON stringified EIP-712 typed data | #### TypedData Structure The typed data must follow EIP-712 format: ```typescript { types: { EIP712Domain: [...], [primaryType]: [...], }, primaryType: string, domain: { name: string, version: string, chainId: number, verifyingContract?: string, }, message: object, } ``` #### Example ```json [ "0x1234567890123456789012345678901234567890", "..." ] ``` ### Response Returns the signature as a hexadecimal string. **Type:** `0x${string}` #### Example ```typescript "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab" ``` ### Behavior * Opens popup showing structured data in human-readable format * Validates EIP-712 format before signing * Shows domain, message types, and values to user ### Errors | Code | Description | | ------ | --------------------------------------- | | 4001 | User rejected the request | | 4100 | Unauthorized (not authenticated) | | -32602 | Invalid params (malformed EIP-712 data) | ### Example ```typescript const typedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, ], Mail: [ { name: 'from', type: 'Person' }, { name: 'to', type: 'Person' }, { name: 'contents', type: 'string' }, ], Person: [ { name: 'name', type: 'string' }, { name: 'wallet', type: 'address' }, ], }, primaryType: 'Mail', domain: { name: 'My DApp', version: '1', chainId: 1, }, message: { from: { name: 'Alice', wallet: '0x1234...', }, to: { name: 'Bob', wallet: '0x5678...', }, contents: 'Hello Bob!', }, }; const signature = await jaw.provider.request({ method: 'eth_signTypedData_v4', params: [account, JSON.stringify(typedData)], }); ``` ### Related Methods * [personal\_sign](/api-reference/personal_sign) - Sign plain messages * [wallet\_sign](/api-reference/wallet_sign) - Generic signing method ## Provider - RPC Reference Complete reference for all RPC methods supported by the JAW SDK provider. ### Installation :::code-group ```bash [npm] npm install @jaw.id/core viem ``` ```bash [pnpm] pnpm add @jaw.id/core viem ``` ```bash [yarn] yarn add @jaw.id/core viem ``` ```bash [bun] bun add @jaw.id/core viem ``` ::: ### Setup ```typescript import { JAW } from '@jaw.id/core'; const jaw = JAW.create({ apiKey: 'YOUR_API_KEY', appName: 'My App', }); ``` ### Making Requests All requests are made through the provider's `request` method: ```typescript const result = await jaw.provider.request({ method: 'method_name', params: [...], }); ``` ### Provider Events The provider emits events following the EIP-1193 specification: ```typescript // Account changes jaw.provider.on('accountsChanged', (accounts: string[]) => { console.log('Accounts changed:', accounts); }); // Chain changes jaw.provider.on('chainChanged', (chainId: string) => { console.log('Chain changed:', chainId); }); // Connection jaw.provider.on('connect', (info: { chainId: string }) => { console.log('Connected to chain:', info.chainId); }); // Disconnection jaw.provider.on('disconnect', (error: ProviderRpcError) => { console.log('Disconnected:', error); }); ``` ### Account Methods Methods for managing user accounts and authentication. | Method | Description | Auth Required | | ---------------------------------------------------------- | ---------------------------------------------- | ------------- | | [eth\_requestAccounts](/api-reference/eth_requestAccounts) | Request user authentication and account access | No | | [eth\_accounts](/api-reference/eth_accounts) | Get currently connected accounts | No | ### Chain Methods Methods for querying and switching blockchain networks. | Method | Description | Auth Required | | ------------------------------------------------------------------------ | --------------------------- | ------------- | | [eth\_chainId](/api-reference/eth_chainId) | Get current chain ID (hex) | No | | [wallet\_switchEthereumChain](/api-reference/wallet_switchEthereumChain) | Switch to a different chain | Yes | ### Transaction Methods Methods for sending transactions and batch operations. | Method | Description | Auth Required | | ---------------------------------------------------------------- | ---------------------------------------- | ------------- | | [eth\_sendTransaction](/api-reference/eth_sendTransaction) | Broadcast a transaction to the network | Yes | | [wallet\_sendCalls](/api-reference/wallet_sendCalls) | Broadcast bundle of calls to the network | Yes | | [wallet\_showCallsStatus](/api-reference/wallet_showCallsStatus) | Show batch transaction status UI | Yes | | [wallet\_getCallsStatus](/api-reference/wallet_getCallsStatus) | Get batch transaction status | No | ### Signing Methods Methods for signing messages and typed data. | Method | Description | Auth Required | | ------------------------------------------------------------- | ---------------------------------------------------------- | ------------- | | [personal\_sign](/api-reference/personal_sign) | Sign a message with EIP-191 | Yes | | [eth\_signTypedData\_v4](/api-reference/eth_signTypedData_v4) | Sign structured typed data (EIP-712) | Yes | | [wallet\_sign](/api-reference/wallet_sign) | Unified signing method supporting multiple message formats | No\* | \*Can use ephemeral signer if not authenticated ### Wallet Methods Methods for wallet connection and lifecycle management. | Method | Description | Auth Required | | ------------------------------------------------------ | --------------------------------------------------- | ------------- | | [wallet\_connect](/api-reference/wallet_connect) | Connect with advanced capabilities (SIWE, subnames) | No | | [wallet\_disconnect](/api-reference/wallet_disconnect) | Disconnect current session | No | ### Capability Methods Methods for querying wallet capabilities. | Method | Description | Auth Required | | ---------------------------------------------------------------- | -------------------------------------------- | ------------- | | [wallet\_getCapabilities](/api-reference/wallet_getCapabilities) | Get wallet capabilities per chain (EIP-5792) | No | ### Permission Methods Methods for managing granular permissions for delegated transactions. | Method | Description | Auth Required | | -------------------------------------------------------------------- | --------------------------------------------- | ------------- | | [wallet\_grantPermissions](/api-reference/wallet_grantPermissions) | Grant call and spend permissions to a spender | Yes | | [wallet\_revokePermissions](/api-reference/wallet_revokePermissions) | Revoke previously granted permissions | Yes | | [wallet\_getPermissions](/api-reference/wallet_getPermissions) | Get all permissions for an account | No\* | \*Requires address parameter if not authenticated ### Asset Methods Methods for querying token balances. | Method | Description | Auth Required | | ---------------------------------------------------- | ------------------------------------------- | ------------- | | [wallet\_getAssets](/api-reference/wallet_getAssets) | Get token balances across chains (EIP-7811) | No\* | \*Requires address parameter ### Error Codes JAW SDK follows EIP-1193 error codes: | Code | Message | Description | | ------ | --------------------- | ----------------------- | | 4001 | User Rejected Request | User declined in popup | | 4100 | Unauthorized | Not authenticated | | 4200 | Unsupported Method | Method not supported | | 4900 | Disconnected | Provider disconnected | | 4901 | Chain Disconnected | Chain not available | | -32700 | Parse Error | Invalid JSON | | -32600 | Invalid Request | Invalid request object | | -32601 | Method Not Found | Method doesn't exist | | -32602 | Invalid Params | Invalid parameters | | -32603 | Internal Error | Internal JSON-RPC error | ## personal\_sign Signs an EIP-191 personal message. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'personal_sign', params: ['0x48656c6c6f20576f726c64', '0x1234...'], }); ``` #### Parameters | Name | Type | Required | Description | | --------- | ------------- | -------- | ----------------------------- | | `message` | `0x${string}` | Yes | Message to sign (hex encoded) | | `address` | `0x${string}` | Yes | Address to sign with | #### Example ```typescript ["0x48656c6c6f20576f726c64", "0x1234567890123456789012345678901234567890"] ``` ### Response Returns the signature as a hexadecimal string. **Type:** `0x${string}` #### Example ```typescript "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1c" ``` ### Behavior * Opens popup for user approval * Displays human-readable message to user * Returns EIP-191 signature with "\x19Ethereum Signed Message:\n" prefix ### Errors | Code | Description | | ------ | ---------------------------------------- | | 4001 | User rejected the request | | 4100 | Unauthorized (not authenticated) | | -32602 | Invalid params (message not hex encoded) | ### Example ```typescript import { toHex } from 'viem'; const message = 'Hello World'; const messageHex = toHex(message); const account = '0x1234...'; const signature = await jaw.provider.request({ method: 'personal_sign', params: [messageHex, account], }); console.log('Signature:', signature); ``` ### Related Methods * [eth\_signTypedData\_v4](/api-reference/eth_signTypedData_v4) - Sign structured data (EIP-712) * [wallet\_sign](/api-reference/wallet_sign) - Generic signing method ## wallet\_connect Requests to connect account(s) with optional capabilities. **Authentication Required:** No (triggers authentication) ### Request ```typescript await jaw.provider.request({ method: 'wallet_connect', params: [{ capabilities: { signInWithEthereum: { nonce: 'abc123xyz789', chainId: '0x1', domain: 'my-dapp.com', uri: 'https://my-dapp.com', statement: 'Sign in to My DApp with your JAW account' }, subnameTextRecords: [ { key: 'avatar', value: 'https://my-dapp.com/avatars/1.png' }, { key: 'description', value: 'Premium member' } ] } }], }); ``` #### Parameters | Name | Type | Required | Description | | -------------- | -------- | -------- | ---------------------- | | `capabilities` | `object` | No | Requested capabilities | #### Capabilities ##### signInWithEthereum | Name | Type | Required | Description | | ---------------- | -------- | -------- | --------------------------- | | `nonce` | `string` | Yes | Random nonce for SIWE | | `chainId` | `string` | Yes | Chain ID (hex format) | | `domain` | `string` | No | Domain requesting signature | | `uri` | `string` | No | URI of the application | | `statement` | `string` | No | Human-readable statement | | `version` | `string` | No | SIWE version (default: "1") | | `issuedAt` | `string` | No | ISO 8601 timestamp | | `expirationTime` | `string` | No | ISO 8601 expiration | ##### subnameTextRecords Array of key-value pairs to attach as ENS text records to the user's subname on account creation. **Note:** This capability is only used if an ENS domain is configured in the SDK preferences (via the `ens` option). If no ENS domain is configured, these records are disregarded. | Name | Type | Description | | ------- | -------- | --------------------------------------- | | `key` | `string` | Text record key (e.g., "avatar", "url") | | `value` | `string` | Text record value | ### Response Returns connection result with accounts and capabilities. **Type:** `object` #### Example ```json { "accounts": [{ "address": "0x1234567890123456789012345678901234567890", "capabilities": { "signInWithEthereum": { "message": "my-dapp.com wants you to sign in with your Ethereum account:\n0x1234...", "signature": "0xabcdef..." } } }] } ``` ### Behavior * Opens authentication popup * User creates account or signs in with existing account * If `signInWithEthereum` capability is requested: * Generates and signs a SIWE message with the provided parameters * Returns the signed message in the response capabilities * If `subnameTextRecords` capability is provided and `ens` is configured in SDK preferences: * Attaches the provided text records to the subname * Returns accounts with capability responses ### Errors | Code | Description | | ------ | ------------------------------------ | | 4001 | User rejected the request | | 4100 | Unauthorized (authentication failed) | | -32602 | Invalid params | ### Example ```typescript const result = await jaw.provider.request({ method: 'wallet_connect', params: [{}], }); ``` ### Related Methods * [eth\_requestAccounts](/api-reference/eth_requestAccounts) - Simple authentication without capabilities ## wallet\_disconnect Disconnect the current session and clean up all authentication state. **Authentication Required:** No ### Request ```typescript await jaw.provider.request({ method: 'wallet_disconnect', }); ``` #### Parameters None ### Response Returns `null` on success. **Type:** `null` ### Behavior * Clears the active session and authentication state * Emits `disconnect` event * Same effect as calling `jaw.disconnect()` ### Example Usage #### Disconnect Button ```typescript await jaw.provider.request({ method: 'wallet_disconnect', }); ``` #### Listen to Disconnect Event ```typescript // Listen for disconnect events jaw.provider.on('disconnect', (error) => { console.log('Wallet disconnected:', error.message); }); await jaw.provider.request({ method: 'wallet_disconnect', }); ``` ### Related Methods * [eth\_requestAccounts](/api-reference/eth_requestAccounts) - Reconnect wallet * [wallet\_connect](/api-reference/wallet_connect) - Connect with capabilities ## wallet\_getAssets Get token balances across multiple chains for an account. Implements EIP-7811. **Authentication Required:** No (requires account parameter) ### Request ```typescript await jaw.provider.request({ method: 'wallet_getAssets', params: [{ account: '0x1234...', assetFilter: { '0x1': [{ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', type: 'erc20' }], }, }], }); ``` #### Parameters | Name | Type | Required | Description | | ----------------- | ------------- | -------- | --------------------------------------------- | | `account` | `string` | Yes | Account address to query | | `chainFilter` | `string[]` | No | Limit to specific chains (hex chain IDs) | | `assetTypeFilter` | `AssetType[]` | No | Filter by asset type: `'native'` or `'erc20'` | | `assetFilter` | `AssetFilter` | No | Filter by specific assets per chain | #### Types ```typescript type AssetType = 'native' | 'erc20'; type AssetFilter = Record<`0x${string}`, AssetFilterEntry[]>; type AssetFilterEntry = { address: `0x${string}`; type: AssetType; }; ``` #### Example ```json [{ "account": "0x1234567890123456789012345678901234567890", "chainFilter": ["0x1", "0x89"] }] ``` With asset type filter (ERC-20 tokens only): ```json [{ "account": "0x1234567890123456789012345678901234567890", "assetTypeFilter": ["erc20"] }] ``` With specific asset filter: ```json [{ "account": "0x1234567890123456789012345678901234567890", "assetFilter": { "0x1": [{ "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "type": "erc20" }], "0x2105": [{ "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "type": "erc20" }] } }] ``` ### Response Returns an object mapping chain IDs to asset arrays. **Type:** `WalletGetAssetsResponse` ```typescript type WalletGetAssetsResponse = { [chainId: string]: Asset[]; }; type Asset = { /** Token contract address, null for native tokens */ address: string | null; /** Balance in hex format */ balance: string; /** Asset metadata */ metadata: { decimals: number; name: string; symbol: string; } | null; /** Asset type */ type: 'native' | 'erc20'; }; ``` #### Example ```json { "0x1": [ { "address": null, "balance": "0x1bc16d674ec80000", "metadata": { "decimals": 18, "name": "Ether", "symbol": "ETH" }, "type": "native" }, { "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "balance": "0x5f5e100", "metadata": { "decimals": 6, "name": "USD Coin", "symbol": "USDC" }, "type": "erc20" } ], "0x89": [ { "address": null, "balance": "0x2b5e3af16b1880000", "metadata": { "decimals": 18, "name": "POL", "symbol": "POL" }, "type": "native" } ] } ``` #### Asset Object Fields | Field | Type | Description | | ---------- | --------------------- | ------------------------------------------------ | | `address` | `string \| null` | Token contract address, `null` for native tokens | | `balance` | `string` | Balance in smallest unit (wei/raw), hex format | | `metadata` | `object \| null` | Token metadata (decimals, name, symbol) | | `type` | `'native' \| 'erc20'` | Asset type | ### Behavior * Includes native tokens (ETH, MATIC, etc.) * Includes ERC-20 tokens with non-zero balances * Auto-filters by `showTestnets` SDK preference * Can be called without authentication * Returns only tokens with non-zero balances ### Example #### Get All Assets ```typescript const assets = await jaw.provider.request({ method: 'wallet_getAssets', params: [{ account: '0x1234...', }], }); ``` #### Filter by Asset Type ```typescript // Only get ERC-20 tokens const tokens = await jaw.provider.request({ method: 'wallet_getAssets', params: [{ account: '0x1234...', assetTypeFilter: ['erc20'], }], }); ``` #### Filter by Specific Assets ```typescript // Get only USDC on mainnet and Base const usdc = await jaw.provider.request({ method: 'wallet_getAssets', params: [{ account: '0x1234...', assetFilter: { '0x1': [{ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', type: 'erc20' }], '0x2105': [{ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', type: 'erc20' }], }, }], }); ``` ### Related Methods * [eth\_getBalance](/api-reference) (Proxied) - Get native balance for single chain * [wallet\_getCapabilities](/api-reference/wallet_getCapabilities) - Check supported chains ## wallet\_getCallsStatus Get the status of a batch of calls. **Authentication Required:** No ### Request ```typescript await jaw.provider.request({ method: 'wallet_getCallsStatus', params: ['0x123abc...'], }); ``` #### Parameters | Name | Type | Required | Description | | --------- | -------- | -------- | ---------------------------------------- | | `batchId` | `string` | Yes | Batch ID returned from wallet\_sendCalls | #### Example ```json ["0x123abc456def..."] ``` ### Response Returns detailed status information about the batch execution. #### Example ```json { "id": "0x123abc...", "chainId": "0x1", "status": 200, "atomic": true, "receipts": [{ "logs": [{ "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "data": "0x...", "topics": ["0x..."] }], "status": "0x1", "blockHash": "0x...", "blockNumber": "0x...", "gasUsed": "0x...", "transactionHash": "0x..." }] } ``` #### Response Fields | Field | Type | Description | | ---------- | --------- | ------------------------------------- | | `id` | `string` | Batch identifier | | `chainId` | `string` | Chain ID (hex) | | `status` | `number` | Status code (see below) | | `atomic` | `boolean` | Whether calls are atomic | | `receipts` | `array` | Transaction receipts (when completed) | #### Status Codes | Code | Status | Description | | ---- | ---------------- | --------------------------------------------------- | | 100 | Pending | Not yet on-chain | | 200 | Completed | Successfully executed on-chain | | 400 | Offchain Failure | Failed before submission (wallet won't retry) | | 500 | Onchain Revert | Reverted on-chain (has receipt with status 0x0) | | 600 | Partial Revert | Some calls reverted (not applicable for atomic ops) | ### Behavior * Can be called without authentication * Returns current status of the batch operation * Receipts included only when status is 200 or 500 * Background monitoring updates status automatically ### Example #### Poll for Completion ```typescript const result = await jaw.provider.request({ method: 'wallet_sendCalls', params: [{ version: '1.0', chainId: '0x1', from: account, calls: [...], }], }); // Poll until completed async function waitForCompletion(batchId: string) { while (true) { const status = await jaw.provider.request({ method: 'wallet_getCallsStatus', params: [batchId], }); if (status.status === 200) { console.log('Batch completed successfully!'); return status; } else if (status.status >= 400) { console.error('Batch failed:', status); throw new Error('Batch execution failed'); } // Still pending, wait and retry await new Promise(resolve => setTimeout(resolve, 2000)); } } await waitForCompletion(result.id); ``` ### Related Methods * [wallet\_sendCalls](/api-reference/wallet_sendCalls) - Send atomic batch of calls * [wallet\_showCallsStatus](/api-reference/wallet_showCallsStatus) - Show status UI ## wallet\_getCapabilities Get wallet capabilities for supported chains. Implements EIP-5792 for capability discovery. **Authentication Required:** No ### Request ```typescript await jaw.provider.request({ method: 'wallet_getCapabilities', params: ['0x1234...', ['0x1', '0x2105']], }); ``` #### Parameters | Name | Type | Required | Description | | ---------- | ---------- | -------- | ---------------------------------- | | `address` | `string` | No | Account address | | `chainIds` | `string[]` | No | Filter by specific chain IDs (hex) | #### Example ```json ["0x1234567890123456789012345678901234567890", ["0x1", "0x2105"]] ``` Or without filtering: ```json [] ``` ### Response Returns capabilities per chain as an object keyed by chain ID (hex). **Type:** `Record` #### Example ```json { "0x1": { "atomicBatch": { "supported": true }, "atomic": { "status": "supported" }, "paymasterService": { "supported": true }, "permissions": { "supported": true }, "feeToken": { "supported": true, "tokens": [ { "uid": "ethereum", "symbol": "ETH", "address": "0x0000000000000000000000000000000000000000", "interop": false, "decimals": 18, "feeToken": true } ] } }, "0x2105": { "atomicBatch": { "supported": true }, "atomic": { "status": "supported" }, "paymasterService": { "supported": true }, "permissions": { "supported": true }, "feeToken": { "supported": true, "tokens": [ { "uid": "ethereum", "symbol": "ETH", "address": "0x0000000000000000000000000000000000000000", "interop": true, "decimals": 18, "feeToken": true } ] } } } ``` ### Capabilities Explained #### atomicBatch **Value:** `{ supported: true }` Indicates support for `wallet_sendCalls` - the ability to send multiple transactions atomically (all succeed or all fail). #### atomic **Value:** `{ status: "supported" }` Indicates atomic transaction execution where all operations in a batch are guaranteed to succeed or fail together. #### paymasterService **Value:** `{ supported: true }` Indicates support for ERC-7677 paymaster service - gasless transactions sponsored by paymasters. #### permissions **Value:** `{ supported: true }` Indicates support for the permission system (`wallet_grantPermissions`, `wallet_revokePermissions`). #### feeToken **Value:** `{ supported: true, tokens: [...] }` Indicates the supported fee tokens for gas payments on each chain. | Field | Type | Description | | ------------------- | --------- | ------------------------------------------------------- | | `supported` | `boolean` | Whether fee token selection is supported | | `tokens` | `array` | List of available fee tokens | | `tokens[].uid` | `string` | Unique identifier for the token | | `tokens[].symbol` | `string` | Token symbol (e.g., "ETH", "USDC") | | `tokens[].address` | `string` | Token contract address (zero address for native token) | | `tokens[].interop` | `boolean` | Whether the token supports cross-chain interoperability | | `tokens[].decimals` | `number` | Token decimal places | | `tokens[].feeToken` | `boolean` | Whether this token can be used for gas fees | ### Behavior * Can be called without authentication * Returns capabilities for all supported chains by default * Filters by chain IDs if `chainIds` parameter provided * When `showTestnets` is disabled (default), only mainnet chains are returned ### Example ```typescript const capabilities = await jaw.provider.request({ method: 'wallet_getCapabilities', }); ``` ### Related Methods * [wallet\_sendCalls](/api-reference/wallet_sendCalls) - Use atomic batch capability * [wallet\_grantPermissions](/api-reference/wallet_grantPermissions) - Use permission capability ## wallet\_getPermissions Get all permissions for an account. **Authentication Required:** No (if address provided) ### Request ```typescript await jaw.provider.request({ method: 'wallet_getPermissions', params: [{ address: '0x1234...' }], }); ``` #### Parameters | Name | Type | Required | Description | | --------- | -------- | -------- | ------------------------ | | `address` | `string` | No\* | Account address to query | \*Required if not authenticated. Auto-injected if authenticated and not provided. #### Example ```json [{ "address": "0x1234567890123456789012345678901234567890" }] ``` Or when authenticated (auto-injects address): ```json [] ``` ### Response Returns an array of permission objects. **Type:** `array` #### Example ```json [ { "id": "0xabc123...", "account": "0x1234567890123456789012345678901234567890", "spender": "0x5678901234567890123456789012345678901234", "chainId": "0x1", "expiry": 1735689600, "calls": [{ "target": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "selector": "0xa9059cbb" }], "spends": [{ "token": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "limit": "0x16345785d8a0000", "period": "day" }] } ] ``` #### Permission Object Fields | Field | Type | Description | | --------- | -------- | -------------------------------------- | | `id` | `string` | Unique permission identifier (hash) | | `account` | `string` | Account that granted the permission | | `spender` | `string` | Address that received permissions | | `chainId` | `string` | Chain ID (hex) | | `expiry` | `number` | Unix timestamp when permission expires | | `calls` | `array` | Granted call permissions | | `spends` | `array` | Granted spend limits | ### Behavior * Can be called without authentication if address provided * Auto-injects connected address if authenticated * Returns empty array if no permissions found ### Errors | Code | Description | | ------ | -------------------------------------------------------- | | -32602 | Invalid params (address required when not authenticated) | ### Example ```typescript const permissions = await jaw.provider.request({ method: 'wallet_getPermissions', }); ``` ### Related Methods * [wallet\_grantPermissions](/api-reference/wallet_grantPermissions) - Grant new permissions * [wallet\_revokePermissions](/api-reference/wallet_revokePermissions) - Revoke permissions ## wallet\_grantPermissions Grant granular permissions to a spender address for delegated transaction execution with spending limits and call restrictions. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'wallet_grantPermissions', params: [{ expiry: 1735689600, // Unix timestamp spender: '0x5678...', // Who gets permissions permissions: { calls: [{ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', functionSignature: 'transfer(address,uint256)', }], spends: [{ token: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', allowance: '0x16345785d8a0000', // 0.1 ETH unit: 'day', }], }, // chainId: '0x1', // Optional - defaults to connected chain }], }); ``` #### Parameters | Name | Type | Required | Description | | ------------- | ------------- | -------- | --------------------------------------------- | | `expiry` | `number` | Yes | Expiration timestamp (seconds) | | `spender` | `0x${string}` | Yes | Address receiving permissions | | `permissions` | `object` | Yes | Permission details | | `chainId` | `0x${string}` | No | Target chain ID (defaults to connected chain) | #### Permission Details ##### calls Array of call permissions. Each permission: | Name | Type | Required | Description | | ------------------- | ------------- | -------- | ------------------------------------------------------ | | `target` | `0x${string}` | Yes | Contract address | | `functionSignature` | `string` | No\* | Function signature (e.g., "transfer(address,uint256)") | | `selector` | `0x${string}` | No\* | 4-byte function selector (0x...) | \*Either `functionSignature` or `selector` is required. ##### spends (Optional\*) Array of spend limits. Each limit: | Name | Type | Required | Description | | ------------ | ------------- | -------- | -------------------------------------------- | | `token` | `0x${string}` | Yes | Token address (`0xEeee...` for native token) | | `allowance` | `0x${string}` | Yes | Spending allowance in wei (hex) | | `unit` | `string` | Yes | Time unit for allowance reset | | `multiplier` | `number` | No | Multiplier for the time unit (default: 1) | **Valid Units:** * `minute` - Resets every minute * `hour` - Resets every hour * `day` - Resets every day * `week` - Resets every week * `month` - Resets every month * `year` - Resets every year * `forever` - Never resets (one-time allowance) \*Spend Limits are required if moving funds. ### Response Returns permission grant result with generated permission ID. ```json { "account": "0x1234567890123456789012345678901234567890", "spender": "0x5678901234567890123456789012345678901234", "start": 1704067200, "end": 1735689600, "salt": "0x1a2b3c...", "calls": [{ "target": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "selector": "0xa9059cbb" }], "spends": [{ "token": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "allowance": "0x16345785d8a0000", "unit": "day", "multiplier": 1 }], "permissionId": "0xabc123...", "chainId": "0x1" } ``` ### Behavior * Opens popup for user approval * Approves permission * Returns permission ID for future revocation ### Errors | Code | Description | | ------ | -------------------------------- | | 4001 | User rejected the request | | 4100 | Unauthorized (not authenticated) | | -32602 | Invalid params | ### Example ```typescript // Allow DeFi protocol to swap USDC with daily limit const permission = await jaw.provider.request({ method: 'wallet_grantPermissions', params: [{ expiry: Math.floor(Date.now() / 1000) + (365 * 24 * 60 * 60), // 1 year spender: dexRouterAddress, permissions: { calls: [{ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', functionSignature: 'approve(address,uint256)', }, { target: dexRouterAddress, functionSignature: 'swap(address,address,uint256,uint256)', }], spends: [{ token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', allowance: '0x' + (1000n * 10n**6n).toString(16), unit: 'day', }], }, // chainId: '0x1', // Optional - defaults to connected chain }], }); ``` ### Related Methods * [wallet\_revokePermissions](/api-reference/wallet_revokePermissions) - Revoke permissions * [wallet\_getPermissions](/api-reference/wallet_getPermissions) - Query permissions ## wallet\_revokePermissions Revoke previously granted permissions. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'wallet_revokePermissions', params: [{ id: '0xabc123...', // Permission ID }], }); ``` #### Parameters | Name | Type | Required | Description | | ---- | -------- | -------- | ----------------------- | | `id` | `string` | Yes | Permission ID to revoke | #### Example ```json [{ "id": "0xabc123456def789..." }] ``` ### Response Returns revocation result. #### Example ```json { "success": true } ``` ### Behavior * Opens popup for user approval * Revokes permission ### Errors | Code | Description | | ------ | ---------------------------------------- | | 4001 | User rejected the request | | 4100 | Unauthorized (not authenticated) | | -32602 | Invalid params (permission ID not found) | ### Example ```typescript const permissionId = '0xabc123...'; const result = await jaw.provider.request({ method: 'wallet_revokePermissions', params: [{ id: permissionId, }], }); ``` ### Related Methods * [wallet\_grantPermissions](/api-reference/wallet_grantPermissions) - Grant permissions * [wallet\_getPermissions](/api-reference/wallet_getPermissions) - List all permissions ## wallet\_sendCalls Broadcast bundle of calls to the network. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'wallet_sendCalls', params: [{ calls: [ { to: '0xContractA...', value: '0x0', data: '0x...', }, { to: '0xContractB...', value: '0x0', data: '0x...', }, ], }], }); ``` #### Parameters | Name | Type | Required | Description | | --------------- | ------------- | -------- | --------------------------------------------- | | `calls` | `array` | Yes | Array of call objects | | `calls[].to` | `0x${string}` | Yes | Recipient address | | `calls[].value` | `0x${string}` | No | Value in wei (hex), default "0x0" | | `calls[].data` | `0x${string}` | No | Call data (hex), default "0x" | | `chainId` | `0x${string}` | No | Target chain ID (defaults to connected chain) | | `capabilities` | `object` | No | Additional capabilities (see below) | #### Capabilities | Name | Type | Description | | ----------------------------- | ------------- | --------------------------------------------- | | `capabilities.permissions` | `object` | Permission capability for delegated execution | | `capabilities.permissions.id` | `0x${string}` | ID of the permission to use for execution | ### Response Returns a batch call identifier. #### Example ```json { "id": "0x123abc..." } ``` ### Behavior * All calls succeed atomically or all fail together * Uses currently connected chain if `chainId` not specified * Returns batch ID immediately for status tracking * Background task monitors completion * Gas is sponsored by paymaster if configured * Users offered native ERC-20 paymaster option for gas fees (no configuration needed) ### Errors | Code | Description | | ------ | -------------------------------- | | 4001 | User rejected the request | | 4100 | Unauthorized (not authenticated) | | -32602 | Invalid params | ### Example #### Transfer Multiple ERC-20 Tokens ```typescript import { encodeFunctionData } from 'viem'; const transferAbi = [{ name: 'transfer', type: 'function', inputs: [ { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }, ], }]; const account = '0x1234...'; const recipient = '0x5678...'; // Encode both transfers const usdcTransfer = encodeFunctionData({ abi: transferAbi, functionName: 'transfer', args: [recipient, 1000000n], }); const uniTransfer = encodeFunctionData({ abi: transferAbi, functionName: 'transfer', args: [recipient, 1000000000000000000n], }); // Send both transfers atomically const result = await jaw.provider.request({ method: 'wallet_sendCalls', params: [{ calls: [ { to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', data: usdcTransfer, }, { to: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', data: uniTransfer, }, ], }], }); console.log('Batch ID:', result.id); ``` #### Check Status ```typescript // Get status of the batch const status = await jaw.provider.request({ method: 'wallet_getCallsStatus', params: [result.id], }); console.log('Status:', status.status); ``` #### Execute with Permission (Delegated Execution) When a permission has been granted via `wallet_grantPermissions`, you can execute calls using that permission. This allows the spender to execute transactions on behalf of the account within the permission's constraints. ```typescript import { encodeFunctionData } from 'viem'; // First, get the permission ID from a previous wallet_grantPermissions call const permissionId = '0x1234...'; // The permission ID returned from grantPermissions const transferData = encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: ['0x5678...', 1000000n], }); // Execute the transfer using the permission const result = await jaw.provider.request({ method: 'wallet_sendCalls', params: [{ calls: [ { to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC data: transferData, }, ], capabilities: { permissions: { id: permissionId, }, }, }], }); console.log('Batch ID:', result.id); ``` When using the `permissions` capability: * The calls are executed through the JustaPermissionManager contract * The permission's call and spend limits are enforced * The spender must have been granted appropriate permissions for the calls being made ### Related Methods * [wallet\_getCallsStatus](/api-reference/wallet_getCallsStatus) - Get batch status * [wallet\_showCallsStatus](/api-reference/wallet_showCallsStatus) - Show status UI * [wallet\_grantPermissions](/api-reference/wallet_grantPermissions) - Grant permissions for delegated execution ## wallet\_showCallsStatus Show the status UI popup for a batch of calls sent via `wallet_sendCalls`. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'wallet_showCallsStatus', params: ['0x123abc...'], }); ``` #### Parameters | Name | Type | Required | Description | | --------- | -------- | -------- | ---------------------------------------- | | `batchId` | `string` | Yes | Batch ID returned from wallet\_sendCalls | #### Example ```json ["0x123abc456def..."] ``` ### Response Returns `void` (no return value). **Type:** `void` ### Behavior * Opens popup showing transaction status UI * Displays pending, completed, or failed status * Shows individual call details in the batch * Auto-updates as status changes in real-time * User can close popup at any time ### Errors | Code | Description | | ------ | ----------------------------------- | | 4001 | User closed the popup | | 4100 | Unauthorized (not authenticated) | | -32602 | Invalid params (batch ID not found) | ### Example ```typescript await jaw.provider.request({ method: 'wallet_showCallsStatus', params: [result.id], }); ``` ### Related Methods * [wallet\_sendCalls](/api-reference/wallet_sendCalls) - Send atomic batch of calls * [wallet\_getCallsStatus](/api-reference/wallet_getCallsStatus) - Get status programmatically ## wallet\_sign Unified signing method supporting multiple message formats (ERC-7871). Delegates to `personal_sign` or `eth_signTypedData_v4` based on the request type. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'wallet_sign', params: [{ request: { type: '0x45', // or '0x01' data: { message: 'Hello World' } // or TypedData object for 0x01 }, }], }); ``` #### Parameters | Name | Type | Required | Description | | -------------- | -------- | -------- | ------------------------------------------------------------------------- | | `request.type` | `string` | Yes | Signature type: `"0x45"` (personal\_sign) or `"0x01"` (signTypedData\_v4) | | `request.data` | `object` | Yes | For 0x45: `{ message: string }`. For 0x01: TypedData object (EIP-712) | #### Supported Types | Type | EIP Standard | Description | Data Format | | ------ | ------------ | ----------------------------- | ----------------------------------------------------------------- | | `0x45` | EIP-191 | Personal message signing | `{ message: string }` - UTF-8 message string | | `0x01` | EIP-712 | Structured typed data signing | TypedData object with `types`, `domain`, `primaryType`, `message` | ### Response Returns the signature as a hexadecimal string. **Type:** `0x${string}` #### Example ```typescript "0x1234567890abcdef..." ``` ### Behavior * **Type 0x45**: Delegates to `personal_sign` - adds EIP-191 prefix and signs the UTF-8 message * **Type 0x01**: Delegates to `eth_signTypedData_v4` - signs structured EIP-712 data * Opens popup for user approval * Returns signature in same format as the underlying method ### Errors | Code | Description | | ------ | --------------------------------------------------- | | 4001 | User rejected the request | | 4100 | Unauthorized (not authenticated) | | -32602 | Invalid params (unsupported type or malformed data) | ### Example #### Personal Message (Type 0x45) ```typescript const message = 'Hello World'; const signature = await jaw.provider.request({ method: 'wallet_sign', params: [{ request: { type: '0x45', data: { message: message, // UTF-8 message string }, }, }], }); ``` #### Typed Data (Type 0x01) ```typescript const typedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, ], Person: [ { name: 'name', type: 'string' }, { name: 'wallet', type: 'address' }, ], }, primaryType: 'Person', domain: { name: 'My DApp', version: '1', chainId: 1, }, message: { name: 'Alice', wallet: '0x...', }, }; const signature = await jaw.provider.request({ method: 'wallet_sign', params: [{ request: { type: '0x01', data: typedData, // TypedData object directly }, }], }); ``` #### SIWE (Sign-In With Ethereum) ```typescript const siweMessage = `example.com wants you to sign in with your Ethereum account: 0x1234... Sign in to Example DApp URI: https://example.com Version: 1 Chain ID: 1 Nonce: ${crypto.randomUUID()} Issued At: ${new Date().toISOString()}`; const signature = await jaw.provider.request({ method: 'wallet_sign', params: [{ request: { type: '0x45', data: { message: siweMessage, // UTF-8 message string }, }, }], }); ``` ### Related Methods * [personal\_sign](/api-reference/personal_sign) - Direct EIP-191 message signing (type 0x45) * [eth\_signTypedData\_v4](/api-reference/eth_signTypedData_v4) - Direct EIP-712 typed data signing (type 0x01) ## wallet\_switchEthereumChain Switch to a different chain. **Authentication Required:** Yes ### Request ```typescript await jaw.provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x89' }], }); ``` #### Parameters | Name | Type | Required | Description | | --------- | ------------- | -------- | ------------------------------------- | | `chainId` | `0x${string}` | Yes | Target chain ID in hexadecimal format | #### Example ```json [{ "chainId": "0x89" }] ``` ### Response Returns `null` on success. **Type:** `null` ### Behavior * Switches to the specified chain if supported * Emits `chainChanged` event with the new chain ID * All subsequent transactions use the new chain * Only supported chains can be switched to ### Errors | Code | Description | | ---- | -------------------------------- | | 4001 | User rejected the request | | 4100 | Unauthorized (not authenticated) | | 4902 | Chain not supported | ### Example #### Basic Chain Switch ```typescript try { await jaw.provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x89' }], // Polygon }); console.log('Switched to Polygon'); } catch (error) { if (error.code === 4902) { console.error('Polygon is not supported'); } } ``` #### Switch with Event Listener ```typescript // Listen for chain changes jaw.provider.on('chainChanged', (chainId) => { console.log('Chain changed to:', parseInt(chainId, 16)); }); // Switch chain await jaw.provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x2105' }], // Base }); ``` ### Related Methods * [eth\_chainId](/api-reference/eth_chainId) - Get current chain ID ## Custom UI Handler Build your own UI for app-specific mode by implementing the `UIHandler` interface. This is useful for non-React applications or when you need complete control over the user experience. ### Overview When using `Mode.AppSpecific`, the SDK needs a way to display approval dialogs for wallet operations. The `UIHandler` interface defines how your application handles these UI requests. ```typescript import { JAW, Mode } from '@jaw.id/core'; import { MyCustomUIHandler } from './my-ui-handler'; const jaw = JAW.create({ apiKey: 'your-api-key', preference: { mode: Mode.AppSpecific, uiHandler: new MyCustomUIHandler(), }, }); ``` ### UIHandler Interface ```typescript import { UIHandler, UIRequest, UIResponse, UIHandlerConfig, } from '@jaw.id/core'; class MyCustomUIHandler implements UIHandler { private config?: UIHandlerConfig; /** * Initialize the handler with SDK configuration. * Called by the SDK before any requests are made. */ init(config: UIHandlerConfig): void { this.config = config; // Use config.apiKey, config.defaultChainId, config.paymasters, etc. } /** * Request user approval for an action. * This is the main method you must implement. */ async request(request: UIRequest): Promise> { // Display UI based on request.type // Wait for user interaction // Return the response } /** * Optional: Check if this handler supports a request type. */ canHandle(request: UIRequest): boolean { return true; } /** * Optional: Clean up when handler is no longer needed. */ async cleanup(): Promise { // Close any open dialogs, release resources } } ``` ### UIHandlerConfig The SDK passes configuration to your handler via the `init()` method: ```typescript interface UIHandlerConfig { /** JAW API key for RPC URL resolution */ apiKey?: string; /** Default chain ID */ defaultChainId?: number; /** Paymaster configuration per chain for gasless transactions */ paymasters?: Record }>; /** App name shown in dialogs */ appName?: string; /** App logo URL */ appLogoUrl?: string | null; } ``` ### Request Types Your handler will receive different request types based on the wallet operation: #### wallet\_connect Connection request when user wants to connect their wallet. ```typescript interface ConnectUIRequest { id: string; type: 'wallet_connect'; timestamp: number; correlationId?: string; data: { appName: string; appLogoUrl: string | null; origin: string; chainId: number; capabilities?: Record; }; } ``` **Expected response data:** `WalletConnectResponse` with `accounts` array. #### personal\_sign EIP-191 personal message signing. ```typescript interface SignatureUIRequest { id: string; type: 'personal_sign'; timestamp: number; correlationId?: string; data: { message: string; // Hex-encoded message address: Address; }; } ``` **Expected response data:** Signature string (hex). #### eth\_signTypedData\_v4 EIP-712 typed data signing. ```typescript interface TypedDataUIRequest { id: string; type: 'eth_signTypedData_v4'; timestamp: number; correlationId?: string; data: { typedData: string; // JSON string address: Address; }; } ``` **Expected response data:** Signature string (hex). #### wallet\_sendCalls Transaction batch request (also used for `eth_sendTransaction`). ```typescript interface TransactionUIRequest { id: string; type: 'wallet_sendCalls'; timestamp: number; correlationId?: string; data: { version: '1.0'; from: Address; calls: Array<{ to: string; value?: string; data?: string; }>; chainId: number; atomicRequired?: boolean; }; } ``` **Expected response data:** `{ id: string; chainId: number }` - the transaction bundle ID. #### wallet\_grantPermissions Permission grant request for session keys. ```typescript interface PermissionUIRequest { id: string; type: 'wallet_grantPermissions'; timestamp: number; correlationId?: string; data: { address: Address; chainId: number | string; expiry: number; spender: Address; permissions: { spends?: Array<{ limit: string; period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year' | 'forever'; token: Address; }>; calls?: Array<{ target: Address; selector: `0x${string}`; functionSignature?: string; }>; }; }; } ``` **Expected response data:** Permission grant response object. #### wallet\_revokePermissions Permission revocation request. ```typescript interface RevokePermissionUIRequest { id: string; type: 'wallet_revokePermissions'; timestamp: number; correlationId?: string; data: { permissionId: string; address: Address; chainId?: number; }; } ``` **Expected response data:** Revocation confirmation. #### wallet\_sign Unified signing request (type 0x45 for personal sign, 0x01 for EIP-712). ```typescript interface WalletSignUIRequest { id: string; type: 'wallet_sign'; timestamp: number; correlationId?: string; data: { address: Address; chainId?: number; request: { type: '0x45' | '0x01'; data: string; }; }; } ``` **Expected response data:** Signature string (hex). ### UIResponse Structure Your handler must return a `UIResponse` object: ```typescript interface UIResponse { id: string; // Must match request.id approved: boolean; // true if user approved, false if rejected data?: T; // Result data when approved error?: UIError; // Error when rejected } ``` #### Approval Response ```typescript return { id: request.id, approved: true, data: resultData, // Type depends on request type }; ``` #### Rejection Response ```typescript import { UIError } from '@jaw.id/core'; return { id: request.id, approved: false, error: UIError.userRejected('User declined the request'), }; ``` ### UIError Class Use `UIError` for standardized error handling: ```typescript import { UIError, UIErrorCode } from '@jaw.id/core'; // User rejected the request UIError.userRejected('User declined'); // Request timed out UIError.timeout('Request timed out after 30s'); // Unsupported request type UIError.unsupportedRequest('wallet_unknown'); // Handler not available UIError.handlerNotAvailable(); ``` Error codes: * `UIErrorCode.USER_REJECTED` (4001) * `UIErrorCode.TIMEOUT` (4002) * `UIErrorCode.UNSUPPORTED_REQUEST` (4003) * `UIErrorCode.HANDLER_NOT_AVAILABLE` (4004) ### Complete Example Here's a complete example of a custom UI handler using vanilla JavaScript with DOM manipulation: ```typescript import { UIHandler, UIRequest, UIResponse, UIHandlerConfig, UIError, ConnectUIRequest, SignatureUIRequest, TypedDataUIRequest, TransactionUIRequest, } from '@jaw.id/core'; export class VanillaUIHandler implements UIHandler { private config?: UIHandlerConfig; private modalContainer: HTMLElement | null = null; init(config: UIHandlerConfig): void { this.config = config; // Create modal container this.modalContainer = document.createElement('div'); this.modalContainer.id = 'jaw-modal-container'; document.body.appendChild(this.modalContainer); } async request(request: UIRequest): Promise> { switch (request.type) { case 'wallet_connect': return this.handleConnect(request as ConnectUIRequest) as Promise>; case 'personal_sign': return this.handleSign(request as SignatureUIRequest) as Promise>; case 'eth_signTypedData_v4': return this.handleTypedData(request as TypedDataUIRequest) as Promise>; case 'wallet_sendCalls': return this.handleTransaction(request as TransactionUIRequest) as Promise>; default: throw UIError.unsupportedRequest(request.type); } } private async handleConnect(request: ConnectUIRequest): Promise> { return new Promise((resolve) => { const modal = this.createModal(`

Connect to ${request.data.appName}

Origin: ${request.data.origin}

Chain ID: ${request.data.chainId}

`); modal.querySelector('#approve')?.addEventListener('click', async () => { this.closeModal(); // Here you would trigger passkey authentication // and return the account data resolve({ id: request.id, approved: true, data: { accounts: [{ address: '0x...' }], }, }); }); modal.querySelector('#reject')?.addEventListener('click', () => { this.closeModal(); resolve({ id: request.id, approved: false, error: UIError.userRejected(), }); }); }); } private async handleSign(request: SignatureUIRequest): Promise> { return new Promise((resolve) => { // Decode hex message for display const message = Buffer.from(request.data.message.slice(2), 'hex').toString(); const modal = this.createModal(`

Sign Message

Address: ${request.data.address}

${message}
`); modal.querySelector('#approve')?.addEventListener('click', async () => { this.closeModal(); // Here you would sign the message with the user's passkey const signature = '0x...'; // Your signing logic resolve({ id: request.id, approved: true, data: signature, }); }); modal.querySelector('#reject')?.addEventListener('click', () => { this.closeModal(); resolve({ id: request.id, approved: false, error: UIError.userRejected(), }); }); }); } private async handleTypedData(request: TypedDataUIRequest): Promise> { // Similar to handleSign but parse and display typed data // ... } private async handleTransaction(request: TransactionUIRequest): Promise> { // Display transaction details and get approval // Execute transaction and return bundle ID // ... } private createModal(content: string): HTMLElement { if (!this.modalContainer) throw new Error('Handler not initialized'); const modal = document.createElement('div'); modal.className = 'jaw-modal'; modal.innerHTML = `
${content}
`; this.modalContainer.appendChild(modal); return modal; } private closeModal(): void { if (this.modalContainer) { this.modalContainer.innerHTML = ''; } } canHandle(request: UIRequest): boolean { return ['wallet_connect', 'personal_sign', 'eth_signTypedData_v4', 'wallet_sendCalls'].includes(request.type); } async cleanup(): Promise { this.closeModal(); this.modalContainer?.remove(); this.modalContainer = null; } } ``` ### Best Practices 1. **Always match request.id in response** - The SDK uses this to correlate requests and responses. 2. **Handle all request types you support** - Use `canHandle()` to indicate which types your handler supports. 3. **Provide clear user feedback** - Display relevant information about what the user is approving. 4. **Implement cleanup** - Release resources and close dialogs when the handler is cleaned up. 5. **Use UIError for rejections** - This ensures consistent error handling across the SDK. 6. **Consider mobile responsiveness** - Your UI should work well on all device sizes. 7. **Security considerations**: * Show full transaction details before approval * Allow users to review typed data structures * Clearly indicate when gas will be sponsored vs user-paid ### Related * [preference.mode](/configuration/preference#mode) - Authentication mode configuration * [preference.uiHandler](/configuration/preference#uihandler) - UIHandler option reference * [Provider - RPC Reference](/api-reference) - All supported RPC methods ## Advanced Advanced topics for customizing and extending the JAW SDK. ### Topics #### [Custom UI Handler](/advanced/custom-ui-handler) Build your own UI for app-specific mode by implementing the `UIHandler` interface. This is useful for non-React applications or when you need complete control over the user experience. #### [Custom Passkey Server](/advanced/passkey-server) Run your own passkey metadata server for managing passkey credentials. This gives you full control over passkey metadata storage and management. ### When to Use Advanced Features Most applications will work well with the default SDK configuration and the provided `ReactUIHandler` from `@jaw.id/ui`. Consider the advanced topics when: * **Custom UI Handler**: You're not using React, or you need pixel-perfect control over all approval dialogs * **Custom Passkey Server**: You want to manage passkey metadata on your own infrastructure For standard usage, see the [Configuration](/configuration) documentation. ## Custom Passkey Server Run your own passkey metadata server for managing passkey credentials in app-specific mode. ### Overview The SDK needs a server to store passkey metadata (credential IDs, public keys, display names). By default, the SDK uses `https://api.justaname.id/wallet/v2/passkeys`, but you can host your own. :::warning **Important:** This server does NOT store passkeys themselves—passkeys are always stored by the browser/device. The server only stores metadata to enable passkey lookups and management. ::: ### Configuration Point the SDK to your custom server: ```typescript import { JAW, Mode } from '@jaw.id/core'; import { ReactUIHandler } from '@jaw.id/ui'; const jaw = JAW.create({ apiKey: 'your-api-key', preference: { mode: Mode.AppSpecific, uiHandler: new ReactUIHandler(), serverUrl: 'https://your-server.example.com/passkeys', }, }); ``` ### Required Endpoints Your server must implement the following endpoints: #### GET / - Lookup Passkeys by Credential IDs Retrieves passkey information for one or more credential IDs. **Query Parameters:** * `credentialIds` (string\[], repeatable): Array of credential IDs to lookup **Success Response (200):** ```json { "statusCode": 200, "result": { "data": { "passkeys": [ { "credentialId": "string", "publicKey": "0x...", "displayName": "string" } ] }, "error": null } } ``` **Error Response (404):** ```json { "statusCode": 404, "result": { "data": null, "error": "Passkeys not found" } } ``` #### POST / - Register a New Passkey Registers a new passkey credential. **Headers:** * `Content-Type: application/json` **Request Body:** ```json { "credentialId": "string", "publicKey": "0x...", "displayName": "string" } ``` **Response:** * Status `200`/`201` for successful registration * Status `4xx`/`5xx` for errors ### Data Format Requirements | Field | Format | Description | | ---------------- | --------------------------- | -------------------------------------- | | **credentialId** | Base64url string | Standard WebAuthn credential ID format | | **publicKey** | Hex string with `0x` prefix | The passkey's public key | | **displayName** | String | Human-readable name for the passkey | ### When to Self-Host Consider running your own passkey server when: * **Data sovereignty**: You need full control over user passkey metadata * **Custom logic**: You want to add additional validation or processing * **Development/staging**: You need isolated environments for testing * **Compliance**: Your organization requires self-hosted infrastructure ### Related * [preference.serverUrl](/configuration/preference#serverurl) - Server URL configuration * [preference.mode](/configuration/preference#mode) - Authentication mode configuration ## Account.create() Create a new account with a passkey. **Type:** `static async` ### Signature ```typescript static async create( config: AccountConfig, options: CreateAccountOptions ): Promise ``` ### Parameters #### config Type: `AccountConfig` | Property | Type | Required | Description | | -------------- | -------- | -------- | ---------------------------------------- | | `chainId` | `number` | Yes | Chain ID for the account | | `apiKey` | `string` | Yes | API key for JAW services | | `paymasterUrl` | `string` | No | Custom paymaster URL for gas sponsorship | #### options Type: `CreateAccountOptions` | Property | Type | Required | Description | | ---------- | -------- | -------- | ----------------------------------------------------------------- | | `username` | `string` | Yes | Username/display name for the passkey | | `rpId` | `string` | No | Relying party identifier (defaults to `window.location.hostname`) | | `rpName` | `string` | No | Relying party name (defaults to `'JAW'`) | ### Returns `Promise` - The newly created Account instance ### Behavior 1. Triggers WebAuthn credential creation (user will see browser passkey dialog) 2. Creates the smart account from the passkey 3. Stores the passkey account and authentication state 4. Returns the ready-to-use Account instance ### Errors | Error | Description | | --------------- | ------------------------------------------------------------------- | | WebAuthn errors | User cancelled passkey creation or browser doesn't support WebAuthn | | Network errors | Failed to communicate with JAW services | ### Examples #### Basic Usage ```typescript import { Account } from '@jaw.id/core'; const account = await Account.create( { chainId: 1, apiKey: 'your-api-key' }, { username: 'alice' } ); console.log('Created account:', account.address); console.log('Username:', account.getMetadata()?.username); ``` #### With Custom RP Settings ```typescript const account = await Account.create( { chainId: 1, apiKey: 'your-api-key' }, { username: 'alice', rpId: 'myapp.com', rpName: 'My Awesome App', } ); ``` ### Usage in Custom UI Handler ```typescript class MyUIHandler implements UIHandler { private config?: UIHandlerConfig; async handleConnect(request: ConnectUIRequest) { // Show your custom onboarding UI const username = await this.showUsernameInput(); const account = await Account.create( { chainId: request.data.chainId, apiKey: this.config?.apiKey, }, { username } ); return { id: request.id, approved: true, data: { accounts: [{ address: account.address }], }, }; } } ``` ### Related * [Account.get()](/account/get) - Login with existing account * [Account.import()](/account/import) - Import from cloud backup * [Account.getStoredAccounts()](/account/getStoredAccounts) - List created accounts ## account.estimateGas() Estimate gas for a transaction. **Type:** `instance async` ### Signature ```typescript async estimateGas(calls: TransactionCall[]): Promise ``` ### Parameters #### calls Type: `TransactionCall[]` Array of transaction calls to estimate. ```typescript interface TransactionCall { /** Target contract address */ to: Address; /** Value to send in wei (bigint or hex string) */ value?: bigint | string; /** Call data */ data?: Hex; } ``` ### Returns `Promise` - The estimated gas amount in wei. ### Behavior Estimates the total gas required for the user operation, including: * Call execution gas * Verification gas * Pre-verification gas This estimate is used by the bundler to determine if the operation can be submitted. ### Example #### Getting an Account Instance Before calling instance methods, create an account using one of the factory methods: ```typescript import { Account } from '@jaw.id/core'; // Option 1: Restore existing session or login with passkey const account = await Account.get({ chainId: 1, apiKey: 'your-api-key' }); // Option 2: Create a new account with passkey const account = await Account.create( { chainId: 1, apiKey: 'your-api-key' }, { username: 'alice' } ); // Option 3: Import from cloud backup const account = await Account.import({ chainId: 1, apiKey: 'your-api-key' }); // Option 4: From a local account (server-side / embedded wallets) const account = await Account.fromLocalAccount( { chainId: 1, apiKey: 'your-api-key' }, localAccount ); ``` #### Basic Usage ```typescript import { parseEther } from 'viem'; const gas = await account.estimateGas([ { to: '0x...', value: parseEther('0.1') } ]); console.log('Estimated gas:', gas.toString()); ``` ### Related * [account.sendCalls()](/account/sendCalls) - Send batch transactions * [account.sendTransaction()](/account/sendTransaction) - Send the transaction ## Account.fromLocalAccount() Create an account from a viem LocalAccount. Ideal for server-side usage or integration with embedded wallet providers. **Type:** `static async` ### Signature ```typescript static async fromLocalAccount( config: AccountConfig, localAccount: LocalAccount ): Promise ``` ### Parameters #### config Type: `AccountConfig` | Property | Type | Required | Description | | -------------- | -------- | -------- | ---------------------------------------- | | `chainId` | `number` | Yes | Chain ID for the account | | `apiKey` | `string` | Yes | API key for JAW services | | `paymasterUrl` | `string` | No | Custom paymaster URL for gas sponsorship | #### localAccount Type: `LocalAccount` (from viem) A viem LocalAccount instance. Can be created from: * Private key * Mnemonic * Embedded wallet providers (Privy, Dynamic, Magic, Turnkey, etc.) ### Returns `Promise` - The Account instance ### Behavior 1. Creates a smart account using the LocalAccount as the owner 2. No passkey or WebAuthn involved 3. No authentication state stored (since there's no passkey) 4. `getMetadata()` returns `null` for these accounts ### Examples #### From Private Key ```typescript import { Account } from '@jaw.id/core'; import { privateKeyToAccount } from 'viem/accounts'; const localAccount = privateKeyToAccount('0x...'); const account = await Account.fromLocalAccount( { chainId: 1, apiKey: 'your-api-key' }, localAccount ); console.log('Smart account address:', account.address); console.log('Has metadata:', account.getMetadata()); // null ``` ### When to Use Use `fromLocalAccount()` when: * **Server-side automation** - Backend services that need to execute transactions * **Embedded wallets** - Integrating with Privy, Dynamic, Magic, Turnkey, etc. Do **not** use when: * You want passkey-based authentication (use `create()` or `get()`) ### Related * [Account.get()](/account/get) - Passkey-based authentication * [Account.create()](/account/create) - Create passkey account * [getMetadata()](/account/getMetadata) - Returns null for LocalAccount-based accounts ## Account.get() Get an account instance - restores an existing session or triggers WebAuthn login. **Type:** `static async` ### Signature ```typescript static async get( config: AccountConfig, credentialId?: string ): Promise ``` ### Parameters #### config Type: `AccountConfig` | Property | Type | Required | Description | | -------------- | -------- | -------- | ---------------------------------------- | | `chainId` | `number` | Yes | Chain ID for the account | | `apiKey` | `string` | Yes | API key for JAW services | | `paymasterUrl` | `string` | No | Custom paymaster URL for gas sponsorship | #### credentialId Type: `string | undefined` Optional credential ID to login with. If provided and not already authenticated, triggers WebAuthn authentication. ### Returns `Promise` - The Account instance ### Behavior This is the primary method to get an Account instance: 1. **If `credentialId` is provided**: Always triggers WebAuthn authentication, even if already authenticated. This ensures user verification when selecting a specific account. 2. **If no `credentialId` and already authenticated**: Restores the account from storage without prompting WebAuthn. 3. **If no `credentialId` and not authenticated**: Throws an error. ### Errors | Error | Description | | ------------------------------------ | ---------------------------------------------- | | `Not authenticated` | Not authenticated and no credentialId provided | | `No account found for credential ID` | Provided credentialId doesn't exist in storage | ### Examples #### Restore Existing Session ```typescript import { Account } from '@jaw.id/core'; // Restore without WebAuthn prompt (if already authenticated) try { const account = await Account.get({ chainId: 1, apiKey: 'your-api-key', }); console.log('Restored account:', account.address); } catch (error) { console.log('Not authenticated'); } ``` #### Login with Specific Account ```typescript import { Account } from '@jaw.id/core'; // Get stored accounts const accounts = Account.getStoredAccounts('your-api-key'); if (accounts.length > 0) { // Login with first account (triggers WebAuthn) const account = await Account.get( { chainId: 1, apiKey: 'your-api-key' }, accounts[0].credentialId ); console.log('Logged in as:', account.address); } ``` ### Related * [Account.create()](/account/create) - Create a new account * [Account.getAuthenticatedAddress()](/account/getAuthenticatedAddress) - Check authentication status * [Account.getStoredAccounts()](/account/getStoredAccounts) - List available accounts ## account.getAddress() Get the smart account address (async). **Type:** `instance async` ### Signature ```typescript async getAddress(): Promise
``` ### Parameters None. ### Returns `Promise
` - The smart account address. ### Behavior This async method returns the smart account address. It's useful for: 1. **Counterfactual addresses** - Getting the address before the account is deployed on-chain 2. **Address verification** - Confirming the computed address matches expectations For deployed accounts, this returns the same value as the synchronous `address` property. ### Example ```typescript import { Account } from '@jaw.id/core'; const account = await Account.get({ chainId: 1, apiKey: 'your-api-key', }); const address = await account.getAddress(); console.log('Address:', address); ``` ### Related * [account.address](/account#instance-properties) - Synchronous address property * [account.getSmartAccount()](/account/getSmartAccount) - Get underlying smart account * [account.sendTransaction()](/account/sendTransaction) - Deploys account if needed ## Account.getAuthenticatedAddress() Get the authenticated account address without fully loading the account. **Type:** `static` ### Signature ```typescript static getAuthenticatedAddress(apiKey: string): Address | null ``` ### Parameters #### apiKey Type: `string` API key for JAW services. Used to scope the storage lookup. ### Returns `Address | null` - The account address if authenticated, or `null` if not authenticated. ### Behavior This is a synchronous method that checks the authentication state stored locally: 1. Checks localStorage for stored authentication state 2. Returns the address if found and valid 3. Returns `null` if not authenticated This method does **not** trigger WebAuthn or any network requests. ### Example ```typescript import { Account } from '@jaw.id/core'; const address = Account.getAuthenticatedAddress('your-api-key'); if (address) { console.log('Authenticated as:', address); } else { console.log('Not authenticated'); } ``` ### Use Cases * **Check before restore** - Verify authentication before calling `Account.get()` * **Conditional UI** - Show different UI based on authentication state * **Quick status check** - Get address without full account initialization ### Related * [Account.get()](/account/get) - Load the full account instance * [Account.getStoredAccounts()](/account/getStoredAccounts) - List all stored accounts * [Account.logout()](/account/logout) - Clear authentication state ## account.getCallStatus() Get the status of a previously submitted call batch. **Type:** `instance sync` ### Signature ```typescript getCallStatus(batchId: Hash): CallStatusResponse | undefined ``` ### Parameters #### batchId Type: `Hash` The batch ID (userOpHash) returned from `sendCalls()`. ### Returns `CallStatusResponse | undefined` - The call status in EIP-5792 format, or `undefined` if the batch ID is not found. ```typescript interface CallStatusResponse { /** EIP-5792 version */ version: string; /** The batch ID (userOpHash) */ id: `0x${string}`; /** Chain ID in hex format */ chainId: `0x${string}`; /** Status code: 100=pending, 200=completed, 400=offchain failure, 500=onchain revert */ status: number; /** Whether the operation is atomic (always true for ERC-4337) */ atomic: boolean; /** Transaction receipts (present when completed or reverted) */ receipts?: CallReceipt[]; } interface CallReceipt { logs: Array<{ address: `0x${string}`; data: `0x${string}`; topics: `0x${string}`[]; }>; status: `0x${string}`; blockHash: `0x${string}`; blockNumber: `0x${string}`; gasUsed: `0x${string}`; transactionHash: `0x${string}`; } ``` ### Status Codes | Code | Name | Description | | ---- | ---------------- | ------------------------------------------------ | | 100 | Pending | Not yet completed onchain | | 200 | Completed | Included onchain without reverts | | 400 | Offchain Failure | Not included onchain, wallet won't retry | | 500 | Complete Revert | Reverted completely, has receipt with status 0x0 | ### Behavior 1. Looks up the batch ID in the internal call status store 2. Returns the status in EIP-5792 format 3. Returns `undefined` if the batch ID is not found 4. Status is automatically updated in the background after `sendCalls()` ### Example #### Getting an Account Instance Before calling instance methods, create an account using one of the factory methods: ```typescript import { Account } from '@jaw.id/core'; // Option 1: Restore existing session or login with passkey const account = await Account.get({ chainId: 1, apiKey: 'your-api-key' }); // Option 2: Create a new account with passkey const account = await Account.create( { chainId: 1, apiKey: 'your-api-key' }, { username: 'alice' } ); // Option 3: Import from cloud backup const account = await Account.import({ chainId: 1, apiKey: 'your-api-key' }); // Option 4: From a local account (server-side / embedded wallets) const account = await Account.fromLocalAccount( { chainId: 1, apiKey: 'your-api-key' }, localAccount ); ``` #### Basic Usage ```typescript // Send calls and get the batch ID const { id } = await account.sendCalls([ { to: '0x...', value: parseEther('0.1') } ]); // Check status immediately const status = account.getCallStatus(id); ``` ### Related * [account.sendCalls()](/account/sendCalls) - Send calls without waiting * [account.sendTransaction()](/account/sendTransaction) - Send and wait for receipt * [wallet\_getCallsStatus](/api-reference/wallet_getCallsStatus) - EIP-5792 RPC method ## account.getChain() Get the chain configuration. **Type:** `instance` ### Signature ```typescript getChain(): Chain ``` ### Parameters None. ### Returns `Chain` - The chain configuration object. #### Chain ```typescript interface Chain { /** Chain ID */ id: number; /** RPC URL for the chain */ rpcUrl: string; /** Optional paymaster URL for gas sponsorship */ paymasterUrl?: string; } ``` ### Behavior Returns a copy of the chain configuration used by this account instance. Modifying the returned object does not affect the account. ### Example ```typescript import { Account } from '@jaw.id/core'; const account = await Account.get({ chainId: 1, apiKey: 'your-api-key', }); const chain = account.getChain(); console.log('Chain ID:', chain.id); console.log('RPC URL:', chain.rpcUrl); console.log('Paymaster URL:', chain.paymasterUrl); ``` ### Related * [account.chainId](/account#instance-properties) - Quick access to chain ID * [account.getSmartAccount()](/account/getSmartAccount) - Get underlying smart account * [AccountConfig](/account#accountconfig) - Configuration interface ## account.getMetadata() Get account metadata (only available for passkey-based accounts). **Type:** `instance` ### Signature ```typescript getMetadata(): AccountMetadata | null ``` ### Parameters None. ### Returns `AccountMetadata | null` - Account metadata or `null` for LocalAccount-based accounts. #### AccountMetadata ```typescript interface AccountMetadata { /** Username/display name */ username: string; /** ISO date string when the account was created */ creationDate: string; /** Whether the account was imported from cloud backup */ isImported: boolean; } ``` ### Behavior * For accounts created via `Account.create()`: Returns full metadata * For accounts created via `Account.import()`: Returns metadata with `isImported: true` * For accounts created via `Account.fromLocalAccount()`: Returns `null` ### Example ```typescript import { Account } from '@jaw.id/core'; const account = await Account.get({ chainId: 1, apiKey: 'your-api-key', }); const metadata = account.getMetadata(); if (metadata) { console.log('Username:', metadata.username); console.log('Created:', metadata.creationDate); console.log('Imported:', metadata.isImported); } else { console.log('Local account - no metadata available'); } ``` ### Related * [Account.create()](/account/create) - Creates account with metadata * [Account.import()](/account/import) - Imports account (sets isImported: true) * [Account.fromLocalAccount()](/account/fromLocalAccount) - Creates account without metadata ## account.getPermission() Get details of a previously granted permission. **Type:** `instance async` ### Signature ```typescript async getPermission(permissionId: Hex): Promise ``` ### Parameters #### permissionId Type: `Hex` The permission ID (hash) to fetch. This is the `permissionId` field returned from `grantPermissions()`. ### Returns `Promise` - The permission details. ```typescript interface WalletGrantPermissionsResponse { /** Smart account this permission is valid for */ account: Address; /** Entity that can use this permission */ spender: Address; /** Timestamp (in seconds) that specifies when this permission becomes valid */ start: number; /** Timestamp (in seconds) that specifies the time by which this permission expires */ end: number; /** Salt used for permission uniqueness (as hex string) */ salt: Hex; /** Array of call permissions */ calls: CallPermissionDetail[]; /** Array of spend permissions */ spends: SpendPermissionDetail[]; /** Permission identifier - the permission hash from the contract */ permissionId: Hex; /** Chain ID in hex format */ chainId: Hex; } ``` ### Behavior 1. Fetches the permission from the JAW relay by ID 2. Returns the full permission details in the same format as `grantPermissions()` 3. Throws if the permission is not found ### Example ```typescript import { Account } from '@jaw.id/core'; const account = await Account.get({ chainId: 1, apiKey: 'your-api-key', }); const permission = await account.getPermission( '0x1234567890abcdef...' // Permission ID ); console.log('Spender:', permission.spender); console.log('Expires:', new Date(permission.end * 1000)); console.log('Call permissions:', permission.calls); console.log('Spend permissions:', permission.spends); ``` ### Related * [account.grantPermissions()](/account/grantPermissions) - Grant new permissions * [account.revokePermission()](/account/revokePermission) - Revoke a permission * [Subscription Payments Guide](/guides/subscription) - Learn about the permission system ## account.getSmartAccount() Get the underlying viem SmartAccount for advanced operations. **Type:** `instance` ### Signature ```typescript getSmartAccount(): SmartAccount ``` ### Parameters None. ### Returns `SmartAccount` - The raw viem SmartAccount instance. ### Behavior Returns the underlying viem `SmartAccount` instance, giving you direct access to all viem smart account methods and properties. ### Example ```typescript import { Account } from '@jaw.id/core'; const account = await Account.get({ chainId: 1, apiKey: 'your-api-key', }); const smartAccount = account.getSmartAccount(); console.log('SmartAccount type:', smartAccount.type); ``` ### Related * [account.getAddress()](/account/getAddress) - Get account address * [account.getChain()](/account/getChain) - Get chain configuration * [Account Overview](/account) - High-level Account API ## Account.getStoredAccounts() Get all stored passkey accounts. **Type:** `static` ### Signature ```typescript static getStoredAccounts(apiKey: string): PasskeyAccount[] ``` ### Parameters #### apiKey Type: `string` API key for JAW services. Used to scope the storage lookup. ### Returns `PasskeyAccount[]` - Array of stored passkey accounts. #### PasskeyAccount ```typescript interface PasskeyAccount { /** WebAuthn credential ID */ credentialId: string; /** Public key (hex encoded) */ publicKey: Hex; /** Smart account address */ address: Address; /** Username/display name */ username: string; /** ISO date string when created */ creationDate: string; /** Whether imported from cloud backup */ isImported: boolean; } ``` ### Behavior This is a synchronous method that retrieves all passkey accounts stored locally: 1. Reads from localStorage 2. Returns all stored accounts regardless of authentication state 3. Returns an empty array if no accounts are stored ### Example ```typescript import { Account } from '@jaw.id/core'; const accounts = Account.getStoredAccounts('your-api-key'); console.log(`Found ${accounts.length} stored accounts:`); accounts.forEach(acc => { console.log(`- ${acc.username}: ${acc.address}`); }); ``` ### Use Cases * **Account selector** - Build UI for selecting which account to login with * **Onboarding flow** - Check if user has existing accounts * **Account management** - Display list of all user accounts * **Pre-fetch account info** - Show account details before authentication ### Related * [Account.get()](/account/get) - Login with a specific account * [Account.getAuthenticatedAddress()](/account/getAuthenticatedAddress) - Get current authenticated address * [Account.create()](/account/create) - Create a new account * [Account.import()](/account/import) - Import account from cloud backup ## account.grantPermissions() Grant permissions to a spender. **Type:** `instance async` ### Signature ```typescript async grantPermissions( expiry: number, spender: Address, permissions: PermissionsDetail ): Promise ``` ### Parameters #### expiry Type: `number` Unix timestamp (in seconds) when the permission expires. #### spender Type: `Address` The address that can use this permission to execute calls on behalf of the account. #### permissions Type: `PermissionsDetail` The permissions to grant. ```typescript interface PermissionsDetail { /** Call permissions - which contracts and functions can be called */ calls?: CallPermissionDetail[]; /** Spend permissions - token spending limits */ spends?: SpendPermissionDetail[]; } interface CallPermissionDetail { /** Target contract address */ target: Address; /** Function selector (4 bytes) */ selector: Hex; } interface SpendPermissionDetail { /** Token address (use 0xEee...EEeE for native ETH) */ token: Address; /** Maximum amount that can be spent per period */ limit: string; /** Time period for the limit */ period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; } ``` ### Returns `Promise` - The granted permission details. ```typescript interface WalletGrantPermissionsResponse { /** Smart account this permission is valid for */ account: Address; /** Entity that can use this permission */ spender: Address; /** Timestamp (in seconds) that specifies when this permission becomes valid */ start: number; /** Timestamp (in seconds) that specifies the time by which this permission expires */ end: number; /** Salt used for permission uniqueness (as hex string) */ salt: Hex; /** Array of call permissions */ calls: CallPermissionDetail[]; /** Array of spend permissions */ spends: SpendPermissionDetail[]; /** Permission identifier - the permission hash from the contract */ permissionId: Hex; /** Chain ID in hex format */ chainId: Hex; } ``` ### Behavior 1. Creates a permission with the specified constraints 2. Signs the permission with the smart account 3. Registers the permission with the JAW relay 4. Returns the permission details including the unique ID ### Example ```typescript // Grant both call and spend permissions const permission = await account.grantPermissions( Math.floor(Date.now() / 1000) + 86400 * 7, // 1 week '0xSpenderAddress...', { calls: [ { target: ROUTER_ADDRESS, selector: '0x38ed1739', // swapExactTokensForTokens }, { target: ROUTER_ADDRESS, selector: '0x7ff36ab5', // swapExactETHForTokens } ], spends: [ { token: USDC_ADDRESS, limit: '1000000000', // 1000 USDC period: 'week', } ], } ); console.log('Permission ID:', permission.permissionId); console.log('Expires:', new Date(permission.end * 1000)); ``` ### Use Cases * **Session keys** - Allow a temporary key to perform specific actions * **Subscription services** - Grant recurring spending permissions * **Automated trading** - Allow a bot to execute specific trades * **Gaming** - Let a game contract execute moves without prompts ### Related * [account.revokePermission()](/account/revokePermission) - Revoke a granted permission * [account.getPermission()](/account/getPermission) - Get permission details * [Subscription Payments Guide](/guides/subscription) - Learn more about the permission system ## Account.import() Import a passkey from cloud backup. **Type:** `static async` ### Signature ```typescript static async import(config: AccountConfig): Promise ``` ### Parameters #### config Type: `AccountConfig` | Property | Type | Required | Description | | -------------- | -------- | -------- | ---------------------------------------- | | `chainId` | `number` | Yes | Chain ID for the account | | `apiKey` | `string` | Yes | API key for JAW services | | `paymasterUrl` | `string` | No | Custom paymaster URL for gas sponsorship | ### Returns `Promise` - The imported Account instance ### Behavior 1. Triggers WebAuthn authentication with empty `allowCredentials` (lets user select any available passkey) 2. User selects a passkey from their cloud-synced credentials (iCloud Keychain, Google Password Manager, etc.) 3. Creates the smart account from the imported passkey 4. Stores the passkey account marked as imported 5. Returns the ready-to-use Account instance ### Errors | Error | Description | | --------------------------------------------- | -------------------------------------------------- | | WebAuthn errors | User cancelled or browser doesn't support WebAuthn | | `Failed to retrieve imported passkey account` | Backend lookup failed | ### Examples #### Basic Usage ```typescript import { Account } from '@jaw.id/core'; const account = await Account.import({ chainId: 1, apiKey: 'your-api-key', }); console.log('Imported account:', account.address); const metadata = account.getMetadata(); console.log('Is imported:', metadata?.isImported); // true ``` ### Usage in Custom UI Handler ```typescript class MyUIHandler implements UIHandler { async handleConnect(request: ConnectUIRequest) { // Show import option in your UI const action = await this.showConnectOptions(); if (action === 'import') { const account = await Account.import({ chainId: request.data.chainId, apiKey: this.config?.apiKey, }); return { id: request.id, approved: true, data: { accounts: [{ address: account.address }], }, }; } // ... handle other actions } } ``` ### Cloud Sync Providers Passkeys can be synced via: * **Apple**: iCloud Keychain (iOS, macOS, Safari) * **Google**: Google Password Manager (Android, Chrome) * **Microsoft**: Windows Hello (Windows, Edge) * **1Password, Dashlane, etc.**: Third-party password managers The import flow allows users to access their JAW account on any device where their passkeys are synced. ### Related * [Account.create()](/account/create) - Create a new account * [Account.get()](/account/get) - Login with existing account * [Account.getStoredAccounts()](/account/getStoredAccounts) - List local accounts ## Account The `Account` class provides a low-level API for smart account operations. It's the recommended way to interact with JAW smart accounts when building custom UI handlers or server-side applications. ### When to Use Account The `Account` class is ideal for: * **Custom UI Handlers** - When implementing your own `UIHandler` for app-specific mode * **Server-side Applications** - Using `fromLocalAccount()` with embedded wallets (Privy, Dynamic, Turnkey) * **Direct Integration** - When you need fine-grained control over account operations For standard client-side usage with the built-in UI, use the provider-based API instead. ### Installation :::code-group ```bash [npm] npm install @jaw.id/core viem ``` ```bash [pnpm] pnpm add @jaw.id/core viem ``` ```bash [yarn] yarn add @jaw.id/core viem ``` ```bash [bun] bun add @jaw.id/core viem ``` ::: ### Import The `Account` class is exported from `@jaw.id/core`: ```typescript import { Account } from '@jaw.id/core'; ``` ### Static Methods Factory methods for creating Account instances: | Method | Description | | ------------------------------------------------------- | ------------------------------------------------ | | [Account.get()](/account/get) | Get account - restores session or triggers login | | [Account.create()](/account/create) | Create new account with passkey | | [Account.import()](/account/import) | Import passkey from cloud backup | | [Account.fromLocalAccount()](/account/fromLocalAccount) | Create from viem LocalAccount (server-side) | Utility methods: | Method | Description | | --------------------------------------------------------------------- | --------------------------------- | | [Account.getAuthenticatedAddress()](/account/getAuthenticatedAddress) | Get current authenticated address | | [Account.getStoredAccounts()](/account/getStoredAccounts) | Get all stored passkey accounts | | [Account.logout()](/account/logout) | Clear authentication state | ### Instance Properties | Property | Type | Description | | --------- | --------- | --------------------- | | `address` | `Address` | Smart account address | | `chainId` | `number` | Current chain ID | ### Instance Methods #### Information | Method | Description | | --------------------------------------------- | ---------------------------------------------- | | [getMetadata()](/account/getMetadata) | Get account metadata (username, creation date) | | [getSmartAccount()](/account/getSmartAccount) | Get underlying viem SmartAccount | | [getChain()](/account/getChain) | Get chain configuration | | [getAddress()](/account/getAddress) | Get address (async, for counterfactual) | #### Signing | Method | Description | | ----------------------------------------- | --------------------------------- | | [signMessage()](/account/signMessage) | Sign a personal message (EIP-191) | | [signTypedData()](/account/signTypedData) | Sign typed data (EIP-712) | #### Transactions | Method | Description | | --------------------------------------------- | ---------------------------------------- | | [sendTransaction()](/account/sendTransaction) | Send transaction and wait for receipt | | [sendCalls()](/account/sendCalls) | Send bundled calls (returns immediately) | | [estimateGas()](/account/estimateGas) | Estimate gas for calls | #### Permissions | Method | Description | | ----------------------------------------------- | ------------------------------ | | [grantPermissions()](/account/grantPermissions) | Grant permissions to a spender | | [revokePermission()](/account/revokePermission) | Revoke a permission | | [getPermission()](/account/getPermission) | Get permission details | ### Interfaces #### AccountConfig Configuration for creating or loading an account: ```typescript interface AccountConfig { /** Chain ID for the account */ chainId: number; /** API key for JAW services (required) */ apiKey: string; /** Custom paymaster URL for gas sponsorship */ paymasterUrl?: string; } ``` #### CreateAccountOptions Options for creating a new account: ```typescript interface CreateAccountOptions { /** Username/display name for the passkey */ username: string; /** Relying party identifier (defaults to window.location.hostname) */ rpId?: string; /** Relying party name (defaults to 'JAW') */ rpName?: string; } ``` #### TransactionCall Transaction call structure: ```typescript interface TransactionCall { /** Target contract address */ to: Address; /** Value to send in wei (bigint or hex string) */ value?: bigint | string; /** Call data */ data?: Hex; } ``` #### AccountMetadata Account metadata returned by `getMetadata()`: ```typescript interface AccountMetadata { /** Username/display name */ username: string; /** ISO date string when created */ creationDate: string; /** Whether imported from cloud */ isImported: boolean; } ``` ### Value Format The `value` field in `TransactionCall` must be in **wei** (the smallest unit of ETH): ```typescript import { parseEther } from 'viem'; // BigInt (wei) { to: '0x...', value: 1000000000000000000n } // 1 ETH in wei // Hex string (wei) { to: '0x...', value: '0x0de0b6b3a7640000' } // 1 ETH in wei // Using parseEther for convenience { to: '0x...', value: parseEther('1') } // 1 ETH { to: '0x...', value: parseEther('0.1') } // 0.1 ETH ``` Use `parseEther()` from viem to convert human-readable ETH amounts to wei. ### Related * [Custom UI Handler](/advanced/custom-ui-handler) - Build custom UI with Account class * [Provider - RPC Reference](/api-reference) - Provider-based RPC methods ## Account.logout() Clear authentication state (logout). **Type:** `static` ### Signature ```typescript static logout(apiKey: string): void ``` ### Parameters #### apiKey Type: `string` API key for JAW services. Used to scope the storage operation. ### Returns `void` ### Behavior This method clears the current authentication state: 1. Removes the authenticated address from storage 2. Clears the current credential ID association 3. Does **not** remove stored passkey accounts (user can still login later) After logout: * `Account.getAuthenticatedAddress()` returns `null` * `Account.get()` without credentialId will throw * `Account.getStoredAccounts()` still returns all accounts ### Example ```typescript import { Account } from '@jaw.id/core'; Account.logout('your-api-key'); console.log('Logged out'); ``` ### Important Notes * Logout is **local only** - it doesn't affect the smart account * Stored passkey accounts remain available for future login * The user can still access their account by logging in again ### Related * [Account.get()](/account/get) - Login to an account * [Account.getAuthenticatedAddress()](/account/getAuthenticatedAddress) - Check authentication status * [Account.getStoredAccounts()](/account/getStoredAccounts) - List available accounts ## account.revokePermission() Revoke a previously granted permission. **Type:** `instance async` ### Signature ```typescript async revokePermission(permissionId: Hex): Promise ``` ### Parameters #### permissionId Type: `Hex` The permission ID (hash) to revoke. This is the `id` field returned from `grantPermissions()`. ### Returns `Promise` - The revocation response. ```typescript interface RevokePermissionApiResponse { /** Whether the revocation was successful */ success: boolean; /** Transaction hash if on-chain revocation was needed */ transactionHash?: Hex; } ``` ### Behavior 1. Sends a revocation request to the JAW relay 2. Signs the revocation with the smart account 3. The permission is invalidated and can no longer be used 4. Returns confirmation of the revocation ### Example ```typescript import { Account } from '@jaw.id/core'; const account = await Account.get({ chainId: 1, apiKey: 'your-api-key', }); // Revoke a permission by its ID const response = await account.revokePermission( '0x1234567890abcdef...' // Permission ID from grantPermissions() ); if (response.success) { console.log('Permission revoked successfully'); } else { console.log('Revocation failed'); } ``` ### Related * [account.grantPermissions()](/account/grantPermissions) - Grant new permissions * [account.getPermission()](/account/getPermission) - Get permission details * [Subscription Payments Guide](/guides/subscription) - Learn about the permission system ## account.sendCalls() Send multiple calls as a bundled user operation without waiting for receipt. **Type:** `instance async` ### Signature ```typescript async sendCalls( calls: TransactionCall[], options?: SendCallsOptions ): Promise ``` ### Parameters #### calls Type: `TransactionCall[]` Array of transaction calls to execute. ```typescript interface TransactionCall { /** Target contract address */ to: Address; /** Value to send in wei (bigint or hex string) */ value?: bigint | string; /** Call data */ data?: Hex; } ``` #### options (optional) Type: `SendCallsOptions` Optional settings for call execution. ```typescript interface SendCallsOptions { /** Permission ID to use for executing the calls through the permission manager */ permissionId?: Hex; } ``` ### Returns `Promise` - The user operation ID and chain ID. ```typescript interface BundledTransactionResult { /** User operation hash */ id: Hex; /** Chain ID where the operation was submitted */ chainId: number; } ``` ### Behavior 1. Bundles all calls into a single user operation 2. Sends the user operation to the bundler 3. **Returns immediately** with the user operation ID 4. Does not wait for transaction inclusion For waiting until the transaction is mined, use [`sendTransaction()`](/account/sendTransaction). ### Example #### Getting an Account Instance Before calling instance methods, create an account using one of the factory methods: ```typescript import { Account } from '@jaw.id/core'; // Option 1: Restore existing session or login with passkey const account = await Account.get({ chainId: 1, apiKey: 'your-api-key' }); // Option 2: Create a new account with passkey const account = await Account.create( { chainId: 1, apiKey: 'your-api-key' }, { username: 'alice' } ); // Option 3: Import from cloud backup const account = await Account.import({ chainId: 1, apiKey: 'your-api-key' }); // Option 4: From a local account (server-side / embedded wallets) const account = await Account.fromLocalAccount( { chainId: 1, apiKey: 'your-api-key' }, localAccount ); ``` #### Batch Multiple Calls ```typescript import { encodeFunctionData } from 'viem'; // Multiple token transfers in one operation const transfers = recipients.map(({ address, amount }) => ({ to: USDC_ADDRESS, data: encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [address, amount], }), })); const { id } = await account.sendCalls(transfers); console.log('Batch transfer submitted:', id); ``` #### Execute with Permission (Delegated Execution) When a permission has been granted via `account.grantPermissions()`, you can execute calls using that permission. This allows delegated execution within the permission's constraints. ```typescript import { encodeFunctionData } from 'viem'; // First, get the permission ID from a previous grantPermissions call const permissionId = '0x1234...'; // The permission ID returned from grantPermissions const transferData = encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: ['0x5678...', 1000000n], }); // Execute the transfer using the permission const { id } = await account.sendCalls( [{ to: USDC_ADDRESS, data: transferData }], { permissionId } ); console.log('Batch transfer submitted with permission:', id); ``` When using the `permissionId` option: * The calls are executed through the JustaPermissionManager contract * The permission's call and spend limits are enforced * The spender must have been granted appropriate permissions for the calls being made ### sendTransaction vs sendCalls | | `sendTransaction()` | `sendCalls()` | | -------- | ------------------- | ----------------------- | | Returns | Transaction hash | User operation ID | | Waits | Yes, until mined | No, returns immediately | | Use case | Need confirmation | Fire and forget | ### Related * [account.sendTransaction()](/account/sendTransaction) - Send and wait for receipt * [account.estimateGas()](/account/estimateGas) - Estimate gas before sending ## account.sendTransaction() Send a transaction and wait for the receipt. **Type:** `instance async` ### Signature ```typescript async sendTransaction(calls: TransactionCall[]): Promise ``` ### Parameters #### calls Type: `TransactionCall[]` Array of transaction calls to execute. ```typescript interface TransactionCall { /** Target contract address */ to: Address; /** Value to send in wei (bigint or hex string) */ value?: bigint | string; /** Call data */ data?: Hex; } ``` ### Returns `Promise` - The transaction hash after the transaction is included in a block. ### Value Format The `value` field must be in **wei** (the smallest unit of ETH): ```typescript import { parseEther } from 'viem'; // BigInt (wei) { to: '0x...', value: 1000000000000000000n } // 1 ETH in wei // Hex string (wei) { to: '0x...', value: '0x0de0b6b3a7640000' } // 1 ETH in wei // Using parseEther for convenience { to: '0x...', value: parseEther('1') } // 1 ETH { to: '0x...', value: parseEther('0.1') } // 0.1 ETH ``` ### Example #### Getting an Account Instance Before calling instance methods, create an account using one of the factory methods: ```typescript import { Account } from '@jaw.id/core'; // Option 1: Restore existing session or login with passkey const account = await Account.get({ chainId: 1, apiKey: 'your-api-key' }); // Option 2: Create a new account with passkey const account = await Account.create( { chainId: 1, apiKey: 'your-api-key' }, { username: 'alice' } ); // Option 3: Import from cloud backup const account = await Account.import({ chainId: 1, apiKey: 'your-api-key' }); // Option 4: From a local account (server-side / embedded wallets) const account = await Account.fromLocalAccount( { chainId: 1, apiKey: 'your-api-key' }, localAccount ); ``` #### Send ETH ```typescript import { parseEther } from 'viem'; // Send 0.1 ETH const hash = await account.sendTransaction([ { to: '0xRecipient...', value: parseEther('0.1') } ]); console.log('Transaction hash:', hash); ``` #### Contract Call ```typescript import { encodeFunctionData } from 'viem'; // Encode the function call const data = encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: ['0xRecipient...', 1000000n], // 1 USDC (6 decimals) }); const hash = await account.sendTransaction([ { to: USDC_ADDRESS, data } ]); ``` ### Related * [account.sendCalls()](/account/sendCalls) - Send without waiting for receipt * [account.estimateGas()](/account/estimateGas) - Estimate gas before sending ## account.signMessage() Sign a personal message (EIP-191). **Type:** `instance async` ### Signature ```typescript async signMessage(message: string): Promise ``` ### Parameters #### message Type: `string` The message to sign. ### Returns `Promise` - The signature as a hex string. ### Behavior 1. Creates an EIP-191 personal sign message 2. Signs with the smart account (passkey or local account) 3. Returns the signature wrapped for smart account validation ### Example Before calling instance methods, create an account using one of the factory methods: ```typescript import { Account } from '@jaw.id/core'; // Option 1: Restore existing session or login with passkey const account = await Account.get({ chainId: 1, apiKey: 'your-api-key' }); // Option 2: Create a new account with passkey const account = await Account.create( { chainId: 1, apiKey: 'your-api-key' }, { username: 'alice' } ); // Option 3: Import from cloud backup const account = await Account.import({ chainId: 1, apiKey: 'your-api-key' }); // Option 4: From a local account (server-side / embedded wallets) const account = await Account.fromLocalAccount( { chainId: 1, apiKey: 'your-api-key' }, localAccount ); ``` #### Basic Usage ```typescript const signature = await account.signMessage('Hello, World!'); console.log('Signature:', signature); ``` #### Sign-In with Ethereum (SIWE) ```typescript // Construct SIWE message const siweMessage = `myapp.com wants you to sign in with your Ethereum account: ${account.address} Sign in to MyApp URI: https://myapp.com Version: 1 Chain ID: 1 Nonce: ${generateNonce()} Issued At: ${new Date().toISOString()}`; const signature = await account.signMessage(siweMessage); // Send to backend for verification await fetch('/api/verify-siwe', { method: 'POST', body: JSON.stringify({ message: siweMessage, signature, address: account.address, }), }); ``` ### Related * [account.signTypedData()](/account/signTypedData) - Sign EIP-712 typed data * [account.sendTransaction()](/account/sendTransaction) - Send transactions * [Account Overview](/account) - High-level Account API ## account.signTypedData() Sign EIP-712 typed data. **Type:** `instance async` ### Signature ```typescript async signTypedData( typedData: TypedDataDefinition ): Promise ``` ### Parameters #### typedData Type: `TypedDataDefinition` The EIP-712 typed data to sign, including domain, types, primary type, and message. ```typescript interface TypedDataDefinition { domain: { name?: string; version?: string; chainId?: number; verifyingContract?: Address; salt?: Hex; }; types: Record>; primaryType: string; message: Record; } ``` ### Returns `Promise` - The signature as a hex string. ### Behavior 1. Encodes the typed data according to EIP-712 2. Signs with the smart account (passkey or local account) 3. Returns the signature wrapped for smart account validation ### Example #### Getting an Account Instance Before calling instance methods, create an account using one of the factory methods: ```typescript import { Account } from '@jaw.id/core'; // Option 1: Restore existing session or login with passkey const account = await Account.get({ chainId: 1, apiKey: 'your-api-key' }); // Option 2: Create a new account with passkey const account = await Account.create( { chainId: 1, apiKey: 'your-api-key' }, { username: 'alice' } ); // Option 3: Import from cloud backup const account = await Account.import({ chainId: 1, apiKey: 'your-api-key' }); // Option 4: From a local account (server-side / embedded wallets) const account = await Account.fromLocalAccount( { chainId: 1, apiKey: 'your-api-key' }, localAccount ); ``` #### Basic Usage ```typescript const signature = await account.signTypedData({ domain: { name: 'MyApp', version: '1', chainId: 1, verifyingContract: '0x...', }, types: { Message: [ { name: 'content', type: 'string' }, { name: 'timestamp', type: 'uint256' }, ], }, primaryType: 'Message', message: { content: 'Hello, World!', timestamp: BigInt(Date.now()), }, }); ``` ### Related * [account.signMessage()](/account/signMessage) - Sign personal messages * [account.sendTransaction()](/account/sendTransaction) - Send transactions * [EIP-712 Specification](https://eips.ethereum.org/EIPS/eip-712) - Typed data standard