Viem Integration
Using RISE Wallet with Viem for low-level control
This guide covers integrating RISE Wallet with Viem, 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
npm i rise-wallet viempnpm add rise-wallet viemyarn add rise-wallet viembun add rise-wallet viemBasic Usage
Creating a Client
Use Rise Wallet's EIP-1193 provider with Viem's custom transport:
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:
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
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)
const result = await Actions.sendCalls(client, {
account: "0x...",
calls: [
{
to: "0x...",
data: "0x...",
},
],
});Sign Message
const signature = await Actions.signMessage(client, {
account: "0x...",
message: "Hello RISE!",
});RISE Wallet Actions
Grant Permissions
Create session keys with specific permissions:
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:
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:
await WalletActions.revokePermissions(client, {
keyId: "0x...",
});Key Management
RISE Wallet provides utilities for working with different key types:
P256 Keys
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
// 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:
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:
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 }],
});Custom Actions
Create your own actions by extending Viem:
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:
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:
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,
}],
},
};Error Handling
Handle Viem errors appropriately:
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);
}
}Best Practices
1. Use Action Modules
Import actions from their respective modules for better tree-shaking:
// Good
import { sendTransaction } from "viem/actions";
import { connect } from "rise-wallet/viem/WalletActions";
// Less optimal
import * as Actions from "viem/actions";2. Handle Account State
Always check account connection before operations:
const accounts = await client.request({ method: "eth_accounts" });
if (!accounts.length) {
// Request connection first
await WalletActions.connect(client);
}3. Optimize Gas
Use Viem's gas utilities:
const gasPrice = await client.getGasPrice();
const gasEstimate = await client.estimateGas({
to: "0x...",
data: "0x...",
});