Skip to main content

Channel Management Methods

Channel management methods enable clients to create, modify, and close payment channels with a clearnode on various blockchain networks.


Overview

Payment channels are the foundation of the Nitrolite protocol. They lock funds on-chain while enabling instant off-chain operations within a unified balance.

Channel Lifecycle Summary


create_channel

Name

create_channel

Usage

Initiates the creation of a payment channel between user and a clearnode on a specific blockchain. The clearnode validates the request, generates a channel configuration with a unique nonce, prepares the initial funding state, and signs it. The user receives the complete channel data and the clearnode's signature, which they must then submit to the blockchain's Custody contract via the create() function to finalize channel creation and lock funds on-chain. This two-step process (off-chain preparation, on-chain execution) ensures the clearnode has agreed on channel creation and received an on-chain confirmation that it was created.

When to Use

When a user wants to establish a payment channel on a specific blockchain network. This is the first operation after authentication if the user doesn't have an open channel yet. On subsequent connections, users won't need to create a channel again unless they closed it.

Two-Step Process

Channel creation is intentionally split into two steps:

  1. Off-chain preparation: The clearnode prepares and signs the initial state
  2. On-chain execution: User submits transaction to create the channel

This ensures the clearnode has committed to the channel before the user submits the on-chain transaction.

Prerequisites

  • User must be authenticated
  • Target blockchain and token must be supported by the clearnode
  • User must have native currency for gas fees

Request

ParameterTypeRequiredDescriptionDefaultExampleNotes
chain_iduint32YesBlockchain network identifier

Examples:
• 1: Ethereum Mainnet
• 137: Polygon
• 8453: Base
• 42161: Arbitrum One
• 10: Optimism
137Use get_config to see supported chains
tokenstring (wallet address)YesERC-20 token contract address on the specified chain

Format: 0x-prefixed hex (20 bytes)
"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"Must be supported; see get_assets
Initial Channel State

Channels are created with zero initial balance for both participants. To add funds to the channel, use the resize_channel method after creation. The challenge period is set to 1 hour (3600 seconds) by default.

Response

Quick Reference

Structures: ChannelStateStateAllocation

ParameterTypeDescriptionSee Also
channel_idstringComputed channel identifier (0x-prefixed hex, 32 bytes)
channelChannelOn-chain channel params↓ Structure
stateStateInitial state (intent INITIALIZE, version 0, empty data, zero allocations)↓ Structure
server_signaturestringClearnode signature over packed state (hex string)

Channel Structure

FieldTypeDescriptionNotes
participantswallet address[]Array of two wallet addresses: [User, Clearnode]Order: Index 0 = User, Index 1 = Clearnode
Order is critical for signature verification
adjudicatorwallet addressAdjudicator contract address for this channelTypically SimpleConsensus for payment channels
Validates state transitions during disputes
challengeuint64Challenge period in secondsDefault: 3600 seconds (1 hour)
nonceuint64Unique identifier for this channelEnsures channelId uniqueness even with same participants
Server-generated timestamp or counter

Example:

{
"participants": ["0x742d35Cc...", "0x123456Cc..."],
"adjudicator": "0xAdjudicator123...",
"challenge": 86400,
"nonce": 1699123456
}

State Structure

FieldTypeDescriptionNotes
intentStateIntentState purpose indicatorFor creation: INITIALIZE (1)
versionuint64State sequence numberFor creation: 0
state_datastringState data (hex)For creation: "0x"
allocationsStateAllocation[]Fund allocations (raw units)Order matches participants array; both 0 on creation

Example:

{
"intent": 1,
"version": 0,
"state_data": "0x",
"allocations": [
{"participant": "0x742d35Cc...", "token": "0x2791Bca1...", "amount": "0"},
{"participant": "0x123456Cc...", "token": "0x2791Bca1...", "amount": "0"}
]
}

StateAllocation Structure

FieldTypeDescription
participantstring (wallet address)Participant's wallet address
tokenstring (wallet address)Token contract address
amountstringAmount in smallest unit (e.g., "100000000" for 100 USDC with 6 decimals)
Clearnode Signature First

The clearnode provides its signature BEFORE the user commits funds on-chain. This ensures both parties have committed to the channel before any on-chain transaction occurs.

