-
Channel Not Found Error After Creation - ClearNode Integration IssueEnvironment
Problem StatementSymptomChannel is successfully created on-chain with confirmed transaction, but subsequent operations (specifically Impact
Current Implementation Flow1. Channel Creation Process// Step 1: Connect to ClearNode WebSocket
await clearNodeClient.connect({
walletAddress,
signerAddress: walletAddress,
walletClient,
});
// Step 2: Get broker configuration
const { createGetConfigMessage } = await import("@erc7824/nitrolite");
const messageSigner = clearNodeClient.getMessageSigner();
const signer = async (payload: any) => {
return messageSigner({ payload, walletClient });
};
const getConfigMsg = await createGetConfigMessage(signer);
const brokerConfig = await clearNodeClient.sendRequest(getConfigMsg);
const polygonNetwork = brokerConfig.networks.find((n) => n.chain_id === 137);
// Step 3: Initialize Nitrolite client
const nitroliteClient = new NitroliteClient({
publicClient,
walletClient,
stateSigner,
addresses: {
custody: polygonNetwork.custody_address,
adjudicator: polygonNetwork.adjudicator_address,
guestAddress: config.broker_address,
},
chainId: 137,
challengeDuration: 3600n,
});
// Step 4: Request broker signature for channel
const { createCreateChannelMessage } = await import("@erc7824/nitrolite");
const signedChannelRequest = await createCreateChannelMessage(signer, {
chain_id: 137,
token: tokenAddress,
amount: amountBigInt.toString(),
});
const brokerResponse = await clearNodeClient.sendRequest(signedChannelRequest);
// Step 5: Approve USDC for custody contract
const approveHash = await walletClient.writeContract({
account: walletClient.account,
address: tokenAddress,
abi: [
/* ERC20 approve ABI */
],
functionName: "approve",
args: [polygonNetwork.custody_address, amountBigInt],
chain: null,
});
// Wait for approval confirmation
await new Promise((resolve) => setTimeout(resolve, 3000));
// Step 6: Create channel on-chain
const channelResult = await nitroliteClient.createChannel({
channel: {
participants: channelData.channel.participants,
adjudicator: channelData.channel.adjudicator,
challenge: BigInt(channelData.channel.challenge),
nonce: BigInt(channelData.channel.nonce),
},
unsignedInitialState: {
intent: channelData.state.intent,
version: BigInt(channelData.state.version),
data: channelData.state.state_data,
allocations: channelData.state.allocations.map((a) => ({
destination: a.destination,
token: a.token,
amount: BigInt(a.amount),
})),
},
serverSignature: channelData.server_signature,
});
console.log("✅ Channel created on-chain!");
console.log(" Channel ID:", channelData.channel_id);
console.log(" Tx hash:", channelResult.txHash);Result: Transaction URL: https://polygonscan.com/tx/0x107e8d0ed2ea4f53e70c8e27a49e7a313311c592b8d07c0716a56555959f54f9 2. Channel Registration Listener (Implemented)After channel creation, we listen for ClearNode's channel registration: const channelRegisteredPromise = new Promise<void>((resolve, reject) => {
const timeout = setTimeout(() => {
unsubscribe();
reject(new Error("Timeout waiting for channel registration (60s)"));
}, 60000); // 60 second timeout
const unsubscribe = clearNodeClient.onMessage((message: any) => {
if (message.res && Array.isArray(message.res)) {
const method = message.res[1];
const data = message.res[2];
// Listen for individual channel update "cu"
if (method === "cu" && data.channel_id === channelData.channel_id) {
console.log("✅ ClearNode registered channel!");
clearTimeout(timeout);
unsubscribe();
resolve();
}
// Listen for batch channel list "channels"
else if (method === "channels" && data.channels) {
if (
data.channels.some(
(c: any) => c.channel_id === channelData.channel_id,
)
) {
console.log("✅ ClearNode registered channel (batch)!");
clearTimeout(timeout);
unsubscribe();
resolve();
}
}
}
});
});
try {
await channelRegisteredPromise;
} catch (err) {
console.warn("⚠️ Channel registration timeout - will try resize anyway");
}Result: ❌ Timeout after 60 seconds - NO 3. Attempted Resize OperationAfter timeout, we attempt to resize the channel to move funds to unified balance: const resizeRequest = {
channel_id: channelData.channel_id,
allocate_amount: amountBigInt.toString(), // Move ALL to unified balance
resize_amount: "0", // Don't resize, just deallocate
funds_destination: walletAddress,
};
const timestamp = Date.now();
const reqId = timestamp;
const requestPayload: [number, string, any, number] = [
reqId,
"resize_channel",
resizeRequest,
timestamp,
];
const payloadString = JSON.stringify(requestPayload.slice(1, -1));
const signature = await messageSigner({
payload: payloadString,
walletClient,
});
const request = {
req: requestPayload,
sig: [signature],
};
const resizeResponse = await clearNodeClient.sendRequest(
JSON.stringify(request),
);Result: ❌ Error: 4. Verification with get_channels RPCconst { createGetChannelsMessage } = await import("@erc7824/nitrolite");
const getChannelsMsg = await createGetChannelsMessage(signer, walletAddress);
const channelsResponse = await clearNodeClient.sendRequest(getChannelsMsg);
console.log("Channels response:", channelsResponse);Result: {
"channels": []
}ClearNode returns empty array - no knowledge of the channel we just created. Extended WebSocket listener timeout from 30s to 60s. Result: ❌ Still timeout, no Expected BehaviorAccording to Nitrolite DocumentationFrom ClearNode API docs:
Expected Message: {
"res": [
1234567890123,
"cu",
{
"channel_id": "0x240a4b4e8e0024e024b42a8a5b6dad69e49f1ff16e6017151798f76dffc84798",
"participant": "0x5b8fc87af470a7f7f86f7633f5019e4a24bcac9a5a22cf2d4dfc6aa72de08c23",
"status": "open",
"token": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"amount": "10000",
"chain_id": 137,
"adjudicator": "0x14980dF216722f14c42CA7357b06dEa7eB408b10",
"challenge": 3600,
"nonce": 1,
"version": 0,
"created_at": "2025-10-21T...",
"updated_at": "2025-10-21T..."
},
1619123456789
],
"sig": ["0x..."]
}Actual Behavior: NO message received Use Case ArchitectureOur payment system relies on ClearNode's 3-layer architecture: Blocked Step: Cannot move from Ledger Channel → Unified Balance because Reproduction StepsPrerequisites
Additional ContextMultiple Channel AttemptsThis issue is consistent across multiple channel creation attempts:
Pattern: 100% failure rate on channel detection/registration ClearNode Client ImplementationOur WebSocket client wrapper: // clearNodeClient.ts
class ClearNodeClient {
private ws: WebSocket | null = null;
private messageHandlers: Set<(message: any) => void> = new Set();
private requestMap: Map<number, PendingRequest> = new Map();
async connect({ walletAddress, signerAddress, walletClient }) {
this.ws = new WebSocket("wss://clearnet.yellow.com/ws");
this.ws.onopen = async () => {
// Authenticate immediately
const authMsg = await createAuthRequestMessage(signer, walletAddress);
this.ws.send(authMsg);
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
// Handle RPC responses
if (message.res && Array.isArray(message.res)) {
const reqId = message.res[0];
const pending = this.requestMap.get(reqId);
if (pending) {
pending.resolve(message.res[2]); // Response data
this.requestMap.delete(reqId);
}
// ALSO notify all registered handlers (for unsolicited messages like "cu")
this.messageHandlers.forEach((handler) => handler(message));
}
};
}
onMessage(handler: (message: any) => void): () => void {
this.messageHandlers.add(handler);
return () => this.messageHandlers.delete(handler); // Unsubscribe function
}
async sendRequest(message: string): Promise<any> {
const parsed = JSON.parse(message);
const reqId = parsed.req[0];
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
this.requestMap.delete(reqId);
reject(new Error("Request timeout"));
}, 30000);
this.requestMap.set(reqId, { resolve, reject, timeout });
this.ws.send(message);
});
}
}Any help would be greatly appreciated! We're blocked on implementing our payment system for the project and need to understand why channels aren't being registered by ClearNode despite successful on-chain creation. System Information
Thank you for your time and assistance! 🙏 |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
|
@a-singh09 , thanks for such a detailed report! |
Beta Was this translation helpful? Give feedback.
-
|
@nksazonov Thanks for the quick reply! |
Beta Was this translation helpful? Give feedback.
-
|
@a-singh09 We have identified the issue. As you are specifying the wallet and the session key to be equal in the create channel request, the Clearnode should have denied signing your request. However, this check is missing in the handler, thus you got the response and sent a We will add the missing check in the next patch. Then your current code should be failing. |
Beta Was this translation helpful? Give feedback.
-
|
@nksazonov Thanks, the channels creation issue has been resolved. |
Beta Was this translation helpful? Give feedback.
@a-singh09 We have identified the issue.
The problem is that for the channel creation the tx sender ("the wallet") and channel participant specified on your behalf ("the signer" / "the session key") must be different. You can read more about "wallet-signer" restrictions here. These restrictions are true for all operations, not only Authentication (we will change that in the docs).
As you are specifying the wallet and the session key to be equal in the create channel request, the Clearnode should have denied signing your request. However, this check is missing in the handler, thus you got the response and sent a
create(...)tx, which emitted theCreated(...)event. Still, theCreated(...)…