Migration Guide
Guide to migrate to newer versions of Nitrolite
Migration Guide
If you are coming from an earlier version of Nitrolite, you will need to account for the following breaking changes.
0.3.x Breaking changes
The 0.3.x release includes breaking changes to the SDK architecture, smart contract interfaces, and Clearnode API enhancements listed below.
Not ready to migrate? Unfortunately, at this time Yellow Network does not provide ClearNodes running the previous version of the protocol, so you will need to migrate to the latest version to continue using the Network.
Nitrolite SDK
You should definitely read this section if you are using the Nitrolite SDK.
Client: Replaced stateWalletClient
with StateSigner
The stateWalletClient
parameter of NitroliteClient
has been replaced with a required stateSigner
parameter that implements the StateSigner
interface.
When initializing the client, you should use either WalletStateSigner
or SessionKeyStateSigner
to handle state signing.
import { createNitroliteClient } from '@erc7824/nitrolite';
const client = createNitroliteClient({
publicClient,
walletClient,
stateWalletClient: sessionWalletClient,
addresses,
});
import {
createNitroliteClient,
WalletStateSigner
} from '@erc7824/nitrolite';
const client = createNitroliteClient({
publicClient,
walletClient,
stateSigner: new WalletStateSigner(walletClient),
addresses,
});
For session key signing:
import { SessionKeyStateSigner } from '@erc7824/nitrolite';
const stateSigner = new SessionKeyStateSigner('0x...' as Hex);
Actions: Modified createChannel
Parameters
The CreateChannelParams
interface has been fully restructured for better clarity.
You should use the new CreateChannel
ClearNode API endpoint to get the response, that fully resembles the channel creation parameters.
const { channelId, initialState, txHash } = await client.createChannel(
tokenAddress,
{
initialAllocationAmounts: [amount1, amount2],
stateData: '0x...',
}
);
const { channelId, initialState, txHash } = await client.createChannel({
channel: {
participants: [address1, address2],
adjudicator: adjudicatorAddress,
challenge: 86400n,
nonce: 42n,
},
unsignedInitialState: {
intent: StateIntent.Initialize,
version: 0n,
data: '0x',
allocations: [
{ destination: address1, token: tokenAddress, amount: amount1 },
{ destination: address2, token: tokenAddress, amount: amount2 },
],
},
serverSignature: '0x...',
});
Actions: Structured Typed RPC Request Parameters
RPC requests now use endpoint-specific object-based parameters instead of untyped arrays for improved type safety.
You should update your RPC request creation code to use the new structured format and RPC types.
const request = NitroliteRPC.createRequest(
requestId,
RPCMethod.GetChannels,
[participant, status],
timestamp
);
const request = NitroliteRPC.createRequest({
method: RPCMethod.GetChannels,
params: {
participant,
status,
},
requestId,
timestamp,
});
Actions: Standardized Channel Operations Responses
The responses for CloseChannel
and ResizeChannel
methods have been aligned with newly added CreateChannel
endpoint for consistency.
Update your response handling code to use the new RPCChannelOperation
type.
export interface ResizeChannelResponseParams {
channelId: Hex;
stateData: Hex;
intent: number;
version: number;
allocations: RPCAllocation[];
stateHash: Hex;
serverSignature: ServerSignature;
}
export interface CloseChannelResponseParams {
channelId: Hex;
intent: number;
version: number;
stateData: Hex;
allocations: RPCAllocation[];
stateHash: Hex;
serverSignature: ServerSignature;
}
export interface RPCChannelOperation {
channelId: Hex;
state: RPCChannelOperationState;
serverSignature: Hex;
}
export interface CreateChannelResponse extends GenericRPCMessage {
method: RPCMethod.CreateChannel;
params: RPCChannelOperation & {
channel: RPCChannel;
};
}
export interface ResizeChannelResponse extends GenericRPCMessage {
method: RPCMethod.ResizeChannel;
params: RPCChannelOperation;
}
export interface CloseChannelResponse extends GenericRPCMessage {
method: RPCMethod.CloseChannel;
params: RPCChannelOperation;
}
Actions: Modified Signature
Type
The Signature
struct has been replaced with a simple Hex
type to support EIP-1271 and EIP-6492 signatures.
Update your signature-handling code to use the new Hex
type. Still, if using Nitrolite utils correctly, you will not need to change anything, as the utils will handle the conversion for you.
interface Signature {
v: number;
r: Hex;
s: Hex;
}
const sig: Signature = {
v: 27,
r: '0x...',
s: '0x...'
};
type Signature = Hex;
const sig: Signature = '0x...'; // Combined signature
Added: Pagination Types and Parameters
To support pagination in ClearNode API requests, new types and parameters have been added.
For now, only GetLedgerTransactions
request has been updated to include pagination.
export interface PaginationFilters {
/** Pagination offset. */
offset?: number;
/** Number of transactions to return. */
limit?: number;
/** Sort order by created_at. */
sort?: 'asc' | 'desc';
}
Clearnode API
You should read this section only if you are using the ClearNode API directly, or if you are using the Nitrolite SDK with custom ClearNode API requests.
Actions: Structured Request Parameters
ClearNode API requests have migrated from array-based parameters to structured object parameters for improved type safety and API clarity.
Update all your ClearNode API requests to use object-based parameters instead of arrays.
{
"req": [1, "auth_request", [{
"address": "0x1234567890abcdef...",
"session_key": "0x9876543210fedcba...",
"app_name": "Example App",
"allowances": [ "usdc", "100.0" ],
"scope": "app.create",
"expire": "3600",
"application": "0xApp1234567890abcdef..."
}], 1619123456789],
"sig": ["0x5432abcdef..."]
}
{
"req": [1, "auth_request", {
"address": "0x1234567890abcdef...",
"session_key": "0x9876543210fedcba...",
"app_name": "Example App",
"allowances": [
{
"asset": "usdc",
"amount": "100.0"
}
],
"scope": "app.create",
"expire": "3600",
"application": "0xApp1234567890abcdef..."
}, 1619123456789],
"sig": ["0x5432abcdef..."]
}
Added: create_channel
Method
A new create_channel
method has been added to facilitate the improved single-transaction channel opening flow.
Use this method to request channel creation parameters from the broker, then submit the returned data to the smart contract via Nitrolite SDK or directly.
Request:
{
"req": [1, "create_channel", {
"chain_id": 137,
"token": "0xeeee567890abcdef...",
"amount": "100000000",
"session_key": "0x1234567890abcdef..." // Optional
}, 1619123456789],
"sig": ["0x9876fedcba..."]
}
Response:
{
"res": [1, "create_channel", {
"channel_id": "0x4567890123abcdef...",
"channel": {
"participants": ["0x1234567890abcdef...", "0xbbbb567890abcdef..."],
"adjudicator": "0xAdjudicatorContractAddress...",
"challenge": 3600,
"nonce": 1619123456789
},
"state": {
"intent": 1,
"version": 0,
"state_data": "0xc0ffee",
"allocations": [
{
"destination": "0x1234567890abcdef...",
"token": "0xeeee567890abcdef...",
"amount": "100000000"
},
{
"destination": "0xbbbb567890abcdef...",
"token": "0xeeee567890abcdef...",
"amount": "0"
}
]
},
"server_signature": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1c"
}, 1619123456789],
"sig": ["0xabcd1234..."]
}
API: Standardized Channel Operation Responses
The responses for create_channel
, close_channel
, and resize_channel
methods have been unified for consistency.
Update your response parsing to handle the new unified structure with channel_id
, state
, and server_signature
fields.
{
"res": [1, "close_channel", {
"channelId": "0x4567890123abcdef...",
"intent": 3,
"version": 123,
"stateData": "0x0000000000000000000000000000000000000000000000000000000000001ec7",
"allocations": [...],
"stateHash": "0x...",
"serverSignature": "0x..."
}, 1619123456789],
"sig": ["0xabcd1234..."]
}
{
"res": [1, "close_channel", {
"channel_id": "0x4567890123abcdef...",
"state": {
"intent": 3,
"version": 123,
"state_data": "0xc0ffee",
"allocations": [
{
"destination": "0x1234567890abcdef...",
"token": "0xeeee567890abcdef...",
"amount": "50000"
}
]
},
"server_signature": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1c"
}, 1619123456789],
"sig": ["0xabcd1234..."]
}
Added: Pagination Metadata
Pagination-supporting endpoints now include a metadata
struct in their responses with pagination information.
Update your response handling for get_channels
, get_app_sessions
, get_ledger_entries
, and get_ledger_transactions
to use the new metadata structure.
{
"res": [1, "get_channels", [
[
{
"channel_id": "0xfedcba9876543210...",
"status": "open",
// ... channel data
}
]
], 1619123456789],
"sig": ["0xabcd1234..."]
}
{
"res": [1, "get_channels", {
"channels": [
{
"channel_id": "0xfedcba9876543210...",
"status": "open",
// ... channel data
}
],
"metadata": {
"page": 1,
"per_page": 10,
"total_count": 56,
"page_count": 6
}
}, 1619123456789],
"sig": ["0xabcd1234..."]
}
The metadata fields provide:
page
: Current page numberper_page
: Number of items per pagetotal_count
: Total number of items availablepage_count
: Total number of pages
Contracts
You should read this section only if you are using the Nitrolite smart contracts directly.
Action: Replaced Signature
Struct with bytes
The Signature
struct has been removed and replaced with bytes
type to support EIP-1271, EIP-6492, and other signature formats.
Update all contract interactions that use signatures to pass bytes
instead of the struct.
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
function join(
bytes32 channelId,
uint256 index,
Signature calldata sig
) external returns (bytes32);
function challenge(
bytes32 channelId,
State calldata candidate,
State[] calldata proofs,
Signature calldata challengerSig
) external;
// Signature struct is removed
function join(
bytes32 channelId,
uint256 index,
bytes calldata sig
) external returns (bytes32);
function challenge(
bytes32 channelId,
State calldata candidate,
State[] calldata proofs,
bytes calldata challengerSig
) external;
Actions: Updated State
Signature Array
The State
struct now uses bytes[]
for signatures instead of Signature[]
.
struct State {
uint8 intent;
uint256 version;
bytes data;
Allocation[] allocations;
Signature[] sigs;
}
struct State {
uint8 intent;
uint256 version;
bytes data;
Allocation[] allocations;
bytes[] sigs;
}
Added: Auto-Join Channel Creation Flow
Channels can now become operational immediately after the create()
call if all participant signatures are provided.
When calling create()
with complete signatures from all participants, the channel automatically becomes active without requiring a separate join()
call.
Single signature (requires join):
// Create channel with only creator's signature
State memory initialState = State({
intent: StateIntent.Fund,
version: 0,
data: "0x",
allocations: allocations,
sigs: [creatorSignature] // Only one signature
});
bytes32 channelId = custody.create(channel, initialState);
// Channel status: JOINING - requires server to call join()
Complete signatures (auto-active):
// Create channel with all participants' signatures
State memory initialState = State({
intent: StateIntent.Fund,
version: 0,
data: "0x",
allocations: allocations,
sigs: [creatorSignature, serverSignature] // All signatures
});
bytes32 channelId = custody.create(channel, initialState);
// Channel status: ACTIVE - ready for use immediately
Actions: Update Adjudicator Contracts for EIP-712 Support
A new EIP712AdjudicatorBase
base contract has been added to support EIP-712 typed structured data signatures in adjudicator implementations.
The EIP712AdjudicatorBase
provides:
- Domain separator retrieval: Gets EIP-712 domain separator from the channel implementation contract
- ERC-5267 compliance: Automatically handles EIP-712 domain data retrieval
- Ownership management: Built-in access control for updating channel implementation address
- Graceful fallbacks: Returns
NO_EIP712_SUPPORT
constant when EIP-712 is not available
If you have custom adjudicator contracts, inherit from EIP712AdjudicatorBase
to enable EIP-712 signature verification.
import {IAdjudicator} from "../interfaces/IAdjudicator.sol";
import {Channel, State, Allocation, StateIntent} from "../interfaces/Types.sol";
contract MyAdjudicator is IAdjudicator {
function adjudicate(
Channel calldata chan,
State calldata candidate,
State[] calldata proofs
) external view override returns (bool valid) {
return candidate.validateUnanimousSignatures(chan);
}
}
import {IAdjudicator} from "../interfaces/IAdjudicator.sol";
import {Channel, State, Allocation, StateIntent} from "../interfaces/Types.sol";
import {EIP712AdjudicatorBase} from "./EIP712AdjudicatorBase.sol";
contract MyAdjudicator is IAdjudicator, EIP712AdjudicatorBase {
constructor(address owner, address channelImpl)
EIP712AdjudicatorBase(owner, channelImpl) {}
function adjudicate(
Channel calldata chan,
State calldata candidate,
State[] calldata proofs
) external override returns (bool valid) {
bytes32 domainSeparator = getChannelImplDomainSeparator();
return candidate.validateUnanimousStateSignatures(chan, domainSeparator);
}
}
Added: Enhanced Signature Support
Smart contracts now support EIP-191, EIP-712, EIP-1271, and EIP-6492 signature formats for greater compatibility.
The contracts automatically detect and verify the appropriate signature format:
- Raw ECDSA: Traditional
(r, s, v)
signatures - EIP-191: Personal message signatures (
\x19Ethereum Signed Message:\n
) - EIP-712: Typed structured data signatures
- EIP-1271: Smart contract wallet signatures
- EIP-6492: Signatures for undeployed contracts
No changes are needed in your contract calls - the signature verification is handled automatically by the contract.