Next Steps After Receiving Response

  1. Verify Channel Data

    • Recompute channelId = keccak256(abi.encode(channel))
    • Verify computed ID matches response channel_id
    • Check participants[0] is your wallet address
    • Verify token matches your request
  2. Verify the Clearnode's Signature

    • Compute packedState = abi.encode(channelId, state.intent, state.version, state.data, state.allocations)
    • Recover signer from server_signature
    • Verify signer is the clearnode's known wallet address
  3. Sign State with Your Key

    • Sign packedState with your participant key
    • Include your signature when submitting to blockchain
  4. Submit On-Chain Transaction

    • Call Custody.create(channel, state, yourSignature, clearnodeSignature)
    • Wait for transaction confirmation
  5. Monitor for Channel Creation

    • Listen for Opened event (emitted right after transaction is mined)
    • Or poll get_channels until channel appears with status "open"
  6. Channel Active

    • Channel appears in get_channels with status "open"
    • Channel starts with zero balance
    • Use resize_channel to add funds to the channel

Error Cases

Error Format

The protocol does not use numeric error codes. Errors are returned as method "error" with descriptive messages.

Common error scenarios:

ErrorDescriptionRecovery
Authentication requiredNot authenticatedComplete authentication flow
Unsupported chainchain_id not supportedUse get_config
Token not supportedToken not in asset config for chainUse get_assets
Invalid signatureCaller did not sign requestSign with channel participant wallet
Channel already existsOpen channel with broker already existsUse existing channel or close it first
Failed to prepare stateInternal packing/signing issueRetry or contact support

Implementation Notes

  • The nonce is generated by the clearnode to ensure uniqueness
  • The channelId can be computed client-side: keccak256(abi.encode(channel))
  • The packedState should be verified: abi.encode(channelId, state.intent, state.version, state.data, state.allocations)
  • Users should verify the clearnode's signature before proceeding
  • The challenge period can be customized but most users should use defaults

Sequence Diagram


close_channel

Name

close_channel

Usage

Initiates cooperative closure of an active payment channel. The clearnode signs a final state with StateIntent.FINALIZE reflecting the current balance distribution. The user receives this clearnode-signed final state which they must submit to the blockchain's Custody contract via the close() function. This is the preferred and most efficient way to close a channel as it requires only one on-chain transaction and completes immediately without a challenge period. Both parties must agree on the final allocation for cooperative closure to work.

When to Use

When a user wants to withdraw funds from an active channel and both user and the clearnode agree on the final balance distribution. This should be the default closure method when both parties are online and cooperative.

Preferred Closure Method

Cooperative closure is fast (1 transaction), cheap (low gas), and immediate (no waiting period). Always use this method when possible. Challenge-response closure should only be used when the clearnode is unresponsive or disputes the final state.

Prerequisites

  • Channel must exist and be in ACTIVE status
  • User must be authenticated
  • User must have native currency for gas fees
  • Both parties must agree on final allocations (implicitly, by the clearnode signing)

Request

ParameterTypeRequiredDescriptionDefaultExampleNotes
channel_idstringYesIdentifier of the channel to close-"0xabcdef1234567890..."From get_channels or stored after creation
funds_destinationstring (wallet address)YesAddress where your share of channel funds should be sent-"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"Typically your wallet address

Response

ParameterTypeDescriptionExampleNotes
channel_idstringChannel identifier"0xabcdef1234..."
stateStateFinal state with intent FINALIZE and version = current+1
state_data: "0x"
allocations: final fund distribution (raw units)
See State structurechannel field is omitted in close responses
server_signaturestringClearnode signature over packed state"0xabcdef987654..."Hex string

Next Steps After Receiving Response

  1. Verify Final Allocations

    • Check allocations match expectations
    • Verify total matches total locked funds
    • Ensure your allocation is correct
  2. Verify the Clearnode's Signature

    • Compute packedState = abi.encode(channelId, state.intent, state.version, state.data, state.allocations)
    • Verify signature is from the clearnode
  3. Sign Final State

    • Sign packedState with your participant key
    • Include your signature when submitting to blockchain
  4. Submit On-Chain

    • Call Custody.close(channelId, state, yourSignature, clearnodeSignature) on blockchain
    • Both signatures must be present
  5. Wait for Confirmation

    • Transaction confirms
    • Funds distributed according to allocations
  6. Channel Closed

    • Channel deleted from chain
    • Funds in your wallet or custody available balance
  7. Withdraw if Needed

    • If funds in custody, call withdraw() to move to wallet

Error Cases

Error Format

The protocol does not use numeric error codes. Errors are returned as method "error" with descriptive messages.

Common error scenarios:

