# Wagmi Integration (/docs/rise-wallet/wagmi)

import { Tab, Tabs } from 'fumadocs-ui/components/tabs';

This guide covers integrating RISE Wallet with [Wagmi](https://wagmi.sh/), the popular collection of React Hooks for Ethereum.

## Overview

RISE Wallet implements a Wagmi connector and provides custom React hooks that map directly to the wallet's capabilities. The integration is designed to feel natural to developers already familiar with Wagmi.

## Installation

<Tabs groupId="package-manager" items={["npm", "pnpm", "yarn", "bun"]}>
  <Tab value="npm">
    ```bash
    npm i rise-wallet wagmi viem @tanstack/react-query
    ```
  </Tab>

  <Tab value="pnpm">
    ```bash
    pnpm add rise-wallet wagmi viem @tanstack/react-query
    ```
  </Tab>

  <Tab value="yarn">
    ```bash
    yarn add rise-wallet wagmi viem @tanstack/react-query
    ```
  </Tab>

  <Tab value="bun">
    ```bash
    bun add rise-wallet wagmi viem @tanstack/react-query
    ```
  </Tab>
</Tabs>

## Basic Setup

### 1. Configure the RISE Wallet Connector

```ts title="config/wagmi.ts"
import { Chains, RiseWallet } from "rise-wallet";
import { riseWallet } from "rise-wallet/wagmi";
import { createConfig, http } from "wagmi";

// Export the connector for advanced usage
export const rwConnector = riseWallet(RiseWallet.defaultConfig);

// Create wagmi config
export const config = createConfig({
  chains: [Chains.riseTestnet],
  connectors: [rwConnector],
  transports: {
    [Chains.riseTestnet.id]: http("https://testnet.riselabs.xyz"),
  },
});
```

### 2. Set Up Providers

```tsx title="app/providers.tsx"
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { WagmiProvider } from "wagmi";
import { config } from "@/config/wagmi";
import { useState } from "react";

export function Providers({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(() => new QueryClient());

  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiProvider>
  );
}
```

### 3. Wrap Your App in the Provider

Add the `Providers` component to your root layout:

```tsx title="app/layout.tsx"
import { Providers } from "./providers";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

## Standard Wagmi Hooks

All standard Wagmi hooks work seamlessly with RISE Wallet:

### Connection Management

```tsx title="components/WalletButton.tsx"
import { useConnect, useConnection, useDisconnect, useConnectors } from "wagmi";

export function WalletButton() {
  const { address, isConnected } = useConnection();
  const connect = useConnect();
  const disconnect = useDisconnect();
  const connectors = useConnectors();

  if (isConnected) {
    return (
      <div>
        <span>{address}</span>
        <button onClick={() => disconnect.mutate()}>Disconnect</button>
      </div>
    );
  }

  const rwConnector = connectors.find(c => c.id === "com.risechain.wallet");
  if (!rwConnector) return null;

  return (
    <button onClick={() => connect.mutate({ connector: rwConnector })}>
      Connect with Passkey
    </button>
  );
}
```

### Sending Transactions

```tsx title="components/SendTransaction.tsx"
import { useSendCalls } from "wagmi";
import { parseEther } from "viem";

export function SendTransaction() {
  const sendCalls = useSendCalls();
  
  //for sending 0.001 ETH to some address
  const handleSend = async () => {
    const result = await sendCalls.mutateAsync({
      calls: [{
        to: "0x...",
        value: parseEther("0.001"),
      }],
    });

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

  return (
    <button onClick={handleSend} disabled={sendCalls.isPending}>
      {sendCalls.isPending ? "Sending..." : "Send 0.001 ETH"}
    </button>
  );
}
```

## RISE Wallet Specific Hooks

Import custom hooks via the `Hooks` namespace:

```tsx
import { Hooks } from "rise-wallet/wagmi";
```

or individually:

```tsx
import { useGrantPermissions } from "rise-wallet/wagmi/Hooks";
```

### useGrantPermissions

Create session keys with specific permissions:

```tsx
import { parseEther } from "viem";
import { P256, PublicKey } from "ox";

const grantPermissions = Hooks.useGrantPermissions();

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

  // 2. Grant permissions to the session key
  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: [{ // function calls
        to: "0x...", // address of the contract on which the function is being called
        signature: "0x...", // function signature. you can get this via: cast sig "transfer(address,uint256)"
      }],
      
      // token spend limits
      spend: [{
        token: "0x...", // token contract address
        limit: parseEther("50"), // max amount (bigint)
        period: "minute", // time window (eg. "minute", "hour", "day")
      }],
    },
  });

  // 3. Store the private key to sign future transactions without popup
  localStorage.setItem("session_key", privateKey);
};
```

### usePermissions

View current permissions for the connected account:

```tsx
const { data: permissions } = Hooks.usePermissions();

if (permissions) {
  console.log("Active permissions:", permissions);
}
```

### useRevokePermissions

Revoke previously granted permissions:

```tsx
const revokePermissions = Hooks.useRevokePermissions();

const revokeSession = async (keyId: string) => {
  await revokePermissions.mutateAsync({
    keyId,
  });
};
```

### useAssets

Get user's token balances and assets:

```tsx
const { data: assets } = Hooks.useAssets();

return (
  <div>
    {assets?.map(asset => (
      <div key={asset.address}>
        {asset.symbol}: {asset.balance}
      </div>
    ))}
  </div>
);
```

## Polling Transaction Status

When using session keys with `wallet_sendPreparedCalls`, you need to manually poll for transaction status using `wallet_getCallsStatus`:

```tsx
import { P256, Signature } from "ox";

