# Session Keys (/docs/rise-wallet/session-keys)

import { SessionKeysWidget } from "@/components/rise-wallet/SessionKeysWidget";
import { ComponentPreviewTabs } from "@/components/rise-wallet/ComponentPreviewTabs";
import { CODE_EXAMPLES } from "@/components/rise-wallet/code-examples";

# Session Keys

Session keys are temporary keys that are granted specific permissions. They allow your app to sign transactions on behalf of the user without prompting them for confirmation every time.

This is critical for:

* **High-frequency trading**: Place and cancel orders instantly.
* **Games**: Move characters or perform actions without interrupting gameplay.
* **Social apps**: Like posts or follow users with a single tap.

<ComponentPreviewTabs code={CODE_EXAMPLES.sessions}>
  <SessionKeysWidget />
</ComponentPreviewTabs>

## Creating a Session Key

You use the `useGrantPermissions` hook from `rise-wallet/wagmi` to request a new session key. You must define exactly what this key can do (permissions) and how long it lasts (expiry).

```tsx
import { Hooks } from "rise-wallet/wagmi";
import { P256, PublicKey } from "ox";
import { keccak256, parseEther, parseUnits, toHex } from "viem";

// ... inside component
const grantPermissions = Hooks.useGrantPermissions();

const createSession = async () => {
  // 1. Generate a local key pair (P256)
  const privateKey = P256.randomPrivateKey();
  const publicKey = PublicKey.toHex(P256.getPublicKey({ privateKey }), {
    includePrefix: false,
  });

  // 2. Request the session key from the wallet with permissions
  await grantPermissions.mutateAsync({
    key: { publicKey, type: "p256" },
    expiry: Math.floor(Date.now() / 1000) + 3600, // 1 hour
    feeToken: null, // use native token for fees, or { limit: "0.01", symbol: "ETH" }

    // the permissions you want the session key to have
    permissions: {
      calls: [
        {
          to: "0x...", // address of the contract on which the function is being called
          signature: keccak256(toHex("transfer(address,uint256)")).slice(0, 10), // function selector (in this case, 0xa9059cbb)
        }
      ],

      // token spend limits
      spend: [
        {
          token: "0x0000000000000000000000000000000000000000", // native ETH
          limit: parseEther("20"),
          period: "hour",
        },
        {
          token: "0xUSDC...", // USDC (6 decimals)
          limit: parseUnits("100", 6),
          period: "day",
        },
      ],
    },
  });

  // 3. Store the private key securely (e.g., localStorage) to sign future requests
  localStorage.setItem("session_key", privateKey);
};
```

Once created, you can use the stored private key to sign and send `wallet_sendPreparedCalls` requests directly to the RISE RPC, bypassing the wallet popup completely.

## Using a Session Key

After creating a session key, you can use it to sign and execute transactions without wallet popups. Here's a complete example:

```tsx
import { Hex, P256, Signature } from "ox";
import { useAccount, useChainId } from "wagmi";

// ... inside component
const { connector, address } = useAccount();
const chainId = useChainId();

const executeWithSessionKey = async (calls: any[]) => {
  // 1. Get the stored private key and public key
  const privateKey = localStorage.getItem("session_key");
  const publicKey = PublicKey.toHex(P256.getPublicKey({ privateKey }), {
    includePrefix: false,
  });

  // 2. Get the provider
  const provider = (await connector.getProvider()) as any;

  // 3. Prepare the calls (this simulates and estimates fees)
  const intentParams = [
    {
      calls, // Array of { to: address, data?: hex, value?: bigint }
      chainId: Hex.fromNumber(chainId),
      from: address,
      atomicRequired: true,
      key: {
        publicKey,
        type: "p256",
      },
    },
  ];

  // wallet_prepareCalls returns: { digest, capabilities, ...request }
  // digest: The hash you need to sign
  // capabilities: Optional wallet capabilities
  // request: Other fields needed for sending the transaction
  const { digest, capabilities, ...request } = await provider.request({
    method: "wallet_prepareCalls",
    params: intentParams,
  });

  // 4. Sign the digest with your session key
  const signature = Signature.toHex(
    P256.sign({
      payload: digest as `0x${string}`,
      privateKey: privateKey as `0x${string}`,
    })
  );

  // 5. Send the prepared calls with the signature
  const result = await provider.request({
    method: "wallet_sendPreparedCalls",
    params: [
      {
        ...request,
        ...(capabilities ? { capabilities } : {}),
        signature,
      },
    ],
  });

  console.log("Transaction sent:", result);
  return result;
};

// Example usage: Mint an NFT
(async () => {
  await executeWithSessionKey([
    {
      to: "0x..." as `0x${string}`, // NFT contract address
      data: "0x..." as `0x${string}`, // Encoded mint function call
      value: 0n,
    },
  ]);
})();
```

The flow is:

1. **Prepare** - Call `wallet_prepareCalls` to simulate the transaction and get a digest
2. **Sign** - Sign the digest with your session key's private key using P256
3. **Send** - Call `wallet_sendPreparedCalls` with the signature to execute without popup