ErrorDescriptionRecovery
Authentication requiredNot authenticatedRe-authenticate
Channel not foundInvalid channel_idVerify with get_channels
Channel challengedParticipant has challenged channelsResolve challenges first
Channel not open/resizingStatus not open or resizingOnly open/resizing channels can close
Invalid signatureCaller did not sign requestSign with channel participant wallet
Token/asset not foundAsset config missingEnsure channel token is supported
Insufficient/negative balanceLedger balance retrieval or negative balanceEnsure balances are non-negative; retry
Failed to pack/sign stateInternal packing/signing issueRetry or contact support

Comparison: Cooperative vs Challenge Closure

AspectCooperative (this method)Challenge
SpeedFast (1 transaction)Slow (challenge period + 1 transaction)
Gas CostLow (~100k gas)High (~200k+ gas, 2+ transactions)
RequirementsBoth parties online & agreeWorks if other party unresponsive
Waiting PeriodNone (immediate)24+ hours (challenge duration)
Use WhenNormal operationsDisputes or unresponsiveness
When to Use Challenge Closure

Only use challenge closure (on-chain challenge() function) when:

  • Clearnode is unresponsive
  • Clearnode disputes the final allocation
  • Cooperative closure fails repeatedly

Challenge closure requires waiting for the challenge period to expire before funds are released.

Implementation Notes

  • The StateIntent.FINALIZE (3) signals this is a final state
  • All participants must sign the final state for it to be accepted on-chain
  • The allocations determine where funds go when channel closes
  • Clearnode will only sign if the allocations match the current state of the unified balance
  • After closing, funds are distributed according to the allocations specified
  • Users may need to call withdraw() separately to move funds from custody ledger to their wallet

resize_channel

Name

resize_channel

Usage

Adjusts the allocations of an existing channel by locking or unlocking funds without closing the channel. Unlike older implementations, this uses the resize() function on the Custody contract to perform an in-place update of the channel's allocations. The same channelId persists throughout the operation, and the channel remains in ACTIVE status. Clearnode prepares a resize state with delta amounts (positive for deposit, negative for withdrawal) that all participants must sign before submitting on-chain.

When to Use

When a user wants to adjust channel allocations while keeping the same channel active. This is more efficient than closing and reopening, and maintains the channel's history and state version continuity.

In-Place Update

The resize operation updates the channel in place. The channelId stays the same, and the channel remains ACTIVE throughout. This is the current implementation of channel allocation adjustment.

Prerequisites

  • Channel must exist and be in ACTIVE status
  • User must be authenticated
  • Positive deltas require enough available unified balance
  • Negative deltas require sufficient channel balance
  • All participants must sign the resize state (consensus required)
  • User must have native currency for gas fees

Request

ParameterTypeRequiredDescriptionDefaultExampleNotes
channel_idstringYesIdentifier of the channel to resize (stays the same)-"0xabcdef1234567890..."0x-prefixed hex string (32 bytes)
This channel_id will NOT change after resize
allocate_amountstring (decimal)NoAmount to add/remove between unified balance and the channel before resize0"50.0"Decimal string; can be used together with resize_amount; at least one of the two must be non-zero
resize_amountstring (decimal)NoDelta to apply to the channel: positive to deposit, negative to withdraw0"75.0" or "-100.0"Decimal string; can be used together with allocate_amount; at least one of the two must be non-zero
funds_destinationstring (wallet address)YesDestination for the user's allocation in the resize state-"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"0x-prefixed hex string (20 bytes)

Response

ParameterTypeDescriptionExampleNotes
channel_idstringSame channel identifier (unchanged)"0xabcdef1234567890..."This does NOT change (in-place update)
stateStateResize state to be submitted on-chain
intent = RESIZE (2)
version = current+1
state_data = ABI-encoded int256[2] of [resize_amount, allocate_amount] (raw units)
allocations = final absolute allocations after resize
See State structurechannel field is omitted in resize responses
server_signaturestringClearnode signature over packed state"0x9876fedcba..."Hex string

Next Steps After Receiving Response

The client must submit the resize state to the blockchain:

  1. Verify the resize state

    • Check channel_id matches (should be unchanged)
    • Verify intent is RESIZE (2)
    • Confirm version is current + 1
    • Check allocations reflect the requested change
  2. Sign the resize state

    packedState = abi.encode(
    channel_id,
    state.intent, // StateIntent.RESIZE (2)
    state.version, // Incremented version
    state.data, // ABI-encoded int256[] deltas
    state.allocations // Final allocations
    )
    user_signature = sign(packedState, participant_private_key)
  3. Ensure sufficient balance

    • Positive deltas require enough available unified balance to cover allocate_amount + resize_amount (after decimals conversion)
    • Negative deltas require the channel to have sufficient funds being deallocated
  4. Call Custody.resize() on-chain

    custody.resize(
    channel_id, // Same channel_id
    state, // Resize state
    yourSignature,
    clearnodeSignature
    )
  5. Wait for transaction confirmation

    • Channel remains ACTIVE (no status change)
    • Funds locked or unlocked based on delta
    • Expected deposits updated to new amounts
  6. Monitor for Resized event

    event Resized(bytes32 indexed channelId, int256[] deltaAllocations)
    • Emitted when resize completes
    • Contains the delta amounts applied
    • Confirms operation success
  7. Update local state

    • Channel_id remains the same (no replacement needed)
    • Unified balance automatically updated
    • Version incremented

