Skip to main content

RPC Message Creation API

The functions detailed below are part of the @erc7824/nitrolite SDK and provide a convenient way to create fully formed, signed, and stringified JSON RPC request messages. These messages are ready for transmission over a WebSocket or any other transport layer communicating with a Nitrolite-compatible broker.

Each function typically takes a MessageSigner (a function you provide to sign messages, usually integrating with a user's wallet) and other relevant parameters, then returns a Promise<string> which resolves to the JSON string of the signed RPC message.

Authentication

These functions are used for the client authentication flow with the broker. The typical sequence is:

  1. Client sends an auth_request (using createAuthRequestMessage).
  2. Broker responds with an auth_challenge.
  3. Client signs the challenge and sends an auth_verify (using createAuthVerifyMessageFromChallenge or createAuthVerifyMessage).
  4. Broker responds with auth_success or auth_failure.
createAuthRequestMessage

Creates the signed, stringified message body for an 'auth_request'. This request initiates the authentication process with the broker.

Parameters

  • signer:MessageSigner
  • clientAddress:Address
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createAuthRequestMessage } from "@erc7824/nitrolite";
// Assuming 'signer' and 'clientAddress' are defined
const authRequestMsg = await createAuthRequestMessage(signer, clientAddress);
// Send authRequestMsg via WebSocket
createAuthVerifyMessageFromChallenge

Creates the signed, stringified message body for an 'auth_verify' request using an explicitly provided challenge string. Use this if you have already parsed the 'auth_challenge' response yourself and extracted the challenge.

Parameters

  • signer:MessageSigner
  • clientAddress:Address
  • challenge:string
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createAuthVerifyMessageFromChallenge } from "@erc7824/nitrolite";
// Assuming 'signer', 'clientAddress', and 'challengeString' are defined
const authVerifyMsg = await createAuthVerifyMessageFromChallenge(signer, clientAddress, challengeString);
// Send authVerifyMsg via WebSocket
createAuthVerifyMessage

Creates the signed, stringified message body for an 'auth_verify' request by parsing the challenge from the raw 'auth_challenge' response received from the broker. This is a convenience function that handles parsing the challenge for you.

Parameters

  • signer:MessageSigner
  • rawChallengeResponse:string | object
  • clientAddress:Address
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createAuthVerifyMessage } from "@erc7824/nitrolite";
// Assuming 'signer', 'rawChallengeResponseData', and 'clientAddress' are defined
const authVerifyMsg = await createAuthVerifyMessage(signer, rawChallengeResponseData, clientAddress);
// Send authVerifyMsg via WebSocket

General & Keep-Alive

Functions for general RPC interactions like keep-alive messages.

createPingMessage

Creates the signed, stringified message body for a 'ping' request. Useful for keeping a WebSocket connection alive.

Parameters

  • signer:MessageSigner
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createPingMessage } from "@erc7824/nitrolite";
// Assuming 'signer' is defined
const pingMsg = await createPingMessage(signer);
// Send pingMsg via WebSocket

Query Operations

Functions for retrieving information from the broker.

createGetConfigMessage

Creates the signed, stringified message body for a 'get_config' request to retrieve broker configuration.

Parameters

  • signer:MessageSigner
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createGetConfigMessage } from "@erc7824/nitrolite";
// Assuming 'signer' is defined
const getConfigMsg = await createGetConfigMessage(signer);
// Send getConfigMsg via WebSocket
createGetLedgerBalancesMessage

Creates the signed, stringified message body for a 'get_ledger_balances' request to fetch balances for a specific participant.

Parameters

  • signer:MessageSigner
  • participant:Address
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createGetLedgerBalancesMessage } from "@erc7824/nitrolite";
// Assuming 'signer' and 'participant' are defined
const getBalancesMsg = await createGetLedgerBalancesMessage(signer, participant);
// Send getBalancesMsg via WebSocket
createGetAppDefinitionMessage

Creates the signed, stringified message body for a 'get_app_definition' request to retrieve the definition of a specific application.

