# Viem Integration (/docs/rise-wallet/viem)

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

This guide covers integrating RISE Wallet with [Viem](https://viem.sh/), a TypeScript interface for Ethereum that provides low-level access to blockchain functionality.

## Overview

RISE Wallet provides first-class Viem support through custom actions and account implementations. This integration is ideal for developers who need fine-grained control over wallet operations.

## Installation

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

  <Tab value="pnpm">
    ```bash
    pnpm add rise-wallet viem
    ```
  </Tab>

  <Tab value="yarn">
    ```bash
    yarn add rise-wallet viem
    ```
  </Tab>

  <Tab value="bun">
    ```bash
    bun add rise-wallet viem
    ```
  </Tab>
</Tabs>

## Basic Usage

### Creating a Client

Use RISE Wallet's EIP-1193 provider with Viem's custom transport:

```ts
import { createClient, custom } from "viem";
import { RiseWallet } from "rise-wallet";

// Create RISE Wallet instance
const rw = RiseWallet.create();

// Create Viem client with RISE Wallet provider
const client = createClient({
  transport: custom(rw.provider),
});
```

### Using Wallet Actions

RISE Wallet exports a `WalletActions` module with RISE Wallet-specific functionality:

```ts
import { WalletActions } from "rise-wallet/viem";

// Connect to wallet
const { accounts } = await WalletActions.connect(client);
console.log("Connected account:", accounts[0]);

// Get user assets
const assets = await WalletActions.getAssets(client);
console.log("User assets:", assets);
```

## Standard Viem Actions

All standard Viem wallet actions work with RISE Wallet:

### Send Transaction

```ts
import { parseEther } from "viem";
import * as Actions from "viem/actions";

const hash = await Actions.sendTransaction(client, {
  to: "0x...",
  value: parseEther("0.001"),
});

console.log("Transaction hash:", hash);
```

### Send Calls (EIP-5792)

```ts
const result = await Actions.sendCalls(client, {
  account: "0x...",
  calls: [
    {
      to: "0x...",
      data: "0x...",
    },
  ],
});
```

### Sign Message

```ts
const signature = await Actions.signMessage(client, {
  account: "0x...",
  message: "Hello RISE!",
});
```

## RISE Wallet Actions

### Grant Permissions

Create session keys with specific permissions:

```ts
import { Key } from "rise-wallet/viem";
import { keccak256, toHex, parseEther } from "viem";

const permissions = await WalletActions.grantPermissions(client, {
  key: Key.createP256(), // Generate new P256 key
  expiry: Math.floor(Date.now() / 1000) + 3600, // 1 hour
  permissions: {
    calls: [
      {
        to: "0x...", // Contract address
        signature: keccak256(toHex("transfer(address,uint256)")).slice(0, 10),
      }
    ],
    spend: [
      {
        limit: parseEther("50"),
        period: "minute",
        token: "0x...",
      }
    ],
  },
});
```

### Get Permissions

View active permissions:

```ts
const permissions = await WalletActions.getPermissions(client);

permissions.forEach(permission => {
  console.log("Key:", permission.key);
  console.log("Expiry:", permission.expiry);
  console.log("Allowed calls:", permission.calls);
});
```

### Revoke Permissions

Remove granted permissions:

```ts
await WalletActions.revokePermissions(client, {
  keyId: "0x...",
});
```

## Key Management

RISE Wallet provides utilities for working with different key types:

### P256 Keys

```ts
import { Key, PublicKey } from "rise-wallet/viem";

// Create new P256 key
const key = Key.createP256();

// Import existing key
const imported = Key.fromP256({
  privateKey: "0x...",
});

// Sign with key
const signature = await Key.sign({
  payload: "0x...",
  privateKey: key.privateKey,
});
```

### WebAuthn Keys

```ts
// Create WebAuthn credential
const credential = await Key.createWebAuthnP256({
  user: {
    name: "alice@example.com",
    displayName: "Alice",
  },
});

// Use for signing
const webAuthnKey = Key.fromWebAuthnP256({
  credential,
  publicKey: credential.publicKey,
});
```

## Advanced Patterns

### Direct Provider Access

For maximum control, access the provider directly:

```ts
const provider = await rw.provider;

// Use JSON-RPC methods directly
const result = await provider.request({
  method: "wallet_prepareCalls",
  params: [{
    calls: [{
      to: "0x...",
      data: "0x...",
    }],
    chainId: "0x...",
    from: "0x...",
  }],
});
```

### Session Key Signing

Sign transactions with session keys:

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

// Prepare transaction
const { digest, ...request } = await provider.request({
  method: "wallet_prepareCalls",
  params: [{
    calls: [...],
    key: { publicKey, type: "p256" },
  }],
});

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

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

// Get the call bundle ID
const bundleId = Array.isArray(result) ? result[0].id : result.id;

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

if (status.status === 200) {
  console.log("Transaction confirmed!");
  console.log("TX hash:", status.receipts[0].transactionHash);
} else {
  console.error("Transaction failed with status:", status.status);
}
```

### Custom Actions

Create your own actions by extending Viem:

```ts
import { type Client } from "viem";

export async function myCustomAction(client: Client, params: any) {
  return client.request({
    method: "my_custom_method",
    params,
  });
}

// Use with client.extend()
const extendedClient = client.extend(() => ({
  myCustomAction,
}));

await extendedClient.myCustomAction({ ... });
```

## WebSocket Support

For realtime updates, use WebSocket transport:

```ts
import { createPublicClient, webSocket } from "viem";
import { riseTestnet } from "viem/chains";

const wsClient = createPublicClient({
  chain: riseTestnet,
  transport: webSocket("wss://testnet.riselabs.xyz/ws"),
});

// Watch for events in realtime
wsClient.watchContractEvent({
  address: "0x...",
  abi: [...],
  eventName: "Transfer",
  onLogs: (logs) => {
    console.log("Transfer events:", logs);
  },
});
```

## Type Safety

RISE Wallet provides comprehensive TypeScript types:

```ts
import type { PermissionsRequest } from "rise-wallet/viem";

// All actions are fully typed
const permissions: PermissionsRequest = {
  key: { publicKey: "...", type: "p256" },
  expiry: 123456789,
  permissions: {
    calls: [{
      to: "0x..." as const,
      signature: "0x..." as const,
    }],
  },
};
```

## Polling Transaction Status

After sending prepared calls, you need to poll for the transaction status using `wallet_getCallsStatus`:

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

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

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

### Status Codes

The `status` field provides a summary of the current bundle status:

| 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       |

### Status Response

```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',
  },
}
```

### Example: Complete Flow with Polling

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

async function sendWithSessionKey(calls: any[], sessionKey: string) {
  const provider = await rw.provider;

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

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

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

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

  // 5. Poll for status (simplified - you may want to add retries/timeouts)
  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;
}
```

<Callout type="info">
  **Note:** `wallet_sendCalls` and `sendCallsSync` handle polling internally. You only need to manually poll `wallet_getCallsStatus` when using `wallet_sendPreparedCalls` for session key transactions.
</Callout>

## Error Handling

Handle Viem errors appropriately:

```ts
import { BaseError } from "viem";

try {
  await Actions.sendTransaction(client, { ... });
} catch (error) {
  if (error instanceof BaseError) {
    console.error("Error name:", error.name);
    console.error("Error details:", error.details);
    console.error("Error version:", error.version);
  }
}
```

## Examples

* [Session Key Implementation](/docs/rise-wallet/session-keys)
* [Contract Interactions](/docs/rise-wallet/minting)

## Related Documentation

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