Error Cases

ErrorCauseResolution
Authentication requiredNot authenticatedComplete authentication flow
Channel not foundInvalid channel_idVerify with get_channels
Channel challengedParticipant has challenged channelsResolve challenged channels first
Operation denied: resize already ongoingChannel status is resizingWait for existing resize to complete
Operation denied: channel is not openStatus not openOnly open channels can resize
Invalid signatureCaller not among channel signersSign request with channel participant
Token/asset not found for channelAsset config missing for channel token/chainEnsure channel token is supported
Resize operation requires non-zero amountsBoth resize_amount and allocate_amount are zeroProvide a non-zero value
Insufficient unified balanceNew channel amount would exceed available balanceReduce amounts or add funds
New channel amount must be positiveResize would make channel balance negativeReduce withdrawal
Failed to pack resize amounts/stateInternal packing/signing errorRetry; contact support if persistent

Resize Scenarios

Scenario 1: Depositing Additional Funds

Initial State:

Channel (on Polygon): 20 USDC
Channel (on Celo): 5 USDC
Unified balance: 25 USDC total

Operation:

resize_channel({
channel_id: "0xCelo_Channel_Id", // Resize Celo channel
allocate_amount: "0",
resize_amount: "75.0", // Deposit 75 USDC
funds_destination: "0x742d35Cc..." // Required, even for deposits
})

Result:

Channel (on Polygon): 20 USDC (unchanged)
Channel (on Celo): 80 USDC (5 + 75 = 80)
Unified balance: 100 USDC total (reduced available balance to fund deposit)
Same channel_id on Celo (unchanged)

Scenario 2: Withdrawing Funds

Initial State:

Channel (on Polygon): 100 USDC
Unified balance: 100 USDC total (all locked in channel)

Operation:

resize_channel({
channel_id: "0xPolygon_Channel_Id",
allocate_amount: "0",
resize_amount: "-100.0", // Withdraw all 100 USDC
funds_destination: "0x742d35Cc..." // User's wallet
})

Result:

Channel (on Polygon): 0 USDC (100 - 100 = 0)
Unified balance: 0 USDC
100 USDC returned to available balance (unified)
Same channel_id (unchanged)
Channel still ACTIVE (can be used again or closed)

Scenario 3: Complex Multi-Chain Rebalancing

Initial State:

Channel (on Polygon): 20 USDC
Channel (on Celo): 80 USDC
Unified balance: 100 USDC total
Want to withdraw all on Polygon (100 USDC)

Operation:

// First, allocate Celo funds to Polygon channel
resize_channel({
channel_id: "0xPolygon_Channel_Id",
allocate_amount: "80.0", // Allocate from Celo
resize_amount: "-100.0", // Withdraw 100 total
funds_destination: "0x742d35Cc..."
})

Result:

Channel (on Polygon): 0 USDC
Channel (on Celo): 0 USDC (deallocated)
100 USDC withdrawn to user's wallet
Complex Rebalancing

Multi-chain rebalancing with allocate_amount is an advanced feature. For simple deposit/withdrawal on a single channel, use only resize_amount with allocate_amount = "0".

Implementation Notes

  • The resize() function operates in place on the same channel
  • channelId never changes (no new channel created)
  • Channel remains in ACTIVE status throughout
  • State version increments like any state update
  • Delta amounts are encoded as int256[] in state.data
  • Positive deltas increase channel balance (and reduce available unified balance)
  • Negative deltas decrease channel balance (and increase available unified balance)
  • All participants must sign the resize state (consensus required)
  • More gas-efficient than close + reopen
  • Unified balance automatically updated by clearnode
  • Channel history and state continuity preserved

Next Steps

Explore other off-chain operations:

  • Transfers - Send instant off-chain payments using unified balance
  • App Sessions - Create multi-party application channels
  • Queries - Check channel status, balances, and history

For protocol fundamentals: