### Complete Yellow App Example with JavaScript Source: https://github.com/layer-3/docs/blob/master/docs/build/quick-start/index.md A full-fledged example demonstrating the lifecycle of a payment application using the Yellow SDK. It covers wallet initialization, WebSocket connection, session creation with defined participants and allocations, and sending instant payments. The example utilizes asynchronous operations and event handling for real-time interactions. Dependencies include `@erc7824/nitrolite` for message creation and parsing, and web3 provider for wallet interaction. ```javascript import { createAppSessionMessage, parseRPCResponse } from '@erc7824/nitrolite'; class SimplePaymentApp { constructor() { this.ws = null; this.messageSigner = null; this.userAddress = null; this.sessionId = null; } async init() { // Step 1: Set up wallet const { userAddress, messageSigner } = await this.setupWallet(); this.userAddress = userAddress; this.messageSigner = messageSigner; // Step 2: Connect to ClearNode this.ws = new WebSocket('wss://clearnet.yellow.com/ws'); this.ws.onopen = () => { console.log('🟢 Connected to Yellow Network!'); }; this.ws.onmessage = (event) => { this.handleMessage(parseRPCResponse(event.data)); }; return userAddress; } async setupWallet() { const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const userAddress = accounts[0]; const messageSigner = async (message) => { return await window.ethereum.request({ method: 'personal_sign', params: [message, userAddress] }); }; return { userAddress, messageSigner }; } async createSession(partnerAddress) { const appDefinition = { protocol: 'payment-app-v1', participants: [this.userAddress, partnerAddress], weights: [50, 50], quorum: 100, challenge: 0, nonce: Date.now() }; const allocations = [ { participant: this.userAddress, asset: 'usdc', amount: '800000' }, { participant: partnerAddress, asset: 'usdc', amount: '200000' } ]; const sessionMessage = await createAppSessionMessage( this.messageSigner, [{ definition: appDefinition, allocations }] ); this.ws.send(sessionMessage); console.log('✅ Payment session created!'); } async sendPayment(amount, recipient) { const paymentData = { type: 'payment', amount: amount.toString(), recipient, timestamp: Date.now() }; const signature = await this.messageSigner(JSON.stringify(paymentData)); this.ws.send(JSON.stringify({ ...paymentData, signature, sender: this.userAddress })); console.log(`💸 Sent ${amount} instantly!`); } handleMessage(message) { switch (message.type) { case 'session_created': this.sessionId = message.sessionId; console.log('✅ Session ready:', this.sessionId); break; case 'payment': console.log('💰 Payment received:', message.amount); break; } } } // Usage const app = new SimplePaymentApp(); await app.init(); await app.createSession('0xPartnerAddress'); await app.sendPayment('100000', '0xPartnerAddress'); // Send 0.1 USDC ``` -------------------------------- ### Install Yellow SDK with npm, yarn, or pnpm Source: https://github.com/layer-3/docs/blob/master/docs/build/quick-start/index.md This snippet shows how to install the `@erc7824/nitrolite` SDK using different package managers (npm, yarn, pnpm). It includes creating a new project directory and initializing it. ```bash mkdir my-yellow-app cd my-yellow-app npm init -y npm install @erc7824/nitrolite ``` ```bash mkdir my-yellow-app cd my-yellow-app yarn init -y yarn add @erc7824/nitrolite ``` ```bash mkdir my-yellow-app cd my-yellow-app pnpm init pnpm add @erc7824/nitrolite ``` -------------------------------- ### Set up Wallet Message Signer with MetaMask Source: https://github.com/layer-3/docs/blob/master/docs/build/quick-start/index.md This JavaScript function sets up a message signer using MetaMask. It requests user accounts and creates a `messageSigner` function that uses `personal_sign` to sign messages. ```javascript // Set up message signer for your wallet async function setupMessageSigner() { if (!window.ethereum) { throw new Error('Please install MetaMask'); } // Request wallet connection const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const userAddress = accounts[0]; // Create message signer function const messageSigner = async (message) => { return await window.ethereum.request({ method: 'personal_sign', params: [message, userAddress] }); }; console.log('✅ Wallet connected:', userAddress); return { userAddress, messageSigner }; } ``` -------------------------------- ### Send Instant Payment via WebSocket Source: https://github.com/layer-3/docs/blob/master/docs/build/quick-start/index.md This JavaScript function sends an instant payment by creating a payment message, signing it with the user's wallet, and sending it over the WebSocket connection to ClearNode. ```javascript async function sendPayment(ws, messageSigner, amount, recipient) { // Create payment message const paymentData = { type: 'payment', amount: amount.toString(), recipient, timestamp: Date.now() }; // Sign the payment const signature = await messageSigner(JSON.stringify(paymentData)); const signedPayment = { ...paymentData, signature, sender: await getCurrentUserAddress() // Assuming getCurrentUserAddress is defined elsewhere }; // Send instantly through ClearNode ws.send(JSON.stringify(signedPayment)); console.log('💸 Payment sent instantly!'); } // Usage example (assuming ws, messageSigner, and partnerAddress are available) // await sendPayment(ws, messageSigner, 100000n, partnerAddress); // Send 0.1 USDC ``` -------------------------------- ### Start Local Development Server with Yarn Source: https://github.com/layer-3/docs/blob/master/README.md Starts a local development server for live preview and hot-reloading. This command opens the website in a browser automatically. ```bash yarn start ``` -------------------------------- ### Install Project Dependencies with Yarn Source: https://github.com/layer-3/docs/blob/master/README.md Installs all the necessary project dependencies using the Yarn package manager. This is a prerequisite for running other commands. ```bash yarn ``` -------------------------------- ### Connect to Yellow Network WebSocket Source: https://github.com/layer-3/docs/blob/master/docs/build/quick-start/index.md This JavaScript code demonstrates how to establish a WebSocket connection to the Yellow Network's ClearNode. It logs connection status and parses incoming messages using `parseRPCResponse` from the Nitrolite SDK. ```javascript import { createAppSessionMessage, parseRPCResponse } from '@erc7824/nitrolite'; // Connect to Yellow Network const ws = new WebSocket('wss://clearnet.yellow.com/ws'); ws.onopen = () => { console.log('✅ Connected to Yellow Network!'); }; ws.onmessage = (event) => { const message = parseRPCResponse(event.data); console.log('📨 Received:', message); }; ws.onerror = (error) => { console.error('Connection error:', error); }; console.log('Connecting to Yellow Network...'); ``` -------------------------------- ### Create Payment Application Session Source: https://github.com/layer-3/docs/blob/master/docs/build/quick-start/index.md This JavaScript function defines and creates a payment application session. It specifies app protocol, participants, balances, and uses `createAppSessionMessage` to generate a signed message to send to ClearNode. ```javascript async function createPaymentSession(messageSigner, userAddress, partnerAddress) { // Define your payment application const appDefinition = { protocol: 'payment-app-v1', participants: [userAddress, partnerAddress], weights: [50, 50], // Equal participation quorum: 100, // Both participants must agree challenge: 0, nonce: Date.now() }; // Initial balances (1 USDC = 1,000,000 units with 6 decimals) const allocations = [ { participant: userAddress, asset: 'usdc', amount: '800000' }, // 0.8 USDC { participant: partnerAddress, asset: 'usdc', amount: '200000' } // 0.2 USDC ]; // Create signed session message const sessionMessage = await createAppSessionMessage( messageSigner, [{ definition: appDefinition, allocations }] ); // Send to ClearNode ws.send(sessionMessage); console.log('✅ Payment session created!'); return { appDefinition, allocations }; } ``` -------------------------------- ### Clearnode Environment Configuration Example Source: https://github.com/layer-3/docs/blob/master/docs/manuals/running-clearnode-locally.md An example .env file for configuring Clearnode. It includes settings for logging, broker private key, network-specific RPC endpoints and contract addresses for Polygon, and token seeding details. ```dotenv # =================================================== # CLEARNODE SERVICE CONFIGURATION # =================================================== CLEARNODE_LOG_LEVEL=info BROKER_PRIVATE_KEY=0xac0974bec38a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 # Networks Configuration POLYGON_INFURA_URL=wss://polygon-mainnet.infura.io/ws/v3/your-api-key POLYGON_CUSTODY_CONTRACT_ADDRESS=0x490fb189DdE3a01B00be9BA5F41e3447FbC838b6 POLYGON_ADJUDICATOR_ADDRESS=0xcbbc03a873c11beeFA8D99477E830be48d8Ae6D7 POLYGON_BALANCE_CHECKER_ADDRESS=0x2352c63A83f9Fd126af8676146721Fa00924d7e4 # BASE_INFURA_URL=... # BASE_CUSTODY_CONTRACT_ADDRESS=... # BASE_ADJUDICATOR_ADDRESS=... # BASE_BALANCE_CHECKER_ADDRESS=... # .. # =================================================== # TOKEN SEEDING # =================================================== # --- Token 1 --- TOKEN_1_ADDRESS=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 TOKEN_1_CHAIN_ID=137 TOKEN_1_SYMBOL=USDC TOKEN_1_DECIMALS=6 # --- Token 2 --- TOKEN_2_ADDRESS=0x7ceb23fd6bc0add59e62ac25578270cff1b9f619 TOKEN_2_CHAIN_ID=137 TOKEN_2_SYMBOL=WETH TOKEN_2_DECIMALS=18 # --- Token 3 --- # ... # =================================================== # POSTGRES DATABASE CONFIGURATION # =================================================== POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=postgres PGPASSWORD=${POSTGRES_PASSWORD} CLEARNODE_DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database:5432/${POSTGRES_DB}?sslmode=disable ``` -------------------------------- ### Start Clearnode Services with Docker Compose Source: https://github.com/layer-3/docs/blob/master/docs/manuals/running-clearnode-locally.md Starts all the services defined in the docker-compose.yml file. This command brings up the Clearnode application, its dependencies like the database, and any other configured services. ```bash docker-compose up ``` -------------------------------- ### Handle Incoming Messages with JavaScript Source: https://github.com/layer-3/docs/blob/master/docs/build/quick-start/index.md Processes various message types received from a WebSocket connection, including session creation confirmations, payments, application messages, and errors. It utilizes a switch statement for routing messages and calls specific functions to update the application state or log information. Dependencies include a `parseRPCResponse` function. ```javascript ws.onmessage = (event) => { const message = parseRPCResponse(event.data); switch (message.type) { case 'session_created': console.log('✅ Session confirmed:', message.sessionId); break; case 'payment': console.log('💰 Payment received:', message.amount); // Update your app's UI updateBalance(message.amount, message.sender); break; case 'session_message': console.log('📨 App message:', message.data); handleAppMessage(message); break; case 'error': console.error('❌ Error:', message.error); break; } }; function updateBalance(amount, sender) { console.log(`Received ${amount} from ${sender}`); // Update your application state } ``` -------------------------------- ### Connection Configuration (TypeScript) Source: https://github.com/layer-3/docs/blob/master/docs/api-reference/index.md Defines the configuration interface for connecting to ClearNode and provides an example configuration object. ```typescript interface ClearNodeConfig { endpoint: string; // WebSocket endpoint apiKey?: string; // Optional authentication timeout: number; // Connection timeout retryAttempts: number; // Reconnection attempts } const config = { endpoint: 'wss://clearnet.yellow.com/ws', timeout: 30000, retryAttempts: 3 }; ``` -------------------------------- ### Clone Clearnode Repository Source: https://github.com/layer-3/docs/blob/master/docs/manuals/running-clearnode-locally.md Clones the Clearnode repository from GitHub and navigates into the cloned directory. This is the first step to getting the project code. ```bash git clone https://github.com/erc7824/nitrolite.git cd clearnode ``` -------------------------------- ### CI/CD Workflow for Yellow App Deployment Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md This GitHub Actions workflow automates the deployment of the Yellow App. It checks out the code, sets up Node.js, installs dependencies, runs tests, deploys smart contracts using Hardhat, and deploys the frontend to AWS S3. ```yaml name: Deploy Yellow App on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Deploy contracts run: | npx hardhat deploy --network polygon npx hardhat verify --network polygon env: PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }} POLYGONSCAN_API_KEY: ${{ secrets.POLYGONSCAN_API_KEY }} - name: Deploy frontend run: | npm run build aws s3 sync ./build s3://${{ secrets.S3_BUCKET }} env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} ``` -------------------------------- ### Define Multi-Signature Requirements Session Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/multi-party.md Defines a multi-signature application session requiring a specific number of signatures from designated participants. This example shows a setup with three signers and one beneficiary, where at least two signatures are needed to proceed. ```javascript const multiSigSession = { protocol: 'multisig-v1', participants: [signer1, signer2, signer3, beneficiary], weights: [33, 33, 34, 0], // 3 signers, 1 beneficiary quorum: 67, // Requires 2 of 3 signatures challenge: 0, nonce: Date.now() }; ``` -------------------------------- ### Error Handling Example (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/api-reference/index.md Demonstrates how to handle various Yellow SDK errors within a try-catch block, including specific error types like NetworkError and ValidationError. ```javascript try { const sessionMessage = await createAppSessionMessage(messageSigner, sessionData); ws.send(sessionMessage); } catch (error) { if (error instanceof NetworkError) { // Handle network connectivity issues await this.reconnectToClearNode(); } else if (error instanceof ValidationError) { // Handle input validation errors console.error('Invalid session parameters:', error.context); } else if (error instanceof SigningError) { // Handle wallet signing errors console.error('Message signing failed:', error.message); } else { // Handle unexpected errors console.error('Unexpected error:', error); } } ``` -------------------------------- ### Gaming Lobby System Implementation (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/multi-party.md A JavaScript class simulating a gaming lobby system. It handles the creation of game rooms, player joining, and starting game sessions, including broadcasting room creation events and initiating game application sessions. ```javascript class GamingLobby { constructor() { this.gameRooms = new Map(); this.playerQueues = new Map(); this.ws = null; this.messageSigner = null; } async createGameRoom(gameType, maxPlayers, buyIn) { const roomId = this.generateRoomId(); this.gameRooms.set(roomId, { gameType, maxPlayers, buyIn, players: [], status: 'WAITING' }); // Broadcast room creation const roomMessage = { type: 'room_created', roomId, gameType, maxPlayers, buyIn, timestamp: Date.now() }; const signature = await this.messageSigner(JSON.stringify(roomMessage)); this.ws.send(JSON.stringify({ ...roomMessage, signature })); return roomId; } async joinGameRoom(roomId, playerAddress) { const room = this.gameRooms.get(roomId); if (room.players.length >= room.maxPlayers) { throw new Error('Room is full'); } room.players.push(playerAddress); // Start game when room is full if (room.players.length === room.maxPlayers) { await this.startGame(roomId); } return room; } async startGame(roomId) { const room = this.gameRooms.get(roomId); // Create game application session const appDefinition = { protocol: `${room.gameType}-v1`, participants: [...room.players, this.serverAddress], weights: [...room.players.map(() => 0), 100], // Server controls game quorum: 100, challenge: 0, nonce: Date.now() }; const allocations = room.players.map(player => ({ participant: player, asset: 'usdc', amount: room.buyIn.toString() })).concat([ { participant: this.serverAddress, asset: 'usdc', amount: '0' } ]); const gameSession = await createAppSessionMessage(this.messageSigner, [{ definition: appDefinition, allocations }]); this.ws.send(gameSession); room.status = 'ACTIVE'; room.sessionId = this.generateSessionId(); return room; } } ``` -------------------------------- ### Restart a Specific Clearnode Service Source: https://github.com/layer-3/docs/blob/master/docs/manuals/running-clearnode-locally.md Restarts a particular service within the Docker Compose setup. This is helpful for applying changes or recovering a misbehaving service. ```bash docker-compose restart ``` -------------------------------- ### Stop Clearnode Services with Docker Compose Source: https://github.com/layer-3/docs/blob/master/docs/manuals/running-clearnode-locally.md Stops and removes all containers, networks, and volumes associated with the Clearnode Docker Compose setup. This command cleanly shuts down the local Clearnode environment. ```bash docker-compose down ``` -------------------------------- ### JavaScript Environment Variable Validation for Secure Configuration Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/security.md Provides a SecureConfig class to validate essential environment variables required for application operation, such as API endpoints and contract addresses. It checks for the presence of specified variables and validates address formats, ensuring a secure configuration setup. ```javascript class SecureConfig { constructor() { this.requiredVars = [ 'CLEARNODE_ENDPOINT', 'CUSTODY_ADDRESS', 'ADJUDICATOR_ADDRESS' ]; } validateEnvironment() { // Check required environment variables for (const varName of this.requiredVars) { if (!process.env[varName]) { throw new ConfigError(`Missing required environment variable: ${varName}`); } } // Validate addresses if (!this.isValidAddress(process.env.CUSTODY_ADDRESS)) { throw new ConfigError('Invalid custody contract address'); } return { clearNodeEndpoint: process.env.CLEARNODE_ENDPOINT, custodyAddress: process.env.CUSTODY_ADDRESS, adjudicatorAddress: process.env.ADJUDICATOR_ADDRESS, environment: process.env.NODE_ENV || 'development' }; } isValidAddress(address) { return /^0x[a-fA-F0-9]{40}$/.test(address); } } ``` -------------------------------- ### Build Static Website Content with Yarn Source: https://github.com/layer-3/docs/blob/master/README.md Generates the static content for the website, typically placed in a 'build' directory. This output can be hosted on any static hosting service. ```bash yarn build ``` -------------------------------- ### Deploy Website with Yarn (SSH) Source: https://github.com/layer-3/docs/blob/master/README.md Deploys the website using SSH. This command builds the site and pushes it to the 'gh-pages' branch, suitable for GitHub Pages. ```bash USE_SSH=true yarn deploy ``` -------------------------------- ### Connect to ClearNode and Create Session (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/architecture.md Demonstrates establishing a WebSocket connection to ClearNode infrastructure and creating an application session using the Nitrolite SDK. It includes sending session messages and handling responses. ```javascript import { createAppSessionMessage, parseRPCResponse } from '@erc7824/nitrolite'; // Connect to ClearNode const ws = new WebSocket('wss://clearnet.yellow.com/ws'); // Create application session const sessionMessage = await createAppSessionMessage(messageSigner, [{ definition: appDefinition, allocations: initialAllocations }]); // Send to ClearNode ws.send(sessionMessage); // Parse responses ws.onmessage = (event) => { const message = parseRPCResponse(event.data); console.log('Received:', message); }; ``` -------------------------------- ### Deploy Website with Yarn (No SSH) Source: https://github.com/layer-3/docs/blob/master/README.md Deploys the website without using SSH, requiring you to specify your GitHub username. This command builds the site and pushes it to the 'gh-pages' branch. ```bash GIT_USER= yarn deploy ``` -------------------------------- ### Batch Deposits and Channel Creation in Javascript Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md Optimizes gas usage by preparing and executing transactions in parallel batches. Handles deposits and channel creation operations efficiently, with a configurable batch size to manage gas limits. ```javascript class OptimizedYellowApp { async batchDepositsAndChannels(operations) { // Prepare all transactions in parallel const preparationPromises = operations.map(async (op) => ({ deposit: await this.client.prepareDeposit(op.amount), channel: await this.client.prepareCreateChannel(op.channelParams) })); const prepared = await Promise.all(preparationPromises); // Execute in optimized batches const batchSize = 5; // Adjust based on gas limits const results = []; for (let i = 0; i < prepared.length; i += batchSize) { const batch = prepared.slice(i, i + batchSize); const batchResults = await Promise.all( batch.map(async ({ deposit, channel }) => { const depositTx = await this.client.executeTransaction(deposit); const channelTx = await this.client.executeTransaction(channel); return { deposit: depositTx, channel: channelTx }; }) ); results.push(...batchResults); } return results; } async optimizeGasUsage() { // Estimate gas for common operations const gasEstimates = await Promise.all([ this.client.estimateDeposit(1000000n), this.client.estimateCreateChannel(this.defaultChannelParams), this.client.estimateCloseChannel(this.defaultCloseParams) ]); // Adjust gas limits based on network conditions const gasMultiplier = await this.getNetworkCongestionMultiplier(); return { deposit: gasEstimates[0] * gasMultiplier, createChannel: gasEstimates[1] * gasMultiplier, closeChannel: gasEstimates[2] * gasMultiplier }; } } ``` -------------------------------- ### Create and Process Subscriptions with SubscriptionService Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/multi-party.md Manages the creation of subscriptions, including defining application parameters and allocations, and processing monthly payments via a ClearNode connection. Requires a message signer and WebSocket connection. ```javascript class SubscriptionService { constructor() { this.subscriptions = new Map(); this.ws = null; this.messageSigner = null; } async createSubscription(subscriber, provider, monthlyFee) { const appDefinition = { protocol: 'subscription-v1', participants: [subscriber, provider, this.serviceAddress], weights: [0, 100, 0], // Provider controls service delivery quorum: 100, challenge: 0, nonce: Date.now() }; const allocations = [ { participant: subscriber, asset: 'usdc', amount: (monthlyFee * 12).toString() }, { participant: provider, asset: 'usdc', amount: '0' }, { participant: this.serviceAddress, asset: 'usdc', amount: '0' } ]; const sessionMessage = await createAppSessionMessage(this.messageSigner, [{ definition: appDefinition, allocations }]); // Send to ClearNode this.ws.send(sessionMessage); const subscriptionId = this.generateSubscriptionId(); this.subscriptions.set(subscriptionId, { subscriber, provider, monthlyFee, createdAt: Date.now() }); return subscriptionId; } async processMonthlyPayment(subscriptionId) { const subscription = this.subscriptions.get(subscriptionId); // Create payment message for this month const paymentMessage = { type: 'monthly_payment', subscriptionId, amount: subscription.monthlyFee, month: this.getCurrentMonth(), timestamp: Date.now() }; const signature = await this.messageSigner(JSON.stringify(paymentMessage)); this.ws.send(JSON.stringify({ ...paymentMessage, signature })); return this.waitForPaymentConfirmation(subscriptionId); } } ``` -------------------------------- ### View Clearnode Service Logs Source: https://github.com/layer-3/docs/blob/master/docs/manuals/running-clearnode-locally.md Follows the logs of a specific Clearnode service in real-time. Replace `` with the actual name of the service you want to inspect. ```bash docker-compose logs -f ``` -------------------------------- ### Create Tournament Application Session with JavaScript Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/multi-party.md Sets up an application session for a multi-player tournament, including players and a house address for tournament control. It defines the protocol, participant weights, and quorum for managing tournament entry fees and prize distribution. The generated session message is sent to ClearNode. ```javascript async function createTournament(players, entryFee, messageSigner) { // Create application session for tournament logic const appDefinition = { protocol: 'tournament-v1', participants: [...players, houseAddress], weights: [...players.map(() => 0), 100], // House controls tournament quorum: 100, challenge: 0, nonce: Date.now() }; const allocations = players.map(player => ({ participant: player, asset: 'usdc', amount: entryFee.toString() })).concat([{ participant: houseAddress, asset: 'usdc', amount: '0' }]); const tournamentMessage = await createAppSessionMessage(messageSigner, [{ definition: appDefinition, allocations }]); // Send to ClearNode ws.send(tournamentMessage); console.log('🎮 Tournament session created!'); return appDefinition; } ``` -------------------------------- ### Message Signing with Wallet (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/architecture.md Illustrates the process of signing messages securely using a user's wallet, such as MetaMask, for authentication within the SDK. This is essential for validating operations and session management. ```javascript // Set up message signer with your wallet const messageSigner = async (message) => { return await window.ethereum.request({ method: 'personal_sign', params: [message, userAddress] }); }; // Use signer for session creation const sessionMessage = await createAppSessionMessage(messageSigner, sessionData); ``` -------------------------------- ### Mainnet Production Configuration (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md Defines the configuration object for deploying a Yellow App to the Polygon mainnet. Includes chain ID, contract addresses, ClearNode URL, challenge duration, and reconnection settings. ```javascript // Production configuration for Polygon mainnet const productionConfig = { chainId: 137, addresses: { custody: '0x...', // Production custody contract adjudicator: '0x...', // Your deployed adjudicator tokenAddress: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174' // USDC on Polygon }, clearNodeUrl: 'wss://clearnet.yellow.com/ws', challengeDuration: 7200n, // 2 hours for mainnet reconnectConfig: { maxAttempts: 10, backoffMultiplier: 1.5, initialDelay: 1000 } }; ``` -------------------------------- ### Testnet Configuration (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md Defines the configuration object for deploying a Yellow App to a testnet (Mumbai). Includes chain ID, contract addresses, ClearNode URL, and adjusted challenge duration and reconnection settings for testing. ```javascript // Testnet configuration for development const testnetConfig = { chainId: 80001, // Mumbai testnet addresses: { custody: '0x...', // Testnet custody contract adjudicator: '0x...', // Your test adjudicator tokenAddress: '0x...' // Test USDC }, clearNodeUrl: 'wss://testnet.clearnet.yellow.com/ws', challengeDuration: 100n, // Shorter for testing reconnectConfig: { maxAttempts: 3, backoffMultiplier: 2, initialDelay: 500 } }; ``` -------------------------------- ### Robust Yellow App Initialization and Error Handling (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md A JavaScript class for robustly initializing a Yellow App, featuring retry logic for network errors and specific handling for different error codes like insufficient funds or contract failures. It also includes WebSocket connection management with timeouts, heartbeats, and automatic reconnection. ```javascript class RobustYellowApp { constructor(config) { this.client = new NitroliteClient(config); this.reconnectAttempts = 0; this.maxReconnectAttempts = config.reconnectConfig.maxAttempts; this.backoffMultiplier = config.reconnectConfig.backoffMultiplier; } async initializeWithRetry() { try { await this.client.deposit(this.config.initialDeposit); const channel = await this.client.createChannel(this.config.channelParams); await this.connectToClearNode(); return channel; } catch (error) { return this.handleInitializationError(error); } } async handleInitializationError(error) { switch (error.code) { case 'INSUFFICIENT_FUNDS': throw new UserError('Please add more funds to your wallet'); case 'NETWORK_ERROR': if (this.reconnectAttempts < this.maxReconnectAttempts) { const delay = this.config.reconnectConfig.initialDelay * Math.pow(this.backoffMultiplier, this.reconnectAttempts); this.reconnectAttempts++; await this.delay(delay); return this.initializeWithRetry(); } throw new NetworkError('Unable to connect after maximum attempts'); case 'CONTRACT_ERROR': throw new ContractError('Smart contract interaction failed: ' + error.message); default: throw error; } } async connectToClearNode() { return new Promise((resolve, reject) => { const ws = new WebSocket(this.config.clearNodeUrl); const connectionTimeout = setTimeout(() => { reject(new Error('ClearNode connection timeout')); }, 10000); ws.onopen = () => { clearTimeout(connectionTimeout); this.setupHeartbeat(ws); this.setupReconnectLogic(ws); resolve(ws); }; ws.onerror = (error) => { clearTimeout(connectionTimeout); reject(error); }; }); } setupHeartbeat(ws) { const heartbeatInterval = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'ping', timestamp: Date.now() })); } else { clearInterval(heartbeatInterval); } }, 30000); // Clear interval when WebSocket closes ws.addEventListener('close', () => { clearInterval(heartbeatInterval); }); } setupReconnectLogic(ws) { ws.addEventListener('close', async (event) => { if (event.code !== 1000) { // Not a normal closure console.log('Connection lost, attempting to reconnect...'); await this.delay(5000); await this.connectToClearNode(); } }); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } ``` -------------------------------- ### Production Logging and Alerting Implementation Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md A JavaScript class for logging events and errors in the production environment using Winston. It includes functionality for logging channel events, application errors, and sending alerts to an on-call system. ```javascript class ProductionLogger { constructor(config) { this.logger = winston.createLogger({ level: config.logLevel, format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), new winston.transports.Console({ format: winston.format.simple() }) ] }); } logChannelEvent(event, channelId, data) { this.logger.info('Channel event', { event, channelId, data, timestamp: Date.now() }); } logError(error, context) { this.logger.error('Application error', { error: error.message, stack: error.stack, context, timestamp: Date.now() }); // Send to alerting system this.sendAlert('ERROR', error.message, context); } async sendAlert(level, message, context) { // Integration with alerting services (PagerDuty, Slack, etc.) if (level === 'ERROR' || level === 'CRITICAL') { await this.notifyOnCall(message, context); } } } ``` -------------------------------- ### Production Environment Configuration Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md Configuration object for the production environment, specifying contract addresses, network details, and monitoring parameters. It reads values from environment variables. ```javascript // config/production.js export const productionConfig = { contracts: { custody: process.env.CUSTODY_CONTRACT_ADDRESS, adjudicator: process.env.ADJUDICATOR_CONTRACT_ADDRESS, tokenAddress: process.env.TOKEN_CONTRACT_ADDRESS }, network: { chainId: parseInt(process.env.CHAIN_ID), rpcUrl: process.env.RPC_URL, clearNodeUrls: process.env.CLEARNODE_URLS.split(',') }, monitoring: { sentryDsn: process.env.SENTRY_DSN, logLevel: process.env.LOG_LEVEL || 'info' } }; ``` -------------------------------- ### JavaScript Multi-Session Manager for ClearNode Connections Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/multi-party.md Manages multiple concurrent WebSocket sessions for ClearNode integration. It handles connection establishment, session creation, message routing, and broadcasting messages across all active sessions. Requires a messageSigner and functions to parse RPC responses and create application session messages. ```javascript class MultiSessionManager { constructor() { this.connections = new Map(); // sessionId -> WebSocket this.sessions = new Map(); // sessionId -> sessionData this.messageSigner = null; } async connectToSession(sessionConfig) { const ws = new WebSocket('wss://clearnet.yellow.com/ws'); return new Promise((resolve, reject) => { ws.onopen = async () => { const sessionMessage = await createAppSessionMessage( this.messageSigner, [sessionConfig] ); ws.send(sessionMessage); const sessionId = this.generateSessionId(); this.connections.set(sessionId, ws); this.sessions.set(sessionId, sessionConfig); resolve({ ws, sessionId }); }; ws.onerror = reject; ws.onmessage = (event) => { this.handleSessionMessage(sessionId, parseRPCResponse(event.data)); }; }); } async broadcastToAllSessions(message) { const broadcasts = Array.from(this.connections.entries()).map(([sessionId, ws]) => { if (ws.readyState === WebSocket.OPEN) { const signature = await this.messageSigner(JSON.stringify(message)); return ws.send(JSON.stringify({ ...message, signature })); } }); return Promise.allSettled(broadcasts); } handleSessionMessage(sessionId, message) { switch (message.type) { case 'session_created': this.handleSessionCreated(sessionId, message.data); break; case 'participant_message': this.handleParticipantMessage(sessionId, message.data); break; case 'error': this.handleSessionError(sessionId, message.error); break; } } } ``` -------------------------------- ### Yellow App Lifecycle Flowchart Source: https://github.com/layer-3/docs/blob/master/docs/learn/introduction/yellow-apps.md Visualizes the typical lifecycle of a Yellow App, from initial funding and channel creation to off-chain transactions and final settlement. ```mermaid graph TD A[Deposit Funds] --> B[Create Channel] B --> C[Connect to ClearNode] C --> D[Create App Session] D --> E[Off-chain Transactions] E --> F[Close Session] F --> G[Settle & Withdraw] ``` -------------------------------- ### Optimize Database State Storage in JavaScript Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md Provides an optimized way to store and manage application states in a database. It includes logic for compressing state data and setting up database indexes for faster queries. ```javascript class OptimizedStateStorage { constructor(dbConfig) { this.db = new Database(dbConfig); this.setupIndexes(); } async setupIndexes() { // Optimize queries for common patterns await this.db.createIndex('states', ['channelId', 'version']); await this.db.createIndex('states', ['channelId', 'intent']); await this.db.createIndex('channels', ['participants']); await this.db.createIndex('transactions', ['timestamp', 'status']); } async storeStateOptimized(channelId, state) { const compressed = await this.compressState(state); // Store with proper indexing await this.db.transaction(async (tx) => { await tx.states.insert({ channelId, version: state.version, intent: state.intent, data: compressed, timestamp: Date.now() }); // Update latest state cache await tx.channels.update( { id: channelId }, { latestVersion: state.version, lastActivity: Date.now() } ); }); } } ``` -------------------------------- ### Event-Driven Application Structure (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/architecture.md An event-driven application class that extends EventEmitter. It initializes a NitroliteClient, sets up event handlers for channel creation, message reception, and session closure, and includes logic for handling different message types. ```javascript class EventDrivenApp extends EventEmitter { constructor(config) { super(); this.client = new NitroliteClient(config); this.setupEventHandlers(); } setupEventHandlers() { this.on('channel:created', this.onChannelCreated.bind(this)); this.on('message:received', this.onMessageReceived.bind(this)); this.on('session:closed', this.onSessionClosed.bind(this)); } async onChannelCreated(channelId) { // Automatically connect to ClearNode await this.connectToClearNode(channelId); // Set up session await this.createApplicationSession(channelId); this.emit('app:ready', channelId); } onMessageReceived(channelId, message) { // Route message based on type switch (message.type) { case 'payment': this.handlePayment(channelId, message); break; case 'game_move': this.handleGameMove(channelId, message); break; } } } ``` -------------------------------- ### Client-Side State Tracking with Versioning (JavaScript) Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/architecture.md Manages client-side state for channels, including version validation, history tracking for dispute resolution, and emitting update events. Uses a Map to store channel states and history. ```javascript class StateTracker { constructor() { this.channelStates = new Map(); this.stateHistory = new Map(); } updateChannelState(channelId, newState) { // Validate state progression const currentState = this.channelStates.get(channelId); if (currentState && newState.version <= currentState.version) { throw new Error('Invalid state version'); } // Store state this.channelStates.set(channelId, newState); // Maintain history for dispute resolution if (!this.stateHistory.has(channelId)) { this.stateHistory.set(channelId, []); } this.stateHistory.get(channelId).push(newState); // Emit event for UI updates this.emit('stateUpdated', { channelId, state: newState }); } getStateHistory(channelId) { return this.stateHistory.get(channelId) || []; } getLatestState(channelId) { return this.channelStates.get(channelId); } } ``` -------------------------------- ### Create Escrow Application Session with JavaScript Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/multi-party.md Creates a new application session for an escrow pattern involving a buyer, seller, and mediator. It defines participant roles, voting weights, and quorum requirements for consensus. This function utilizes the `createAppSessionMessage` utility from the Yellow SDK. ```javascript import { createAppSessionMessage } from '@erc7824/nitrolite'; async function createEscrowSession(buyer, seller, mediator, amount) { const appDefinition = { protocol: 'escrow-v1', participants: [buyer, seller, mediator], weights: [33, 33, 34], // Equal voting with mediator tiebreaker quorum: 67, // Requires 2 of 3 consensus challenge: 0, nonce: Date.now() }; const allocations = [ { participant: buyer, asset: 'usdc', amount: amount.toString() }, { participant: seller, asset: 'usdc', amount: '0' }, { participant: mediator, asset: 'usdc', amount: '0' } ]; return createAppSessionMessage(messageSigner, [{ definition: appDefinition, allocations }]); } ``` -------------------------------- ### Check Clearnode Service Status Source: https://github.com/layer-3/docs/blob/master/docs/manuals/running-clearnode-locally.md Displays the status of all services managed by Docker Compose. This is useful for monitoring if Clearnode and its dependencies are running correctly. ```bash docker-compose ps ``` -------------------------------- ### Clean Up Clearnode Docker Environment Source: https://github.com/layer-3/docs/blob/master/docs/manuals/running-clearnode-locally.md Removes all containers, networks, and volumes defined in the Docker Compose file. Use this command to perform a full cleanup of the local Clearnode environment. ```bash docker-compose down -v ``` -------------------------------- ### Load Balancing Strategy in Javascript Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/deployment.md Manages connections to a set of nodes using a round-robin load balancing strategy. Includes a health check mechanism to monitor node availability and latency, ensuring reliable connections. ```javascript class LoadBalancedConnection { constructor(clearNodeUrls) { this.clearNodeUrls = clearNodeUrls; this.connectionPool = new Map(); this.currentIndex = 0; } async getConnection() { const url = this.getNextUrl(); if (!this.connectionPool.has(url)) { const ws = await this.createConnection(url); this.connectionPool.set(url, ws); } return this.connectionPool.get(url); } getNextUrl() { const url = this.clearNodeUrls[this.currentIndex]; this.currentIndex = (this.currentIndex + 1) % this.clearNodeUrls.length; return url; } async healthCheck() { const healthPromises = this.clearNodeUrls.map(async (url) => { try { const ws = await this.createConnection(url); ws.close(); return { url, healthy: true, latency: Date.now() }; } catch (error) { return { url, healthy: false, error: error.message }; } }); return Promise.all(healthPromises); } } ``` -------------------------------- ### Implement Payment Routing Logic with JavaScript Source: https://github.com/layer-3/docs/blob/master/docs/learn/advanced/multi-party.md A JavaScript class `PaymentRouter` designed to manage multiple payment sessions and route payments efficiently. It includes methods for finding optimal payment paths and executing direct or multi-hop payments, handling session management and liquidity. ```javascript class PaymentRouter { constructor() { this.sessions = new Map(); // route -> sessionId this.liquidity = new Map(); // sessionId -> available amounts this.ws = null; this.messageSigner = null; } async routePayment(amount, recipient) { // Find optimal path considering liquidity and fees const path = await this.findOptimalPath(this.userAddress, recipient, amount); if (path.length === 1) { // Direct session exists return this.sendDirectPayment(path[0], amount, recipient); } else { // Multi-hop payment required return this.executeMultiHopPayment(path, amount, recipient); } } async sendDirectPayment(sessionId, amount, recipient) { const paymentMessage = { sessionId, type: 'payment', amount: amount.toString(), recipient, timestamp: Date.now() }; const signature = await this.messageSigner(JSON.stringify(paymentMessage)); this.ws.send(JSON.stringify({ ...paymentMessage, signature })); return paymentMessage; } } ```