async function sendWithSessionKey(calls: any[], sessionPrivateKey: string, sessionPublicKey: string) {
  const provider = await connector.getProvider();

  // 1. Prepare the calls
  const { digest, ...request } = await provider.request({
    method: "wallet_prepareCalls",
    params: [{
      calls,
      chainId: "0x...",
      from: address,
      key: { publicKey: sessionPublicKey, type: "p256" },
    }],
  });

  // 2. Sign with session key
  const signature = Signature.toHex(
    P256.sign({ payload: digest, privateKey: sessionPrivateKey })
  );

  // 3. Send signed calls
  const result = await provider.request({
    method: "wallet_sendPreparedCalls",
    params: [{ ...request, signature }],
  });

  // 4. Extract bundle ID
  const bundleId = Array.isArray(result) ? result[0].id : result.id;

  // 5. Poll for transaction status
  const status = await provider.request({
    method: "wallet_getCallsStatus",
    params: [bundleId],
  });

  // 6. Check status
  if (status.status !== 200) {
    throw new Error(`Transaction failed with status ${status.status}`);
  }

  // 7. Return transaction hash
  return status.receipts[0].transactionHash;
}
```

### Status Codes

The `status` field in the response indicates the current state of the transaction bundle:

| Code | Description                                                                           |
| ---- | ------------------------------------------------------------------------------------- |
| 100  | **Pending** - Bundle received but not executed onchain yet                            |
| 200  | **Confirmed** - Bundle included onchain without reverts, receipts available           |
| 300  | **Failed (Offchain)** - Bundle not included onchain, wallet will not retry            |
| 400  | **Reverted (Complete)** - Bundle completely reverted, only gas charges may be onchain |
| 500  | **Reverted (Partial)** - Bundle partially reverted, some changes may be onchain       |

### Response Structure

```ts
type GetCallsStatusResponse = {
  id: string,                    // Bundle ID
  status: number,                // Status code (100-500)
  receipts: {
    logs: {
      chainId: string,
      address: string,
      data: string,
      topics: string[],
    }[],
    status: string,              // "0x1" for success, "0x0" for failure
    blockHash?: string,
    blockNumber?: string,
    gasUsed: string,
    transactionHash: string,     // The actual transaction hash
  }[],
  capabilities?: {
    interopStatus?: 'pending' | 'confirmed' | 'failed',
  },
}
```

<Callout type="info">
  **Note:** Standard Wagmi hooks like `useSendCalls` handle polling internally using `sendCallsSync`. You only need to manually poll `wallet_getCallsStatus` when using session keys with `wallet_sendPreparedCalls`.
</Callout>

## Advanced Patterns

### Batched Transactions

Execute multiple operations atomically:

```tsx
const sendCalls = useSendCalls();

// Approve and swap in one transaction
await sendCalls.mutateAsync({
  calls: [
    {
      to: TOKEN_ADDRESS,
      data: approveCalldata,
    },
    {
      to: DEX_ADDRESS,
      data: swapCalldata,
    },
  ],
  // Atomic execution - all succeed or all fail
  atomicRequired: true,
});
```

### Reading Transaction Status

Monitor transaction progress:

```tsx
import { useWaitForTransactionReceipt } from "wagmi";

const { data: receipt, isLoading } = useWaitForTransactionReceipt({
  hash: transactionHash,
});

if (receipt) {
  console.log("Transaction confirmed:", receipt);
}
```

### Error Handling

Properly handle wallet errors:

```tsx
import { BaseError } from "wagmi";

const sendCalls = useSendCalls();

try {
  await sendCalls.mutateAsync({...});
} catch (error) {
  if (error instanceof BaseError) {
    // Handle specific error types
    if (error.shortMessage.includes("rejected")) {
      console.log("User rejected the request");
    }
  }
}
```

## TypeScript Support

RISE Wallet provides full TypeScript support:

```tsx
import type { RiseWallet } from "rise-wallet";
import type { Config } from "wagmi";

// Config is fully typed
const config: Config = createConfig({
  chains: [Chains.riseTestnet],
  connectors: [rwConnector],
  transports: {
    [Chains.riseTestnet.id]: http(),
  },
});
```

## Best Practices

### 1. Check Connection State

Always verify the connection state before attempting transactions:

```tsx
const { isConnected } = useConnection();

if (!isConnected) {
  return <ConnectButton />;
}
```

### 2. Handle Loading States

Provide feedback during async operations:

```tsx
const sendCalls = useSendCalls();
// Access: sendCalls.isPending, sendCalls.isSuccess, sendCalls.isError
```

### 3. Use Error Boundaries

Wrap your app with error boundaries to catch unexpected errors:

```tsx
import { ErrorBoundary } from "react-error-boundary";

<ErrorBoundary fallback={<ErrorFallback />}>
  <App />
</ErrorBoundary>
```

## Examples

For complete working examples, check out:

* [Basic Integration](https://github.com/risechain/wallet-demo)
* [Session Keys Demo](https://demo.wallet.risechain.com/)
* [DeFi Integration](/docs/rise-wallet/getting-started/swapping)

## Related Documentation

* [Wagmi Documentation](https://wagmi.sh/)
* [Viem Integration](/docs/rise-wallet/viem)
* [Session Keys Guide](/docs/rise-wallet/session-keys)