Parameters

  • signer:MessageSigner
  • appSessionId:AccountID
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createGetAppDefinitionMessage } from "@erc7824/nitrolite";
// Assuming 'signer' and 'appSessionId' are defined
const getAppDefMsg = await createGetAppDefinitionMessage(signer, appSessionId);
// Send getAppDefMsg via WebSocket
createGetChannelsMessage

Creates the signed, stringified message body for a 'get_channels' request to retrieve channels for a specific participant.

Parameters

  • signer:MessageSigner
  • participant:Address
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createGetChannelsMessage } from "@erc7824/nitrolite";
// Assuming 'signer' and 'participant' are defined
const getChannelsMsg = await createGetChannelsMessage(signer, participant);
// Send getChannelsMsg via WebSocket

Application Session Management

Functions for creating and closing application sessions (state channels).

createAppSessionMessage

Creates the signed, stringified message body for a 'create_app_session' request. This is used to propose the creation of a new state channel (application session).

Parameters

  • signer:MessageSigner
  • params:CreateAppSessionRequest[]
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createAppSessionMessage } from "@erc7824/nitrolite";
// Assuming 'signer' and 'appSessionParams' are defined
const createAppMsg = await createAppSessionMessage(signer, appSessionParams);
// Send createAppMsg via WebSocket
createCloseAppSessionMessage

Creates the signed, stringified message body for a 'close_app_session' request. This is used to propose the closure of an existing state channel (application session) with final allocations. Note: This function only adds the caller's signature; multi-signature coordination happens externally if required by the application definition.

Parameters

  • signer:MessageSigner
  • params:CloseAppSessionRequest[]
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createCloseAppSessionMessage } from "@erc7824/nitrolite";
// Assuming 'signer' and 'closeParams' are defined
const closeAppMsg = await createCloseAppSessionMessage(signer, closeParams);
// Send closeAppMsg via WebSocket

Application-Specific Messaging

Function for sending messages within an active application session.

createApplicationMessage

Creates the signed, stringified message body for sending a generic 'message' within an active application session (state channel). This is used for off-chain state updates and other application-specific communication.

Parameters

  • signer:MessageSigner
  • appSessionId:Hex
  • messageParams:any[]
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createApplicationMessage } from "@erc7824/nitrolite";
// Assuming 'signer', 'appSessionId', and 'appSpecificMessageData' are defined
const appMsg = await createApplicationMessage(signer, appSessionId, [appSpecificMessageData]);
// Send appMsg via WebSocket

Ledger Channel Management

Functions for managing the underlying ledger channels (direct channels with the broker).

createCloseChannelMessage

Creates the signed, stringified message body for a 'close_channel' request. This is used to close a direct ledger channel with the broker.

Parameters

  • signer:MessageSigner
  • channelId:AccountID
  • fundDestination:Address
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createCloseChannelMessage } from "@erc7824/nitrolite";
// Assuming 'signer', 'channelId', and 'fundDestination' are defined
const closeChannelMsg = await createCloseChannelMessage(signer, channelId, fundDestination);
// Send closeChannelMsg via WebSocket
createResizeChannelMessage

Creates the signed, stringified message body for a 'resize_channel' request. This is used to add or remove funds from a direct ledger channel with the broker.

Parameters

  • signer:MessageSigner
  • params:ResizeChannel[]
  • requestId:RequestID
  • timestamp:Timestamp

Returns:Promise<string>

Example


import { createResizeChannelMessage } from "@erc7824/nitrolite";
// Assuming 'signer' and 'resizeParams' are defined
const resizeMsg = await createResizeChannelMessage(signer, resizeParams);
// Send resizeMsg via WebSocket

Advanced: Creating a Local Signer for Development

To begin, you'll often need a fresh key pair (private key, public key, and address). The generateKeyPair utility can be used for this:

import { ethers } from "ethers"; // Make sure ethers is installed

// Definition (as provided in your example)
interface CryptoKeypair {
privateKey: string;
publicKey: string;
address: string;
}

export const generateKeyPair = async (): Promise<CryptoKeypair> => {
try {
const wallet = ethers.Wallet.createRandom();
const privateKeyHash = ethers.utils.keccak256(wallet.privateKey);
const walletFromHashedKey = new ethers.Wallet(privateKeyHash);

return {
privateKey: privateKeyHash,
publicKey: walletFromHashedKey.publicKey,
address: walletFromHashedKey.address,
};
} catch (error) {
console.error('Error generating keypair, using fallback:', error);
const randomHex = ethers.utils.randomBytes(32);
const privateKey = ethers.utils.keccak256(randomHex);
const wallet = new ethers.Wallet(privateKey);

return {
privateKey: privateKey,
publicKey: wallet.publicKey,
address: wallet.address,
};
}
};

// Usage:
async function main() {
const keyPair = await generateKeyPair();
console.log("Generated Private Key:", keyPair.privateKey);
console.log("Generated Address:", keyPair.address);
// Store keyPair.privateKey securely if you need to reuse this signer
}

This function creates a new random wallet, hashes its private key for deriving a new one (a common pattern for deterministic key generation or added obfuscation, though the security implications depend on the exact use case), and returns the private key, public key, and address.

Creating a Signer from a Private Key

Once you have a private key (either generated as above or from a known development account), you can create a MessageSigner compatible with the RPC message creation functions. The MessageSigner interface typically expects an asynchronous sign method.

import { ethers } from "ethers";
import { Hex } from "viem"; // Assuming Hex type is from viem or similar

// Definitions (as provided in your example)
type RequestData = unknown; // Placeholder for actual request data type
type ResponsePayload = unknown; // Placeholder for actual response payload type

interface WalletSigner {
publicKey: string;
address: Hex;
sign: (payload: RequestData | ResponsePayload) => Promise<Hex>;
}

export const createEthersSigner = (privateKey: string): WalletSigner => {
try {
const wallet = new ethers.Wallet(privateKey);

return {
publicKey: wallet.publicKey,
address: wallet.address as Hex,
sign: async (payload: RequestData | ResponsePayload): Promise<Hex> => {
try {
// The NitroliteRPC.hashMessage method should ideally be used here
// to ensure the exact same hashing logic as the SDK internals.
// For demonstration, using a generic hashing approach:
const messageToSign = JSON.stringify(payload);
const messageHash = ethers.utils.id(messageToSign); // ethers.utils.id performs keccak256
const messageBytes = ethers.utils.arrayify(messageHash);

const flatSignature = await wallet._signingKey().signDigest(messageBytes);
const signature = ethers.utils.joinSignature(flatSignature);
return signature as Hex;
} catch (error) {
console.error('Error signing message:', error);
throw error;
}
},
};
} catch (error) {
console.error('Error creating ethers signer:', error);
throw error;
}
};

// Usage:
async function setupSigner() {
const keyPair = await generateKeyPair(); // Or use a known private key
const localSigner = createEthersSigner(keyPair.privateKey);

// Now 'localSigner' can be passed to the RPC message creation functions:
// const authRequest = await createAuthRequestMessage(localSigner.sign, localSigner.address as Address);
// console.log("Auth Request with local signer:", authRequest);
}

Important Considerations for createEthersSigner:

  • Hashing Consistency: The sign method within createEthersSigner must hash the payload in a way that is identical to how the NitroliteRPC class (specifically NitroliteRPC.hashMessage) expects messages to be hashed before signing. The example above uses ethers.utils.id(JSON.stringify(payload)). It's crucial to verify if the SDK's internal hashing uses a specific message prefix (e.g., EIP-191 personal_sign prefix) or a different serialization method. If the SDK does not use a standard EIP-191 prefix, or uses a custom one, your local signer's hashing logic must replicate this exactly for signatures to be valid. Using NitroliteRPC.hashMessage(payload) directly (if payload matches the NitroliteRPCMessage structure) is the safest way to ensure consistency.
  • Type Compatibility: Ensure the Address type expected by functions like createAuthRequestMessage is compatible with localSigner.address. The example uses localSigner.address as Address assuming Address is 0x${string}.
  • Error Handling: The provided examples include basic error logging. Robust applications should implement more sophisticated error handling.