diff --git a/.github/workflows/vale.yml b/.github/workflows/vale.yml new file mode 100644 index 0000000..53f12e0 --- /dev/null +++ b/.github/workflows/vale.yml @@ -0,0 +1,45 @@ +name: Vale + +on: + pull_request: + paths: + - ".github/workflows/vale.yml" + - ".pre-commit-config.yaml" + - ".vale.ini" + - ".vale/**" + - "docs/**/*.md" + - "docs/**/*.mdx" + - "templates/**/*.md" + - "templates/**/*.mdx" + workflow_dispatch: + +permissions: + contents: read + +jobs: + vale: + name: Vale advisory lint + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install Vale + run: | + VALE_VERSION=3.14.2 + curl -sSL "https://github.com/errata-ai/vale/releases/download/v${VALE_VERSION}/vale_${VALE_VERSION}_Linux_64-bit.tar.gz" -o /tmp/vale.tar.gz + tar -xzf /tmp/vale.tar.gz -C /tmp vale + sudo mv /tmp/vale /usr/local/bin/vale + vale --version + + - name: Run Vale + run: | + vale sync + set +e + vale --minAlertLevel=error docs templates + status=$? + if [ "$status" -ne 0 ]; then + echo "::warning::Vale found advisory issues. See the log above." + fi + exit 0 diff --git a/.gitignore b/.gitignore index b580350..90af70d 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* .vercel +.gstack/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ec05aec --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ +repos: + - repo: local + hooks: + - id: vale-docs + name: Vale docs language lint + entry: vale --minAlertLevel=error + language: system + files: ^(docs|templates)/.*\.(md|mdx)$ diff --git a/.vale.ini b/.vale.ini new file mode 100644 index 0000000..5152eca --- /dev/null +++ b/.vale.ini @@ -0,0 +1,19 @@ +StylesPath = .vale/styles +MinAlertLevel = suggestion + +[formats] +mdx = md + +[*.{md,mdx}] +BasedOnStyles = Yellow + +# Quadrant-specific rules. Off by default; per-folder overlays opt in. +Yellow.NoMetaphor = NO +Yellow.SpecVoice = NO +Yellow.NoNarrative = NO +Yellow.LearnCrossLink = NO +Yellow.ProtocolCrossLink = NO +Yellow.BuildPageShapeTLDR = NO +Yellow.BuildPageShapeStepByStep = NO +Yellow.BuildPageShapeVerify = NO +Yellow.BuildPageShapeNextSteps = NO diff --git a/.vale/styles/Yellow/AISlop.yml b/.vale/styles/Yellow/AISlop.yml new file mode 100644 index 0000000..b018edd --- /dev/null +++ b/.vale/styles/Yellow/AISlop.yml @@ -0,0 +1,33 @@ +extends: existence +message: "Avoid '%s'. Use direct, concrete wording." +level: error +ignorecase: true +tokens: + - "—" + - "–" + - "\\bdelve\\b" + - "\\bleverage\\b" + - "\\bharness(?:es|ed|ing)?\\s+(?:the|your|its|our|all|this|these|those|every|any)\\b" + - "\\bseamlessly\\b" + - "\\bseamless\\b" + - "\\brobust\\b" + - "\\bpowerful\\b" + - "\\bboasts?\\b" + - "\\beffortless\\b" + - "\\belevate\\b" + - "\\bstreamline\\b" + - "\\brevolutionary\\b" + - "\\bcutting-edge\\b" + - "\\bbleeding-edge\\b" + - "\\bin today's\\b" + - "\\bit's worth noting\\b" + - "\\bit is important to note\\b" + - "\\bnavigating the landscape\\b" + - "\\bthe world of\\b" + - "\\bat the end of the day\\b" + - "\\bthat being said\\b" + - "\\byou may want to\\b" + - "\\byou might consider\\b" + - "\\bfeel free to\\b" + - "\\bembark on your journey\\b" + - "\\bunlock the power of\\b" diff --git a/.vale/styles/Yellow/BuildPageShapeNextSteps.yml b/.vale/styles/Yellow/BuildPageShapeNextSteps.yml new file mode 100644 index 0000000..eeed080 --- /dev/null +++ b/.vale/styles/Yellow/BuildPageShapeNextSteps.yml @@ -0,0 +1,6 @@ +extends: occurrence +message: "Build how-to pages must include a '## Next steps' heading." +level: error +scope: raw +token: "(?m)^## Next steps\\b" +min: 1 diff --git a/.vale/styles/Yellow/BuildPageShapeStepByStep.yml b/.vale/styles/Yellow/BuildPageShapeStepByStep.yml new file mode 100644 index 0000000..5114e62 --- /dev/null +++ b/.vale/styles/Yellow/BuildPageShapeStepByStep.yml @@ -0,0 +1,6 @@ +extends: occurrence +message: "Build how-to pages must include a '## Step-by-step' heading." +level: error +scope: raw +token: "(?m)^## Step-by-step\\b" +min: 1 diff --git a/.vale/styles/Yellow/BuildPageShapeTLDR.yml b/.vale/styles/Yellow/BuildPageShapeTLDR.yml new file mode 100644 index 0000000..438e813 --- /dev/null +++ b/.vale/styles/Yellow/BuildPageShapeTLDR.yml @@ -0,0 +1,6 @@ +extends: occurrence +message: "Build how-to pages must include a '## TL;DR' heading." +level: error +scope: raw +token: "(?m)^## TL;DR\\b" +min: 1 diff --git a/.vale/styles/Yellow/BuildPageShapeVerify.yml b/.vale/styles/Yellow/BuildPageShapeVerify.yml new file mode 100644 index 0000000..70c53f7 --- /dev/null +++ b/.vale/styles/Yellow/BuildPageShapeVerify.yml @@ -0,0 +1,6 @@ +extends: occurrence +message: "Build how-to pages must include a '## Verify' heading." +level: error +scope: raw +token: "(?m)^## Verify\\b" +min: 1 diff --git a/.vale/styles/Yellow/LearnCrossLink.yml b/.vale/styles/Yellow/LearnCrossLink.yml new file mode 100644 index 0000000..c209382 --- /dev/null +++ b/.vale/styles/Yellow/LearnCrossLink.yml @@ -0,0 +1,6 @@ +extends: occurrence +message: "Learn pages must declare 'protocol_link' in frontmatter to point at their Protocol counterpart." +level: error +scope: raw +token: "(?m)^protocol_link:" +min: 1 diff --git a/.vale/styles/Yellow/NeedBasedLabels.yml b/.vale/styles/Yellow/NeedBasedLabels.yml new file mode 100644 index 0000000..3900190 --- /dev/null +++ b/.vale/styles/Yellow/NeedBasedLabels.yml @@ -0,0 +1,10 @@ +extends: existence +message: "Consider a task-based title instead of internal label '%s'." +level: suggestion +scope: heading +ignorecase: true +tokens: + - "\\bwdk-wallet-ton\\b" + - "\\bnitronode-rpc-overview\\b" + - "\\bsdk-compat-eventpoller\\b" + - "\\bapp-sessions-multi-party\\b" diff --git a/.vale/styles/Yellow/NoMetaphor.yml b/.vale/styles/Yellow/NoMetaphor.yml new file mode 100644 index 0000000..1368b93 --- /dev/null +++ b/.vale/styles/Yellow/NoMetaphor.yml @@ -0,0 +1,11 @@ +extends: existence +message: "Avoid analogy or metaphor in reference prose: '%s'." +level: warning +ignorecase: true +tokens: + - "\\blike a\\b" + - "\\blike an\\b" + - "\\bas if\\b" + - "\\bthink of\\b" + - "\\banalogy\\b" + - "\\bmetaphor\\b" diff --git a/.vale/styles/Yellow/NoNarrative.yml b/.vale/styles/Yellow/NoNarrative.yml new file mode 100644 index 0000000..86660f9 --- /dev/null +++ b/.vale/styles/Yellow/NoNarrative.yml @@ -0,0 +1,6 @@ +extends: occurrence +message: "Reference prose should stay terse. Split long paragraphs before they become narrative." +level: warning +scope: paragraph +token: "\\b\\w+\\b" +max: 90 diff --git a/.vale/styles/Yellow/ProtocolCrossLink.yml b/.vale/styles/Yellow/ProtocolCrossLink.yml new file mode 100644 index 0000000..19c5e2e --- /dev/null +++ b/.vale/styles/Yellow/ProtocolCrossLink.yml @@ -0,0 +1,6 @@ +extends: occurrence +message: "Protocol pages must declare 'learn_link' in frontmatter to point at their Learn counterpart." +level: error +scope: raw +token: "(?m)^learn_link:" +min: 1 diff --git a/.vale/styles/Yellow/SpecVoice.yml b/.vale/styles/Yellow/SpecVoice.yml new file mode 100644 index 0000000..cd70021 --- /dev/null +++ b/.vale/styles/Yellow/SpecVoice.yml @@ -0,0 +1,10 @@ +extends: existence +message: "Use definitional spec voice instead of narrative phrasing: '%s'." +level: warning +ignorecase: true +tokens: + - "\\bwe (?:will|can|should|need to)\\b" + - "\\byou (?:will|can|should|need to)\\b" + - "\\blet's\\b" + - "\\bthis guide (?:shows|walks|explains)\\b" + - "\\bin this (?:guide|page|section)\\b" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..348cfd0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,7 @@ +# Codex Instructions for yellow-docs + +- For Docusaurus changes, run `npm run typecheck` and `npm run build`. +- After build, run a local Docusaurus server with `npm run serve -- --host 127.0.0.1 --port ` and fetch each changed public route with `curl -fsS -L`. +- For navigation or versioning changes, fetch both current and versioned routes and confirm the expected links, labels, and version switch targets appear in the returned HTML. +- Internal Docusaurus doc links should be extensionless. Do not add `.md` or `.mdx` suffixes to Markdown links unless linking to a raw file intentionally. +- For Nitrolite protocol docs, keep content language-agnostic and source-bound to `/Users/maharshimishra/Documents/nitrolite/docs/protocol`. diff --git a/README.md b/README.md index b28211a..c8a01c0 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,23 @@ GIT_USER= yarn deploy ``` If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. + +## Site Architecture + +This site hosts two product docs as separate sub-sites under one Docusaurus instance: + +- **`/nitrolite/*`** — State channel SDK docs (versioned 1.x / 0.5.x) +- **`/clearnet/*`** — Decentralized clearing protocol docs (unversioned) +- **`/`** — Portal landing page linking to both sub-sites + +Source layout: + +- `docs/nitrolite/` — Nitrolite content (managed by the preset's docs plugin) +- `docs/clearnet/` — Clearnet content (managed by a second `@docusaurus/plugin-content-docs` instance, id: `clearnet`) +- `sidebars-nitrolite.ts` / `sidebars-clearnet.ts` — sidebar configs per sub-site +- `src/theme/Navbar/Content/` — swizzled component that filters top-navbar items by current path (via `customProps.showOn`) + +### Known Limitations + +- **Search**: lunr-search uses a single index across both sub-sites. A query from `/nitrolite/...` may surface Clearnet results and vice versa. To scope search per sub-site, replace lunr-search with Algolia (separate indices) or a custom search component. +- **Versioned docs links**: `versioned_docs/version-0.5.x/` is a historical snapshot and still contains `/docs/...` links from when the site lived under that path. Those links break under the new `/nitrolite/0.5.x/...` routing. Either rewrite versioned content links or accept them as archived. diff --git a/docs/api-reference/app-sessions.md b/docs/api-reference/app-sessions.md deleted file mode 100644 index 60dad55..0000000 --- a/docs/api-reference/app-sessions.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -sidebar_position: 3 -title: App Sessions -description: API reference for virtual application session management -keywords: [app session, create, submit, close, virtual application, api] -displayed_sidebar: apiSidebar ---- - -# App Sessions - -API methods for managing virtual application sessions. - -## create_app_session - -Creates a virtual application session between participants. - -**Request:** -```json -{ - "req": [1, "create_app_session", { - "definition": { - "protocol": "NitroRPC/0.2" | "NitroRPC/0.4", - "participants": Address[], - "weights": number[], - "quorum": number, - "challenge": number, - "nonce": number - }, - "allocations": AppAllocation[], - "session_data": string // Optional - }, timestamp], - "sig": Hex[] -} -``` - -**Response:** -```json -{ - "res": [1, "create_app_session", { - "app_session_id": Hex, - "version": string, - "status": "open" - }, timestamp], - "sig": [Hex] -} -``` - -## submit_app_state - -Updates session state and redistributes funds. - -### NitroRPC/0.2 - -**Request:** -```json -{ - "req": [1, "submit_app_state", { - "app_session_id": Hex, - "allocations": AppAllocation[], - "session_data": string // Optional - }, timestamp], - "sig": Hex[] -} -``` - -### NitroRPC/0.4 - -**Request:** -```json -{ - "req": [1, "submit_app_state", { - "app_session_id": Hex, - "intent": "operate" | "deposit" | "withdraw", - "version": number, - "allocations": AppAllocation[], - "session_data": string // Optional - }, timestamp], - "sig": Hex[] -} -``` - -**Response:** -```json -{ - "res": [1, "submit_app_state", { - "app_session_id": Hex, - "version": string, - "status": "open" - }, timestamp], - "sig": [Hex] -} -``` - -## close_app_session - -Closes session and finalizes fund distribution. - -**Request:** -```json -{ - "req": [1, "close_app_session", { - "app_session_id": Hex, - "allocations": AppAllocation[], - "session_data": string // Optional - }, timestamp], - "sig": Hex[] -} -``` - -**Response:** -```json -{ - "res": [1, "close_app_session", { - "app_session_id": Hex, - "version": string, - "status": "closed" - }, timestamp], - "sig": [Hex] -} -``` - -## Types - -### AppAllocation -```typescript -interface AppAllocation { - participant: Address; - asset: string; - amount: string; -} -``` - -### AppDefinition -```typescript -interface AppDefinition { - protocol: "NitroRPC/0.2" | "NitroRPC/0.4"; - participants: Address[]; - weights: number[]; - quorum: number; - challenge: number; - nonce: number; -} -``` - -## Intent Types (NitroRPC/0.4) - -- `operate`: Redistribute existing session funds -- `deposit`: Add funds from participants' unified balances -- `withdraw`: Remove funds to participants' unified balances - -## Session Status - -- `open`: Session is active and accepting state updates -- `closed`: Session is finalized, no further updates allowed \ No newline at end of file diff --git a/docs/api-reference/index.md b/docs/api-reference/index.md deleted file mode 100644 index caeb7ac..0000000 --- a/docs/api-reference/index.md +++ /dev/null @@ -1,263 +0,0 @@ ---- -sidebar_position: 9 -title: API Reference -description: Complete Yellow SDK method documentation with examples and type definitions -keywords: [api reference, methods, types, virtualapp client, documentation] -displayed_sidebar: apiSidebar ---- - -# API Reference - -Complete reference for all Yellow SDK methods, types, and utilities. - -## VirtualAppRPC Functions - -### Message Creation - -#### `createAppSessionMessage(signer: MessageSigner, sessions: AppSession[]): Promise` - -Creates a signed application session message. - -**Parameters:** -```typescript -type MessageSigner = (payload: any) => Promise; - -interface AppSession { - definition: AppDefinition; - allocations: AppAllocation[]; -} - -interface AppDefinition { - protocol: string; - participants: Address[]; - weights: number[]; - quorum: number; - challenge: number; - nonce: number; -} - -interface AppAllocation { - participant: Address; - asset: string; - amount: string; -} -``` - -#### `createStateUpdateMessage(signer: MessageSigner, update: StateUpdate): Promise` - -Creates a signed state update message. - -#### `parseRPCResponse(data: string): RPCMessage` - -Parses ClearNode response messages. - -**Returns:** -```typescript -interface RPCMessage { - id?: number; - method?: string; - params?: any; - result?: any; - error?: RPCError; -} -``` - -### Utilities - -#### `computeChannelId(channel: Channel): ChannelId` - -Calculates deterministic channel identifier. - -#### `computeStateHash(state: State, channelId: ChannelId): Hash` - -Calculates state hash for signatures. - -#### `validateSignature(state: State, signature: Hex, signer: Address): Promise` - -Verifies state signature. - -## Type Definitions - -### Core Types - -```typescript -type Address = `0x${string}`; -type Hash = `0x${string}`; -type Hex = `0x${string}`; -type ChannelId = Hash; - -interface Channel { - participants: Address[]; - adjudicator: Address; - challenge: bigint; - nonce: bigint; -} - -interface State { - intent: StateIntent; - version: bigint; - data: Hex; - allocations: Allocation[]; - sigs: Hex[]; -} - -interface Allocation { - destination: Address; - token: Address; - amount: bigint; -} - -enum StateIntent { - OPERATE = 0, - INITIALIZE = 1, - RESIZE = 2, - FINALIZE = 3 -} - -enum ChannelStatus { - VOID = 0, - INITIAL = 1, - ACTIVE = 2, - DISPUTE = 3, - FINAL = 4 -} -``` - -### Connection Configuration - -```typescript -interface ClearNodeConfig { - endpoint: string; // WebSocket endpoint - apiKey?: string; // Optional authentication - timeout: number; // Connection timeout - retryAttempts: number; // Reconnection attempts -} - -const config = { - endpoint: 'wss://clearnet-sandbox.yellow.com/ws', // or wss://clearnet.yellow.com/ws for production - timeout: 30000, - retryAttempts: 3 -}; -``` - -### RPC Types - -```typescript -interface RPCRequest { - id: number; - method: string; - params: any[]; - timestamp?: number; -} - -interface RPCResponse { - id: number; - result?: any; - error?: RPCError; - timestamp: number; -} - -interface RPCError { - code: number; - message: string; - data?: any; -} -``` - -## Error Types - -### Client Errors - -```typescript -class YellowSDKError extends Error { - constructor(message: string, public code: string, public context?: any) { - super(message); - this.name = 'YellowSDKError'; - } -} - -class NetworkError extends YellowSDKError { - constructor(message: string, context?: any) { - super(message, 'NETWORK_ERROR', context); - } -} - -class ContractError extends YellowSDKError { - constructor(message: string, context?: any) { - super(message, 'CONTRACT_ERROR', context); - } -} - -class ValidationError extends YellowSDKError { - constructor(message: string, context?: any) { - super(message, 'VALIDATION_ERROR', context); - } -} -``` - -### Error Handling - -```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); - } -} -``` - -## Constants - -### Network Endpoints - -```typescript -const CLEARNODE_ENDPOINTS = { - PRODUCTION: 'wss://clearnet.yellow.com/ws', - SANDBOX: 'wss://clearnet-sandbox.yellow.com/ws', - LOCAL: 'ws://localhost:8080/ws' -}; - -const PROTOCOLS = { - PAYMENT: 'payment-app-v1', - GAMING: 'gaming-app-v1', - ESCROW: 'escrow-app-v1', - TOURNAMENT: 'tournament-v1', - SUBSCRIPTION: 'subscription-v1' -}; -``` - -### Message Types - -```typescript -const MESSAGE_TYPES = { - SESSION_CREATE: 'session_create', - SESSION_MESSAGE: 'session_message', - PAYMENT: 'payment', - STATE_UPDATE: 'state_update', - PARTICIPANT_JOIN: 'participant_join', - SESSION_CLOSE: 'session_close', - ERROR: 'error' -}; - -const SESSION_STATUS = { - PENDING: 'pending', - ACTIVE: 'active', - CLOSING: 'closing', - CLOSED: 'closed', - ERROR: 'error' -}; -``` - -This API reference provides everything you need to integrate VirtualAppRPC into your applications with confidence and precision. \ No newline at end of file diff --git a/docs/build/api/_category_.json b/docs/build/api/_category_.json deleted file mode 100644 index 69cc463..0000000 --- a/docs/build/api/_category_.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "label": "API", - "position": 3, - "link": { - "type": "generated-index" - }, - "collapsible": false, - "collapsed": false -} diff --git a/docs/build/quick-start/index.md b/docs/build/quick-start/index.md deleted file mode 100644 index 1a63788..0000000 --- a/docs/build/quick-start/index.md +++ /dev/null @@ -1,359 +0,0 @@ ---- -sidebar_position: 2 -sidebar_label: Quick Start -title: Quick Start -description: Build your first Yellow App in 5 minutes - a complete beginner's guide -keywords: [yellow sdk, quick start, tutorial, virtualapp, state channels, beginner guide] -displayed_sidebar: buildSidebar ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Quick Start Guide - -Build your first Yellow App in 5 minutes! This guide walks you through creating a simple payment application using state channels. - -## What You'll Build - -A basic payment app where users can: -- Deposit funds into a state channel -- Send instant payments to another user -- Withdraw remaining funds - -No blockchain knowledge required - we'll handle the complexity for you! - -## Prerequisites - -- **Node.js 16+** installed on your computer -- **A wallet** (MetaMask recommended) -- **Basic JavaScript/TypeScript** knowledge - -## Step 1: Installation - -Create a new project and install the Yellow SDK: - - - - -```bash showLineNumbers -mkdir my-yellow-app -cd my-yellow-app -npm init -y -npm install @erc7824/nitrolite -``` - - - - -```bash showLineNumbers -mkdir my-yellow-app -cd my-yellow-app -yarn init -y -yarn add @erc7824/nitrolite -``` - - - - -```bash showLineNumbers -mkdir my-yellow-app -cd my-yellow-app -pnpm init -pnpm add @erc7824/nitrolite -``` - - - - -## Step 2: Connect to ClearNode - -Create a file `app.js` and connect to the Yellow Network. - -:::tip Clearnode Endpoints -- **Production**: `wss://clearnet.yellow.com/ws` -- **Sandbox**: `wss://clearnet-sandbox.yellow.com/ws` (recommended for testing) -::: - -```javascript title="app.js" showLineNumbers -import { createAppSessionMessage, parseAnyRPCResponse } from '@erc7824/nitrolite'; - -// Connect to Yellow Network (using sandbox for testing) -const ws = new WebSocket('wss://clearnet-sandbox.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...'); -``` - -## Step 3: Create Application Session - -Set up your wallet for signing messages: - -```javascript showLineNumbers -// 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 }; -} -``` - -## Step 4: Create Application Session - -Create a session for your payment app: - -```javascript showLineNumbers -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 }; -} -``` - -## Step 5: Send Instant Payments - -```javascript showLineNumbers -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() - }; - - // Send instantly through ClearNode - ws.send(JSON.stringify(signedPayment)); - console.log('💸 Payment sent instantly!'); -} - -// Usage -await sendPayment(ws, messageSigner, 100000n, partnerAddress); // Send 0.1 USDC -``` - -## Step 6: Handle Incoming Messages - -```javascript showLineNumbers -// Enhanced message handling -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 -} -``` - -## Complete Example - -Here's a complete working example you can copy and run: - -```javascript title="SimplePaymentApp.js" showLineNumbers -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 (sandbox for testing) - this.ws = new WebSocket('wss://clearnet-sandbox.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 -``` - -## What's Next? - -Congratulations! You've built your first Yellow App. Here's what to explore next: - -- **[Advanced Topics](../../learn/introduction/architecture-at-a-glance)**: Learn about architecture, multi-party applications, and production deployment -- **[API Reference](../../api-reference)**: Explore all available SDK methods and options - -## Need Help? - -- **Documentation**: Continue reading the guides for in-depth explanations -- **Community**: Join our developer community for support -- **Examples**: Check out our GitHub repository for sample applications - -You're now ready to build fast, scalable apps with Yellow SDK! diff --git a/docs/build/sdk/go/api-reference.mdx b/docs/build/sdk/go/api-reference.mdx deleted file mode 100644 index e73c5f1..0000000 --- a/docs/build/sdk/go/api-reference.mdx +++ /dev/null @@ -1,165 +0,0 @@ ---- -title: API Reference -description: Complete API reference for the Clearnode Go SDK -sidebar_position: 2 ---- - -# Go SDK API Reference - -## State Operations (Off-Chain) - -```go -client.Deposit(ctx, blockchainID, asset, amount) // Prepare deposit state -client.Withdraw(ctx, blockchainID, asset, amount) // Prepare withdrawal state -client.Transfer(ctx, recipientWallet, asset, amount) // Prepare transfer state -client.CloseHomeChannel(ctx, asset) // Prepare finalize state -client.Acknowledge(ctx, asset) // Acknowledge received state -``` - -All return `(*core.State, error)`. Use `Checkpoint` to settle on-chain. - ---- - -## Blockchain Settlement - -```go -client.Checkpoint(ctx, asset) // Settle latest state on-chain -client.Challenge(ctx, state) // Submit on-chain challenge -client.ApproveToken(ctx, chainID, asset, amount) // Approve ChannelHub to spend tokens -client.GetOnChainBalance(ctx, chainID, asset, wallet) // Query on-chain token balance -``` - -`Checkpoint` routes automatically based on transition type and channel status: -- **Void** → creates channel -- **Deposit/Withdrawal** → checkpoints state -- **Finalize** → closes channel - ---- - -## Node Information - -```go -client.Ping(ctx) // Health check -client.GetConfig(ctx) // Node configuration -client.GetBlockchains(ctx) // Supported blockchains -client.GetAssets(ctx, &blockchainID) // Supported assets (nil for all) -``` - ---- - -## User Queries - -```go -client.GetBalances(ctx, wallet) // User balances -client.GetTransactions(ctx, wallet, opts) // Transaction history (paginated) -``` - ---- - -## Channel Queries - -```go -client.GetHomeChannel(ctx, wallet, asset) // Home channel info -client.GetEscrowChannel(ctx, escrowChannelID) // Escrow channel info -client.GetLatestState(ctx, wallet, asset, onlySigned) // Latest state -``` - ---- - -## App Registry - -```go -apps, meta, err := client.GetApps(ctx, &sdk.GetAppsOptions{ - AppID: &appID, - OwnerWallet: &wallet, -}) - -err := client.RegisterApp(ctx, "my-app", `{"name": "My App"}`, false) -``` - ---- - -## App Sessions - -```go -sessions, meta, err := client.GetAppSessions(ctx, opts) -def, err := client.GetAppDefinition(ctx, appSessionID) -sessionID, version, status, err := client.CreateAppSession(ctx, def, data, sigs) -nodeSig, err := client.SubmitAppSessionDeposit(ctx, update, sigs, asset, amount) -err := client.SubmitAppState(ctx, update, sigs) -batchID, err := client.RebalanceAppSessions(ctx, signedUpdates) -``` - ---- - -## Session Keys — App Sessions - -```go -state := app.AppSessionKeyStateV1{ - UserAddress: client.GetUserAddress(), - SessionKey: "0xSessionKey...", - Version: 1, - ApplicationIDs: []string{"app1"}, - AppSessionIDs: []string{}, - ExpiresAt: time.Now().Add(24 * time.Hour), -} -sig, err := client.SignSessionKeyState(state) -state.UserSig = sig -err = client.SubmitAppSessionKeyState(ctx, state) - -states, err := client.GetLastAppKeyStates(ctx, userAddress, nil) -``` - ---- - -## Session Keys — Channels - -```go -state := core.ChannelSessionKeyStateV1{ - UserAddress: client.GetUserAddress(), - SessionKey: "0xSessionKey...", - Version: 1, - Assets: []string{"usdc", "weth"}, - ExpiresAt: time.Now().Add(24 * time.Hour), -} -sig, err := client.SignChannelSessionKeyState(state) -state.UserSig = sig -err = client.SubmitChannelSessionKeyState(ctx, state) - -states, err := client.GetLastChannelKeyStates(ctx, userAddress, nil) -``` - ---- - -## Utilities - -```go -client.Close() // Close connection -client.WaitCh() // Connection monitor channel -client.SignState(state) // Sign a state (advanced) -client.GetUserAddress() // Get signer's address -client.SetHomeBlockchain(asset, chainID) // Set default blockchain -``` - ---- - -## Types - -```go -// Core types -core.State // Channel state -core.Channel // Channel info -core.Transition // State transition -core.Transaction // Transaction record -core.Asset // Asset info -core.Blockchain // Blockchain info -core.ChannelSessionKeyStateV1 // Channel session key state - -// App types -app.AppV1 // Application definition -app.AppInfoV1 // Application info with timestamps -app.AppSessionInfoV1 // Session info -app.AppDefinitionV1 // Session definition -app.AppStateUpdateV1 // Session update -app.AppSessionKeyStateV1 // App session key state -``` diff --git a/docs/build/sdk/go/getting-started.mdx b/docs/build/sdk/go/getting-started.mdx deleted file mode 100644 index fc002d2..0000000 --- a/docs/build/sdk/go/getting-started.mdx +++ /dev/null @@ -1,156 +0,0 @@ ---- -title: Getting Started -description: Install and set up the Clearnode Go SDK -sidebar_position: 1 ---- - -# Getting Started with the Go SDK - -Go SDK for Clearnode payment channels providing both high-level and low-level operations in a unified client. It has full feature parity with the TypeScript SDK. - -## Requirements - -- Go 1.21+ -- Running Clearnode instance -- Blockchain RPC endpoint (for `Checkpoint` settlement) - -## Installation - -```bash -go get github.com/layer-3/nitrolite/sdk/go -``` - -## Quick Start - -```go -package main - -import ( - "context" - "fmt" - "github.com/layer-3/nitrolite/pkg/core" - "github.com/layer-3/nitrolite/pkg/sign" - sdk "github.com/layer-3/nitrolite/sdk/go" - "github.com/shopspring/decimal" -) - -func main() { - // 1. Create signers from private key - msgSigner, _ := sign.NewEthereumMsgSigner(privateKeyHex) - stateSigner, _ := core.NewChannelDefaultSigner(msgSigner) - txSigner, _ := sign.NewEthereumRawSigner(privateKeyHex) - - // 2. Create unified client - client, _ := sdk.NewClient( - "wss://clearnode.example.com/ws", - stateSigner, - txSigner, - sdk.WithBlockchainRPC(80002, "https://polygon-amoy.alchemy.com/v2/KEY"), - ) - defer client.Close() - - ctx := context.Background() - - // 3. Build and co-sign states off-chain - state, _ := client.Deposit(ctx, 80002, "usdc", decimal.NewFromInt(100)) - fmt.Printf("Deposit state version: %d\n", state.Version) - - // 4. Settle on-chain via Checkpoint - txHash, _ := client.Checkpoint(ctx, "usdc") - fmt.Printf("On-chain tx: %s\n", txHash) - - // 5. Transfer (off-chain only) - state, _ = client.Transfer(ctx, "0xRecipient...", "usdc", decimal.NewFromInt(50)) - - // 6. Low-level operations on the same client - config, _ := client.GetConfig(ctx) - balances, _ := client.GetBalances(ctx, client.GetUserAddress()) -} -``` - -## Creating a Client - -```go -// Step 1: Create signers -msgSigner, err := sign.NewEthereumMsgSigner("0x1234...") -stateSigner, err := core.NewChannelDefaultSigner(msgSigner) -txSigner, err := sign.NewEthereumRawSigner("0x1234...") - -// Step 2: Create unified client -client, err := sdk.NewClient( - wsURL, - stateSigner, // core.ChannelSigner for channel states - txSigner, // sign.Signer for blockchain transactions - sdk.WithBlockchainRPC(chainID, rpcURL), - sdk.WithHandshakeTimeout(10*time.Second), - sdk.WithPingInterval(5*time.Second), -) - -// Step 3: (Optional) Set home blockchain for assets -err = client.SetHomeBlockchain("usdc", 80002) -``` - -## Channel Signers - -The Go SDK wraps raw signers with a `ChannelSigner` interface that prepends a type byte to every signature: - -| Type | Byte | Struct | Usage | -|------|------|--------|-------| -| Default | `0x00` | `core.ChannelDefaultSigner` | Main wallet signs directly | -| Session Key | `0x01` | `core.ChannelSessionKeySignerV1` | Delegated session key | - -```go -// Default signer (wraps EthereumMsgSigner with 0x00 prefix) -msgSigner, _ := sign.NewEthereumMsgSigner(privateKeyHex) -channelSigner, _ := core.NewChannelDefaultSigner(msgSigner) -client, _ := sdk.NewClient(wsURL, channelSigner, txSigner, opts...) -``` - -## Key Concepts - -### Two-Step Pattern - -```go -// Step 1: Build and co-sign state off-chain -state, _ := client.Deposit(ctx, 80002, "usdc", decimal.NewFromInt(100)) - -// Step 2: Settle on-chain (when needed) -txHash, _ := client.Checkpoint(ctx, "usdc") -``` - -### Channel Lifecycle - -1. **Void** — No channel exists -2. **Create** — `Deposit()` creates channel on-chain via `Checkpoint()` -3. **Open** — Channel active; can deposit, withdraw, transfer -4. **Challenged** — Dispute initiated -5. **Closed** — Channel finalized - -## Configuration Options - -```go -sdk.WithBlockchainRPC(chainID, rpcURL) // Required for Checkpoint -sdk.WithHandshakeTimeout(duration) // Default: 5s -sdk.WithPingInterval(duration) // Default: 5s -sdk.WithErrorHandler(func(error)) // Connection error handler -``` - -## Error Handling - -```go -state, err := client.Deposit(ctx, 80002, "usdc", amount) -if err != nil { - log.Printf("State error: %v", err) -} - -txHash, err := client.Checkpoint(ctx, "usdc") -if err != nil { - log.Printf("Checkpoint error: %v", err) -} -``` - -Common errors: -- `"home blockchain not set for asset"` — Missing `SetHomeBlockchain` -- `"blockchain RPC not configured for chain"` — Missing `WithBlockchainRPC` -- `"no channel exists for asset"` — `Checkpoint` without a co-signed state -- `"insufficient balance"` — Not enough funds in channel/wallet diff --git a/docs/build/sdk/index.md b/docs/build/sdk/index.md deleted file mode 100644 index 3493fb4..0000000 --- a/docs/build/sdk/index.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: SDK Overview -description: Yellow Network SDKs for building state channel applications -sidebar_position: 1 ---- - -# SDK Overview - -Yellow Network provides official SDKs for building applications on top of Nitrolite payment channels. All SDKs share the same two-step architecture: **build and co-sign states off-chain**, then **settle on-chain when needed**. - -## Available SDKs - -| Package | Language | Description | -|---------|----------|-------------| -| [`@yellow-org/sdk`](./typescript/getting-started) | TypeScript | Main SDK with full API coverage | -| [`@yellow-org/sdk-compat`](./typescript-compat/overview) | TypeScript | Compatibility layer for migrating from v0.5.3 | -| [`clearnode-go-sdk`](./go/getting-started) | Go | Go SDK with full feature parity | - -## Architecture - -All SDKs follow a unified design: - -- **State Operations** (off-chain): `deposit()`, `withdraw()`, `transfer()`, `closeHomeChannel()`, `acknowledge()` — build and co-sign channel states without touching the blockchain. -- **Blockchain Settlement**: `checkpoint()` — the single entry point for all on-chain transactions. Routes to the correct contract method based on transition type and channel status. -- **Low-Level Operations**: Direct RPC access for app sessions, session keys, queries, and custom flows. - -```mermaid -sequenceDiagram - participant App - participant SDK - participant Node as Clearnode - participant Chain as Blockchain - - App->>SDK: deposit(chain, asset, amount) - SDK->>Node: Build & co-sign state - Node-->>SDK: Co-signed state - SDK-->>App: State object - - App->>SDK: checkpoint(asset) - SDK->>Chain: Create/checkpoint/close channel - Chain-->>SDK: Transaction hash - SDK-->>App: tx hash -``` - -## Choosing an SDK - -- **New TypeScript projects**: Use [`@yellow-org/sdk`](./typescript/getting-started) directly. -- **Migrating from v0.5.3**: Use [`@yellow-org/sdk-compat`](./typescript-compat/overview) to minimise code changes, then migrate to the main SDK at your own pace. -- **Go projects**: Use the [Go SDK](./go/getting-started) — it has full feature parity with the TypeScript SDK. diff --git a/docs/build/sdk/typescript-compat/migration-offchain.mdx b/docs/build/sdk/typescript-compat/migration-offchain.mdx deleted file mode 100644 index a5efde4..0000000 --- a/docs/build/sdk/typescript-compat/migration-offchain.mdx +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: "Migration: Off-Chain" -description: Off-chain migration from v0.5.3 to the compat layer -sidebar_position: 4 ---- - -# Off-Chain Migration Guide - -Covers authentication, app sessions, transfers, ledger queries, and event polling when migrating from v0.5.3. - -## Authentication - -v1.0.0 handles authentication internally when using `NitroliteClient`. For legacy WebSocket-auth code paths, the compat layer keeps `createAuthRequestMessage`, `createAuthVerifyMessage`, `createAuthVerifyMessageWithJWT`, and `createEIP712AuthMessageSigner` available. - -## App Sessions - -### List - -**Before:** `createGetAppSessionsMessage` + `sendRequest` + `parseGetAppSessionsResponse` - -**After:** - -```typescript -const sessions = await client.getAppSessionsList(); -``` - -### Create - -**Before:** - -```typescript -const msg = await createAppSessionMessage(signer.sign, { definition, allocations }); -const raw = await sendRequest(msg); -parseCreateAppSessionResponse(raw); -``` - -**After:** - -```typescript -await client.createAppSession(definition, allocations); -``` - -### Close - -**Before:** `createCloseAppSessionMessage` + send + parse - -**After:** - -```typescript -await client.closeAppSession(appSessionId, allocations); -``` - -### Submit State - -**Before:** `createSubmitAppStateMessage` + send - -**After:** - -```typescript -await client.submitAppState(params); -``` - -## Transfers - -**Before:** - -```typescript -const msg = await createTransferMessage(signer.sign, { destination, allocations }); -await sendRequest(msg); -``` - -**After:** - -```typescript -await client.transfer(destination, allocations); -``` - -## Ledger Queries - -**Before:** `createGetLedgerBalancesMessage` / `createGetLedgerEntriesMessage` + send + parse - -**After:** - -```typescript -const balances = await client.getBalances(); -const entries = await client.getLedgerEntries(); -``` - -## Event Polling - -v0.5.3 used WebSocket push events (`ChannelUpdate`, `BalanceUpdate`). v1.0.0 uses polling. The `EventPoller` bridges this gap: - -```typescript -import { EventPoller } from '@yellow-org/sdk-compat'; - -const poller = new EventPoller(client, { - onChannelUpdate: (channels) => updateUI(channels), - onBalanceUpdate: (balances) => updateBalances(balances), - onAssetsUpdate: (assets) => updateAssets(assets), - onError: (err) => console.error(err), -}, 5000); - -poller.start(); -``` - -## RPC Compatibility Helpers - -The `create*Message` and `parse*Response` functions still exist so existing imports compile. Most are transitional placeholders. Prefer `NitroliteClient` methods directly for new code. diff --git a/docs/build/sdk/typescript-compat/migration-onchain.mdx b/docs/build/sdk/typescript-compat/migration-onchain.mdx deleted file mode 100644 index 73a0ee4..0000000 --- a/docs/build/sdk/typescript-compat/migration-onchain.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: "Migration: On-Chain" -description: On-chain migration from v0.5.3 to the compat layer -sidebar_position: 3 ---- - -# On-Chain Migration Guide - -Covers deposits, withdrawals, channel operations, amount handling, and contract addresses when migrating from v0.5.3. - -## Deposits - -**Before (v0.5.3):** Manual approve → deposit → createChannel - -```typescript -await approveToken(custody, tokenAddress, amount); -await sendRequest(createDepositMessage(signer.sign, { token: tokenAddress, amount })); -await sendRequest(createCreateChannelMessage(signer.sign, { token: tokenAddress, amount })); -``` - -**After (compat):** Single call — approval and channel creation are implicit - -```typescript -await client.deposit(tokenAddress, amount); -``` - -## Withdrawals - -**Before (v0.5.3):** Manual close → checkpoint → withdraw - -```typescript -const closeMsg = await createCloseChannelMessage(signer.sign, { channel_id }); -const raw = await sendRequest(closeMsg); -await sendRequest(createWithdrawMessage(signer.sign, { token, amount })); -``` - -**After (compat):** Single call - -```typescript -await client.withdrawal(tokenAddress, amount); -``` - -## Channel Operations - -| Operation | v0.5.3 | Compat | -|-----------|--------|--------| -| Create | Explicit `createChannel()` | Implicit on first `deposit()` | -| Close | `createCloseChannelMessage` + send + parse | `client.closeChannel()` | -| Resize | `createResizeChannelMessage` + send + parse | `client.resizeChannel({ allocate_amount, token })` | - -## Amount Handling - -**Before (v0.5.3):** Raw `BigInt` everywhere; app must handle decimals - -```typescript -const amount = 11_000_000n; // 11 USDC (6 decimals) -``` - -**After (compat):** Accepts both; conversion handled internally - -```typescript -// Raw BigInt still works -await client.deposit(tokenAddress, 11_000_000n); - -// Or use helpers -const formatted = client.formatAmount(tokenAddress, 11_000_000n); // "11.0" -const parsed = client.parseAmount(tokenAddress, "11.0"); // 11_000_000n -``` - -For transfers and allocations, compat accepts human-readable strings: `{ asset: 'usdc', amount: '5.0' }`. - -## Contract Addresses - -**Before (v0.5.3):** Manual config - -```typescript -const addresses = { custody: '0x...', adjudicator: '0x...' }; -``` - -**After (compat):** Fetched from clearnode `get_config` — no manual setup. The `addresses` field in config is deprecated and ignored. diff --git a/docs/build/sdk/typescript-compat/migration-overview.mdx b/docs/build/sdk/typescript-compat/migration-overview.mdx deleted file mode 100644 index 8a71402..0000000 --- a/docs/build/sdk/typescript-compat/migration-overview.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Migration Overview -description: Migrating from v0.5.3 to the compat layer -sidebar_position: 2 ---- - -# Migrating from v0.5.3 to Compat Layer - -This guide explains how to migrate your Nitrolite dApp from the v0.5.3 SDK to the compat layer, which bridges the old API to the v1.0.0 runtime with minimal code changes. - -## Why Use the Compat Layer - -A direct migration from v0.5.3 to v1.0.0 touches **20+ files** per app with deep, scattered rewrites. The compat layer reduces this to **~5 file changes** per app. - -:::tip Automate with Codemod -Before migrating manually, try the [yellow-sdk-codemod](https://github.com/layer-3/yellow-sdk-codemod). It rewrites `@erc7824/nitrolite` imports, renames auth fields, migrates client constructors, updates `package.json`, and flags RPC patterns with `// TODO [codemod]` comments for the remaining manual steps. -::: - -## Installation - -```bash -npm install @yellow-org/sdk-compat -# Peer dependencies -npm install @yellow-org/sdk viem -``` - -## Import Swap - -| Before (v0.5.3) | After (compat) | -|-----------------|----------------| -| `import { createGetChannelsMessage, parseGetChannelsResponse } from '@layer-3/nitrolite'` | `import { NitroliteClient } from '@yellow-org/sdk-compat'` | -| Types: `AppSession`, `LedgerChannel`, `RPCAppDefinition` | Same types — re-exported from `@yellow-org/sdk-compat` | - -For **types**, just change the package name. For **functions**, switch to client methods instead of `create*Message` / `parse*Response`. - -## The Key Pattern Change - -**Before (v0.5.3):** create-sign-send-parse - -```typescript -const msg = await createGetChannelsMessage(signer.sign, addr); -const raw = await sendRequest(msg); -const parsed = parseGetChannelsResponse(raw); -const channels = parsed.params.channels; -``` - -**After (compat):** direct client method - -```typescript -const client = await NitroliteClient.create(config); -const channels = await client.getChannels(); -``` - -## What Stays the Same - -- **Type shapes:** `AppSession`, `LedgerChannel`, `RPCAppDefinition`, `RPCBalance`, `RPCAsset`, etc. -- **Response formats:** Balances, ledger entries, app sessions — same structure as v0.5.3. -- **Auth helpers:** `createAuthRequestMessage`, `createAuthVerifyMessage`, `createAuthVerifyMessageWithJWT` remain available. - -## What Changes - -| Concern | v0.5.3 | Compat | -|---------|--------|--------| -| WebSocket | App creates and manages `WebSocket` | Managed internally by the client | -| Signing | App passes `signer.sign` into every message | Internal — client uses `WalletClient` | -| Amounts | Raw `BigInt` everywhere | Compat accepts both; conversion handled internally | -| Contract addresses | Manual config | Fetched from clearnode `get_config` | -| Channel creation | Explicit `createChannel()` | Implicit on first `deposit()` | - -## Next Steps - -- [On-Chain Changes](./migration-onchain) — Deposits, withdrawals, channel operations, amount handling -- [Off-Chain Changes](./migration-offchain) — App sessions, transfers, ledger queries, event polling diff --git a/docs/build/sdk/typescript-compat/overview.mdx b/docs/build/sdk/typescript-compat/overview.mdx deleted file mode 100644 index db1e283..0000000 --- a/docs/build/sdk/typescript-compat/overview.mdx +++ /dev/null @@ -1,256 +0,0 @@ ---- -title: Overview -description: Compatibility layer bridging Nitrolite SDK v0.5.3 API to v1.0.0 -sidebar_position: 1 ---- - -# TypeScript Compat SDK - -Compatibility layer that bridges the Nitrolite SDK **v0.5.3 API** to the **v1.0.0 runtime**, letting existing dApps upgrade to the new protocol with minimal code changes. - -
-Already integrated with @erc7824/nitrolite? - -If your app is already built on `@erc7824/nitrolite` (v0.4 through v0.5.3), you can speed up the migration using the [yellow-sdk-codemod](https://github.com/layer-3/yellow-sdk-codemod) tool. It handles the repetitive parts of the upgrade automatically: rewriting imports, migrating client constructors, renaming auth fields, and updating your `package.json`. Where the change requires context (collapsing RPC call chains, removing manual WebSocket setup), the codemod leaves a `// TODO [codemod]` comment with the exact replacement so you know what to do. - -```bash -# Clone the codemod and install its dependencies -git clone https://github.com/layer-3/yellow-sdk-codemod.git -cd yellow-sdk-codemod && npm install - -# Scan your project to see what needs changing (no files are modified) -npx tsx src/cli.ts scan --path /path/to/your-app/src - -# Apply all transforms and update dependencies -npx tsx src/cli.ts run --path /path/to/your-app --update-deps -``` - -After running, search for `TODO [codemod]` in your codebase and work through the remaining items. See the [codemod README](https://github.com/layer-3/yellow-sdk-codemod#readme) for the full list of transforms and CLI options. - -
- -## Why Use the Compat Layer - -The v1.0.0 protocol introduces breaking changes across 14 dimensions — wire format, authentication, WebSocket lifecycle, unit system, asset resolution, and more. A direct migration touches 20+ files per app with deep, scattered rewrites. - -The compat layer centralises this complexity into **~1,000 lines** that absorb the protocol differences, reducing per-app integration effort by an estimated **56–70%**. - -## Installation - -```bash -npm install @yellow-org/sdk-compat -# peer dependencies -npm install @yellow-org/sdk viem -``` - -## Quick Start - -```typescript -import { NitroliteClient, blockchainRPCsFromEnv } from '@yellow-org/sdk-compat'; - -// Create client (replaces new Client(ws, signer)) -const client = await NitroliteClient.create({ - wsURL: 'wss://clearnode.example.com/ws', - walletClient, // viem WalletClient with account - chainId: 11155111, // Sepolia - blockchainRPCs: blockchainRPCsFromEnv(), -}); - -// Deposit (creates channel if needed) -await client.deposit(tokenAddress, 11_000_000n); - -// Query -const channels = await client.getChannels(); -const balances = await client.getBalances(); -const sessions = await client.getAppSessionsList(); - -// Transfer -await client.transfer(recipientAddress, [{ asset: 'usdc', amount: '5.0' }]); - -// Cleanup -await client.closeChannel(); -await client.close(); -``` - -## Method Cheat Sheet - -### Channel Operations - -| Method | Description | -|--------|-------------| -| `deposit(token, amount)` | Deposit to channel (creates if needed) | -| `depositAndCreateChannel(token, amount)` | Alias for `deposit()` | -| `withdrawal(token, amount)` | Withdraw from channel | -| `closeChannel(params?)` | Close open channels (optionally for a specific token) | -| `resizeChannel({ allocate_amount, token })` | Resize an existing channel | -| `challengeChannel({ state })` | Challenge a channel on-chain | -| `createChannel()` | No-op in v1 (channel creation is implicit on `deposit()`) | - -### Queries - -| Method | Description | -|--------|-------------| -| `getChannels()` | List all ledger channels | -| `getChannelData(channelId)` | Full channel + state for a specific channel | -| `getBalances(wallet?)` | Get ledger balances | -| `getLedgerEntries(wallet?)` | Get transaction history | -| `getAppSessionsList(wallet?, status?)` | List app sessions | -| `getLastAppSessionsListError()` | Last error from `getAppSessionsList()` (if any) | -| `getAssetsList()` | List supported assets | -| `getAccountInfo()` | Aggregate balance + channel count | -| `getConfig()` | Node configuration | - -### Transfers - -| Method | Description | -|--------|-------------| -| `transfer(destination, allocations)` | Off-chain transfer to another participant | - -### App Sessions - -| Method | Description | -|--------|-------------| -| `createAppSession(definition, allocations, quorumSigs?)` | Create an app session | -| `closeAppSession(appSessionId, allocations, quorumSigs?)` | Close an app session | -| `submitAppState(params)` | Submit state update (operate/deposit/withdraw/close) | -| `getAppDefinition(appSessionId)` | Get session definition | - -### App Session Signing Helpers - -| Helper | Description | -|--------|-------------| -| `packCreateAppSessionHash(params)` | Deterministic hash for `createAppSession` quorum signing | -| `packSubmitAppStateHash(params)` | Deterministic hash for `submitAppState` quorum signing | -| `toWalletQuorumSignature(signature)` | Prefix wallet signature for app-session quorum format | -| `toSessionKeyQuorumSignature(signature)` | Prefix session key signature (`0xa2`) for quorum format | - -### Session Keys - -| Method | Description | -|--------|-------------| -| `signChannelSessionKeyState(state)` | Sign a channel session-key state | -| `submitChannelSessionKeyState(state)` | Register channel session-key | -| `getLastChannelKeyStates(userAddress, sessionKey?)` | Get active channel session-key states | -| `signSessionKeyState(state)` | Sign an app-session key state | -| `submitSessionKeyState(state)` | Register app-session key | -| `getLastKeyStates(userAddress, sessionKey?)` | Get active app-session key states | - -### Asset Resolution - -| Method | Description | -|--------|-------------| -| `resolveToken(tokenAddress)` | Look up asset info by token address | -| `resolveAsset(symbol)` | Look up asset info by symbol | -| `resolveAssetDisplay(tokenAddress, chainId?)` | Get display-friendly symbol + decimals | -| `getTokenDecimals(tokenAddress)` | Get decimals for a token | -| `formatAmount(tokenAddress, rawAmount)` | Raw bigint → human-readable string | -| `parseAmount(tokenAddress, humanAmount)` | Human-readable string → raw bigint | -| `findOpenChannel(tokenAddress, chainId?)` | Find an open channel for a given token | - -### Lifecycle - -| Method | Description | -|--------|-------------| -| `ping()` | Health check | -| `close()` | Close the WebSocket connection | -| `refreshAssets()` | Re-fetch the asset map from the clearnode | - -## Properties - -| Property | Type | Description | -|----------|------|-------------| -| `innerClient` | `Client` (readonly) | The underlying v1.0.0 SDK Client | -| `userAddress` | `Address` (readonly) | The connected wallet address | - -## Configuration - -```typescript -interface NitroliteClientConfig { - wsURL: string; // Clearnode WebSocket URL - walletClient: WalletClient; // viem WalletClient with account - chainId: number; // Chain ID (e.g. 11155111) - blockchainRPCs?: Record; // Chain ID → RPC URL map - channelSessionKeySigner?: { // Optional session key for quick approvals - sessionKeyPrivateKey: Hex; - walletAddress: Address; - metadataHash: Hex; - authSig: Hex; - }; -} -``` - -### Environment Variables - -`blockchainRPCsFromEnv()` reads from `NEXT_PUBLIC_BLOCKCHAIN_RPCS`: - -```text -NEXT_PUBLIC_BLOCKCHAIN_RPCS=11155111:https://rpc.sepolia.io,1:https://mainnet.infura.io/v3/KEY -``` - -## Accessing the v1.0.0 SDK - -The underlying v1.0.0 `Client` is exposed for advanced use cases not covered by the compat surface: - -```typescript -const v1Client = client.innerClient; -await v1Client.getHomeChannel(wallet, 'usdc'); -await v1Client.checkpoint('usdc'); -await v1Client.approveToken(chainId, 'usdc', amount); -``` - -## Error Handling - -| Error Class | Code | Description | -|-------------|------|-------------| -| `AllowanceError` | `ALLOWANCE_INSUFFICIENT` | Token approval needed | -| `UserRejectedError` | `USER_REJECTED` | User cancelled in wallet | -| `InsufficientFundsError` | `INSUFFICIENT_FUNDS` | Not enough balance | -| `NotInitializedError` | `NOT_INITIALIZED` | Client not connected | - -```typescript -import { getUserFacingMessage, AllowanceError } from '@yellow-org/sdk-compat'; - -try { - await client.deposit(token, amount); -} catch (err) { - // Convert raw errors to typed compat errors - const typed = NitroliteClient.classifyError(err); - if (typed instanceof AllowanceError) { - // prompt user to approve token spending - } - showToast(getUserFacingMessage(err)); -} -``` - -## Event Polling - -v0.5.3 used WebSocket push events. v1.0.0 uses polling. The `EventPoller` bridges this gap: - -```typescript -import { EventPoller } from '@yellow-org/sdk-compat'; - -const poller = new EventPoller(client, { - onChannelUpdate: (channels) => updateUI(channels), - onBalanceUpdate: (balances) => updateBalances(balances), - onAssetsUpdate: (assets) => updateAssets(assets), - onError: (err) => console.error(err), -}, 5000); - -poller.start(); -``` - -## Next.js Integration - -Add to `transpilePackages` in `next.config.ts`: - -```typescript -const nextConfig = { - transpilePackages: ['@yellow-org/sdk', '@yellow-org/sdk-compat'], -}; -``` - -## Migration Guides - -- [Migration Overview](./migration-overview) — Pattern changes, import swaps -- [On-Chain Changes](./migration-onchain) — Deposits, withdrawals, channels -- [Off-Chain Changes](./migration-offchain) — Auth, app sessions, transfers diff --git a/docs/protocol/architecture.mdx b/docs/clearnet/learn/architecture.mdx similarity index 99% rename from docs/protocol/architecture.mdx rename to docs/clearnet/learn/architecture.mdx index 3588146..e4e6e15 100644 --- a/docs/protocol/architecture.mdx +++ b/docs/clearnet/learn/architecture.mdx @@ -126,7 +126,7 @@ sequenceDiagram participant Client participant Clearnode participant Blockchain - + Client->>Clearnode: create_channel request Clearnode->>Client: channel struct + signed initial state (clearnode signature) Client->>Client: Add own signature @@ -148,7 +148,7 @@ Channel opening requires cooperation between both parties, ensuring mutual agree 1. Participants exchange signed state updates: - **For Payment Channels** (User ↔ Clearnode): States are exchanged directly via Nitro RPC - + - **For App Sessions** (Multi-party): State exchange is managed by the App itself (peer-to-peer). Once the state has enough signatures to satisfy quorum, a responsible party submits the signed state to the Clearnode 2. No blockchain transactions required @@ -191,7 +191,7 @@ graph LR A[Active Channel] --> B{Parties Agree?} B -->|Yes| C[Cooperative Close
Fast, Cheap] B -->|No| D[Challenge-Response
Slow, Secure] - + style C fill:#90EE90,stroke:#333,color:#111 style D fill:#FFB6C1,stroke:#333,color:#111 ``` @@ -202,7 +202,7 @@ The following diagram illustrates how funds flow through the VirtualApp protocol ```mermaid graph TB - A["User Wallet
(ERC-20)"] -->|deposit| B["Available
(Custody SC)"] + A["User Wallet
(ERC-20)"] -->|deposit| B["Available
(Custody SC)"] B -->|resize| D["Unified Balance
(Clearnode)"] B <-->|resize| C["Channel-Locked
(Custody SC)"] C <-->|resize| D @@ -210,7 +210,7 @@ graph TB E -->|withdraw / close session| D D -->|resize / close channel| B B -->|withdraw| A - + style A fill:#90EE90,stroke:#333,color:#111 style B fill:#87CEEB,stroke:#333,color:#111 style C fill:#FFD700,stroke:#333,color:#111 diff --git a/docs/build/api/contracts/_category_.json b/docs/clearnet/learn/contracts/_category_.json similarity index 76% rename from docs/build/api/contracts/_category_.json rename to docs/clearnet/learn/contracts/_category_.json index 7cb0321..510e0b2 100644 --- a/docs/build/api/contracts/_category_.json +++ b/docs/clearnet/learn/contracts/_category_.json @@ -3,7 +3,7 @@ "position": 1, "link": { "type": "doc", - "id": "build/api/contracts/index" + "id": "learn/contracts/index" }, "collapsible": false, "collapsed": false diff --git a/docs/build/api/contracts/api-reference/_category_.json b/docs/clearnet/learn/contracts/api-reference/_category_.json similarity index 100% rename from docs/build/api/contracts/api-reference/_category_.json rename to docs/clearnet/learn/contracts/api-reference/_category_.json diff --git a/docs/build/api/contracts/api-reference/app-registry.md b/docs/clearnet/learn/contracts/api-reference/app-registry.md similarity index 95% rename from docs/build/api/contracts/api-reference/app-registry.md rename to docs/clearnet/learn/contracts/api-reference/app-registry.md index efaaec0..fd52ad5 100644 --- a/docs/build/api/contracts/api-reference/app-registry.md +++ b/docs/clearnet/learn/contracts/api-reference/app-registry.md @@ -2,14 +2,14 @@ title: "AppRegistry" description: "App builder registry with slashing." sidebar_position: 4 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # AppRegistry [Git Source](https://github.com/layer-3/yellow/blob/11ed85c3dabaaddeee431052032791a80eaf2a0e/src/AppRegistry.sol) **Inherits:** -[Locker](/src/Locker.sol/abstract.Locker.md), [ISlash](/src/interfaces/ISlash.sol/interface.ISlash.md), AccessControl +[Locker](./locker), [ISlash](./interfaces/islash), AccessControl **Title:** AppRegistry @@ -146,4 +146,3 @@ event MinSlashAmountUpdated(uint256 oldAmount, uint256 newAmount); ```solidity error SlashCooldownActive(uint256 availableAt); ``` - diff --git a/docs/build/api/contracts/api-reference/faucet.md b/docs/clearnet/learn/contracts/api-reference/faucet.md similarity index 98% rename from docs/build/api/contracts/api-reference/faucet.md rename to docs/clearnet/learn/contracts/api-reference/faucet.md index 9ee6f99..54670cb 100644 --- a/docs/build/api/contracts/api-reference/faucet.md +++ b/docs/clearnet/learn/contracts/api-reference/faucet.md @@ -2,7 +2,7 @@ title: "Faucet" description: "Testnet token faucet." sidebar_position: 7 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Faucet diff --git a/docs/build/api/contracts/api-reference/interfaces/_category_.json b/docs/clearnet/learn/contracts/api-reference/interfaces/_category_.json similarity index 100% rename from docs/build/api/contracts/api-reference/interfaces/_category_.json rename to docs/clearnet/learn/contracts/api-reference/interfaces/_category_.json diff --git a/docs/build/api/contracts/api-reference/interfaces/ilock.md b/docs/clearnet/learn/contracts/api-reference/interfaces/ilock.md similarity index 99% rename from docs/build/api/contracts/api-reference/interfaces/ilock.md rename to docs/clearnet/learn/contracts/api-reference/interfaces/ilock.md index ae5753e..e59ec79 100644 --- a/docs/build/api/contracts/api-reference/interfaces/ilock.md +++ b/docs/clearnet/learn/contracts/api-reference/interfaces/ilock.md @@ -2,7 +2,7 @@ title: "ILock" description: "Lock/unlock/withdraw interface." sidebar_position: 1 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # ILock diff --git a/docs/build/api/contracts/api-reference/interfaces/islash.md b/docs/clearnet/learn/contracts/api-reference/interfaces/islash.md similarity index 98% rename from docs/build/api/contracts/api-reference/interfaces/islash.md rename to docs/clearnet/learn/contracts/api-reference/interfaces/islash.md index 8a71ea8..010a979 100644 --- a/docs/build/api/contracts/api-reference/interfaces/islash.md +++ b/docs/clearnet/learn/contracts/api-reference/interfaces/islash.md @@ -2,7 +2,7 @@ title: "ISlash" description: "Slashing interface." sidebar_position: 2 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # ISlash diff --git a/docs/build/api/contracts/api-reference/locker.md b/docs/clearnet/learn/contracts/api-reference/locker.md similarity index 97% rename from docs/build/api/contracts/api-reference/locker.md rename to docs/clearnet/learn/contracts/api-reference/locker.md index 62a9118..410e9ab 100644 --- a/docs/build/api/contracts/api-reference/locker.md +++ b/docs/clearnet/learn/contracts/api-reference/locker.md @@ -2,14 +2,14 @@ title: "Locker" description: "Abstract lock/unlock/withdraw state machine." sidebar_position: 2 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Locker [Git Source](https://github.com/layer-3/yellow/blob/11ed85c3dabaaddeee431052032791a80eaf2a0e/src/Locker.sol) **Inherits:** -[ILock](/src/interfaces/ILock.sol/interface.ILock.md), ReentrancyGuard +[ILock](./interfaces/ilock), ReentrancyGuard **Title:** Locker @@ -167,4 +167,3 @@ Hook called after relock. Override to add custom logic. ```solidity function _afterRelock(address account, uint256 balance) internal virtual; ``` - diff --git a/docs/build/api/contracts/api-reference/node-registry.md b/docs/clearnet/learn/contracts/api-reference/node-registry.md similarity index 94% rename from docs/build/api/contracts/api-reference/node-registry.md rename to docs/clearnet/learn/contracts/api-reference/node-registry.md index 17dfc09..85a06f3 100644 --- a/docs/build/api/contracts/api-reference/node-registry.md +++ b/docs/clearnet/learn/contracts/api-reference/node-registry.md @@ -2,14 +2,14 @@ title: "NodeRegistry" description: "Node operator registry with voting power." sidebar_position: 3 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # NodeRegistry [Git Source](https://github.com/layer-3/yellow/blob/11ed85c3dabaaddeee431052032791a80eaf2a0e/src/NodeRegistry.sol) **Inherits:** -[Locker](/src/Locker.sol/abstract.Locker.md), Votes +[Locker](./locker), Votes **Title:** NodeRegistry @@ -60,4 +60,3 @@ Returns the locked collateral as weight units for the OZ Votes system. ```solidity function _getVotingUnits(address account) internal view override returns (uint256); ``` - diff --git a/docs/build/api/contracts/api-reference/treasury.md b/docs/clearnet/learn/contracts/api-reference/treasury.md similarity index 97% rename from docs/build/api/contracts/api-reference/treasury.md rename to docs/clearnet/learn/contracts/api-reference/treasury.md index 96df3c6..59eedeb 100644 --- a/docs/build/api/contracts/api-reference/treasury.md +++ b/docs/clearnet/learn/contracts/api-reference/treasury.md @@ -2,7 +2,7 @@ title: "Treasury" description: "Secure vault for Foundation assets." sidebar_position: 6 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Treasury diff --git a/docs/build/api/contracts/api-reference/yellow-governor.md b/docs/clearnet/learn/contracts/api-reference/yellow-governor.md similarity index 99% rename from docs/build/api/contracts/api-reference/yellow-governor.md rename to docs/clearnet/learn/contracts/api-reference/yellow-governor.md index b2604d1..2484416 100644 --- a/docs/build/api/contracts/api-reference/yellow-governor.md +++ b/docs/clearnet/learn/contracts/api-reference/yellow-governor.md @@ -2,7 +2,7 @@ title: "YellowGovernor" description: "OpenZeppelin Governor for protocol parameters." sidebar_position: 5 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # YellowGovernor diff --git a/docs/build/api/contracts/api-reference/yellow-token.md b/docs/clearnet/learn/contracts/api-reference/yellow-token.md similarity index 95% rename from docs/build/api/contracts/api-reference/yellow-token.md rename to docs/clearnet/learn/contracts/api-reference/yellow-token.md index a00b819..2470286 100644 --- a/docs/build/api/contracts/api-reference/yellow-token.md +++ b/docs/clearnet/learn/contracts/api-reference/yellow-token.md @@ -2,7 +2,7 @@ title: "YellowToken" description: "ERC-20 token with fixed 10B supply." sidebar_position: 1 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # YellowToken diff --git a/docs/build/api/contracts/faq.md b/docs/clearnet/learn/contracts/faq.md similarity index 99% rename from docs/build/api/contracts/faq.md rename to docs/clearnet/learn/contracts/faq.md index 1e5c034..6797c3b 100644 --- a/docs/build/api/contracts/faq.md +++ b/docs/clearnet/learn/contracts/faq.md @@ -2,7 +2,7 @@ title: "FAQ" description: "Frequently asked questions about Yellow Network smart contracts." sidebar_position: 2 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Frequently Asked Questions diff --git a/docs/build/api/contracts/index.md b/docs/clearnet/learn/contracts/index.md similarity index 97% rename from docs/build/api/contracts/index.md rename to docs/clearnet/learn/contracts/index.md index ec00923..7370a5a 100644 --- a/docs/build/api/contracts/index.md +++ b/docs/clearnet/learn/contracts/index.md @@ -2,7 +2,7 @@ title: "Deployed Addresses" description: "Mainnet and testnet contract addresses for Yellow Network." sidebar_position: 1 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- diff --git a/docs/build/api/contracts/integration/_category_.json b/docs/clearnet/learn/contracts/integration/_category_.json similarity index 100% rename from docs/build/api/contracts/integration/_category_.json rename to docs/clearnet/learn/contracts/integration/_category_.json diff --git a/docs/build/api/contracts/integration/deployment.md b/docs/clearnet/learn/contracts/integration/deployment.md similarity index 98% rename from docs/build/api/contracts/integration/deployment.md rename to docs/clearnet/learn/contracts/integration/deployment.md index eb32485..7ec7c53 100644 --- a/docs/build/api/contracts/integration/deployment.md +++ b/docs/clearnet/learn/contracts/integration/deployment.md @@ -2,7 +2,7 @@ title: "Deployment" description: "Deploying contracts and configuration." sidebar_position: 1 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Deployment diff --git a/docs/build/api/contracts/integration/events.md b/docs/clearnet/learn/contracts/integration/events.md similarity index 99% rename from docs/build/api/contracts/integration/events.md rename to docs/clearnet/learn/contracts/integration/events.md index cc3eaaa..245b5f3 100644 --- a/docs/build/api/contracts/integration/events.md +++ b/docs/clearnet/learn/contracts/integration/events.md @@ -2,7 +2,7 @@ title: "Events" description: "Contract events for real-time subscriptions." sidebar_position: 2 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Events diff --git a/docs/build/api/contracts/integration/ui-spec.md b/docs/clearnet/learn/contracts/integration/ui-spec.md similarity index 99% rename from docs/build/api/contracts/integration/ui-spec.md rename to docs/clearnet/learn/contracts/integration/ui-spec.md index 20140f8..a9a84f1 100644 --- a/docs/build/api/contracts/integration/ui-spec.md +++ b/docs/clearnet/learn/contracts/integration/ui-spec.md @@ -2,7 +2,7 @@ title: "UI Specification" description: "Frontend implementation guide." sidebar_position: 3 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # UI Specification diff --git a/docs/build/api/contracts/protocol/_category_.json b/docs/clearnet/learn/contracts/protocol/_category_.json similarity index 100% rename from docs/build/api/contracts/protocol/_category_.json rename to docs/clearnet/learn/contracts/protocol/_category_.json diff --git a/docs/build/api/contracts/protocol/governance.md b/docs/clearnet/learn/contracts/protocol/governance.md similarity index 99% rename from docs/build/api/contracts/protocol/governance.md rename to docs/clearnet/learn/contracts/protocol/governance.md index 944fa07..52c2fb7 100644 --- a/docs/build/api/contracts/protocol/governance.md +++ b/docs/clearnet/learn/contracts/protocol/governance.md @@ -2,7 +2,7 @@ title: "Governance" description: "On-chain parameter administration via YellowGovernor." sidebar_position: 2 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Protocol Parameter Administration diff --git a/docs/build/api/contracts/protocol/overview.md b/docs/clearnet/learn/contracts/protocol/overview.md similarity index 99% rename from docs/build/api/contracts/protocol/overview.md rename to docs/clearnet/learn/contracts/protocol/overview.md index d3ee258..48b6a3c 100644 --- a/docs/build/api/contracts/protocol/overview.md +++ b/docs/clearnet/learn/contracts/protocol/overview.md @@ -2,7 +2,7 @@ title: "Protocol Overview" description: "Architecture, contracts, and how they fit together." sidebar_position: 1 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Protocol Overview diff --git a/docs/build/api/contracts/protocol/slashing.md b/docs/clearnet/learn/contracts/protocol/slashing.md similarity index 98% rename from docs/build/api/contracts/protocol/slashing.md rename to docs/clearnet/learn/contracts/protocol/slashing.md index 0f88eaf..d6d2744 100644 --- a/docs/build/api/contracts/protocol/slashing.md +++ b/docs/clearnet/learn/contracts/protocol/slashing.md @@ -2,7 +2,7 @@ title: "Slashing" description: "Adjudicator slashing and cooldown mechanism." sidebar_position: 4 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Slashing diff --git a/docs/build/api/contracts/protocol/staking.md b/docs/clearnet/learn/contracts/protocol/staking.md similarity index 99% rename from docs/build/api/contracts/protocol/staking.md rename to docs/clearnet/learn/contracts/protocol/staking.md index 0049c71..3ea7e57 100644 --- a/docs/build/api/contracts/protocol/staking.md +++ b/docs/clearnet/learn/contracts/protocol/staking.md @@ -2,7 +2,7 @@ title: "Collateral & Staking" description: "Lock/unlock state machine for NodeRegistry and AppRegistry." sidebar_position: 3 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Collateral diff --git a/docs/build/api/contracts/protocol/treasury.md b/docs/clearnet/learn/contracts/protocol/treasury.md similarity index 98% rename from docs/build/api/contracts/protocol/treasury.md rename to docs/clearnet/learn/contracts/protocol/treasury.md index add28c0..f4a0c70 100644 --- a/docs/build/api/contracts/protocol/treasury.md +++ b/docs/clearnet/learn/contracts/protocol/treasury.md @@ -2,7 +2,7 @@ title: "Treasury" description: "Foundation asset management." sidebar_position: 5 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # Treasury diff --git a/docs/build/api/contracts/sdk/_category_.json b/docs/clearnet/learn/contracts/sdk/_category_.json similarity index 100% rename from docs/build/api/contracts/sdk/_category_.json rename to docs/clearnet/learn/contracts/sdk/_category_.json diff --git a/docs/build/api/contracts/sdk/api-reference.md b/docs/clearnet/learn/contracts/sdk/api-reference.md similarity index 95% rename from docs/build/api/contracts/sdk/api-reference.md rename to docs/clearnet/learn/contracts/sdk/api-reference.md index 033f31d..a044f2e 100644 --- a/docs/build/api/contracts/sdk/api-reference.md +++ b/docs/clearnet/learn/contracts/sdk/api-reference.md @@ -2,7 +2,7 @@ title: "SDK API Reference" description: "All exports: ABIs, addresses, and types." sidebar_position: 2 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # SDK — API Reference @@ -91,4 +91,4 @@ type ContractAddresses = { ### Current Addresses -See [Deployed Addresses](../operations/addresses.md) for the full list. +See [Deployed Addresses](../index.md) for the full list. diff --git a/docs/build/api/contracts/sdk/examples.md b/docs/clearnet/learn/contracts/sdk/examples.md similarity index 99% rename from docs/build/api/contracts/sdk/examples.md rename to docs/clearnet/learn/contracts/sdk/examples.md index c338051..6b582a6 100644 --- a/docs/build/api/contracts/sdk/examples.md +++ b/docs/clearnet/learn/contracts/sdk/examples.md @@ -2,7 +2,7 @@ title: "Examples" description: "Code samples for viem, ethers.js, and wagmi." sidebar_position: 3 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # SDK — Examples diff --git a/docs/build/api/contracts/sdk/getting-started.md b/docs/clearnet/learn/contracts/sdk/getting-started.md similarity index 98% rename from docs/build/api/contracts/sdk/getting-started.md rename to docs/clearnet/learn/contracts/sdk/getting-started.md index 8f5e48f..f179048 100644 --- a/docs/build/api/contracts/sdk/getting-started.md +++ b/docs/clearnet/learn/contracts/sdk/getting-started.md @@ -2,7 +2,7 @@ title: "Getting Started" description: "Install, import, and use the contracts SDK." sidebar_position: 1 -displayed_sidebar: buildSidebar +displayed_sidebar: clearnetSidebar --- # SDK — Getting Started diff --git a/docs/protocol/decentralized-layer/_category_.json b/docs/clearnet/learn/decentralized-layer/_category_.json similarity index 100% rename from docs/protocol/decentralized-layer/_category_.json rename to docs/clearnet/learn/decentralized-layer/_category_.json diff --git a/docs/protocol/decentralized-layer/cluster-lifecycle.mdx b/docs/clearnet/learn/decentralized-layer/cluster-lifecycle.mdx similarity index 100% rename from docs/protocol/decentralized-layer/cluster-lifecycle.mdx rename to docs/clearnet/learn/decentralized-layer/cluster-lifecycle.mdx diff --git a/docs/protocol/decentralized-layer/elastic-security.mdx b/docs/clearnet/learn/decentralized-layer/elastic-security.mdx similarity index 100% rename from docs/protocol/decentralized-layer/elastic-security.mdx rename to docs/clearnet/learn/decentralized-layer/elastic-security.mdx diff --git a/docs/protocol/decentralized-layer/liquidity-layer.mdx b/docs/clearnet/learn/decentralized-layer/liquidity-layer.mdx similarity index 100% rename from docs/protocol/decentralized-layer/liquidity-layer.mdx rename to docs/clearnet/learn/decentralized-layer/liquidity-layer.mdx diff --git a/docs/protocol/decentralized-layer/overview.mdx b/docs/clearnet/learn/decentralized-layer/overview.mdx similarity index 92% rename from docs/protocol/decentralized-layer/overview.mdx rename to docs/clearnet/learn/decentralized-layer/overview.mdx index 03f4b91..08b1db7 100644 --- a/docs/protocol/decentralized-layer/overview.mdx +++ b/docs/clearnet/learn/decentralized-layer/overview.mdx @@ -9,7 +9,7 @@ import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; # Network Overview -The Yellow Network Protocol (YNP) is a decentralized overlay network that sits on top of existing blockchains as a **Layer 3 clearing and settlement layer**. Where the [App Layer](../app-layer/on-chain/overview.mdx) sections describe the VirtualApp state channel primitives ([on-chain](../app-layer/on-chain/overview.mdx) and [off-chain](../app-layer/off-chain/overview.mdx)), this section describes how the network of clearnodes organizes itself to manage accounts, route transactions, and secure assets across multiple chains. +The Yellow Network Protocol (YNP) is a decentralized overlay network that sits on top of existing blockchains as a **Layer 3 clearing and settlement layer**. Where the [App Layer](/nitrolite/protocol/introduction) section describes state channel primitives, this section describes how the network of clearnodes organizes itself to manage accounts, route transactions, and secure assets across multiple chains. ## Architecture Layers diff --git a/docs/protocol/decentralized-layer/protocol-lifecycle.mdx b/docs/clearnet/learn/decentralized-layer/protocol-lifecycle.mdx similarity index 100% rename from docs/protocol/decentralized-layer/protocol-lifecycle.mdx rename to docs/clearnet/learn/decentralized-layer/protocol-lifecycle.mdx diff --git a/docs/protocol/decentralized-layer/security.mdx b/docs/clearnet/learn/decentralized-layer/security.mdx similarity index 100% rename from docs/protocol/decentralized-layer/security.mdx rename to docs/clearnet/learn/decentralized-layer/security.mdx diff --git a/docs/protocol/introduction.mdx b/docs/clearnet/learn/introduction.mdx similarity index 92% rename from docs/protocol/introduction.mdx rename to docs/clearnet/learn/introduction.mdx index 55ae822..f27ea1c 100644 --- a/docs/protocol/introduction.mdx +++ b/docs/clearnet/learn/introduction.mdx @@ -69,8 +69,6 @@ This documentation defines the Yellow Protocol in a **programming language-agnos | Section | Layer | Covers | |---------|-------|--------| | [Decentralized Layer](./decentralized-layer/overview) | Network | DHT topology, cluster lifecycle, elastic security, protocol lifecycle, liquidity, security analysis | -| [App Layer — On-Chain](./app-layer/on-chain/overview) | VirtualApp | Smart contracts for fund custody, dispute resolution, and settlement | -| [App Layer — Off-Chain](./app-layer/off-chain/overview) | VirtualApp | Nitro RPC protocol for state channel operations, transfers, and app sessions | :::caution Language Independence Implementation-specific details are referenced but not mandated by this specification. The protocol description is abstract and can be implemented in any programming language. diff --git a/docs/guides/index.md b/docs/guides/index.md deleted file mode 100644 index 5f86ab5..0000000 --- a/docs/guides/index.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Guides -description: Comprehensive guides and tutorials -sidebar_position: 1 -displayed_sidebar: guidesSidebar ---- - -# Guides - -**[Migration Guide](/docs/guides/migration-guide)** - Guide for migrating from previous versions of the Yellow SDK. - - -**[Multi-Party App Sessions](/docs/guides/multi-party-app-sessions)** - Learn how to create, manage, and close multi-party application sessions using the Yellow Network and VirtualApp protocol. - -## Manuals - -**[Running Clearnode Locally](/docs/guides/manuals/running-clearnode-locally)** - Run a Clearnode locally using Docker Compose for development and testing. - -**[Request Asset Support](/docs/guides/manuals/request-asset-support)** - Guide for requesting asset and token support in Clearnode environments. - -**[Request Blockchain Support](/docs/guides/manuals/request-blockchain-support)** - Guide for requesting blockchain network support in Clearnode environments. diff --git a/docs/guides/manuals/_category_.json b/docs/guides/manuals/_category_.json deleted file mode 100644 index d553862..0000000 --- a/docs/guides/manuals/_category_.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "label": "Manuals", - "position": 10, - "link": { - "type": "generated-index" - }, - "collapsible": false, - "collapsed": false -} diff --git a/docs/guides/manuals/request-asset-support.md b/docs/guides/manuals/request-asset-support.md deleted file mode 100644 index db9c19c..0000000 --- a/docs/guides/manuals/request-asset-support.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -title: Requesting Asset Support -description: Guide for requesting asset and token support in Clearnode Sandbox or Production environments -sidebar_label: Request Asset Support -keywords: [clearnode, asset, token, configuration, ERC20, production, sandbox] ---- - -# Requesting Asset Support - -This guide is primarily for **project teams, token issuers, and integration partners** who need to add support for new assets or tokens in the Clearnode infrastructure. Following this process will enable Clearnode to recognize and process transfers of your asset across supported blockchain networks. - -## What Happens When Support is Added - -Once an asset is supported in Clearnode: - -- Users can deposit and withdraw the asset through Clearnode's state channels -- The asset becomes available for instant, off-chain transfers via the VirtualApp protocol -- The asset can be used in cross-chain operations (deposit on one chain, withdraw on another) for blockchains it was configured on -- Applications built on Yellow Network can integrate the asset for payments and settlements -- The asset's unified balance is tracked across all supported blockchains - -:::info ERC20 Compatibility -Adding support for ERC20 tokens is straightforward and requires only configuration changes. Non-standard token implementations may require additional development work and testing. -::: - -:::warning Liquidity Requirements for Withdrawals -For a token to be immediately withdrawable on a specific blockchain, Clearnode must be provided with sufficient liquidity on that chain. Without liquidity, users can deposit tokens but cannot withdraw them until liquidity is available. Please contact our Business team to arrange liquidity provision before requesting asset support. -::: - -## Understanding Environments - -Clearnode operates in two distinct environments with separate asset configurations: - -| Environment | Purpose | Typical Assets | -|-------------|---------|----------------| -| **Sandbox** | Development, testing, and experimentation | Testnet tokens (Sepolia USDC, Amoy ETH, test tokens, etc.) | -| **Production** | Live operations with real assets | Mainnet tokens (USDC, USDT, DAI, etc.) | - -**Configuration file location:** `clearnode/chart/config//assets.yaml` - -**Important Considerations:** - -- **Production will not support test network tokens** - mainnet tokens only -- **Sandbox will not support mainnet tokens** - testnet tokens only -- You must decide which environment needs the asset support based on your use case -- If you need support in both environments (e.g., testnet token for development, mainnet token for production), you must submit configuration changes to both files - -## How to Request Support - -Asset support is requested by creating or modifying a configuration file in the [nitrolite](https://github.com/erc7824/nitrolite) repository: - -1. **Fork the repository**: `https://github.com/erc7824/nitrolite` -2. **Navigate to the appropriate configuration file**: - - For Sandbox: `clearnode/chart/config/sandbox/assets.yaml` - - For Production: `clearnode/chart/config/prod/assets.yaml` -3. **Add your asset configuration** at the end of the list (see next section for structure) -4. **Submit a Pull Request** with a clear description of the asset being added -5. **Wait for review** by the development team - -The next section provides detailed guidance on the configuration structure and whether you need to add a new asset or just a token. - -## Asset Configuration Structure - -### Understanding Assets vs Tokens - -In Clearnode's configuration model: - -- An **asset** represents a logical currency or token type (e.g., "USDC", "ETH") -- A **token** is a specific implementation of that asset on a particular blockchain - -**Example:** USDC is an asset that has different token implementations: - -- USDC on Polygon at address `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` -- USDC on Base at address `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` -- USDC on Ethereum at address `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` - -### Before You Start: Check if the Asset Exists - -Before adding a new asset, check if Clearnode already supports the asset on other blockchains. If the asset exists but your target blockchain is not listed, you only need to add a **token entry** to the existing asset. If the asset doesn't exist at all, you need to add both the **asset** and its **token(s)**. - -#### Scenario 1: Asset exists, add a token to a new blockchain - -```yaml -assets: - - name: "USD Coin" - symbol: "usdc" - tokens: - - blockchain_id: 137 - address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359" - decimals: 6 - # Add your new token here - - blockchain_id: 8453 # Base - address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" - decimals: 6 -``` - -#### Scenario 2: New asset, add both asset and token(s) - -```yaml -assets: - - name: "My New Token" - symbol: "mnt" - tokens: - - blockchain_id: 137 - address: "0xYourTokenAddressOnPolygon" - decimals: 18 -``` - -### Configuration Fields - -#### Asset Level - -Each asset entry requires the following fields: - -```yaml -assets: - - name: "USD Coin" # Human-readable name - symbol: "usdc" # Ticker symbol (lowercase) - disabled: false # If set to `true`, then it is not loaded into configuration - tokens: [...] # Array of token implementations -``` - -| Field | Required | Description | -|-------|----------|-------------| -| `name` | No | Human-readable name of the asset (e.g., "USD Coin"). If omitted, defaults to the symbol. | -| `symbol` | **Yes** | Ticker symbol for the asset (must be lowercase, e.g., "usdc", "eth") | -| `disabled` | No | Set to `true` to temporarily disable processing this asset (default: `false`) | -| `tokens` | **Yes** | Array of blockchain-specific token implementations | - -#### Token Level - -Each token within an asset requires the following fields: - -```yaml -tokens: - - name: "USD Coin on Polygon" # Token-specific name - symbol: "usdc" # Token-specific symbol - blockchain_id: 137 # Chain ID - disabled: false # Skip processing (default: false) - address: "0x3c499c542cEF..." # Contract address - decimals: 6 # Token decimals -``` - -| Field | Required | Description | -|-------|----------|-------------| -| `name` | No | Token name on this blockchain (inherits from asset if not specified) | -| `symbol` | No | Token symbol on this blockchain (inherits from asset if not specified) | -| `blockchain_id` | **Yes** | Chain ID where this token is deployed (must match a supported blockchain) | -| `disabled` | No | Set to `true` to temporarily disable this token (default: `false`) | -| `address` | **Yes** | Token's smart contract address (must be a valid address on the specified chain) | -| `decimals` | **Yes** | Number of decimal places the token uses (e.g., 6 for USDC, 18 for ETH) | - -### Prerequisites for Adding Assets - -Before submitting your configuration: - -1. **Blockchain Support**: Ensure the blockchain (by `blockchain_id`) is already supported in `blockchains.yaml` -2. **Token Deployment**: The token contract must be deployed and verified on the target blockchain -3. **ERC20 Compliance**: Token should follow the standard ERC20 interface for seamless integration -4. **Correct Decimals**: Verify the token's decimal places (commonly 6 or 18, but varies by token) -5. **Valid Address**: Double-check the contract address is correct and matches the intended blockchain - -:::warning Blockchain Must Be Supported First -You cannot add a token on a blockchain that is not yet supported in Clearnode. If your target blockchain is not in the `blockchains.yaml` configuration, you must first follow the [Request Blockchain Support](./request-blockchain-support.md) guide before requesting asset support. -::: - -## Need Help? - -If you have questions about asset support requests, encounter issues during configuration, or need clarification on any part of this process, please don't hesitate to contact the development team. diff --git a/docs/guides/manuals/request-blockchain-support.md b/docs/guides/manuals/request-blockchain-support.md deleted file mode 100644 index 065fe16..0000000 --- a/docs/guides/manuals/request-blockchain-support.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Requesting Blockchain Support -description: Guide for requesting blockchain network support in Clearnode Sandbox or Production environments -sidebar_label: Request Blockchain Support -keywords: [clearnode, blockchain, configuration, EVM, production, sandbox] ---- - -# Requesting Blockchain Support - -This guide is primarily for **blockchain integrators, infrastructure teams, and project partners** who need to add support for new blockchain networks in the Clearnode infrastructure. Following this process will enable Clearnode to connect to your blockchain and provide off-chain clearing and settlement services. - -## What Happens When Support is Added - -Once a blockchain is supported in Clearnode: - -- Clearnode can establish RPC connections to the specified blockchain network -- The VirtualApp protocol smart contracts (custody, adjudicator, and balance checker) will be monitored on that chain -- Users can deposit and withdraw assets on that blockchain through Clearnode -- State channels can leverage that blockchain for on-chain settlement when needed -- Assets deployed on the supported blockchain become available for cross-chain clearing - -:::warning Non-EVM and Limited Infrastructure Support -If your blockchain support request concerns a **non-EVM blockchain** or a blockchain with **limited infrastructure support** (e.g., scarce RPC providers, incomplete tooling, experimental networks), **you must contact the development team before proceeding** with this guide. These cases require custom development and cannot be handled through standard configuration. - -Contact the dev team via [GitHub Issues](https://github.com/layer-3/nitrolite/issues). -::: - -## Understanding Environments - -Clearnode operates in two distinct environments with separate configurations: - -| Environment | Purpose | Typical Networks | -|-------------|---------|------------------| -| **Sandbox** | Development, testing, and experimentation | Testnets (Sepolia, Polygon Amoy, etc.) | -| **Production** | Live operations with real assets | Mainnets (Ethereum, Polygon, Base, Linea, etc.) | - -**Configuration file location:** `clearnode/chart/config//blockchains.yaml` - -**Important Considerations:** - -- **Production will not support test networks** - mainnet blockchains only -- **Sandbox will not support mainnet networks** - testnet blockchains only -- You must decide which environment needs the blockchain support based on your use case -- If you need support in both environments (e.g., testnet for development, mainnet for production), you must submit configuration changes to both files - -## How to Request Support - -Blockchain support is requested by creating or modifying a configuration file in the [nitrolite](https://github.com/erc7824/nitrolite) repository: - -1. **Fork the repository**: `https://github.com/erc7824/nitrolite` -2. **Navigate to the appropriate configuration file**: - - For Sandbox: `clearnode/chart/config/sandbox/blockchains.yaml` - - For Production: `clearnode/chart/config/prod/blockchains.yaml` -3. **Add your blockchain configuration** at the end of the `blockchains` list (see next section for structure) -4. **Submit a Pull Request** with a clear description of the blockchain being added -5. **Wait for review** by the development team - -The next section provides detailed guidance on the configuration structure. - -## Blockchain Configuration Structure - -### Overview - -The `blockchains.yaml` file contains two main sections: - -- `default_contract_addresses`: Default smart contract addresses applied to all blockchains (unless overridden) -- `blockchains`: Array of blockchain configurations - -### Configuration Fields - -Each blockchain entry requires the following fields: - -```yaml -blockchains: - - name: polygon # Blockchain name (lowercase, underscores allowed) - id: 137 # Chain ID for validation - disabled: false # Whether to disable (default: false) - block_step: 10000 # Block range for scanning (default: 10000) - contract_addresses: # Override default contract addresses - custody: "0x..." - adjudicator: "0x..." - balance_checker: "0x..." -``` - -**Field Descriptions:** - -| Field | Required | Description | -|-------|----------|-------------| -| `name` | **Yes** | Unique identifier for the blockchain (lowercase, underscores allowed, e.g., `polygon`, `base`, `arbitrum_one`) | -| `id` | **Yes** | Chain ID used for validation (must match the blockchain's official chain ID) | -| `disabled` | No | Set to `true` to disable or `false` to enable (default: `false`) | -| `block_step` | No | Number of blocks to scan per query when monitoring events (default: `10000`). Adjust based on blockchain or RPC provider performance. | -| `contract_addresses` | No | Override default contract addresses for this specific blockchain | - -### Contract Deployment - -When requesting the addition of a new blockchain, addresses of the infrastructure smart contracts must be provided: - -- **Custody Contract**: Manages user deposits and withdrawals -- **Adjudicator Contract**: Handles dispute resolution for state channels -- **Balance Checker Contract**: Provides efficient balance queries - -:::info Smart contract deployment -For now, you don't need to deploy these contracts yourself. The development team will handle contract deployment on the new blockchain as part of the support process. - -You can submit your request with smart contract addresses set to placeholder values (e.g., `0x0000000000000000000000000000000000000000`). The team will replace them with the actual deployed addresses during integration. -::: - -:::info Coming Soon: Cross-Chain Contract Deployment Tool -We are developing a tool to simplify the deployment of VirtualApp protocol smart contracts across multiple blockchains with deterministic addresses. This will enable deploying contracts to the same address on different chains, making configuration management significantly easier. -::: - -Read on to learn how to specify contract addresses in the configuration. - -You have two options for providing contract addresses: - -#### Option 1: Using Default Contract Addresses - -If you deploy contracts at the addresses specified in the `default_contract_addresses`, you don't need to specify `contract_addresses` in each blockchain entry. - -```yaml -default_contract_addresses: - custody: "0x490fb189DdE3a01B00be9BA5F41e3447FbC838b6" - adjudicator: "0xcbbc03a873c11beeFA8D99477E830be48d8Ae6D7" - balance_checker: "0x2352c63A83f9Fd126af8676146721Fa00924d7e4" - -blockchains: - - name: polygon - id: 137 - enabled: true - - name: base - id: 8453 - enabled: true -``` - -This approach is cleaner when contracts are deployed at identical addresses. - -#### Option 2: Blockchain-Specific Contract Addresses - -If contract addresses differ on your blockchain, specify them individually: - -```yaml -blockchains: - - name: polygon - id: 137 - enabled: true - contract_addresses: - custody: "0xPolygonCustodyAddress..." - adjudicator: "0xPolygonAdjudicatorAddress..." - balance_checker: "0xPolygonBalanceCheckerAddress..." - - name: base - id: 8453 - enabled: true - contract_addresses: - custody: "0xBaseCustodyAddress..." - adjudicator: "0xBaseAdjudicatorAddress..." - balance_checker: "0xBaseBalanceCheckerAddress..." -``` - -:::warning Contract Address Requirements -Each blockchain **must have all three contract addresses configured** either through `default_contract_addresses` or blockchain-specific `contract_addresses`. If defaults are not provided, every blockchain must explicitly define all three addresses. Missing contract addresses will cause Clearnode to fail on startup. -::: - -## Need Help? - -If you have questions about blockchain support requests, encounter issues during integration, or need clarification on any part of this process, please don't hesitate to contact the development team. diff --git a/docs/guides/manuals/running-clearnode-locally.md b/docs/guides/manuals/running-clearnode-locally.md deleted file mode 100644 index cba7efc..0000000 --- a/docs/guides/manuals/running-clearnode-locally.md +++ /dev/null @@ -1,222 +0,0 @@ -# Running Clearnode Locally - -This manual explains how to run a Clearnode locally using Docker Compose for development and testing purposes. Clearnode is an open-source implementation of a message broker node developed and maintained by Layer3 Fintech Ltd., providing ledger services for the VirtualApp protocol, which enables efficient off-chain payment channels with on-chain settlement capabilities. Independent node operators run this software on their own infrastructure to provide network services. - -## Prerequisites - -- Docker and Docker Compose installed on your system -- Git (to clone the repository) - -## Quick Start - -### 1. Clone the Repository - -```bash -git clone https://github.com/layer-3/nitrolite.git -cd nitrolite/clearnode -``` - -### 2. Configuration Setup - -Create a configuration directory: - -```bash -cp -r config/compose/example config/compose/local -``` - -### 3. Configure Blockchain Connections - -Edit `config/compose/local/blockchains.yaml` to configure your blockchain connections. Here's an example: - -```yaml -default_contract_addresses: - custody: "0x490fb189DdE3a01B00be9BA5F41e3447FbC838b6" - adjudicator: "0xcbbc03a873c11beeFA8D99477E830be48d8Ae6D7" - balance_checker: "0x2352c63A83f9Fd126af8676146721Fa00924d7e4" - -blockchains: - - name: polygon - id: 137 - disabled: false - block_step: 10000 - - name: base - id: 8453 - disabled: true -``` - -### 4. Configure Assets - -Edit `config/compose/local/assets.yaml` to configure supported assets: - -```yaml -assets: - - name: "USD Coin" - symbol: "usdc" - tokens: - - blockchain_id: 137 - address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359" - decimals: 6 - - name: "Wrapped Ether" - symbol: "weth" - tokens: - - blockchain_id: 137 - address: "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619" - decimals: 18 -``` - -### 5. Environment Variables - -Create a `.env` file in `config/compose/local/.env` with the following: - -```bash -# Required -BROKER_PRIVATE_KEY=your_private_key_here - -# Add RPC endpoints for each enabled blockchain -POLYGON_BLOCKCHAIN_RPC=wss://my-polygon-rpc.example.com -# BASE_BLOCKCHAIN_RPC=wss://my-base-rpc.example.com - -# Optional configuration -CLEARNODE_LOG_LEVEL=info -``` - -### 6. Start Services - -Run the following command to start all services: - -```bash -docker compose up -``` - -This will start: -- Clearnode service on port 8000 (WebSocket/HTTP) -- PostgreSQL database -- Prometheus metrics on port 4242 - -### 7. Stop Services - -To stop all services: - -```bash -docker compose down -``` - -## Configuration Reference - -### Environment Variables - -| Variable | Description | Required | Default | -|------------------------------------|--------------------------------------------------|----------|--------------| -| `BROKER_PRIVATE_KEY` | Private key used for signing broker messages | Yes | - | -| `DATABASE_DRIVER` | Database driver to use (postgres/sqlite) | No | sqlite | -| `CLEARNODE_CONFIG_DIR_PATH` | Path to directory containing configuration files | No | . | -| `CLEARNODE_DATABASE_URL` | Database connection string | No | clearnode.db | -| `CLEARNODE_LOG_LEVEL` | Logging level (debug, info, warn, error) | No | info | -| `HTTP_PORT` | Port for the HTTP/WebSocket server | No | 8000 | -| `METRICS_PORT` | Port for Prometheus metrics | No | 4242 | -| `MSG_EXPIRY_TIME` | Time in seconds for message timestamp validation | No | 60 | -| `_BLOCKCHAIN_RPC` | RPC endpoint for each enabled blockchain | Yes | - | - -### Blockchain Configuration (blockchains.yaml) - -**Configuration Structure:** - -- **default_contract_addresses**: That's the optional set of default contract addresses applied to all blockchains unless overridden - - `custody`: Custody contract address - - `adjudicator`: Adjudicator contract address - - `balance_checker`: Balance checker contract address - -- **blockchains**: Array of blockchain configurations - - `name`: Blockchain name (required; lowercase, underscores allowed) - - `id`: Chain ID for validation (required) - - `disabled`: Whether to disable this blockchain (optional, default: false) - - `block_step`: Block range for scanning (optional, default: 10000) - - `contract_addresses`: Override default addresses for this specific blockchain (optional) - - `custody`: Custody contract address - - `adjudicator`: Adjudicator contract address - - `balance_checker`: Balance checker contract address - -:::warning -Even though both `default_contract_addresses` and blockchain-specific `contract_addresses` are described as optional, each blockchain must have all required contract addresses set. If no defaults are provided under `default_contract_addresses`, you must specify `custody`, `adjudicator`, and `balance_checker` addresses for every blockchain in its `contract_addresses` section. Otherwise, Clearnode will fail to start due to missing contract address configuration. -::: - -RPC endpoints follow the pattern: `_BLOCKCHAIN_RPC` - -Example: -```bash -MY_NETWORK_BLOCKCHAIN_RPC=wss://my-network-rpc.example.com -``` - -### Asset Configuration (assets.yaml) - -**Configuration Structure:** - -- **assets**: Array of asset configurations - - `name`: Human-readable name of the asset (e.g., "USD Coin") - - `symbol`: Ticker symbol for the asset (required; lowercase, e.g., "usdc") - - `disabled`: Whether to skip processing this asset (optional, default: false) - - `tokens`: Array of blockchain-specific token implementations - - `name`: Token name on this blockchain (optional, inherits from asset) - - `symbol`: Token symbol on this blockchain (optional, inherits from asset) - - `blockchain_id`: Chain ID where this token is deployed (required) - - `disabled`: Whether to skip processing this token (optional, default: false) - - `address`: Token's contract address (required) - - `decimals`: Number of decimal places for the token (required) - -**Asset Token Inheritance:** -- If a token's `name` is not specified, it uses the asset's `name` -- If a token's `symbol` is not specified, it uses the asset's `symbol` -- If an asset's `name` is not specified, it defaults to the asset's `symbol` - -## Key Features - -- **Multi-Chain Support**: Connect to multiple EVM blockchains simultaneously -- **Off-Chain Payments**: Efficient payment channels for high-throughput transactions -- **Virtual Applications**: Create multi-participant applications -- **Message Forwarding**: Bi-directional message routing between participants -- **Flexible Database**: Support for both PostgreSQL and SQLite -- **Prometheus Metrics**: Built-in monitoring on port 4242 -- **Quorum-Based Signatures**: Multi-signature schemes with weight-based quorums - -## Troubleshooting - -### Common Issues - -1. **Port Conflicts**: If you encounter port conflicts, check which services are running on ports 8000 (HTTP/WebSocket) or 4242 (metrics) and either stop them or modify the ports in docker-compose.yml - -2. **RPC Configuration**: Ensure RPC endpoints match the pattern `_BLOCKCHAIN_RPC` and that the chain ID matches your configuration - -3. **Configuration Files**: Make sure `blockchains.yaml` and `assets.yaml` are properly formatted YAML files in your CONFIG_DIR_PATH - -4. **Database Connection**: If using PostgreSQL, ensure the database service is running and accessible - -### Useful Commands - -Check service status: - -```bash -docker-compose ps -``` - -View logs for a specific service: - -```bash -docker-compose logs -f -``` - -Restart a specific service: - -```bash -docker-compose restart -``` - -Clean up (remove containers, networks, and volumes): - -```bash -docker-compose down -v -``` - -## Development Tips - -1. **Debug Mode**: Set `CLEARNODE_LOG_LEVEL=debug` for verbose logging -2. **Database Access**: Use a database client to connect to `localhost:5432` with PostgreSQL credentials diff --git a/docs/guides/migration-guide.md b/docs/guides/migration-guide.md deleted file mode 100644 index f11d61f..0000000 --- a/docs/guides/migration-guide.md +++ /dev/null @@ -1,955 +0,0 @@ ---- -sidebar_position: 2 -title: Migration Guide -description: Guide to migrate to newer versions of VirtualApp -keywords: [migration, upgrade, breaking changes, nitrolite, erc7824] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Migration Guide - -If you are coming from an earlier version of VirtualApp, you will need to account for the following breaking changes. - -## 0.5.x Breaking changes - -The 0.5.x release includes fundamental protocol changes affecting session keys, channel operations, state signatures, and channel resize rules. The main objective of these changes is to enhance security, and provide better experience for developers and users by ability to limit allowances for specific applications. - -**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. - -### Protocol Changes - -These protocol-level changes affect all implementations and integrations with the Yellow Network. - -#### Session Keys: Applications, Allowances, and Expiration - -Session keys now have enhanced properties that define their access levels and capabilities: - -- **Application field**: Determines the scope of session key permissions. Setting this to an application name (e.g., "My Trading App") grants application-scoped access with enforced allowances. Setting it to "clearnode" grants root access equivalent to the wallet itself. - -- **Allowances field**: Defines spending limits for application-scoped session keys. These limits are tracked cumulatively across all operations and are enforced by the protocol. - -- **Expires_at field**: Uses a bigint timestamp (seconds since epoch). Once expired, session keys are permanently frozen and cannot be reactivated. This is particularly critical for root access keys (application set to "clearnode") - if they expire, you lose the ability to perform channel operations. - -#### Channel Creation: Separate Create and Fund Steps - -Clearnode no longer supports creating channels with an initial deposit. All channels must be created with zero balance and funded separately through a resize operation. This two-step process ensures cleaner state management and prevents edge cases in channel initialization. - -#### State Signatures: Wallet vs Session Key Signing - -A fundamental change in how channel states are signed: - -- **Channels created before v0.5.0**: The participant address is the session key, and all states must be signed by that session key. - -- **Channels created after v0.5.0**: The participant address is the wallet address, and all states must be signed by the wallet. - -This change improves security and aligns with standard practices, but requires careful handling during the transition period. - -#### Resize Operations: Strict Channel Balance Rules - -The protocol now enforces strict rules about channel balances and their impact on other operations: - -- **Blocked operations**: Users with any channel containing non-zero amounts cannot perform transfers, submit app states with deposit intent, or create app sessions with non-zero allocations. - -- **Resizing state**: After a resize request, channels enter a "resizing" state with locked funds until the on-chain transaction is confirmed. If a channel remains stuck in this state for an extended period, the recommended action is to close the channel and create a new one. - -- **Allocate amount semantics**: The resize operation uses `allocate_amount` where negative values withdraw from the channel to unified balance, and positive values deposit to the channel. - -:::warning -**Legacy channel migration**: Users with existing channels containing non-zero amounts must either resize them to zero (by providing "resize_amount" as 0 and "allocate_amount" as your **negative** on-chain balance) or close them to enable full protocol functionality. If you are unsure how to adjust resize parameters, the safe option is to close the old on-chain channel entirely, and open a new one. -::: - -#### Non-Zero Channel Allocations: Operation Restrictions - -The following operations will return errors if the user has any channel with non-zero amount: - -- **Transfer**: Returns error code indicating blocked due to non-zero channel balance -- **Submit App State** (with deposit intent): Rejected if attempting to deposit -- **Create App Session** (with allocations): Rejected if attempting to allocate - -The returned error has the following format: `operation denied: non-zero allocation in channel(s) detected owned by wallet
"` - -### VirtualApp SDK - -You should definitely read this section if you are using the VirtualApp SDK. - -#### Update Authentication - -Implementing the new session key protocol changes: - - - - - ```typescript - const authRequest = { - address: '0x...', - session_key: '0x...', - application: 'My Trading App', // Application name for confined access - allowances: [ - { asset: 'usdc', amount: '1000.0' }, - { asset: 'eth', amount: '0.5' } - ], - scope: 'app.create', - expires_at: BigInt(Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60) // 7 days - }; - ``` - - - - - ```typescript - const authRequest = { - address: '0x...', - session_key: '0x...', - application: 'clearnode', // Special value for root access - allowances: [], // Not enforced for root access - scope: 'app.create', - expires_at: BigInt(Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60) // Long expiration recommended - }; - ``` - - - - -**Important considerations:** -- Root access keys (application: "clearnode") cannot perform channel operations after expiration -- Plan expiration times based on your operational needs -- Application-scoped keys track cumulative spending against allowances - -#### Auth Verify Changes - -The `createEIP712AuthMessageSigner` function signature has changed to align with the new session key structure. - -```typescript -const eip712SigningFunction = createEIP712AuthMessageSigner( - walletClient, - { - scope: authMessage.scope, - // remove-next-line - application: authMessage.application, - // remove-next-line - participant: authMessage.session_key, - // remove-next-line - expire: authMessage.expire, - // add-next-line - session_key: authMessage.session_key, - // add-next-line - expires_at: authMessage.expires_at, - allowances: authMessage.allowances, - }, - getAuthDomain(), -); -``` - -#### Migrate Channel Creation - -Channels must now be created with zero initial deposit and funded separately via the `resizeChannel` method: - -```typescript -const { channelId } = await client.createChannel({ - chain_id: 1, - token: tokenAddress, - // remove-next-line - amount: BigInt(1000000), // Initial deposit - // remove-next-line - session_key: '0x...' // Optional -}); - -// add-start -// Step 2: Fund the channel separately -await client.resizeChannel({ - channel_id: channelId, - amount: BigInt(1000000), -}); -// add-end -``` - -#### Resize correctly - -Channel resizing must be negotiated with the ClearNode through WebSocket. Use `resize_amount` and `allocate_amount` with correct sign convention (`resize_amount = -allocate_amount`) and help users with non-zero channel balances migrate by resizing to zero or reopening channels. - -Channel resize can be requested as follows: - -```typescript -const resizeMessage = await createResizeChannelMessage(messageSigner, { - channel_id: channelId, - resize_amount: BigInt(50), // Positive = deposit to channel, negative = withdraw from channel to custody ledger - allocate_amount: BigInt(-50), // Negative = deposit to unified balance, negative = withdraw from unified balance to channel - funds_destination: walletAddress, -}); - -const resizeResponse = {}; // send the message and wait for Clearnode's response - -const { params: resizeResponseParams } = parseResizeChannelResponse(resizeResponse); -const resizeParams = { - resizeState: { - channelId, - ...resizeResponseParams.state, - serverSignature: resizeResponseParams.serverSignature, - data: resizeResponseParams.state.stateData as Hex, - version: BigInt(resizeResponseParams.state.version), - }, - // `previousState` is either initial or previous resizing state, depending on which has higher version number - // can be obtained with `await (client.getChannelData(channelId)).lastValidState` - proofStates: [previousState], -} - -const {txHash} = await client.resizeChannel(resizeParams); -``` - -Here is how you can migrate your channels: - -```typescript -// Check and migrate channels with non-zero amounts -const channels = await client.getOpenChannels(); - -for (const channel of channels) { - if (channel.amount > 0) { - // Must empty channel to enable transfers/app operations - const resizeMessage = await createResizeChannelMessage(messageSigner, { - channel_id: channel.channelId, - resize_amount: BigInt(0), - allocate_amount: -BigInt(channel.amount), - funds_destination: walletAddress, - }); - - // perform the resize as shown above - } -} -``` - - -**Critical:** Operations blocked when any channel has non-zero amount: -- Off-chain transfers -- App state submissions with deposit intent -- Creating app sessions with allocations - -#### Test State Signatures - -If you plan to work with on-chain channels opened PRIOR to v0.5.0, then on VirtualAppClient initialization the `stateSigner` you specify must be based on a Session Key used in the channel as participant. Even if this session key is or will expire, you still need to provide a `stateSigner` based on it. - -On the other hand, if you plan to work with channels created SINCE v0.5.0, you can specify the `stateSigner` based on the `walletClient` you have specified. - -#### Manage Session Keys - -New methods have been added for comprehensive session key management, including retrieval and revocation. - -```typescript -// Get all active session keys -const sessionKeys = await client.getSessionKeys(); - -// Revoke a specific session key -await client.revokeSessionKey({ - session_key: '0x...' -}); - -// Session key data structure -interface RPCSessionKey { - id: string; - sessionKey: Address; - application: string; - allowances: RPCAllowanceUsage[]; // Includes usage tracking - scope: string; - expiresAt: bigint; - createdAt: bigint; -} -``` - -#### EIP-712 Signatures: String-based Amounts - -EIP-712 signature types now use string values for amounts instead of numeric types to support better precision with decimal values. - -```typescript -const types = { - Allowance: [ - { name: 'asset', type: 'string' }, - // remove-next-line - { name: 'amount', type: 'uint256' }, - // add-next-line - { name: 'amount', type: 'string' }, - ] -}; -``` - -### ClearNode API - -You should read this section only if you are using the ClearNode API directly. - -#### Update Authentication - -Use the new session key parameters with proper `application`, `allowances`, and `expires_at` fields: - - - - - ```json - { - "req": [1, "auth_request", { - "address": "0x1234567890abcdef...", - "session_key": "0x9876543210fedcba...", - "application": "My Trading App", - "allowances": [ - { "asset": "usdc", "amount": "1000.0" }, - { "asset": "eth", "amount": "0.5" } - ], - "scope": "app.create", - "expires_at": 1719123456789 - }, 1619123456789], - "sig": ["0x..."] - } - ``` - - - - - ```json - { - "req": [1, "auth_request", { - "address": "0x1234567890abcdef...", - "session_key": "0x9876543210fedcba...", - "application": "clearnode", - "allowances": [], - "scope": "app.create", - "expires_at": 1750659456789 - }, 1619123456789], - "sig": ["0x..."] - } - ``` - - - - -#### Migrate Channel Creation - -Implement the two-step process (create empty, then resize to fund) - -The `create_channel` method no longer accepts `amount` and `session_key` parameters: - -```json -{ - "req": [1, "create_channel", { - "chain_id": 137, - "token": "0xeeee567890abcdef...", - // remove-next-line - "amount": "100000000", - // remove-next-line - "session_key": "0x1234567890abcdef..." - }, 1619123456789], - "sig": ["0x9876fedcba..."] -} -``` - -#### Manage Session Keys - -New methods for session key operations have been added. - -##### Get Session Keys - -Request: -```json -{ - "req": [1, "get_session_keys", {}, 1619123456789], - "sig": ["0x..."] -} -``` - -Response: -```json -{ - "res": [1, "get_session_keys", { - "session_keys": [{ - "id": "sk_123", - "session_key": "0x9876543210fedcba...", - "application": "My Trading App", - "allowances": [ - { "asset": "usdc", "amount": "1000.0", "used": "250.0" } - ], - "scope": "app.create", - "expires_at": 1719123456789, - "created_at": 1619123456789 - }] - }, 1619123456789], - "sig": ["0x..."] -} -``` - -##### Revoke Session Key Request - -Request: -```json -{ - "req": [1, "revoke_session_key", { - "session_key": "0x1234567890abcdef..." - }, 1619123456789], - "sig": ["0x..."] -} -``` - -Response: -```json -{ - "res": [1, "revoke_session_key", { - "session_key": "0x1234567890abcdef..." - }, 1619123456789], - "sig": ["0x..."] -} -``` - -## 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. - -### VirtualApp SDK - -You should definitely read this section if you are using the VirtualApp SDK. - -#### Client: Replaced `stateWalletClient` with `StateSigner` - -The `stateWalletClient` parameter of `VirtualAppClient` 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. - -```typescript -// remove-next-line -import { createNitroliteClient } from '@erc7824/nitrolite'; -// add-start -import { - createVirtualAppClient, - WalletStateSigner -} from '@erc7824/nitrolite'; -// add-end - -const client = createVirtualAppClient({ - publicClient, - walletClient, - // remove-next-line - stateWalletClient: sessionWalletClient, - // add-next-line - stateSigner: new WalletStateSigner(walletClient), - addresses, -}); -``` - -**For session key signing:** - -```typescript -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](#added-create_channel-method) to get the response, that fully resembles the channel creation parameters. - -```typescript -// remove-start -const { channelId, initialState, txHash } = await client.createChannel( - tokenAddress, - { - initialAllocationAmounts: [amount1, amount2], - stateData: '0x...', - } -); -// remove-end -// add-start -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...', -}); -// add-end -``` - -#### 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. - -```typescript -// remove-start -const request = VirtualAppRPC.createRequest( - requestId, - RPCMethod.GetChannels, - [participant, status], - timestamp -); -// remove-end -// add-start -const request = VirtualAppRPC.createRequest({ - method: RPCMethod.GetChannels, - params: { - participant, - status, - }, - requestId, - timestamp, -}); -// add-end -``` - -#### 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. - -```typescript -// remove-start -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; -} -// remove-end -// add-start -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; -} -// add-end -``` - -#### 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 VirtualApp utils correctly, you will not need to change anything, as the utils will handle the conversion for you. - -```typescript -// remove-start -interface Signature { - v: number; - r: Hex; - s: Hex; -} - -const sig: Signature = { - v: 27, - r: '0x...', - s: '0x...' -}; -// remove-end -// add-start -type Signature = Hex; - -const sig: Signature = '0x...'; -// add-end -``` - -#### 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. - -```typescript -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 VirtualApp 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. - -```json -{ - // remove-next-line - "req": [1, "auth_request", [{ - // add-next-line - "req": [1, "auth_request", { - "address": "0x1234567890abcdef...", - "session_key": "0x9876543210fedcba...", - "app_name": "Example App", - // remove-next-line - "allowances": [ "usdc", "100.0" ], - // add-start - "allowances": [ - { - "asset": "usdc", - "amount": "100.0" - } - ], - // add-end - "scope": "app.create", - "expire": "3600", - "application": "0xApp1234567890abcdef..." - // remove-next-line - }], 1619123456789], - // add-next-line - }, 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 VirtualApp SDK or directly. - -**Request:** -```json -{ - "req": [1, "create_channel", { - "chain_id": 137, - "token": "0xeeee567890abcdef...", - "amount": "100000000", - "session_key": "0x1234567890abcdef..." // Optional - }, 1619123456789], - "sig": ["0x9876fedcba..."] -} -``` - -**Response:** -```json -{ - "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. - -```json -// remove-start -{ - "res": [1, "close_channel", { - "channelId": "0x4567890123abcdef...", - "intent": 3, - "version": 123, - "stateData": "0x0000000000000000000000000000000000000000000000000000000000001ec7", - "allocations": [...], - "stateHash": "0x...", - "serverSignature": "0x..." - }, 1619123456789], - "sig": ["0xabcd1234..."] -} -// remove-end -// add-start -{ - "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..."] -} -// add-end -``` - -#### 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. - -```json -// remove-start -{ - "res": [1, "get_channels", [ - [ - { - "channel_id": "0xfedcba9876543210...", - "status": "open", - // ... channel data - } - ] - ], 1619123456789], - "sig": ["0xabcd1234..."] -} -// remove-end -// add-start -{ - "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..."] -} -// add-end -``` - -The metadata fields provide: -- `page`: Current page number -- `per_page`: Number of items per page -- `total_count`: Total number of items available -- `page_count`: Total number of pages - -### Contracts - -You should read this section only if you are using the VirtualApp 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. - -```solidity -// remove-start -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; -// remove-end -// add-start -// 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; -// add-end -``` - -#### Actions: Updated `State` Signature Array - -The `State` struct now uses `bytes[]` for signatures instead of `Signature[]`. - -```solidity -struct State { - uint8 intent; - uint256 version; - bytes data; - Allocation[] allocations; - // remove-next-line - Signature[] sigs; - // add-next-line - 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):** -```solidity -// 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):** -```solidity -// 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. - -```solidity -// remove-start -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); - } -} -// remove-end -// add-start -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); - } -} -// add-end -``` - -#### 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. \ No newline at end of file diff --git a/docs/guides/multi-party-app-sessions.mdx b/docs/guides/multi-party-app-sessions.mdx deleted file mode 100644 index c5ab11e..0000000 --- a/docs/guides/multi-party-app-sessions.mdx +++ /dev/null @@ -1,591 +0,0 @@ ---- -sidebar_position: 3 -title: Multi-Party Application Sessions -description: Learn how to create, manage, and close multi-party application sessions using the Yellow Network and VirtualApp protocol -keywords: [app sessions, multi-party, state channels, quorum, voting, signatures, allocations] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Multi-Party Application Sessions Tutorial - -## Overview - -Application sessions in VirtualApp enable multiple participants to interact within a shared off-chain state channel. This is particularly powerful for use cases requiring coordinated actions between parties without on-chain overhead. - -This tutorial demonstrates how to create, manage, and close a multi-party application session using the Yellow Network and VirtualApp protocol. - -:::tip Run the Full Example -The complete runnable script for this tutorial is available at: -[`scripts/app_sessions/app_session_two_signers.ts`](https://github.com/stevenzeiler/yellow-sdk-tutorials/blob/main/scripts/app_sessions/app_session_two_signers.ts) - -```bash -npx tsx scripts/app_sessions/app_session_two_signers.ts -``` -::: - -## What is an Application Session? - -An **application session** is a multi-party state channel that allows participants to: - -- **Execute off-chain logic** without blockchain transactions -- **Update shared state** with cryptographic signatures -- **Transfer value** between participants instantly - -Unlike simple payment channels (1-to-1), application sessions support: -- Multiple participants (2+) -- Complex state logic -- Voting mechanisms (weights and quorum) -- Flexible allocation rules - -## Prerequisites - -### Environment Setup - -You'll need two wallet seed phrases in your `.env` file: - -```bash -WALLET_1_SEED_PHRASE="first wallet 12 or 24 word mnemonic here" -WALLET_2_SEED_PHRASE="second wallet 12 or 24 word mnemonic here" -``` - -### Funded Wallets - -Both wallets should have: -1. **Funds in Yellow ledger** (deposited via custody contract) - -### Install Dependencies - -```bash -npm install -``` - ---- - -## Key Concepts - -### 1. App Definition - -The application definition specifies the rules of the session: - -```typescript -const appDefinition: RPCAppDefinition = { - protocol: RPCProtocolVersion.NitroRPC_0_5, - participants: [address1, address2], - weights: [50, 50], // Voting power distribution - quorum: 100, // Percentage needed for decisions (100 = unanimous) - challenge: 0, // Challenge period in seconds - nonce: Date.now(), // Unique session ID - application: 'Test app', -}; -``` - -**Key parameters:** - -| Parameter | Description | -|-----------|-------------| -| `participants` | Array of wallet addresses involved | -| `weights` | Voting power for each participant (must sum to 100 or appropriate total) | -| `quorum` | Required percentage of votes for actions (50 = majority, 100 = unanimous) | -| `challenge` | Time window for disputing state changes | -| `nonce` | Unique identifier to prevent replay attacks | - -### 2. Allocations - -Allocations define how assets are distributed among participants: - - - - -```typescript -const allocations: RPCAppSessionAllocation[] = [ - { participant: address1, asset: 'ytest.usd', amount: '0.01' }, - { participant: address2, asset: 'ytest.usd', amount: '0.00' } -]; -``` - - - - -```typescript -const allocations: RPCAppSessionAllocation[] = [ - { participant: address1, asset: 'usdc', amount: '0.01' }, - { participant: address2, asset: 'usdc', amount: '0.00' } -]; -``` - - - - -**Rules:** -- Total allocations cannot exceed session funding -- Amounts are strings (to maintain precision) -- Must account for all participants - -### 3. Multi-Party Signatures - -For actions requiring consensus (closing, etc.), signatures from multiple participants are collected: - -```typescript -// First participant signs -const closeMessage = await createCloseAppSessionMessage( - messageSigner1, - { app_session_id: sessionId, allocations: finalAllocations } -); - -// Second participant signs -const signature2 = await messageSigner2(closeMessage.req); - -// Add second signature -closeMessage.sig.push(signature2); - -// Submit with all signatures -await yellow.sendMessage(JSON.stringify(closeMessage)); -``` - ---- - -## Step-by-Step Walkthrough - -### Step 1: Connect to Yellow Network - - - - -```typescript -const yellow = new Client({ - url: 'wss://clearnet-sandbox.yellow.com/ws', -}); - -await yellow.connect(); -console.log('Connected to Yellow clearnet (Sandbox)'); -``` - - - - -```typescript -const yellow = new Client({ - url: 'wss://clearnet.yellow.com/ws', -}); - -await yellow.connect(); -console.log('Connected to Yellow clearnet (Production)'); -``` - - - - -### Step 2: Set Up Participant Wallets - -```typescript -// Create wallet clients for both participants -const wallet1Client = createWalletClient({ - account: mnemonicToAccount(process.env.WALLET_1_SEED_PHRASE as string), - chain: base, - transport: http(), -}); - -const wallet2Client = createWalletClient({ - account: mnemonicToAccount(process.env.WALLET_2_SEED_PHRASE as string), - chain: base, - transport: http(), -}); -``` - -### Step 3: Authenticate Both Participants - -Each participant needs their own session key: - -```typescript -// Authenticate first participant -const sessionKey1 = await authenticateWallet(yellow, wallet1Client); -const messageSigner1 = createECDSAMessageSigner(sessionKey1.privateKey); - -// Authenticate second participant -const sessionKey2 = await authenticateWallet(yellow, wallet2Client); -const messageSigner2 = createECDSAMessageSigner(sessionKey2.privateKey); -``` - -### Step 4: Define Application Configuration - -```typescript -const appDefinition: RPCAppDefinition = { - protocol: RPCProtocolVersion.NitroRPC_0_5, - participants: [wallet1Client.account.address, wallet2Client.account.address], - weights: [50, 50], - quorum: 100, - challenge: 0, - nonce: Date.now(), - application: 'Test app', -}; -``` - -### Step 5: Create Session with Initial Allocations - - - - -```typescript -const allocations = [ - { participant: wallet1Client.account.address, asset: 'ytest.usd', amount: '0.01' }, - { participant: wallet2Client.account.address, asset: 'ytest.usd', amount: '0.00' } -]; - -const sessionMessage = await createAppSessionMessage( - messageSigner1, - { definition: appDefinition, allocations } -); - -const sessionResponse = await yellow.sendMessage(sessionMessage); -const sessionId = sessionResponse.params.appSessionId; -``` - - - - -```typescript -const allocations = [ - { participant: wallet1Client.account.address, asset: 'usdc', amount: '0.01' }, - { participant: wallet2Client.account.address, asset: 'usdc', amount: '0.00' } -]; - -const sessionMessage = await createAppSessionMessage( - messageSigner1, - { definition: appDefinition, allocations } -); - -const sessionResponse = await yellow.sendMessage(sessionMessage); -const sessionId = sessionResponse.params.appSessionId; -``` - - - - -### Step 6: Update Session State - -You can update allocations to reflect state changes (e.g., a transfer). Since the quorum is 100%, both participants must sign: - - - - -```typescript -const newAllocations = [ - { participant: wallet1Client.account.address, asset: 'ytest.usd', amount: '0.00' }, - { participant: wallet2Client.account.address, asset: 'ytest.usd', amount: '0.01' } -]; - -// Create update message signed by first participant -const updateMessage = await createSubmitAppStateMessage( - messageSigner1, - { app_session_id: sessionId, allocations: newAllocations } -); - -const updateMessageJson = JSON.parse(updateMessage); - -// Second participant signs the same state update -const signature2 = await messageSigner2(updateMessageJson.req as RPCData); - -// Append second signature to meet quorum requirement -updateMessageJson.sig.push(signature2); - -// Submit with all required signatures -await yellow.sendMessage(JSON.stringify(updateMessageJson)); -``` - - - - -```typescript -const newAllocations = [ - { participant: wallet1Client.account.address, asset: 'usdc', amount: '0.00' }, - { participant: wallet2Client.account.address, asset: 'usdc', amount: '0.01' } -]; - -// Create update message signed by first participant -const updateMessage = await createSubmitAppStateMessage( - messageSigner1, - { app_session_id: sessionId, allocations: newAllocations } -); - -const updateMessageJson = JSON.parse(updateMessage); - -// Second participant signs the same state update -const signature2 = await messageSigner2(updateMessageJson.req as RPCData); - -// Append second signature to meet quorum requirement -updateMessageJson.sig.push(signature2); - -// Submit with all required signatures -await yellow.sendMessage(JSON.stringify(updateMessageJson)); -``` - - - - -### Step 7: Close Session with Multi-Party Signatures - -```typescript -// Create close message (signed by participant 1) -const closeMessage = await createCloseAppSessionMessage( - messageSigner1, - { app_session_id: sessionId, allocations: finalAllocations } -); - -const closeMessageJson = JSON.parse(closeMessage); - -// Participant 2 signs -const signature2 = await messageSigner2(closeMessageJson.req as RPCData); -closeMessageJson.sig.push(signature2); - -// Submit with all signatures -const closeResponse = await yellow.sendMessage(JSON.stringify(closeMessageJson)); -``` - ---- - -## Running the Example - -```bash -npx tsx scripts/app_session_two_signers.ts -``` - -### Expected Output - -``` -Connected to Yellow clearnet -Wallet address: 0x1234... -Wallet address: 0x5678... -Session message created: {...} -Session message sent -Session response: { appSessionId: '0xabc...' } -Submit app state message: {...} -Wallet 2 signed close session message: 0xdef... -Close session message (with all signatures): {...} -Close session message sent -Close session response: { success: true } -``` - ---- - -## Use Cases - -:::note Asset Names in Examples -The examples below use `usdc` for production scenarios. When testing on Sandbox, replace `usdc` with `ytest.usd`. -::: - -### 1. Peer-to-Peer Escrow - -```typescript -// Buyer and seller agree on terms -const appDefinition = { - participants: [buyer, seller], - weights: [50, 50], - quorum: 100, // Both must agree to release funds - // ... -}; - -// Buyer funds escrow -const allocations = [ - { participant: buyer, asset: 'usdc', amount: '0' }, - { participant: seller, asset: 'usdc', amount: '100' } // Released to seller -]; -``` - -### 2. Multi-Player Gaming - -```typescript -const appDefinition = { - participants: [player1, player2, player3, player4], - weights: [25, 25, 25, 25], - quorum: 75, // 3 out of 4 players must agree - challenge: 3600, // 1 hour challenge period - application: 'poker-game', -}; -``` - -### 3. Multi-Signature Treasury Management - -```typescript -const appDefinition = { - participants: [member1, member2, member3, member4, member5], - weights: [20, 20, 20, 20, 20], - quorum: 60, // 60% approval needed - application: 'multi-sig-treasury', -}; -``` - -### 4. Atomic Swaps - -```typescript -// Party A has USDC, wants ETH -// Party B has ETH, wants USDC -const allocations = [ - { participant: partyA, asset: 'usdc', amount: '100' }, - { participant: partyA, asset: 'eth', amount: '0' }, - { participant: partyB, asset: 'usdc', amount: '0' }, - { participant: partyB, asset: 'eth', amount: '0.05' } -]; - -// After swap -const finalAllocations = [ - { participant: partyA, asset: 'usdc', amount: '0' }, - { participant: partyA, asset: 'eth', amount: '0.05' }, - { participant: partyB, asset: 'usdc', amount: '100' }, - { participant: partyB, asset: 'eth', amount: '0' } -]; -``` - ---- - -## Advanced Topics - -### Dynamic Participants - -For applications requiring flexible participation: - -```typescript -// Start with 2 participants -let participants = [user1, user2]; - -// Add a third participant (requires re-creating session) -participants.push(user3); - -const newAppDefinition = { - participants, - weights: [33, 33, 34], - // ... -}; -``` - -### Weighted Voting - -Different participants can have different voting power: - -```typescript -const appDefinition = { - participants: [founder, participant1, participant2], - weights: [50, 30, 20], // Founder has 50% voting power - quorum: 60, // Founder + one participant = 60% - // ... -}; -``` - -### Challenge Periods - -Add time for participants to dispute state changes: - -```typescript -const appDefinition = { - // ... - challenge: 86400, // 24 hours in seconds -}; - -// Participants have 24 hours to challenge a close request before finalization -``` - -### State Validation - -Implement custom logic to validate state transitions: - -```typescript -function validateStateTransition( - oldAllocations: RPCAppSessionAllocation[], - newAllocations: RPCAppSessionAllocation[] -): boolean { - // Ensure total amounts are preserved - const oldTotal = oldAllocations.reduce((sum, a) => sum + parseFloat(a.amount), 0); - const newTotal = newAllocations.reduce((sum, a) => sum + parseFloat(a.amount), 0); - - return Math.abs(oldTotal - newTotal) < 0.000001; -} -``` - ---- - -## Troubleshooting - -### "Authentication failed for participant" - -**Cause**: Session key authentication failed - -**Solution**: -- Ensure both `WALLET_1_SEED_PHRASE` and `WALLET_2_SEED_PHRASE` are set in `.env` -- Verify wallets have been authenticated on Yellow network before - -### "Unsupported token" - -**Cause**: Using the wrong asset for your environment (e.g., `usdc` on Sandbox or `ytest.usd` on Production) - -**Solution**: -- **Sandbox** (`wss://clearnet-sandbox.yellow.com/ws`): Use `ytest.usd` -- **Production** (`wss://clearnet.yellow.com/ws`): Use `usdc` - -Ensure the asset in your allocations matches the connected network. - -### "Insufficient balance" - -**Cause**: Participant doesn't have enough funds in Yellow ledger - -**Solution**: - -Deposit sufficient funds into the yellow network account unified balance for each wallet - -### "Invalid signatures" - -**Cause**: Not all required signatures were collected - -**Solution**: -- Ensure quorum is met (if quorum is 100, need all signatures) -- Check that signatures are added in correct order -- Verify message signers correspond to participants - -### "Session already closed" - -**Cause**: Trying to update or close an already-finalized session - -**Solution**: -- Create a new session -- Check session status before operations - -### "Quorum not reached" - -**Cause**: Insufficient voting weight for action - -**Solution**: - -```typescript -// Example: quorum is 60, weights are [30, 30, 40] -// Need at least 2 participants to sign - -// Check current signature weight -const signatureWeight = signatures.reduce((sum, sig) => { - const participantIndex = findParticipantIndex(sig); - return sum + weights[participantIndex]; -}, 0); - -console.log(`Current weight: ${signatureWeight}, Required: ${quorum}`); -``` - ---- - -## Best Practices - -1. **Always validate allocations** before submitting state updates -2. **Store session IDs** for future reference and auditing -3. **Implement timeout handling** for multi-party signatures -4. **Use appropriate quorum settings** based on trust model -5. **Test with small amounts** before production use -6. **Keep participants informed** of state changes -7. **Handle disconnections gracefully** (participants may come back) -8. **Document application logic** for all participants - ---- - -## Further Reading - -- [App Sessions Core Concepts](/docs/learn/core-concepts/app-sessions) — Understanding app sessions -- [App Session Methods](/docs/protocol/app-layer/off-chain/app-sessions) — Complete API reference -- [Client-Side App Session Signing Guide](/docs/guides/client-side-app-session-signing) — Signing implementation details -- [Session Keys](/docs/learn/core-concepts/session-keys) — Managing session keys diff --git a/docs/learn/advanced/_category_.json b/docs/learn/advanced/_category_.json deleted file mode 100644 index 83cb82e..0000000 --- a/docs/learn/advanced/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Advanced", - "position": 3, - "collapsible": false, - "collapsed": false -} \ No newline at end of file diff --git a/docs/learn/advanced/managing-session-keys.mdx b/docs/learn/advanced/managing-session-keys.mdx deleted file mode 100644 index 83ddc33..0000000 --- a/docs/learn/advanced/managing-session-keys.mdx +++ /dev/null @@ -1,197 +0,0 @@ ---- -sidebar_position: 1 -title: Managing Session Keys -description: Create, list, and revoke session keys with complete API examples -keywords: [session keys, authentication, API, create, revoke, manage] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Managing Session Keys - -This guide covers the operational details of creating, listing, and revoking session keys via the Clearnode API. - -:::info Prerequisites -Before diving into session key management, make sure you understand the core concepts: what session keys are, how applications and allowances work, and the expiration rules. See **[Session Keys](../core-concepts/session-keys.mdx)** for the conceptual foundation. -::: - ---- - -## How to Manage Session Keys - -### Clearnode - -#### Create and Configure - -To create a session key, use the `auth_request` method during authentication. This registers the session key with its configuration: - -**Request:** - -```json -{ - "req": [ - 1, - "auth_request", - { - "address": "0x1234567890abcdef...", - "session_key": "0x9876543210fedcba...", - "application": "Chess Game", - "allowances": [ - { - "asset": "usdc", - "amount": "100.0" - }, - { - "asset": "eth", - "amount": "0.5" - } - ], - "scope": "app.create", - "expires_at": 1762417328 - }, - 1619123456789 - ], - "sig": ["0x5432abcdef..."] -} -``` - -**Parameters:** - -- `address` (required): The wallet address that owns this session key -- `session_key` (required): The address of the session key to register -- `application` (optional): Name of the application using this session key (defaults to "clearnode" if not provided) -- `allowances` (optional): Array of asset allowances specifying spending limits -- `scope` (optional): Permission scope (e.g., "app.create", "ledger.readonly"). **Note:** This feature is not yet implemented -- `expires_at` (required): Unix timestamp (in seconds) when this session key expires - -:::note -When authenticating with an already registered session key, you must still fill in all fields in the request, at least with arbitrary values. This is required by the request itself, however, the values will be ignored as the system uses the session key configuration stored during initial registration. This behavior will be improved in future versions. -::: - -#### List Active Session Keys - -Use the `get_session_keys` method to retrieve all active (non-expired) session keys for the authenticated user: - -**Request:** - -```json -{ - "req": [1, "get_session_keys", {}, 1619123456789], - "sig": ["0x9876fedcba..."] -} -``` - -**Response:** - -```json -{ - "res": [ - 1, - "get_session_keys", - { - "session_keys": [ - { - "id": 1, - "session_key": "0xabcdef1234567890...", - "application": "Chess Game", - "allowances": [ - { - "asset": "usdc", - "allowance": "100.0", - "used": "45.0" - }, - { - "asset": "eth", - "allowance": "0.5", - "used": "0.0" - } - ], - "scope": "app.create", - "expires_at": "2024-12-31T23:59:59Z", - "created_at": "2024-01-01T00:00:00Z" - } - ] - }, - 1619123456789 - ], - "sig": ["0xabcd1234..."] -} -``` - -**Response Fields:** - -- `id`: Unique identifier for the session key record -- `session_key`: The address of the session key -- `application`: Application name this session key is authorized for -- `allowances`: Array of allowances with usage tracking: - - `asset`: Symbol of the asset (e.g., "usdc", "eth") - - `allowance`: Maximum amount the session key can spend - - `used`: Amount already spent by this session key -- `scope`: Permission scope (omitted if empty) -- `expires_at`: When this session key expires (ISO 8601 format) -- `created_at`: When the session key was created (ISO 8601 format) - -#### Revoke a Session Key - -To immediately invalidate a session key, use the `revoke_session_key` method: - -**Request:** - -```json -{ - "req": [ - 1, - "revoke_session_key", - { - "session_key": "0xabcdef1234567890..." - }, - 1619123456789 - ], - "sig": ["0x9876fedcba..."] -} -``` - -**Response:** - -```json -{ - "res": [ - 1, - "revoke_session_key", - { - "session_key": "0xabcdef1234567890..." - }, - 1619123456789 - ], - "sig": ["0xabcd1234..."] -} -``` - -**Permission Rules:** - -- A wallet can revoke any of its session keys -- A session key can revoke itself -- A session key with `application: "clearnode"` can revoke other session keys belonging to the same wallet -- A non-"clearnode" session key cannot revoke other session keys (only itself) - -**Important Notes:** - -- Revocation is **immediate and cannot be undone** -- After revocation, any operations attempted with the revoked session key will fail with a validation error -- The revoked session key will no longer appear in the `get_session_keys` response -- Revocation is useful for security purposes when a session key may have been compromised - -**Error Cases:** - -- Session key does not exist, belongs to another wallet, or is expired: `"operation denied: provided address is not an active session key of this user"` -- Non-"clearnode" session key attempting to revoke another session key: `"operation denied: insufficient permissions for the active session key"` - -### VirtualApp SDK - -The VirtualApp SDK provides a higher-level abstraction for managing session keys. For detailed information on using session keys with the VirtualApp SDK, please refer to the SDK documentation. - diff --git a/docs/learn/core-concepts/app-sessions.mdx b/docs/learn/core-concepts/app-sessions.mdx deleted file mode 100644 index f457ce7..0000000 --- a/docs/learn/core-concepts/app-sessions.mdx +++ /dev/null @@ -1,184 +0,0 @@ ---- -sidebar_position: 2 -title: App Sessions -description: Multi-party application channels with custom governance and state management -keywords: [app sessions, multi-party, governance, quorum, NitroRPC] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# App Sessions - -App sessions are off-chain channels built on top of the unified balance that enable multi-party applications with custom governance rules. - -**Goal**: Understand how app sessions work for building multi-party applications. - ---- - -## What is an App Session? - -An **app session** is a temporary shared account where multiple participants can: - -- Lock funds from their unified balance -- Execute application-specific logic (games, escrow, predictions) -- Redistribute funds based on outcomes -- Close and release funds back to unified balances - -Think of it as a programmable escrow with custom voting rules. - ---- - -## App Session vs Payment Channel - -| Feature | Payment Channel | App Session | -|---------|-----------------|-------------| -| **Participants** | Always 2 | 2 or more | -| **Governance** | Both must sign | Quorum-based | -| **Fund source** | On-chain deposit | Unified balance | -| **Mid-session changes** | Via resize (on-chain) | Via intent (off-chain) | -| **Use case** | Transfers | Applications | - ---- - -## App Session Definition - -Every app session starts with a **definition** that specifies the rules: - -| Field | Description | -|-------|-------------| -| `protocol` | Version (`NitroRPC/0.4` recommended) | -| `participants` | Wallet addresses (order matters for signatures) | -| `weights` | Voting power per participant | -| `quorum` | Minimum weight required for state updates | -| `challenge` | Dispute window in seconds | -| `nonce` | Unique identifier (typically timestamp) | - -The `app_session_id` is computed deterministically from the definition using `keccak256(JSON.stringify(definition))`. - ---- - -## Governance with Quorum - -The quorum system enables flexible governance patterns. - -### How It Works - -1. Each participant has a **weight** (voting power) -2. State updates require signatures with total weight ≥ **quorum** -3. Not everyone needs to sign—just enough to meet quorum - -### Common Patterns - -| Pattern | Setup | Use Case | -|---------|-------|----------| -| **Unanimous** | `weights: [50, 50]`, `quorum: 100` | Both must agree | -| **Trusted Judge** | `weights: [0, 0, 100]`, `quorum: 100` | App determines outcome | -| **2-of-3 Escrow** | `weights: [40, 40, 50]`, `quorum: 80` | Any two can proceed | -| **Weighted Voting** | `weights: [20, 25, 30, 25]`, `quorum: 51` | Majority by weight | - ---- - -## Session Lifecycle - -```mermaid -stateDiagram-v2 - [*] --> Open: create_app_session - Open --> Open: submit_app_state - Open --> Closed: close_app_session - Closed --> [*] -``` - -### 1. Creation - -- Funds locked from participants' unified balances -- All participants with non-zero allocations must sign -- Status becomes `open`, version starts at `1` - -### 2. State Updates - -- Redistribute funds with `submit_app_state` -- Version must increment by exactly 1 -- Quorum of signatures required - -### 3. Closure - -- Final allocations distributed to unified balances -- Session becomes `closed` (cannot reopen) -- Quorum of signatures required - ---- - -## Intent System (NitroRPC/0.4) - -The intent system enables dynamic fund management during active sessions: - -| Intent | Purpose | Rule | -|--------|---------|------| -| **OPERATE** | Redistribute existing funds | Sum unchanged | -| **DEPOSIT** | Add funds from unified balance | Sum increases | -| **WITHDRAW** | Remove funds to unified balance | Sum decreases | - -:::info Allocations Are Final State -Allocations always represent the **final state**, not the delta. The Clearnode computes deltas internally. -::: - ---- - -## Fund Flow - -```mermaid -graph TB - subgraph Unified["Unified Balances"] - UA["Alice: 200 USDC"] - UB["Bob: 200 USDC"] - end - - subgraph Session["App Session"] - SA["Alice: 100 USDC"] - SB["Bob: 100 USDC"] - end - - UA -->|"create (lock)"| SA - UB -->|"create (lock)"| SB - - SA -->|"close (release)"| UA - SB -->|"close (release)"| UB - - style Unified fill:#e1f5ff,stroke:#333 - style Session fill:#ffe1f5,stroke:#333 -``` - ---- - -## Protocol Versions - -| Version | Status | Key Features | -|---------|--------|--------------| -| **NitroRPC/0.2** | Legacy | Basic state updates only | -| **NitroRPC/0.4** | Current | Intent system (OPERATE, DEPOSIT, WITHDRAW) | - -Always use `NitroRPC/0.4` for new applications. Protocol version is set at creation and cannot be changed. - ---- - -## Best Practices - -1. **Set appropriate challenge periods**: 1 hour minimum, 24 hours recommended -2. **Include commission participants**: Apps often have a judge that takes a small fee -3. **Plan for disputes**: Design allocations that can be verified by third parties -4. **Version carefully**: Each state update must be exactly `current + 1` - ---- - -## Deep Dive - -For complete method specifications and implementation details: - -- **[App Session Methods](/docs/protocol/app-layer/off-chain/app-sessions.mdx)** — Complete method specifications -- **[Communication Flows](/docs/protocol/communication-flows.mdx#app-session-lifecycle-flow)** — Sequence diagrams -- **[Implementation Checklist](/docs/protocol/implementation-checklist.mdx#state-management)** — Building app session support diff --git a/docs/learn/core-concepts/challenge-response.mdx b/docs/learn/core-concepts/challenge-response.mdx deleted file mode 100644 index ab33d31..0000000 --- a/docs/learn/core-concepts/challenge-response.mdx +++ /dev/null @@ -1,159 +0,0 @@ ---- -sidebar_position: 4 -title: Challenge-Response & Disputes -description: How Yellow Network handles disputes and ensures fund safety -keywords: [challenge, dispute, security, settlement, fund recovery] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Challenge-Response & Disputes - -In this guide, you will learn how Yellow Network resolves disputes and ensures your funds are always recoverable. - -**Goal**: Understand the security guarantees that make off-chain transactions safe. - ---- - -## Why Challenge-Response Matters - -In any off-chain system, a critical question arises: **What if someone tries to cheat?** - -State channels solve this with a challenge-response mechanism: - -1. Anyone can submit a state to the blockchain -2. Counterparties have time to respond with a newer state -3. The newest valid state always wins -4. Funds are distributed according to that state - ---- - -## The Trust Model - -State channels are **trustless** because: - -| Guarantee | How It's Achieved | -|-----------|-------------------| -| **Fund custody** | Smart contract holds funds, not Clearnode | -| **State validity** | Only signed states are accepted | -| **Dispute resolution** | On-chain fallback if disagreement | -| **Recovery** | You can always get your funds back | - ---- - -## Channel Dispute Flow - -### Scenario: Clearnode Becomes Unresponsive - -You have a channel with 100 USDC. The Clearnode stops responding. - -**Your options:** - -1. Wait for Clearnode to recover -2. Force settlement on-chain via challenge - -### The Process - -1. **Initiate Challenge**: Submit your latest signed state to the blockchain -2. **Challenge Period**: Contract sets a timer (e.g., 24 hours) -3. **Response Window**: Counterparty can submit a newer state -4. **Resolution**: After timeout, challenged state becomes final - -```mermaid -stateDiagram-v2 - [*] --> ACTIVE - ACTIVE --> DISPUTE: challenge() - DISPUTE --> ACTIVE: checkpoint() with newer state - DISPUTE --> FINAL: Timeout expires - FINAL --> [*] - - note right of DISPUTE: Anyone can submit
newer valid state -``` - ---- - -## Why This Works - -### States Are Ordered - -Every state has a version number. A newer (higher version) state always supersedes older states. - -### States Are Signed - -With the default SimpleConsensus adjudicator, both parties must sign every state. If someone signed a state, they can't later claim they didn't agree. - -:::note Other Adjudicators -Different adjudicators may have different signing requirements. For example, a Remittance adjudicator may only require the sender's signature. The signing rules are defined by the channel's adjudicator contract. -::: - -### Challenge Period Provides Fairness - -The waiting window ensures honest parties have time to respond. Network delays don't cause losses. - -### On-Chain Contract is Neutral - -The smart contract accepts any valid signed state, picks the highest version, and distributes funds exactly as specified. - ---- - -## Challenge Period Selection - -| Duration | Trade-offs | -|----------|------------| -| **1 hour** | Fast resolution, tight response window | -| **24 hours** | Balanced (recommended) | -| **7 days** | Maximum safety, slow settlement | - -The Custody Contract enforces a minimum of 1 hour. - ---- - -## Checkpoint vs Challenge - -| Operation | Purpose | Channel Status | -|-----------|---------|----------------| -| `checkpoint()` | Record state without dispute | Stays ACTIVE | -| `challenge()` | Force dispute resolution | Changes to DISPUTE | - -Use checkpoint for safety snapshots. Use challenge when you need to force settlement. - ---- - -## What Happens If... - -| Scenario | Outcome | -|----------|---------| -| **Clearnode goes offline** | Challenge with latest state, withdraw after timeout | -| **You lose state history** | Challenge with old state; counterparty submits newer if they have it | -| **Counterparty submits wrong state** | Submit your newer state via checkpoint | -| **Block reorg occurs** | Replay events from last confirmed block | - ---- - -## Key Takeaways - -| Concept | Remember | -|---------|----------| -| **Challenge** | Force on-chain dispute resolution | -| **Response** | Submit newer state to defeat challenge | -| **Timeout** | After period, challenged state becomes final | -| **Checkpoint** | Record state without dispute | - -:::success Security Guarantee -You can **always** recover your funds according to the latest mutually signed state, regardless of counterparty behavior. -::: - ---- - -## Deep Dive - -For technical implementation details: - -- **[Channel Lifecycle](/docs/protocol/app-layer/on-chain/channel-lifecycle.mdx)** — Full state machine -- **[Security Considerations](/docs/protocol/app-layer/on-chain/security.mdx)** — Threat model and best practices -- **[Communication Flows](/docs/protocol/communication-flows.mdx#challenge-response-closure-flow)** — Sequence diagrams diff --git a/docs/learn/core-concepts/message-envelope.mdx b/docs/learn/core-concepts/message-envelope.mdx deleted file mode 100644 index 6260d5f..0000000 --- a/docs/learn/core-concepts/message-envelope.mdx +++ /dev/null @@ -1,147 +0,0 @@ ---- -sidebar_position: 5 -title: Message Envelope (RPC Protocol) -description: Overview of the Nitro RPC message format and communication protocol -keywords: [Nitro RPC, message format, WebSocket, protocol, signatures] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Message Envelope (RPC Protocol) - -In this guide, you will learn the essentials of how messages are structured and transmitted in Yellow Network. - -**Goal**: Understand the Nitro RPC protocol at a conceptual level. - ---- - -## Protocol Overview - -**Nitro RPC** is a lightweight RPC protocol optimized for state channel communication: - -| Feature | Benefit | -|---------|---------| -| **Compact format** | ~30% smaller than traditional JSON-RPC | -| **Signature-based auth** | Every message is cryptographically verified | -| **Bidirectional** | Real-time updates via WebSocket | -| **Ordered timestamps** | Replay attack prevention | - ---- - -## Message Structure - -Every Nitro RPC message uses a compact JSON array format: - -| Component | Type | Description | -|-----------|------|-------------| -| **requestId** | uint64 | Unique identifier for correlation | -| **method** | string | RPC method name (snake_case) | -| **params/result** | object | Method-specific data | -| **timestamp** | uint64 | Unix milliseconds | - -### Request Wrapper - -``` -{ "req": [requestId, method, params, timestamp], "sig": [...] } -``` - -### Response Wrapper - -``` -{ "res": [requestId, method, result, timestamp], "sig": [...] } -``` - -### Error Response - -``` -{ "res": [requestId, "error", { "error": "description" }, timestamp], "sig": [...] } -``` - ---- - -## Signature Format - -Each signature is a 65-byte ECDSA signature (r + s + v) represented as a 0x-prefixed hex string. - -| Context | What's Signed | Who Signs | -|---------|---------------|-----------| -| **Requests** | JSON payload hash | Session key (or main wallet) | -| **Responses** | JSON payload hash | Clearnode | - ---- - -## Method Categories - -| Category | Methods | -|----------|---------| -| **Auth** | `auth_request`, `auth_verify` | -| **Channels** | `create_channel`, `close_channel`, `resize_channel` | -| **Transfers** | `transfer` | -| **App Sessions** | `create_app_session`, `submit_app_state`, `close_app_session` | -| **Queries** | `get_ledger_balances`, `get_channels`, `get_app_sessions`, etc. | - ---- - -## Notifications - -The Clearnode pushes real-time updates: - -| Notification | When Sent | -|--------------|-----------| -| `bu` (balance update) | Balance changed | -| `cu` (channel update) | Channel status changed | -| `tr` (transfer) | Incoming/outgoing transfer | -| `asu` (app session update) | App session state changed | - ---- - -## Communication Flow - -```mermaid -sequenceDiagram - participant Client - participant Clearnode - - Client->>Clearnode: Request (signed) - Clearnode->>Clearnode: Verify signature - Clearnode->>Clearnode: Process - Clearnode->>Client: Response (signed) - Client->>Client: Verify signature - - Clearnode-->>Client: Notification (async) -``` - ---- - -## Protocol Versions - -| Version | Status | Key Features | -|---------|--------|--------------| -| **NitroRPC/0.2** | Legacy | Basic state updates | -| **NitroRPC/0.4** | Current | Intent system, enhanced validation | - -Always use NitroRPC/0.4 for new implementations. - ---- - -## Key Points - -1. **Compact arrays** instead of verbose JSON objects -2. **Every message signed** for authenticity -3. **Timestamps** prevent replay attacks -4. **Bidirectional** WebSocket for real-time updates - ---- - -## Deep Dive - -For complete technical specifications: - -- **[Message Format](/docs/protocol/app-layer/off-chain/message-format.mdx)** — Full format specification -- **[Off-Chain Overview](/docs/protocol/app-layer/off-chain/overview.mdx)** — Protocol architecture -- **[Implementation Checklist](/docs/protocol/implementation-checklist.mdx#off-chain-rpc)** — Building RPC support diff --git a/docs/learn/core-concepts/session-keys.mdx b/docs/learn/core-concepts/session-keys.mdx deleted file mode 100644 index 3312ff0..0000000 --- a/docs/learn/core-concepts/session-keys.mdx +++ /dev/null @@ -1,181 +0,0 @@ ---- -sidebar_position: 3 -title: Session Keys -description: Delegated keys for secure, gasless application interactions -keywords: [session keys, authentication, signatures, allowances, security] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Session Keys - -Session keys are delegated keys that enable applications to perform operations on behalf of a user's wallet with specified spending limits, permissions, and expiration times. They provide a secure way to grant limited access to applications without exposing the main wallet's private key. - -:::important -Session keys are **no longer used as on-chain channel participant addresses** for new channels created after the v0.5.0 release. For all new channels, the wallet address is used directly as the participant address. However, session keys still function correctly for channels that were created before v0.5.0, ensuring backward compatibility. -::: - -**Goal**: Understand how session keys enable seamless UX while maintaining security. - ---- - -## Why Session Keys Matter - -Every blockchain operation traditionally requires a wallet signature popup. For high-frequency applications like games or trading, this creates terrible UX—imagine 40+ wallet prompts during a chess game. - -Session keys solve this by allowing you to **sign once**, then operate seamlessly for the duration of the session. - ---- - -## Core Concepts - -### General Rules - -:::important -When authenticating with an already registered session key, you must still provide all parameters in the `auth_request`. However, the configuration values (`application`, `allowances`, `scope`, and `expires_at`) from the request will be ignored, as the system uses the settings from the initial registration. You may provide arbitrary values for these fields, as they are required by the request format but will not be used. -::: - -### Applications - -Each session key is associated with a specific **application name**, which identifies the application or service that will use the session key. The application name is also used to identify **app sessions** that are created using that session key. - -This association serves several purposes: - -- **Application Isolation**: Different applications get separate session keys, preventing one application from using another's delegated access -- **Access Control**: Operations performed with a session key are validated against the application specified during registration -- **Single Active Key**: Only one session key can be active per wallet+application combination. Registering a new session key for the same application automatically invalidates any existing session key for that application - -:::important -Only one session key is allowed per wallet+application combination. If you register a new session key for the same application, the old one is automatically invalidated and removed from the database. -::: - -#### Special Application: "clearnode" - -Session keys registered with the application name `"clearnode"` receive special treatment: - -- **Root Access**: These session keys bypass spending allowance validation and application restrictions -- **Full Permissions**: They can perform any operation the wallet itself could perform -- **Backward Compatibility**: This special behavior facilitates migration from older versions -- **Expiration Still Applies**: Even with root access, the session key expires according to its `expires_at` timestamp - -:::note -The "clearnode" application name is primarily for backward compatibility and will be deprecated after a migration period for developers. -::: - -### Expiration - -All session keys must have an **expiration timestamp** (`expires_at`) that defines when the session key becomes invalid: - -- **Future Timestamp Required**: The expiration time must be set to a future date when registering a session key -- **Automatic Invalidation**: Once the expiration time passes, the session key can no longer be used for any operations -- **No Re-registration**: It is not possible to re-register an expired session key. You must create a new session key instead -- **Applies to All Keys**: Even "clearnode" application session keys must respect the expiration timestamp - -### Allowances - -Allowances define **spending limits** for session keys, specifying which assets the session key can spend and how much: - -```json -{ - "allowances": [ - { - "asset": "usdc", - "amount": "100.0" - }, - { - "asset": "eth", - "amount": "0.5" - } - ] -} -``` - -#### Allowance Validation - -- **Supported Assets Only**: All assets specified in allowances must be supported by the system. Unsupported assets cause authentication to fail -- **Usage Tracking**: The system tracks spending per session key by recording which session key was used for each ledger debit operation -- **Spending Limits**: Once a session key reaches its spending cap for an asset, further operations requiring that asset are rejected with: `"operation denied: insufficient session key allowance: X required, Y available"` -- **Empty Allowances**: Providing an empty `allowances` array (`[]`) means zero spending allowed for all assets—any operation attempting to spend funds will be rejected - -#### Allowances for "clearnode" Application - -Session keys with `application: "clearnode"` are exempt from allowance enforcement: - -- **No Spending Limits**: Allowance checks are bypassed entirely -- **Full Financial Access**: These keys can spend any amount of any supported asset -- **Expiration Still Matters**: Even without allowance restrictions, the session key still expires according to its `expires_at` timestamp - ---- - -## Session Key Lifecycle - -```mermaid -stateDiagram-v2 - [*] --> Unauthenticated - Unauthenticated --> Authenticated: auth_verify success - Authenticated --> Authenticated: Using session key - Authenticated --> Expired: expires_at reached - Authenticated --> Exhausted: Allowance depleted - Authenticated --> Revoked: Manual revocation - Expired --> Unauthenticated: Re-authenticate - Exhausted --> Unauthenticated: Re-authenticate - Revoked --> Unauthenticated: Re-authenticate -``` - ---- - -## Security Model - -| Approach | Risk if Compromised | UX Impact | -|----------|---------------------|-----------| -| **Main wallet always** | Full wallet access | Constant prompts | -| **Session key (limited)** | Only allowance at risk | Seamless | -| **Session key (unlimited)** | Unified balance at risk | Seamless but risky | - -:::warning Session Key Compromise -If a session key is compromised, attackers can only spend up to the configured allowance before expiration. This is why setting appropriate limits is critical. -::: - ---- - -## Best Practices - -### For Users - -1. **Set reasonable allowances**: Don't authorize more than you'll use -2. **Use short expirations**: 24 hours is usually sufficient -3. **Different keys for different apps**: Isolate risk per application -4. **Monitor spending**: Use `get_session_keys` to check usage -5. **Revoke when done**: Clean up unused sessions - -### For Developers - -1. **Secure storage**: Encrypt session keys at rest -2. **Never transmit private keys**: Session key stays on device -3. **Handle expiration gracefully**: Prompt re-authentication before expiry -4. **Verify Clearnode signatures**: Always validate response signatures -5. **Clear on logout**: Delete session keys when user logs out - ---- - -## Alternative: Main Wallet as Root Signer - -You can skip session keys entirely and sign every request with your main wallet. Use this approach for: - -- Single operations -- High-value transactions -- Maximum security required -- Non-interactive applications - ---- - -## Next Steps - -- **[Managing Session Keys](../advanced/managing-session-keys.mdx)** — Create, list, and revoke session keys with full API examples -- **[Authentication Flow](/docs/protocol/app-layer/off-chain/authentication.mdx)** — Full 3-step authentication protocol -- **[Communication Flows](/docs/protocol/communication-flows.mdx#authentication-flow)** — Sequence diagrams for auth diff --git a/docs/learn/core-concepts/state-channels-vs-l1-l2.mdx b/docs/learn/core-concepts/state-channels-vs-l1-l2.mdx deleted file mode 100644 index b5f617c..0000000 --- a/docs/learn/core-concepts/state-channels-vs-l1-l2.mdx +++ /dev/null @@ -1,145 +0,0 @@ ---- -sidebar_position: 1 -title: State Channels vs L1/L2 -description: Compare state channels with Layer 1 and Layer 2 scaling solutions -keywords: [state channels, L1, L2, scaling, comparison, rollups, VirtualApp] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# State Channels vs L1/L2 - -In this guide, you will learn how state channels compare to Layer 1 and Layer 2 solutions, and when each approach is the right choice. - -**Goal**: Understand where state channels fit in the blockchain scaling landscape. - ---- - -## Solution Comparison - -| Solution | Throughput | Latency | Cost per Op | Best For | -|----------|------------|---------|-------------|----------| -| **Layer 1** | 15-65K TPS | 1-15 sec | $0.001-$50 | Settlement, contracts | -| **Layer 2** | 2,000-4,000 TPS | 1-10 sec | $0.01-$0.50 | General dApps | -| **State Channels** | **Unlimited*** | **< 1 sec** | **$0** | High-frequency, known parties | - -*\*Theoretically unlimited—no consensus bottleneck. Real-world throughput depends on signature generation, network latency, and application logic. Benchmarking documentation coming soon.* - ---- - -## How State Channels Work - -State channels operate on a simple principle: - -1. **Lock funds** in a smart contract (on-chain) -2. **Exchange signed states** directly between participants (off-chain) -3. **Settle** when done or if there's a dispute (on-chain) - -The key insight: most interactions between parties don't need immediate on-chain settlement. - ---- - -## State Channel Advantages - -### Instant Finality - -Unlike L2 solutions that still have block times, state channels provide sub-second finality: - -| Solution | Transaction Flow | -|----------|------------------| -| L1 | Transaction → Mempool → Block → Confirmation | -| L2 | Transaction → Sequencer → L2 Block → L1 Data | -| Channels | Signature → Validation → Done | - -### Zero Operational Cost - -| Operation | L1 Cost | L2 Cost | State Channel | -|-----------|---------|---------|---------------| -| 100 transfers | $500-5000 | $10-50 | **$0** | -| 1000 transfers | $5000-50000 | $100-500 | **$0** | - -### Privacy - -Off-chain transactions are only visible to participants. Only opening and final states appear on-chain. - ---- - -## State Channel Limitations - -### Known Participants - -Channels work between specific participants. Yellow Network addresses this through Clearnodes—off-chain service providers that coordinate channels and provide a unified balance across multiple users and chains. - -### Liquidity Requirements - -Funds must be locked upfront. You can't spend more than what's locked in the channel. - -### Liveness Requirements - -Participants must respond to challenges within the challenge period. Users should ensure they can monitor for challenges or use services that provide this functionality. - ---- - -## When to Use Each - -| Choose | When | -|--------|------| -| **L1** | Deploying contracts, one-time large transfers, final settlement | -| **L2** | General dApps, many unknown users, complex smart contracts | -| **State Channels** | Known parties, real-time speed, high frequency, zero gas needed | - ---- - -## Decision Framework - -```mermaid -flowchart TD - A[Transaction] --> B{Known counterparty?} - B -->|No| C[Use L1/L2] - B -->|Yes| D{High frequency?} - D -->|Yes| E[Use State Channel] - D -->|No| F{Large value?} - F -->|Yes| C - F -->|No| E - - style E fill:#9999ff,stroke:#333,color:#111 - style C fill:#99ff99,stroke:#333,color:#111 -``` - ---- - -## How Yellow Network Addresses Limitations - -| Limitation | Solution | -|------------|----------| -| Known participants | Clearnode coordination layer | -| Liquidity | Unified balance across chains | -| Liveness | Always-on Clearnode monitoring | - ---- - -## Key Takeaways - -State channels shine when you have identified participants who will interact frequently—like players in a game, counterparties in a trade, or parties in a payment relationship. - -:::success State Channel Sweet Spot -- Real-time interactions between known parties -- High transaction volumes -- Zero gas costs required -- Instant finality needed -::: - ---- - -## Deep Dive - -For technical details on channel implementation: - -- **[Architecture](/docs/protocol/architecture.mdx)** — System design and fund flows -- **[Channel Lifecycle](/docs/protocol/app-layer/on-chain/channel-lifecycle.mdx)** — State machine and operations -- **[Data Structures](/docs/protocol/app-layer/on-chain/data-structures.mdx)** — Channel and state formats diff --git a/docs/learn/getting-started/key-terms.mdx b/docs/learn/getting-started/key-terms.mdx deleted file mode 100644 index a5596f7..0000000 --- a/docs/learn/getting-started/key-terms.mdx +++ /dev/null @@ -1,343 +0,0 @@ ---- -sidebar_position: 3 -title: Key Terms & Mental Models -description: Essential vocabulary and conceptual frameworks for understanding Yellow Network -keywords: [terminology, glossary, concepts, state channels, mental models] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Key Terms & Mental Models - -In this guide, you will learn the essential vocabulary and mental models for understanding Yellow Network and state channel technology. - -**Goal**: Build a solid conceptual foundation before diving into implementation. - ---- - -## Core Mental Model: Off-Chain Execution - -The fundamental insight behind Yellow Network is simple: - -> **Most interactions don't need immediate on-chain settlement.** - -Think of it like a bar tab: - -| Traditional (L1) | State Channels | -|------------------|----------------| -| Pay for each drink separately | Open a tab, pay once at the end | -| Wait for bartender each time | Instant service, settle later | -| Transaction per item | One transaction for the whole session | - -State channels apply this pattern to blockchain: **lock funds once**, **transact off-chain**, **settle once**. - ---- - -## Essential Vocabulary - -### State Channel - -A **state channel** is a secure pathway for exchanging cryptographically signed states between participants without touching the blockchain. - -**Key properties:** -- Funds are locked in a smart contract -- Participants exchange signed state updates off-chain -- Only opening and closing require on-chain transactions -- Either party can force on-chain settlement if needed - -**Analogy**: Like a private Venmo between two parties, backed by a bank escrow. - ---- - -### Channel - -A **Channel** is the on-chain representation of a state channel. It defines: - -```typescript -{ - participants: ['0xAlice', '0xBob'], // Who can participate - adjudicator: '0xContract', // Rules for state validation - challenge: 86400, // Dispute window (seconds) - nonce: 1699123456789 // Unique identifier -} -``` - -The **channelId** is computed deterministically from these parameters: - -``` -channelId = keccak256(participants, adjudicator, challenge, nonce, chainId) -``` - ---- - -### State - -A **State** is a snapshot of the channel at a specific moment: - -```typescript -{ - intent: 'OPERATE', // Purpose: INITIALIZE, OPERATE, RESIZE, FINALIZE - version: 5, // Incremental counter (higher = newer) - data: '0x...', // Application-specific data - allocations: [...], // How funds are distributed - sigs: ['0xSig1', '0xSig2'] // Participant signatures -} -``` - -**Key rule**: A higher version number always supersedes a lower one, regardless of allocations. - ---- - -### Allocation - -An **Allocation** specifies how funds should be distributed: - -```typescript -{ - destination: '0xAlice', // Recipient address - token: '0xUSDC_CONTRACT', // Token contract - amount: 50000000n // Amount in smallest unit (6 decimals for USDC) -} -``` - -The sum of allocations represents the total funds in the channel. - ---- - -### Clearnode - -A **Clearnode** is operated by independent node operators using open-source software developed and maintained by Layer3 Fintech Ltd. It is the off-chain service that: - -1. **Manages the Nitro RPC protocol** for state channel operations -2. **Provides unified balance** aggregated across multiple chains -3. **Coordinates channels** between users -4. **Hosts app sessions** for multi-party applications - -**Think of it as**: A service node that acts as your entry point to Yellow Network—operated independently, but trustless because of on-chain guarantees. - ---- - -### Unified Balance - -Your **unified balance** is the aggregation of funds across all chains where you have deposits: - -``` -Polygon: 50 USDC ┐ -Base: 30 USDC ├─→ Unified Balance: 100 USDC -Arbitrum: 20 USDC ┘ -``` - -You can: -- Transfer from unified balance instantly (off-chain) -- Withdraw to any supported chain -- Lock funds into app sessions - ---- - -### App Session - -An **App Session** is an off-chain channel built on top of the unified balance for multi-party applications: - -```typescript -{ - protocol: 'NitroRPC/0.4', - participants: ['0xAlice', '0xBob', '0xJudge'], - weights: [40, 40, 50], // Voting power - quorum: 80, // Required weight for state updates - challenge: 3600, // Dispute window - nonce: 1699123456789 -} -``` - -**Use cases**: Games, prediction markets, escrow, any multi-party coordination. - ---- - -### Session Key - -A **session key** is a temporary cryptographic key that: - -- Is generated locally on your device -- Has limited permissions and spending caps -- Expires after a specified time -- Allows gasless signing without wallet prompts - -**Flow**: -1. Generate session keypair locally -2. Main wallet authorizes the session key (one-time EIP-712 signature) -3. All subsequent operations use the session key -4. Session expires or can be revoked - ---- - -## Protocol Components - -### VirtualApp - -**VirtualApp** is the on-chain smart contract protocol: - -- Defines channel data structures -- Implements create, close, challenge, resize operations -- Provides cryptographic verification -- Currently version 0.5.0 - ---- - -### Nitro RPC - -**Nitro RPC** is the off-chain communication protocol: - -- Compact JSON array format for efficiency -- Every message is cryptographically signed -- Bidirectional real-time communication -- Currently version 0.4 - -**Message format**: -```javascript -[requestId, method, params, timestamp] - -// Example -[42, "transfer", {"destination": "0x...", "amount": "50.0"}, 1699123456789] -``` - ---- - -### Custody Contract - -The **Custody Contract** is the main on-chain entry point: - -- Locks and unlocks participant funds -- Tracks channel status (VOID → ACTIVE → FINAL) -- Validates signatures and state transitions -- Handles dispute resolution - ---- - -### Adjudicator - -An **Adjudicator** defines rules for valid state transitions: - -| Type | Rule | -|------|------| -| **SimpleConsensus** | Both participants must sign (default) | -| **Remittance** | Only sender must sign | -| **Custom** | Application-specific logic | - ---- - -## State Lifecycle - -### Channel States - -```mermaid -stateDiagram-v2 - [*] --> VOID: Channel doesn't exist - VOID --> ACTIVE: create() - ACTIVE --> ACTIVE: Off-chain updates - ACTIVE --> DISPUTE: challenge() - ACTIVE --> FINAL: close() - DISPUTE --> ACTIVE: checkpoint() - DISPUTE --> FINAL: Timeout - FINAL --> [*]: Deleted -``` - -| Status | Meaning | -|--------|---------| -| **VOID** | Channel doesn't exist on-chain | -| **INITIAL** | Created, waiting for all participants (legacy) | -| **ACTIVE** | Fully operational, off-chain updates happening | -| **DISPUTE** | Challenge period active, parties can submit newer states | -| **FINAL** | Closed, funds distributed, metadata deleted | - ---- - -### State Intents - -| Intent | When Used | Purpose | -|--------|-----------|---------| -| **INITIALIZE** | `create()` | First state when opening channel | -| **OPERATE** | Off-chain updates | Normal operation, redistribution | -| **RESIZE** | `resize()` | Add or remove funds | -| **FINALIZE** | `close()` | Final state for cooperative closure | - ---- - -## Security Concepts - -### Challenge Period - -When a dispute arises: - -1. Party A submits their latest state via `challenge()` -2. **Challenge period** starts (typically 24 hours) -3. Party B can submit a newer valid state via `checkpoint()` -4. If no newer state, Party A's state becomes final after timeout - -**Purpose**: Gives honest parties time to respond to incorrect claims. - ---- - -### Signatures - -Two contexts for signatures: - -| Context | Hash Method | Signed By | -|---------|-------------|-----------| -| **On-chain** | Raw `packedState` (no prefix) | Main wallet | -| **Off-chain RPC** | JSON payload hash | Session key | - -**On-chain packedState**: -```javascript -keccak256(abi.encode(channelId, intent, version, data, allocations)) -``` - ---- - -### Quorum - -For app sessions, **quorum** defines the minimum voting weight required for state updates: - -``` -Participants: [Alice, Bob, Judge] -Weights: [40, 40, 50] -Quorum: 80 - -Valid combinations: -- Alice + Bob = 80 ✓ -- Alice + Judge = 90 ✓ -- Bob + Judge = 90 ✓ -- Alice alone = 40 ✗ -``` - ---- - -## Quick Reference Table - -| Term | One-Line Definition | -|------|---------------------| -| **State Channel** | Off-chain execution backed by on-chain funds | -| **Clearnode** | Off-chain service coordinating state channels | -| **Unified Balance** | Aggregated funds across all chains | -| **App Session** | Multi-party application channel | -| **Session Key** | Temporary key with limited permissions | -| **Challenge Period** | Dispute resolution window | -| **Quorum** | Minimum signature weight for approval | -| **Allocation** | Fund distribution specification | -| **packedState** | Canonical payload for signing | - ---- - -## Next Steps - -Now that you understand the vocabulary, continue to: - -- **[State Channels vs L1/L2](../core-concepts/state-channels-vs-l1-l2.mdx)** — Deep comparison with other scaling solutions -- **[App Sessions](../core-concepts/app-sessions.mdx)** — Multi-party application patterns -- **[Session Keys](../core-concepts/session-keys.mdx)** — Authentication and security - -For complete definitions, see the **[Glossary](/docs/protocol/glossary.mdx)**. \ No newline at end of file diff --git a/docs/learn/getting-started/prerequisites.mdx b/docs/learn/getting-started/prerequisites.mdx deleted file mode 100644 index 28f64f6..0000000 --- a/docs/learn/getting-started/prerequisites.mdx +++ /dev/null @@ -1,383 +0,0 @@ ---- -sidebar_position: 2 -title: Prerequisites & Environment -description: Set up your development environment for building Yellow Apps -keywords: [prerequisites, setup, development, environment, Node.js, viem] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Prerequisites & Environment - -In this guide, you will set up a complete development environment for building applications on Yellow Network. - -**Goal**: Have a working local environment ready for Yellow App development. - ---- - -## System Requirements - -| Requirement | Minimum | Recommended | -|-------------|---------|-------------| -| **Node.js** | 18.x | 20.x or later | -| **npm/yarn/pnpm** | Latest stable | Latest stable | -| **Operating System** | macOS, Linux, Windows | macOS, Linux | - ---- - -## Required Knowledge - -Before building on Yellow Network, you should be comfortable with: - -| Topic | Why It Matters | -|-------|----------------| -| **JavaScript/TypeScript** | SDK and examples are in TypeScript | -| **Async/await patterns** | All network operations are asynchronous | -| **Basic Web3 concepts** | Wallets, transactions, signatures | -| **ERC-20 tokens** | Fund management involves token operations | - -:::tip New to Web3? -If you're new to blockchain development, start with the [Ethereum Developer Documentation](https://ethereum.org/developers) to understand wallets, transactions, and smart contract basics. -::: - ---- - -## Step 1: Install Node.js - -### macOS (using Homebrew) - -```bash -# Install Homebrew if you don't have it -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - -# Install Node.js -brew install node@20 - -# Verify installation -node --version # Should show v20.x.x -npm --version # Should show 10.x.x -``` - -### Linux (Ubuntu/Debian) - -```bash -# Install Node.js via NodeSource -curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - -sudo apt-get install -y nodejs - -# Verify installation -node --version -npm --version -``` - -### Windows - -Download and run the installer from [nodejs.org](https://nodejs.org/). - ---- - -## Step 2: Install Core Dependencies - -Create a new project and install the required packages: - -```bash -# Create project directory -mkdir yellow-app && cd yellow-app - -# Initialize project -npm init -y - -# Install core dependencies -npm install @erc7824/nitrolite viem - -# Install development dependencies -npm install -D typescript @types/node tsx -``` - -### Package Overview - -| Package | Purpose | -|---------|---------| -| `@erc7824/nitrolite` | Yellow Network SDK for state channel operations | -| `viem` | Modern Ethereum library for wallet and contract interactions | -| `typescript` | Type safety and better developer experience | -| `tsx` | Run TypeScript files directly | - ---- - -## Step 3: Configure TypeScript - -Create `tsconfig.json`: - -```json -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "exclude": ["node_modules"] -} -``` - -Update `package.json`: - -```json -{ - "type": "module", - "scripts": { - "dev": "tsx watch src/index.ts", - "build": "tsc", - "start": "node dist/index.js" - } -} -``` - ---- - -## Step 4: Set Up Environment Variables - -Create `.env` for sensitive configuration: - -```bash -# .env - Never commit this file! - -# Your wallet private key (for development only) -PRIVATE_KEY=0x... - -# RPC endpoints -SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY -BASE_RPC_URL=https://base-sepolia.g.alchemy.com/v2/YOUR_KEY - -# Clearnode WebSocket endpoint -# Production: wss://clearnet.yellow.com/ws -# Sandbox: wss://clearnet-sandbox.yellow.com/ws -CLEARNODE_WS_URL=wss://clearnet-sandbox.yellow.com/ws -``` - -Add to `.gitignore`: - -```bash -# .gitignore -.env -.env.local -node_modules/ -dist/ -``` - -Install dotenv for loading environment variables: - -```bash -npm install dotenv -``` - ---- - -## Step 5: Wallet Setup - -### Development Wallet - -For development, create a dedicated wallet: - -```typescript -// scripts/create-wallet.ts -import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; - -const privateKey = generatePrivateKey(); -const account = privateKeyToAccount(privateKey); - -console.log('New Development Wallet'); -console.log('----------------------'); -console.log('Address:', account.address); -console.log('Private Key:', privateKey); -console.log('\n⚠️ Save this private key securely and add to .env'); -``` - -Run it: - -```bash -npx tsx scripts/create-wallet.ts -``` - -### Get Test Tokens - -#### Yellow Network Sandbox Faucet (Recommended) - -For testing on the Yellow Network Sandbox, you can request test tokens directly to your unified balance: - -```bash -curl -XPOST https://clearnet-sandbox.yellow.com/faucet/requestTokens \ - -H "Content-Type: application/json" \ - -d '{"userAddress":""}' -``` - -Replace `` with your actual wallet address. - -:::tip No On-Chain Operations Needed -Test tokens (ytest.USD) are credited directly to your unified balance on the Sandbox Clearnode. No deposit or channel operations are required—you can start transacting immediately! -::: - -#### Testnet Faucets (For On-Chain Testing) - -If you need on-chain test tokens for Sepolia or Base Sepolia: - -| Network | Faucet | -|---------|--------| -| Sepolia | [sepoliafaucet.com](https://sepoliafaucet.com) | -| Base Sepolia | [base.org/faucet](https://www.coinbase.com/faucets/base-ethereum-goerli-faucet) | - -:::warning Development Only -Never use your main wallet or real funds for development. Always create a separate development wallet with test tokens. -::: - ---- - -## Step 6: Verify Setup - -Create `src/index.ts` to verify everything works: - -```typescript -import 'dotenv/config'; -import { createPublicClient, http } from 'viem'; -import { sepolia } from 'viem/chains'; -import { privateKeyToAccount } from 'viem/accounts'; - -async function main() { - // Verify environment variables - const privateKey = process.env.PRIVATE_KEY; - if (!privateKey) { - throw new Error('PRIVATE_KEY not set in .env'); - } - - // Create account from private key - const account = privateKeyToAccount(privateKey as `0x${string}`); - console.log('✓ Wallet loaded:', account.address); - - // Create public client - const client = createPublicClient({ - chain: sepolia, - transport: http(process.env.SEPOLIA_RPC_URL), - }); - - // Check connection - const blockNumber = await client.getBlockNumber(); - console.log('✓ Connected to Sepolia, block:', blockNumber); - - // Check balance - const balance = await client.getBalance({ address: account.address }); - console.log('✓ ETH balance:', balance.toString(), 'wei'); - - console.log('\n🎉 Environment setup complete!'); -} - -main().catch(console.error); -``` - -Run the verification: - -```bash -npm run dev -``` - -Expected output: - -``` -✓ Wallet loaded: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb -✓ Connected to Sepolia, block: 12345678 -✓ ETH balance: 100000000000000000 wei - -🎉 Environment setup complete! -``` - ---- - -## Project Structure - -Recommended folder structure for Yellow Apps: - -``` -yellow-app/ -├── src/ -│ ├── index.ts # Entry point -│ ├── config.ts # Configuration -│ ├── client.ts # VirtualApp client setup -│ ├── auth.ts # Authentication logic -│ └── channels/ -│ ├── create.ts # Channel creation -│ ├── transfer.ts # Transfer operations -│ └── close.ts # Channel closure -├── scripts/ -│ └── create-wallet.ts # Utility scripts -├── .env # Environment variables (git-ignored) -├── .gitignore -├── package.json -└── tsconfig.json -``` - ---- - -## Supported Networks - -To get the current list of supported chains and contract addresses, query the Clearnode's `get_config` endpoint: - -```javascript -// Example: Fetch supported chains and contract addresses -const ws = new WebSocket('wss://clearnet-sandbox.yellow.com/ws'); - -ws.onopen = () => { - const request = { - req: [1, 'get_config', {}, Date.now()], - sig: [] // get_config is a public endpoint, no signature required - }; - ws.send(JSON.stringify(request)); -}; - -ws.onmessage = (event) => { - const response = JSON.parse(event.data); - console.log('Supported chains:', response.res[2].chains); - console.log('Contract addresses:', response.res[2].contracts); -}; -``` - -:::tip Dynamic Configuration -The `get_config` method returns real-time information about supported chains, contract addresses, and Clearnode capabilities. This ensures you always have the most up-to-date network information. -::: - ---- - -## Next Steps - -Your environment is ready! Continue to: - -- **[Key Terms & Mental Models](./key-terms.mdx)** — Understand the core concepts -- **[Quickstart](./quickstart.mdx)** — Build your first Yellow App -- **[State Channels vs L1/L2](../core-concepts/state-channels-vs-l1-l2.mdx)** — Deep dive into state channels - ---- - -## Common Issues - -### "Module not found" errors -Ensure you have `"type": "module"` in `package.json` and are using ESM imports. - -### "Cannot find module 'viem'" -Run `npm install` to ensure all dependencies are installed. - -### RPC rate limiting -Use a dedicated RPC provider (Infura, Alchemy) instead of public endpoints for production. - -### TypeScript errors with viem -Ensure your `tsconfig.json` has `"moduleResolution": "bundler"` or `"node16"`. - - diff --git a/docs/learn/getting-started/quickstart.mdx b/docs/learn/getting-started/quickstart.mdx deleted file mode 100644 index c03ea1d..0000000 --- a/docs/learn/getting-started/quickstart.mdx +++ /dev/null @@ -1,1081 +0,0 @@ ---- -title: Quickstart -description: Get up and running with the Yellow Network SDK in minutes. ---- - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Quickstart Guide - -This guide provides a step-by-step walkthrough of integrating with the Yellow Network using the VirtualApp SDK. We will build a script to connect to the network, authenticate, manage state channels, and transfer funds. - -## Prerequisites - -- [Node.js](https://nodejs.org/) (v18 or higher) -- [npm](https://www.npmjs.com/) - -## Setup - -1. **Install Dependencies** - - ```bash - npm install - ``` - -2. **Environment Variables** - - Create a `.env` file in your project root: - - ```bash - # .env - PRIVATE_KEY=your_sepolia_private_key_here - ALCHEMY_RPC_URL=your_alchemy_rpc_url_here - ``` - -## 1. Getting Funds - -Before we write code, you need test tokens (`ytest.usd`). In the Sandbox, these tokens land in your **Unified Balance** (Off-Chain), which sits in the Yellow Network's clearing layer. - -Request tokens via the Faucet: - -```bash -curl -XPOST https://clearnet-sandbox.yellow.com/faucet/requestTokens \ - -H "Content-Type: application/json" \ - -d '{"userAddress":""}' -``` - -## 2. Initialization - -First, we setup the `VirtualAppClient` with Viem. This client handles all communication with the Yellow Network nodes and smart contracts. - -```typescript -import { NitroliteClient, WalletStateSigner, createECDSAMessageSigner } from '@erc7824/nitrolite'; -import { createPublicClient, createWalletClient, http } from 'viem'; -import { sepolia } from 'viem/chains'; -import { privateKeyToAccount } from 'viem/accounts'; -import WebSocket from 'ws'; -import 'dotenv/config'; - -// Setup Viem Clients -const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); -const publicClient = createPublicClient({ chain: sepolia, transport: http(process.env.ALCHEMY_RPC_URL) }); -const walletClient = createWalletClient({ chain: sepolia, transport: http(), account }); - -// Initialize VirtualApp Client -const client = new VirtualAppClient({ - publicClient, - walletClient, - stateSigner: new WalletStateSigner(walletClient), - addresses: { - custody: '0x019B65A265EB3363822f2752141b3dF16131b262', - adjudicator: '0x7c7ccbc98469190849BCC6c926307794fDfB11F2', - }, - chainId: sepolia.id, - challengeDuration: 3600n, -}); - -// Connect to Sandbox Node -const ws = new WebSocket('wss://clearnet-sandbox.yellow.com/ws'); -``` - -## 3. Authentication - -Authentication involves generating a temporary **Session Key** and verifying your identity using your main wallet (EIP-712). - -```typescript -// Generate temporary session key -const sessionPrivateKey = generatePrivateKey(); -const sessionSigner = createECDSAMessageSigner(sessionPrivateKey); -const sessionAccount = privateKeyToAccount(sessionPrivateKey); - -// Send auth request -const authRequestMsg = await createAuthRequestMessage({ - address: account.address, - application: 'Test app', - session_key: sessionAccount.address, - allowances: [{ asset: 'ytest.usd', amount: '1000000000' }], - expires_at: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour - scope: 'test.app', -}); -ws.send(authRequestMsg); - -// Handle Challenge (in ws.onmessage) -if (type === 'auth_challenge') { - const challenge = response.res[2].challenge_message; - // Sign with MAIN wallet - const signer = createEIP712AuthMessageSigner(walletClient, authParams, { name: 'Test app' }); - const verifyMsg = await createAuthVerifyMessageFromChallenge(signer, challenge); - ws.send(verifyMsg); -} -``` - -## 4. Channel Lifecycle - -### Creating a Channel - -If no channel exists, we request the Node to open one. - -```typescript -const createChannelMsg = await createCreateChannelMessage( - sessionSigner, // Sign with session key - { - chain_id: 11155111, // Sepolia - token: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', // ytest.usd - } -); -ws.send(createChannelMsg); - -// Listen for 'create_channel' response, then submit to chain -const createResult = await client.createChannel({ - channel, - unsignedInitialState, - serverSignature, -}); -``` - -### Funding (Resizing) - -To fund the channel, we perform a "Resize". Since your funds are in your **Unified Balance** (from the Faucet), we use `allocate_amount` to move them into the Channel. - -> **Important:** Do NOT use `resize_amount` unless you have deposited funds directly into the L1 Custody Contract. - -```typescript -const resizeMsg = await createResizeChannelMessage( - sessionSigner, - { - channel_id: channelId, - allocate_amount: 20n, // Moves 20 units from Unified Balance -> Channel - funds_destination: account.address, - } -); -ws.send(resizeMsg); - -// Submit resize proof to chain -await client.resizeChannel({ resizeState, proofStates }); -``` - -### Closing & Withdrawing - -Finally, we cooperatively close the channel. This settles the balance on the L1 Custody Contract, allowing you to withdraw. - -```typescript -// Close Channel -const closeMsg = await createCloseChannelMessage(sessionSigner, channelId, account.address); -ws.send(closeMsg); - -// Submit close to chain -await client.closeChannel({ finalState, stateData }); - -// Withdraw from Custody Contract to Wallet -const withdrawalTx = await client.withdrawal(tokenAddress, withdrawableBalance); -console.log('Funds withdrawn:', withdrawalTx); -``` - -## Troubleshooting - -Here are common issues and solutions: - -- **`InsufficientBalance`**: - - **Cause**: Trying to use `resize_amount` (L1 funds) without depositing first. - - **Fix**: Use `allocate_amount` to fund from your Off-chain Unified Balance (Faucet). - -- **`DepositAlreadyFulfilled`**: - - **Cause**: Double-submitting a funding request or channel creation. - - **Fix**: Check if the channel is already open or funded before sending requests. - -- **`InvalidState`**: - - **Cause**: Resizing a closed channel or version mismatch. - - **Fix**: Ensure you are using the latest channel state from the Node. - -- **`operation denied: non-zero allocation`**: - - **Cause**: Too many "stale" channels open. - - **Fix**: Run the cleanup script `npx tsx close_all.ts`. - -- **Timeout waiting for User to fund Custody**: - - **Cause**: Re-running scripts without closing channels accumulates balance requirements. - - **Fix**: Run `close_all.ts` to reset. - -### Cleanup Script - -If you get stuck, use this script to close all open channels: - -```bash -npx tsx close_all.ts -``` - -## Complete Code - -### index.ts - -
-Click to view full index.ts - -```typescript -import { - VirtualAppClient, - WalletStateSigner, - createTransferMessage, - createGetConfigMessage, - createECDSAMessageSigner, - createEIP712AuthMessageSigner, - createAuthVerifyMessageFromChallenge, - createCreateChannelMessage, - createResizeChannelMessage, - createGetLedgerBalancesMessage, - createAuthRequestMessage, - createCloseChannelMessage -} from '@erc7824/nitrolite'; -import type { - RPCNetworkInfo, - RPCAsset, - RPCData -} from '@erc7824/nitrolite'; -import { createPublicClient, createWalletClient, http } from 'viem'; -import { sepolia } from 'viem/chains'; -import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts'; -import WebSocket from 'ws'; -import 'dotenv/config'; -import * as readline from 'readline'; - -console.log('Starting script...'); - -// Helper to prompt for input -const askQuestion = (query: string): Promise => { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - return new Promise(resolve => rl.question(query, ans => { - rl.close(); - resolve(ans); - })); -}; - -// Your wallet private key (use environment variables in production!) -let PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`; - -if (!PRIVATE_KEY) { - console.log('PRIVATE_KEY not found in .env'); - const inputKey = await askQuestion('Please enter your Private Key: '); - if (!inputKey) { - throw new Error('Private Key is required'); - } - PRIVATE_KEY = inputKey.startsWith('0x') ? inputKey as `0x${string}` : `0x${inputKey}` as `0x${string}`; -} - -const account = privateKeyToAccount(PRIVATE_KEY); - -// Create viem clients -const ALCHEMY_RPC_URL = process.env.ALCHEMY_RPC_URL; -const FALLBACK_RPC_URL = 'https://1rpc.io/sepolia'; // Public fallback - -const publicClient = createPublicClient({ - chain: sepolia, - transport: http(ALCHEMY_RPC_URL || FALLBACK_RPC_URL), -}); - -const walletClient = createWalletClient({ - chain: sepolia, - transport: http(), - account, -}); - -interface Config { - assets?: RPCAsset[]; - networks?: RPCNetworkInfo[]; - [key: string]: any; -} - -async function fetchConfig(): Promise { - const signer = createECDSAMessageSigner(PRIVATE_KEY); - const message = await createGetConfigMessage(signer); - - const ws = new WebSocket('wss://clearnet-sandbox.yellow.com/ws'); - - return new Promise((resolve, reject) => { - ws.onopen = () => { - ws.send(message); - }; - - ws.onmessage = (event) => { - try { - const response = JSON.parse(event.data.toString()); - // Response format: [requestId, method, result, timestamp] - // or VirtualAppRPCMessage structure depending on implementation - // Based on types: VirtualAppRPCMessage { res: RPCData } - // RPCData: [RequestID, RPCMethod, object, Timestamp?] - - if (response.res && response.res[2]) { - resolve(response.res[2] as Config); - ws.close(); - } else if (response.error) { - reject(new Error(response.error.message || 'Unknown RPC error')); - ws.close(); - } - } catch (err) { - reject(err); - ws.close(); - } - }; - - ws.onerror = (error) => { - reject(error); - ws.close(); - }; - }); -} - -// Initialize VirtualApp client -console.log('Fetching configuration...'); -const config = await fetchConfig(); -console.log('Configuration fetched. Assets count:', config.assets?.length); - -const client = new VirtualAppClient({ - publicClient, - walletClient, - // Use WalletStateSigner for signing states - stateSigner: new WalletStateSigner(walletClient), - // Contract addresses - addresses: { - custody: '0x019B65A265EB3363822f2752141b3dF16131b262', - adjudicator: '0x7c7ccbc98469190849BCC6c926307794fDfB11F2', - }, - chainId: sepolia.id, - challengeDuration: 3600n, // 1 hour challenge period -}); - -console.log('✓ Client initialized'); -console.log(' Wallet Address:', account.address); -console.log(' (Please ensure this address has Sepolia ETH)'); - -// Connect to Clearnode WebSocket (using sandbox for testing) -const ws = new WebSocket('wss://clearnet-sandbox.yellow.com/ws'); - -// Step 1: Generate session keypair locally -const sessionPrivateKey = generatePrivateKey(); -const sessionAccount = privateKeyToAccount(sessionPrivateKey); -const sessionAddress = sessionAccount.address; - -// Helper: Create a signer for the session key -const sessionSigner = createECDSAMessageSigner(sessionPrivateKey); - -// Step 2: Send auth_request -const authParams = { - session_key: sessionAddress, // Session key you generated - allowances: [{ // Add allowance for ytest.usd - asset: 'ytest.usd', - amount: '1000000000' // Large amount - }], - expires_at: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour in seconds - scope: 'test.app', -}; - -const authRequestMsg = await createAuthRequestMessage({ - address: account.address, // Your main wallet address - application: 'Test app', // Match domain name - ...authParams -}); - -// We need to capture channelId to close it. -let activeChannelId: string | undefined; - -// Helper function to trigger resize -const triggerResize = async (channelId: string, token: string, skipResize: boolean = false) => { - console.log(' Using existing channel:', channelId); - - // Add delay to ensure Node indexes the channel - console.log(' Waiting 5s for Node to index channel...'); - await new Promise(resolve => setTimeout(resolve, 5000)); - - // For withdrawal, we don't need to check user balance or allowance - // because the Node (counterparty) is the one depositing funds. - - - // For withdrawal, we don't deposit (we are withdrawing off-chain funds). - // ------------------------------------------------------------------- - // 3. Fund Channel (Resize) - // ------------------------------------------------------------------- - // We use 'allocate_amount' to move funds from the User's Unified Balance (off-chain) - // into the Channel. This assumes the user has funds in their Unified Balance (e.g. from faucet). - - const amountToFund = 20n; - if (!skipResize) console.log('\nRequesting resize to fund channel with 20 tokens...'); - - if (!skipResize) { - const resizeMsg = await createResizeChannelMessage( - sessionSigner, - { - channel_id: channelId as `0x${string}`, - // resize_amount: 10n, // <-- This requires L1 funds in Custody (which we don't have) - allocate_amount: amountToFund, // <-- This pulls from Unified Balance (Faucet) (Variable name adjusted) - funds_destination: account.address, - } - ); - - ws.send(resizeMsg); - - // Wait for resize confirmation - console.log(' Waiting for resize confirmation...'); - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('Resize timeout')), 30000); - const handler = (data: any) => { - const msg = JSON.parse(data.toString()); - if (msg.res && msg.res[1] === 'resize_channel') { - const payload = msg.res[2]; - if (payload.channel_id === channelId) { - clearTimeout(timeout); - ws.off('message', handler); - resolve(); - } - } - }; - ws.on('message', handler); - }); - - // Wait for balance update - await new Promise(r => setTimeout(r, 2000)); - console.log('✓ Resize complete.'); - } else { - console.log(' Skipping resize step (already funded).'); - } - - // Verify Channel Balance - const channelBalances = await publicClient.readContract({ - address: client.addresses.custody, - abi: [{ - name: 'getChannelBalances', - type: 'function', - stateMutability: 'view', - inputs: [{ name: 'channelId', type: 'bytes32' }, { name: 'tokens', type: 'address[]' }], - outputs: [{ name: 'balances', type: 'uint256[]' }] - }], - functionName: 'getChannelBalances', - args: [channelId as `0x${string}`, [token as `0x${string}`]], - }) as bigint[]; - console.log(`✓ Channel funded with ${channelBalances[0]} USDC`); - - // Check User Balance again - let finalUserBalance = 0n; - try { - const result = await publicClient.readContract({ - address: client.addresses.custody, - abi: [{ - type: 'function', - name: 'getAccountsBalances', - inputs: [{ name: 'users', type: 'address[]' }, { name: 'tokens', type: 'address[]' }], - outputs: [{ type: 'uint256[]' }], - stateMutability: 'view' - }] as const, - functionName: 'getAccountsBalances', - args: [[client.account.address], [token as `0x${string}`]], - }) as bigint[]; - finalUserBalance = result[0]; - console.log(`✓ User Custody Balance after resize: ${finalUserBalance}`); - } catch (e) { - console.warn(' Error checking final user balance:', e); - } - - // ------------------------------------------------------------------- - // 4. Off-Chain Transfer - // ------------------------------------------------------------------- -}; - -// State to prevent infinite auth loops -let isAuthenticated = false; - -// Step 3: Sign the challenge with your MAIN wallet (EIP-712) -ws.onmessage = async (event) => { - const response = JSON.parse(event.data.toString()); - console.log('Received WS message:', JSON.stringify(response, null, 2)); - - if (response.error) { - console.error('RPC Error:', response.error); - process.exit(1); // Exit on error to prevent infinite loops - } - - if (response.res && response.res[1] === 'auth_challenge') { - if (isAuthenticated) { - console.log(' Ignoring auth_challenge (already authenticated)'); - return; - } - - const challenge = response.res[2].challenge_message; - - // Create EIP-712 typed data signature with main wallet - const signer = createEIP712AuthMessageSigner( - walletClient, - authParams, - { name: 'Test app' } - ); - - // Send auth_verify using builder - // We sign with the MAIN wallet for the first verification - const verifyMsg = await createAuthVerifyMessageFromChallenge( - signer, - challenge - ); - - ws.send(verifyMsg); - } - - if (response.res && response.res[1] === 'auth_verify') { - console.log('✓ Authenticated successfully'); - isAuthenticated = true; // Mark as authenticated - const sessionKey = response.res[2].session_key; - console.log(' Session key:', sessionKey); - console.log(' JWT token received'); - - // Query Ledger Balances - const ledgerMsg = await createGetLedgerBalancesMessage( - sessionSigner, - account.address, - Date.now() - ); - ws.send(ledgerMsg); - console.log(' Sent get_ledger_balances request...'); - - // Wait for 'channels' message to proceed - - } - - if (response.res && response.res[1] === 'channels') { - const channels = response.res[2].channels; - const openChannel = channels.find((c: any) => c.status === 'open'); - - // Derive token - const chainId = sepolia.id; - const supportedAsset = (config.assets as any)?.find((a: any) => a.chain_id === chainId); - const token = supportedAsset ? supportedAsset.token : '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238'; - - if (openChannel) { - console.log('✓ Found existing open channel'); - - // CORRECT: Check if channel is already funded - const currentAmount = BigInt(openChannel.amount || 0); // Need to parse amount - // Wait, standard RPC returns strings. Let's rely on openChannel structure. - // openChannel object from logs: { ..., amount: "40", ... } - - if (BigInt(openChannel.amount) >= 20n) { - console.log(` Channel already funded with ${openChannel.amount} USDC.`); - console.log(' Skipping resize to avoid "Insufficient Balance" errors.'); - // Call triggerResize but indicate skipping actual resize - await triggerResize(openChannel.channel_id, token, true); - } else { - await triggerResize(openChannel.channel_id, token, false); - } - } else { - console.log(' No existing open channel found, creating new one...'); - console.log(' Using token:', token, 'for chain:', chainId); - - // Request channel creation - const createChannelMsg = await createCreateChannelMessage( - sessionSigner, - { - chain_id: 11155111, // Sepolia - token: token, - } - ); - ws.send(createChannelMsg); - } - } - - if (response.res && response.res[1] === 'create_channel') { - const { channel_id, channel, state, server_signature } = response.res[2]; - activeChannelId = channel_id; - - console.log('✓ Channel prepared:', channel_id); - console.log(' State object:', JSON.stringify(state, null, 2)); - - // Transform state object to match UnsignedState interface - const unsignedInitialState = { - intent: state.intent, - version: BigInt(state.version), - data: state.state_data, // Map state_data to data - allocations: state.allocations.map((a: any) => ({ - destination: a.destination, - token: a.token, - amount: BigInt(a.amount), - })), - }; - - // Submit to blockchain - const createResult = await client.createChannel({ - channel, - unsignedInitialState, - serverSignature: server_signature, - }); - - // createChannel returns an object { txHash, ... } or just hash depending on version. - // Based on logs: { channelId: ..., initialState: ..., txHash: ... } - // We need to handle both or just the object. - const txHash = typeof createResult === 'string' ? createResult : createResult.txHash; - - console.log('✓ Channel created on-chain:', txHash); - console.log(' Waiting for transaction confirmation...'); - await publicClient.waitForTransactionReceipt({ hash: txHash }); - console.log('✓ Transaction confirmed'); - - // Retrieve token from allocations - - const token = state.allocations[0].token; - await triggerResize(channel_id, token, false); - } - - if (response.res && response.res[1] === 'resize_channel') { - const { channel_id, state, server_signature } = response.res[2]; - - console.log('✓ Resize prepared'); - console.log(' Server returned allocations:', JSON.stringify(state.allocations, null, 2)); - - // Construct the resize state object expected by the SDK - const resizeState = { - intent: state.intent, - version: BigInt(state.version), - data: state.state_data || state.data, // Handle potential naming differences - allocations: state.allocations.map((a: any) => ({ - destination: a.destination, - token: a.token, - amount: BigInt(a.amount), - })), - channelId: channel_id, - serverSignature: server_signature, - }; - - console.log('DEBUG: resizeState:', JSON.stringify(resizeState, (key, value) => - typeof value === 'bigint' ? value.toString() : value, 2)); - - let proofStates: any[] = []; - try { - const onChainData = await client.getChannelData(channel_id as `0x${string}`); - console.log('DEBUG: On-chain channel data:', JSON.stringify(onChainData, (key, value) => - typeof value === 'bigint' ? value.toString() : value, 2)); - if (onChainData.lastValidState) { - proofStates = [onChainData.lastValidState]; - } - } catch (e) { - console.log('DEBUG: Failed to fetch on-chain data:', e); - } - - // Calculate total required for the token - const token = resizeState.allocations[0].token; - const requiredAmount = resizeState.allocations.reduce((sum: bigint, a: any) => { - if (a.token === token) return sum + BigInt(a.amount); - return sum; - }, 0n); - - console.log(` Waiting for channel funding (Required: ${requiredAmount})...`); - - // Poll for User's Custody Balance (since User allocation is increasing) - let userBalance = 0n; - let retries = 0; - const userAddress = client.account.address; - - console.log(` Checking User Custody Balance for ${userAddress}... [v2]`); - - // Check initial balance first - try { - const result = await publicClient.readContract({ - address: client.addresses.custody, - abi: [ - { - type: 'function', - name: 'getAccountsBalances', - inputs: [ - { name: 'users', type: 'address[]' }, - { name: 'tokens', type: 'address[]' } - ], - outputs: [{ type: 'uint256[]' }], - stateMutability: 'view' - } - ] as const, - functionName: 'getAccountsBalances', - args: [[userAddress], [token as `0x${string}`]], - }) as bigint[]; - userBalance = result[0]; - } catch (e) { - console.warn(' Error checking initial user balance:', e); - } - - console.log(' Skipping L1 deposit (using off-chain faucet funds)...'); - - if (true) { // Skip the wait loop as we just deposited - // Define ABI fragment for getAccountsBalances - const custodyAbiFragment = [ - { - type: 'function', - name: 'getAccountsBalances', - inputs: [ - { name: 'users', type: 'address[]' }, - { name: 'tokens', type: 'address[]' } - ], - outputs: [{ type: 'uint256[]' }], - stateMutability: 'view' - } - ] as const; - - while (retries < 30) { // Wait up to 60 seconds - try { - const result = await publicClient.readContract({ - address: client.addresses.custody, - abi: custodyAbiFragment, - functionName: 'getAccountsBalances', - args: [[userAddress], [token as `0x${string}`]], - }) as bigint[]; - - userBalance = result[0]; - } catch (e) { - console.warn(' Error checking user balance:', e); - } - - if (userBalance >= requiredAmount) { - console.log(`✓ User funded in Custody (Balance: ${userBalance})`); - break; - } - await new Promise(r => setTimeout(r, 2000)); - retries++; - if (retries % 5 === 0) console.log(` User Custody Balance: ${userBalance}, Waiting...`); - } - - if (userBalance < requiredAmount) { - console.error('Timeout waiting for User to fund Custody account'); - console.warn('Proceeding with resize despite low user balance...'); - } - } else { - console.log(`✓ User funded in Custody (Balance: ${userBalance})`); - } - - console.log(' Submitting resize to chain...'); - // Submit to blockchain - const { txHash } = await client.resizeChannel({ - resizeState, - proofStates: proofStates, - }); - - console.log('✓ Channel resized on-chain:', txHash); - console.log('✓ Channel funded with 20 USDC'); - - // Skip Transfer for debugging - console.log(' Skipping transfer to verify withdrawal amount...'); - console.log(' Debug: channel_id =', channel_id); - - // Wait for server to sync state - await new Promise(r => setTimeout(r, 3000)); - - if (channel_id) { - console.log(' Closing channel:', channel_id); - const closeMsg = await createCloseChannelMessage( - sessionSigner, - channel_id as `0x${string}`, - account.address - ); - ws.send(closeMsg); - } else { - console.log(' No channel ID available to close.'); - } - } - // const secondaryAddress = '0x7df1fef832b57e46de2e1541951289c04b2781aa'; - // console.log(` Attempting Transfer to Secondary Wallet: ${secondaryAddress}...`); - - // const transferMsg = await createTransferMessage( - // sessionSigner, - // { - // destination: secondaryAddress, - // allocations: [{ - // asset: 'ytest.usd', - // amount: '10' - // }] - // }, - // Date.now() - // ); - // ws.send(transferMsg); - // console.log(' Sent transfer request...'); - - // if (response.res && response.res[1] === 'transfer') { - // console.log('✓ Transfer complete!'); - // console.log(' Amount: 10 USDC'); - - // if (activeChannelId) { - // console.log(' Closing channel:', activeChannelId); - // const closeMsg = await createCloseChannelMessage( - // sessionSigner, - // activeChannelId as `0x${string}`, - // account.address - // ); - // ws.send(closeMsg); - // } else { - // console.log(' No active channel ID to close.'); - // } - // } - - if (response.res && response.res[1] === 'close_channel') { - const { channel_id, state, server_signature } = response.res[2]; - console.log('✓ Close prepared'); - console.log(' Submitting close to chain...'); - - // Submit to blockchain - const txHash = await client.closeChannel({ - finalState: { - intent: state.intent, - version: BigInt(state.version), - data: state.state_data || state.data, - allocations: state.allocations.map((a: any) => ({ - destination: a.destination, - token: a.token, - amount: BigInt(a.amount), - })), - channelId: channel_id, - serverSignature: server_signature, - }, - stateData: state.state_data || state.data || '0x', - }); - - console.log('✓ Channel closed on-chain:', txHash); - - // Withdraw funds - console.log(' Withdrawing funds...'); - const token = state.allocations[0].token; - - await new Promise(r => setTimeout(r, 2000)); // Wait for close to settle - - let withdrawableBalance = 0n; - try { - const result = await publicClient.readContract({ - address: client.addresses.custody, - abi: [{ - type: 'function', - name: 'getAccountsBalances', - inputs: [{ name: 'users', type: 'address[]' }, { name: 'tokens', type: 'address[]' }], - outputs: [{ type: 'uint256[]' }], - stateMutability: 'view' - }] as const, - functionName: 'getAccountsBalances', - args: [[client.account.address], [token as `0x${string}`]], - }) as bigint[]; - withdrawableBalance = result[0]; - console.log(`✓ User Custody Balance (Withdrawable): ${withdrawableBalance}`); - } catch (e) { - console.warn(' Error checking withdrawable balance:', e); - } - - if (withdrawableBalance > 0n) { - console.log(` Withdrawing ${withdrawableBalance} of ${token}...`); - const withdrawalTx = await client.withdrawal(token as `0x${string}`, withdrawableBalance); - console.log('✓ Funds withdrawn:', withdrawalTx); - } else { - console.log(' No funds to withdraw.'); - } - - process.exit(0); - } -}; - -// Start the flow -if (ws.readyState === WebSocket.OPEN) { - ws.send(authRequestMsg); -} else { - ws.on('open', () => { - ws.send(authRequestMsg); - }); -} -``` - -
- -### close_all.ts - -
-Click to view full close_all.ts - -```typescript -import { - VirtualAppClient, - WalletStateSigner, - createECDSAMessageSigner, - createEIP712AuthMessageSigner, - createAuthRequestMessage, - createAuthVerifyMessageFromChallenge, - createCloseChannelMessage, -} from '@erc7824/nitrolite'; -import { createPublicClient, createWalletClient, http } from 'viem'; -import { sepolia } from 'viem/chains'; -import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts'; -import WebSocket from 'ws'; -import 'dotenv/config'; -import * as readline from 'readline'; - -// Helper to prompt for input -const askQuestion = (query: string): Promise => { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - return new Promise(resolve => rl.question(query, ans => { - rl.close(); - resolve(ans); - })); -}; - -// Configuration -const WS_URL = 'wss://clearnet-sandbox.yellow.com/ws'; - -async function main() { - console.log('Starting cleanup script...'); - - // Setup Viem Clients - let PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`; - - if (!PRIVATE_KEY) { - console.log('PRIVATE_KEY not found in .env'); - const inputKey = await askQuestion('Please enter your Private Key: '); - if (!inputKey) { - throw new Error('Private Key is required'); - } - PRIVATE_KEY = inputKey.startsWith('0x') ? inputKey as `0x${string}` : `0x${inputKey}` as `0x${string}`; - } - - const account = privateKeyToAccount(PRIVATE_KEY); - - const ALCHEMY_RPC_URL = process.env.ALCHEMY_RPC_URL; - const FALLBACK_RPC_URL = 'https://1rpc.io/sepolia'; // Public fallback - const RPC_URL = ALCHEMY_RPC_URL || FALLBACK_RPC_URL; - const publicClient = createPublicClient({ - chain: sepolia, - transport: http(RPC_URL), - }); - const walletClient = createWalletClient({ - account, - chain: sepolia, - transport: http(RPC_URL), - }); - - // Initialize VirtualApp Client - const client = new VirtualAppClient({ - publicClient, - walletClient, - addresses: { - custody: '0x019B65A265EB3363822f2752141b3dF16131b262', - adjudicator: '0x7c7ccbc98469190849BCC6c926307794fDfB11F2', - }, - challengeDuration: 3600n, - chainId: sepolia.id, - stateSigner: new WalletStateSigner(walletClient), - }); - - // Connect to WebSocket - const ws = new WebSocket(WS_URL); - const sessionPrivateKey = generatePrivateKey(); - const sessionSigner = createECDSAMessageSigner(sessionPrivateKey); - const sessionAccount = privateKeyToAccount(sessionPrivateKey); - - await new Promise((resolve, reject) => { - ws.on('open', () => resolve()); - ws.on('error', (err) => reject(err)); - }); - console.log('✓ Connected to WebSocket'); - - // Authenticate - const authParams = { - session_key: sessionAccount.address, - allowances: [{ asset: 'ytest.usd', amount: '1000000000' }], - expires_at: BigInt(Math.floor(Date.now() / 1000) + 3600), - scope: 'test.app', - }; - - const authRequestMsg = await createAuthRequestMessage({ - address: account.address, - application: 'Test app', - ...authParams - }); - ws.send(authRequestMsg); - - ws.on('message', async (data) => { - const response = JSON.parse(data.toString()); - - if (response.res) { - const type = response.res[1]; - - if (type === 'auth_challenge') { - const challenge = response.res[2].challenge_message; - const signer = createEIP712AuthMessageSigner(walletClient, authParams, { name: 'Test app' }); - const verifyMsg = await createAuthVerifyMessageFromChallenge(signer, challenge); - ws.send(verifyMsg); - } - - if (type === 'auth_verify') { - console.log('✓ Authenticated'); - - // Fetch open channels from L1 Contract - console.log('Fetching open channels from L1...'); - try { - const openChannelsL1 = await client.getOpenChannels(); - console.log(`Found ${openChannelsL1.length} open channels on L1.`); - - if (openChannelsL1.length === 0) { - console.log('No open channels on L1 to close.'); - process.exit(0); - } - - // Iterate and close - for (const channelId of openChannelsL1) { - console.log(`Attempting to close channel ${channelId}...`); - - // Send close request to Node - const closeMsg = await createCloseChannelMessage( - sessionSigner, - channelId, - account.address - ); - ws.send(closeMsg); - - // Small delay to avoid rate limits - await new Promise(r => setTimeout(r, 500)); - } - - } catch (e) { - console.error('Error fetching L1 channels:', e); - process.exit(1); - } - } - - if (type === 'close_channel') { - const { channel_id, state, server_signature } = response.res[2]; - console.log(`✓ Node signed close for ${channel_id}`); - - const finalState = { - intent: state.intent, - version: BigInt(state.version), - data: state.state_data, - allocations: state.allocations.map((a: any) => ({ - destination: a.destination, - token: a.token, - amount: BigInt(a.amount), - })), - channelId: channel_id, - serverSignature: server_signature, - }; - - try { - console.log(` Submitting close to L1 for ${channel_id}...`); - const txHash = await client.closeChannel({ - finalState, - stateData: finalState.data - }); - console.log(`✓ Closed on-chain: ${txHash}`); - } catch (e) { - // If it fails (e.g. already closed or race condition), just log and continue - console.error(`Failed to close ${channel_id} on-chain:`, e); - } - } - - if (response.error) { - console.error('WS Error:', response.error); - } - } - }); -} - -main(); -``` -
diff --git a/docs/learn/index.mdx b/docs/learn/index.mdx deleted file mode 100644 index 228dd13..0000000 --- a/docs/learn/index.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Learn -description: Master Yellow Network and state channel technology -sidebar_position: 1 -displayed_sidebar: learnSidebar ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Learn - -Welcome to the Yellow Network learning path. This section builds your understanding from fundamentals to advanced concepts. - -Yellow Network is a decentralized clearing and settlement infrastructure that operates as a Layer 3 overlay on top of existing blockchains. It enables businesses — brokers, exchanges, and application developers — to move digital assets across multiple blockchain networks through a unified peer-to-peer ledger, without relying on a centralized intermediary. The network is formed by independent node operators who run open-source clearnode software supplied by Layer3 Fintech Ltd. on their own infrastructure. The YELLOW token provides access to network services including clearing, SDK and developer tools, node operation, the AppRegistry, and dispute resolution. - ---- - -## Introduction - -Start here to understand what Yellow Network solves and how it works. - -**[What Yellow Solves](./introduction/what-yellow-solves.mdx)** — Understand the core problems: scaling, cost, and speed. Learn why state channels are the answer for high-frequency applications. - -**[Architecture at a Glance](./introduction/architecture-at-a-glance.mdx)** — See how the three protocol layers (on-chain, off-chain, application) work together to enable fast, secure transactions. - ---- - -## Getting Started - -Get hands-on with Yellow Network in minutes. - -**[Quickstart: Your First Channel](./getting-started/quickstart.mdx)** — Create a state channel, perform an off-chain transfer, and verify the transaction in under 10 minutes. - -**[Prerequisites & Environment](./getting-started/prerequisites.mdx)** — Set up a complete development environment with Node.js, TypeScript, and the VirtualApp SDK. - -**[Key Terms & Mental Models](./getting-started/key-terms.mdx)** — Build your vocabulary and conceptual framework for understanding state channels. - ---- - -## Core Concepts - -Deep dive into the technology powering Yellow Network. - -**[State Channels vs L1/L2](./core-concepts/state-channels-vs-l1-l2.mdx)** — Compare state channels with Layer 1 and Layer 2 solutions. Understand when each approach is the right choice. - -**[App Sessions](./core-concepts/app-sessions.mdx)** — Multi-party application channels with custom governance and state management. - -**[Session Keys](./core-concepts/session-keys.mdx)** — Delegated keys for secure, gasless interactions without repeated wallet prompts. - -**[Challenge-Response & Disputes](./core-concepts/challenge-response.mdx)** — How Yellow Network handles disputes and ensures your funds are always recoverable. - -**[Message Envelope](./core-concepts/message-envelope.mdx)** — Overview of the Nitro RPC message format and communication protocol. - -**[YELLOW Token](./core-concepts/yellow-token.mdx)** — The utility token powering network services, node operator collateral, and dispute resolution. - ---- - -## Protocol Flows - -Detailed v1 protocol flow documentation for deposits, withdrawals, transfers, and app sessions. - -**[Protocol Architecture](./protocol-flows/architecture.mdx)** — The Petal Diagram: Home Chain, Cross-Chain, Transfers, and App Sessions. - -**[Transfer Flow](./protocol-flows/transfer-flow.mdx)** — Off-chain transfers between users via the Clearnode. - -**[Home Channel Flows](./protocol-flows/home-channel-creation.mdx)** — Channel creation, deposits, and withdrawals on the home chain. - -**[App Session Deposit](./protocol-flows/app-session-deposit.mdx)** — Depositing funds into app sessions. - ---- - -## Next Steps - -After completing the Learn section, continue to: - -- **[Build](/docs/build/quick-start)** — Implement complete Yellow Applications -- **[Protocol Reference](/docs/protocol/introduction)** — Authoritative protocol specification - ---- - -## Quick Reference - -| Topic | Time | Difficulty | -|-------|------|------------| -| [What Yellow Solves](./introduction/what-yellow-solves) | 5 min | Beginner | -| [Architecture at a Glance](./introduction/architecture-at-a-glance) | 8 min | Beginner | -| [Quickstart](./getting-started/quickstart) | 10 min | Beginner | -| [Key Terms](./getting-started/key-terms) | 10 min | Beginner | -| [State Channels vs L1/L2](./core-concepts/state-channels-vs-l1-l2) | 12 min | Intermediate | -| [App Sessions](./core-concepts/app-sessions) | 8 min | Intermediate | -| [Session Keys](./core-concepts/session-keys) | 8 min | Intermediate | -| [Challenge-Response](./core-concepts/challenge-response) | 6 min | Intermediate | -| [Message Envelope](./core-concepts/message-envelope) | 5 min | Intermediate | -| [YELLOW Token](./core-concepts/yellow-token) | 8 min | Intermediate | -| [Protocol Architecture](./protocol-flows/architecture) | 10 min | Intermediate | -| [Transfer Flow](./protocol-flows/transfer-flow) | 12 min | Advanced | diff --git a/docs/learn/introduction/architecture-at-a-glance.mdx b/docs/learn/introduction/architecture-at-a-glance.mdx deleted file mode 100644 index b0a2bbb..0000000 --- a/docs/learn/introduction/architecture-at-a-glance.mdx +++ /dev/null @@ -1,261 +0,0 @@ ---- -sidebar_position: 2 -title: Architecture at a Glance -description: High-level overview of Yellow Network's three-layer architecture -keywords: [architecture, state channels, VirtualApp, Clearnode, smart contracts] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Architecture at a Glance - -In this guide, you will learn how Yellow Network's three protocol layers work together to enable fast, secure, off-chain transactions. - ---- - -## The Three Layers - -Yellow Network consists of three interconnected layers, each with a specific responsibility: - -```mermaid -graph TB - subgraph Application["Application Layer"] - direction TB - APP["Your Application
Games, Payments, DeFi"] - end - - subgraph OffChain["Off-Chain Layer"] - direction LR - CLIENT["Client SDK"] - BROKER["Clearnode"] - end - - subgraph OnChain["On-Chain Layer"] - direction TB - CONTRACTS["Custody & Adjudicator Contracts"] - end - - subgraph Blockchain["Blockchain Layer"] - direction TB - CHAIN["Ethereum, Polygon, Base, etc."] - end - - APP --> CLIENT - CLIENT <-->|"Nitro RPC Protocol"| BROKER - CLIENT -.->|"On-chain operations"| CONTRACTS - BROKER -.->|"Monitors events"| CONTRACTS - CONTRACTS --> CHAIN - - style Application fill:#e1f5ff,stroke:#9ad7ff,color:#111 - style OffChain fill:#fff4e1,stroke:#ffd497,color:#111 - style OnChain fill:#ffe1f5,stroke:#ffbde6,color:#111 - style Blockchain fill:#f0f0f0,stroke:#c9c9c9,color:#111 -``` - -| Layer | Purpose | Speed | Cost | -|-------|---------|-------|------| -| **Application** | Your business logic and user interface | — | — | -| **Off-Chain** | Instant state updates via Nitro RPC | < 1 second | Zero gas | -| **On-Chain** | Fund custody, disputes, final settlement | Block time | Gas fees | - ---- - -## On-Chain Layer: Security Foundation - -The on-chain layer provides cryptographic guarantees through smart contracts: - -### Custody Contract - -The **Custody Contract** is the core of VirtualApp's on-chain implementation. It handles: - -- **Channel Creation**: Lock funds and establish participant relationships -- **Dispute Resolution**: Process challenges and validate states -- **Final Settlement**: Distribute funds according to signed final state -- **Fund Management**: Deposit and withdrawal operations - -### Adjudicator Contracts - -**Adjudicators** validate state transitions according to application-specific rules: - -- **SimpleConsensus**: Both participants must sign (default for payment channels) -- **Custom Adjudicators**: Application-specific validation logic - -:::info On-Chain Operations -You only touch the blockchain for: - -1. Opening a channel (lock funds) -2. Resizing a channel (add or remove funds) -3. Closing a channel (unlock and distribute funds) -4. Disputing a state (if counterparty is uncooperative) - -::: - ---- - -## Off-Chain Layer: Speed and Efficiency - -The off-chain layer handles high-frequency operations without blockchain transactions. - -### Clearnode - -A **Clearnode** is operated by independent node operators using open-source software developed and maintained by Layer3 Fintech Ltd. It is the off-chain service that: - -- Manages the Nitro RPC protocol for state channel operations -- Provides a unified balance across multiple chains -- Coordinates payment channels between users -- Hosts app sessions for multi-party applications - -### Nitro RPC Protocol - -**Nitro RPC** is a lightweight protocol optimized for state channel communication: - -- **Compact format**: JSON array structure reduces message size by ~30% -- **Signed messages**: Every request and response is cryptographically signed -- **Real-time updates**: Bidirectional communication via WebSocket - -```javascript -// Compact Nitro RPC format -[requestId, method, params, timestamp] - -// Example: Transfer 50 USDC -[42, "transfer", {"destination": "0x...", "amount": "50.0", "asset": "usdc"}, 1699123456789] -``` - ---- - -## How Funds Flow - -This diagram shows how your tokens move through the system: - -```mermaid -graph TB - A["User Wallet
(ERC-20)"] -->|"1. deposit"| B["Available Balance
(Custody Contract)"] - B -->|"2. resize"| C["Channel-Locked
(Custody Contract)"] - C <-->|"3. resize"| D["Unified Balance
(Clearnode)"] - D -->|"4. open session"| E["App Sessions
(Applications)"] - E -->|"5. close session"| D - D -->|"6. resize/close"| B - B -->|"7. withdraw"| A - - style A fill:#90EE90,stroke:#333,color:#111 - style B fill:#87CEEB,stroke:#333,color:#111 - style C fill:#FFD700,stroke:#333,color:#111 - style D fill:#DDA0DD,stroke:#333,color:#111 - style E fill:#FFA07A,stroke:#333,color:#111 -``` - -### Fund States - -| State | Location | What It Means | -|-------|----------|---------------| -| **User Wallet** | Your EOA | Full control, on-chain | -| **Available Balance** | Custody Contract | Deposited, ready for channels | -| **Channel-Locked** | Custody Contract | Committed to a specific channel | -| **Unified Balance** | Clearnode | Available for off-chain operations | -| **App Session** | Application | Locked in a specific app session | - ---- - -## Channel Lifecycle - -A payment channel progresses through distinct states: - -```mermaid -stateDiagram-v2 - [*] --> VOID - VOID --> ACTIVE: create() with both signatures - ACTIVE --> ACTIVE: Off-chain updates (zero gas) - ACTIVE --> ACTIVE: resize() (add/remove funds) - ACTIVE --> FINAL: close() (cooperative) - ACTIVE --> DISPUTE: challenge() (if disagreement) - DISPUTE --> ACTIVE: checkpoint() (newer state) - DISPUTE --> FINAL: Timeout expires - FINAL --> [*] - - note right of ACTIVE: This is where
99% of activity happens -``` - -:::info Legacy Flow -The diagram above shows the recommended flow where both participants sign the initial state, creating the channel directly in ACTIVE status. A legacy flow also exists where only the creator signs initially (status becomes INITIAL), and other participants call `join()` separately. See [Channel Lifecycle](/docs/protocol/app-layer/on-chain/channel-lifecycle) for details. -::: - -### Typical Flow - -1. **Create**: Both parties sign initial state → channel becomes ACTIVE -2. **Operate**: Exchange signed states off-chain (unlimited, zero gas) -3. **Close**: Both sign final state → funds distributed - -### Dispute Path (Rare) - -If your counterparty becomes unresponsive: - -1. **Challenge**: Submit your latest signed state on-chain -2. **Wait**: Challenge period (typically 24 hours) allows counterparty to respond -3. **Finalize**: If no newer state is submitted, your state becomes final - ---- - -## Communication Patterns - -### Opening a Channel - -```mermaid -sequenceDiagram - participant Client - participant Clearnode - participant Blockchain - - Client->>Clearnode: create_channel request - Clearnode->>Client: channel config + Clearnode signature - Client->>Client: Sign state - Client->>Blockchain: create() with BOTH signatures - Blockchain->>Blockchain: Verify, lock funds, emit event - Blockchain-->>Clearnode: Event detected - Clearnode->>Client: Channel now ACTIVE -``` - -### Off-Chain Transfer - -```mermaid -sequenceDiagram - participant Sender - participant Clearnode - participant Receiver - - Sender->>Clearnode: transfer(destination, amount) - Clearnode->>Clearnode: Validate, update ledger - Clearnode->>Sender: Confirmed ✓ - Clearnode->>Receiver: balance_update notification - - Note over Sender,Receiver: Complete in < 1 second, zero gas -``` - ---- - -## Key Takeaways - -| Concept | What to Remember | -|---------|------------------| -| **On-Chain** | Only for opening, closing, disputes—security layer | -| **Off-Chain** | Where all the action happens—speed layer | -| **Clearnode** | Your gateway to the network—coordination layer | -| **State Channels** | Lock once, transact unlimited times, settle once | - -:::success Security Guarantee -At every stage, funds remain cryptographically secured. You can always recover your funds according to the latest valid signed state, even if a Clearnode becomes unresponsive. -::: - ---- - -## Next Steps - -Ready to start building? Continue to: - -- **[Quickstart](../getting-started/quickstart.mdx)** — Create your first channel in minutes -- **[Prerequisites](../getting-started/prerequisites.mdx)** — Set up your development environment -- **[Core Concepts](../core-concepts/state-channels-vs-l1-l2.mdx)** — Deep dive into state channels diff --git a/docs/learn/introduction/supported-chains.mdx b/docs/learn/introduction/supported-chains.mdx deleted file mode 100644 index d89c8b6..0000000 --- a/docs/learn/introduction/supported-chains.mdx +++ /dev/null @@ -1,305 +0,0 @@ ---- -sidebar_position: 3 -title: Supported Chains & Assets -description: Complete list of supported blockchains and assets on Yellow Network -keywords: [supported chains, blockchains, assets, tokens, USDC, Base, Polygon, Ethereum, sandbox, production] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# Supported Chains & Assets - -This page lists all blockchains and assets currently supported on Yellow Network. - ---- - -## Environments - -Yellow Network operates two separate environments: - -| Environment | Purpose | URL | -|-------------|---------|-----| -| **Sandbox** | Development and testing | `wss://clearnet-sandbox.yellow.com/ws` | -| **Production** | Live operations with real assets | `wss://clearnet.yellow.com/ws` | - -:::info Environment Separation -- Sandbox uses **testnet** blockchains and test tokens (e.g., `ytest.usd`) -- Production uses **mainnet** blockchains and real assets (e.g., `usdc`) -- Assets and blockchains from one environment cannot be used in the other -::: - ---- - -## Supported Blockchains - - - - -| Blockchain | Chain ID | Status | -|------------|----------|--------| -| Base Sepolia | 84532 | ✅ Active | -| Polygon Amoy | 80002 | ✅ Active | -| Ethereum Sepolia | 11155111 | ✅ Active | - - - - -| Blockchain | Chain ID | Status | -|------------|----------|--------| -| Ethereum | 1 | ✅ Active | -| BNB Smart Chain | 56 | ✅ Active | -| Polygon | 137 | ✅ Active | -| World Chain | 480 | ✅ Active | -| Base | 8453 | ✅ Active | -| Linea | 59144 | ✅ Active | -| XRPL EVM Sidechain | 1440000 | ✅ Active | - - - - ---- - -## Supported Assets - - - - -| Asset | Symbol | Description | -|-------|--------|-------------| -| Yellow Test USD | `ytest.usd` | Test stablecoin for sandbox development | - -:::tip Getting Test Tokens -Use the Yellow Network faucet to receive `ytest.usd` tokens for testing in the Sandbox environment. -::: - - - - -| Asset | Symbol | Blockchains | Decimals | -|-------|--------|-------------|----------| -| USD Coin | `usdc` | Ethereum, BNB Smart Chain, Polygon, World Chain, Base, Linea, XRPL EVM | 6 | -| Tether USD | `usdt` | BNB Smart Chain, Base, Linea | 6 | -| Ethereum | `eth` | Base, Linea | 18 | -| Wrapped Ether | `weth` | BNB Smart Chain, Polygon | 18 | -| BNB | `bnb` | BNB Smart Chain | 18 | -| Chainlink | `link` | BNB Smart Chain | 18 | -| XRP | `xrp` | XRPL EVM Sidechain | 18 | -| Beatwav | `beatwav` | Ethereum, Polygon | 18 | - - - - ---- - -## Assets by Blockchain - - - - -### Base Sepolia (84532) - -| Asset | Symbol | -|-------|--------| -| Yellow Test USD | `ytest.usd` | - -### Polygon Amoy (80002) - -| Asset | Symbol | -|-------|--------| -| Yellow Test USD | `ytest.usd` | - - - - -### Ethereum (1) - -| Asset | Symbol | -|-------|--------| -| USD Coin | `usdc` | -| Beatwav | `beatwav` | - -### BNB Smart Chain (56) - -| Asset | Symbol | -|-------|--------| -| BNB | `bnb` | -| USD Coin | `usdc` | -| Tether USD | `usdt` | -| Wrapped Ether | `weth` | -| Chainlink | `link` | - -### Polygon (137) - -| Asset | Symbol | -|-------|--------| -| USD Coin | `usdc` | -| Wrapped Ether | `weth` | -| Beatwav | `beatwav` | - -### World Chain (480) - -| Asset | Symbol | -|-------|--------| -| USD Coin | `usdc` | - -### Base (8453) - -| Asset | Symbol | -|-------|--------| -| Ethereum | `eth` | -| USD Coin | `usdc` | -| Tether USD | `usdt` | - -### Linea (59144) - -| Asset | Symbol | -|-------|--------| -| Ethereum | `eth` | -| USD Coin | `usdc` | -| Tether USD | `usdt` | - -### XRPL EVM Sidechain (1440000) - -| Asset | Symbol | -|-------|--------| -| USD Coin | `usdc` | -| XRP | `xrp` | - - - - ---- - -## Contract Addresses - -Contract addresses vary by blockchain. See the [deployment repository](https://github.com/erc7824/nitrolite/tree/main/contract/deployments) for the latest addresses. - - - - -### Base Sepolia (84532) - -| Contract | Address | -|----------|---------| -| Custody | `0x019B65A265EB3363822f2752141b3dF16131b262` | -| Adjudicator | `0x7c7ccbc98469190849BCC6c926307794fDfB11F2` | - -### Polygon Amoy (80002) - -| Contract | Address | -|----------|---------| -| Custody | `0x019B65A265EB3363822f2752141b3dF16131b262` | -| Adjudicator | `0x7c7ccbc98469190849BCC6c926307794fDfB11F2` | - - - - -### Ethereum (1) - -| Contract | Address | -|----------|---------| -| Custody | `0x6F71a38d919ad713D0AfE0eB712b95064Fc2616f` | -| Adjudicator | `0x14980dF216722f14c42CA7357b06dEa7eB408b10` | - -### BNB Smart Chain (56) - -| Contract | Address | -|----------|---------| -| Custody | `0x6F71a38d919ad713D0AfE0eB712b95064Fc2616f` | -| Adjudicator | `0x14980dF216722f14c42CA7357b06dEa7eB408b10` | - -### Polygon (137) - -| Contract | Address | -|----------|---------| -| Custody | `0x490fb189DdE3a01B00be9BA5F41e3447FbC838b6` | -| Adjudicator | `0x7de4A0736Cf5740fD3Ca2F2e9cc85c9AC223eF0C` | - -### World Chain (480) - -| Contract | Address | -|----------|---------| -| Custody | `0x6F71a38d919ad713D0AfE0eB712b95064Fc2616f` | -| Adjudicator | `0x14980dF216722f14c42CA7357b06dEa7eB408b10` | - -### Base (8453) - -| Contract | Address | -|----------|---------| -| Custody | `0x490fb189DdE3a01B00be9BA5F41e3447FbC838b6` | -| Adjudicator | `0x7de4A0736Cf5740fD3Ca2F2e9cc85c9AC223eF0C` | - -### Linea (59144) - -| Contract | Address | -|----------|---------| -| Custody | `0x6F71a38d919ad713D0AfE0eB712b95064Fc2616f` | -| Adjudicator | `0x14980dF216722f14c42CA7357b06dEa7eB408b10` | - -### XRPL EVM Sidechain (1440000) - -| Contract | Address | -|----------|---------| -| Custody | `0x6F71a38d919ad713D0AfE0eB712b95064Fc2616f` | -| Adjudicator | `0x14980dF216722f14c42CA7357b06dEa7eB408b10` | - - - - ---- - -## Requesting New Support - -Need support for a blockchain or asset not listed here? - -- **[Request Blockchain Support](/docs/guides/manuals/request-blockchain-support)** — Guide for adding new blockchain networks -- **[Request Asset Support](/docs/guides/manuals/request-asset-support)** — Guide for adding new tokens/assets - ---- - -## Quick Code Reference - -### Connecting to the Right Environment - -```typescript -import { Client } from "yellow-ts"; - -// Sandbox (for testing with ytest.usd) -const sandboxClient = new Client({ - url: 'wss://clearnet-sandbox.yellow.com/ws', -}); - -// Production (for real assets like usdc) -const productionClient = new Client({ - url: 'wss://clearnet.yellow.com/ws', -}); -``` - -### Using the Correct Asset - -```typescript -// Sandbox environment -const sandboxAllocations = [ - { participant: address1, asset: 'ytest.usd', amount: '100.0' }, - { participant: address2, asset: 'ytest.usd', amount: '0.0' } -]; - -// Production environment -const productionAllocations = [ - { participant: address1, asset: 'usdc', amount: '100.0' }, - { participant: address2, asset: 'usdc', amount: '0.0' } -]; -``` - ---- - -## See Also - -- [Quick Start Guide](/docs/build/quick-start) — Get started building with Yellow SDK -- [Multi-Party App Sessions](/docs/guides/multi-party-app-sessions) — Create multi-party application sessions -- [API Reference](/docs/api-reference) — Complete SDK documentation diff --git a/docs/learn/introduction/what-yellow-solves.mdx b/docs/learn/introduction/what-yellow-solves.mdx deleted file mode 100644 index ae0e573..0000000 --- a/docs/learn/introduction/what-yellow-solves.mdx +++ /dev/null @@ -1,149 +0,0 @@ ---- -sidebar_position: 1 -title: What Yellow Solves -description: Understand the core problems Yellow Network addresses - scaling, cost, and speed -keywords: [Yellow Network, state channels, blockchain scaling, off-chain, Web3] ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -:::warning[Work in Progress] -This page was carried over from the v0.5.x documentation and has not yet been fully updated for v1.x. Some terminology, code examples, and API references may be outdated. An update is in progress. -::: - -# What Yellow Solves - -In this guide, you will learn why Yellow Network exists, what problems it addresses, and how it provides a faster, cheaper way to build Web3 applications. Yellow Network is developed and maintained by Layer3 Fintech Ltd. and operated by independent node operators running the issuer's open-source software. - ---- - -## The Blockchain Scalability Problem - -Every blockchain transaction requires global consensus. While this guarantees security and decentralization, it creates three fundamental limitations: - -| Challenge | Impact on Users | -|-----------|-----------------| -| **High Latency** | Transactions take 15 seconds to several minutes for confirmation | -| **High Costs** | Gas fees spike during network congestion, making microtransactions impractical | -| **Limited Throughput** | Networks like Ethereum process ~15-30 transactions per second | - -For applications requiring real-time interactions—gaming, trading, micropayments—these constraints make traditional blockchain unusable as a backend. - ---- - -## How Yellow Network Solves This - -Yellow Network uses **state channels** to move high-frequency operations off-chain while preserving blockchain-level security guarantees. - -### The Core Insight - -Most interactions between parties don't need immediate on-chain settlement. Consider a chess game with a 10 USDC wager: - -- **On-chain approach**: Every move requires a transaction → 40+ transactions → $100s in fees -- **State channel approach**: Lock funds once, play off-chain, settle once → 2 transactions → minimal fees - -State channels let you execute unlimited off-chain operations between on-chain checkpoints. - -### What You Get - -| Feature | Benefit | -|---------|---------| -| **Instant Transactions** | Sub-second finality (< 1 second typical) | -| **Zero Gas Costs** | Off-chain operations incur no blockchain fees | -| **Unlimited Throughput*** | No consensus bottleneck limiting operations | -| **Blockchain Security** | Funds are always recoverable via on-chain contracts | - -*\*Theoretically unlimited—state channels have no blockchain consensus overhead. Real-world performance depends on signature generation speed, network latency between participants, and application complexity. We'll be publishing detailed benchmarks soon.* - ---- - -## The VirtualApp Protocol - -Yellow Network is built on **VirtualApp**, a state channel protocol designed for EVM-compatible chains. VirtualApp provides: - -- **Fund Custody**: Smart contracts that securely lock and release assets -- **Dispute Resolution**: Challenge-response mechanism ensuring fair outcomes -- **Final Settlement**: Cryptographic guarantees that final allocations are honored - -:::tip When to Use Yellow Network -Choose Yellow Network when your application needs: - -- Real-time interactions between users -- Microtransactions or streaming payments -- High transaction volumes without gas costs -- Multi-party coordination with instant settlement - -::: - ---- - -## Chain Abstraction with Clearnode - -A **Clearnode** is operated by independent node operators using the issuer's open-source software. It serves as your entry point to Yellow Network. When you connect to a Clearnode: - -1. **Deposit** tokens into the Custody Contract on any supported chain -2. **Resize** your channel to move funds to your unified balance -3. **Transact** instantly with any other user on the network -4. **Withdraw** back through the Custody Contract to any supported chain - -:::note Fund Flow -Funds flow through the Custody Contract (on-chain) before reaching your unified balance (off-chain). The `resize` operation moves funds between your on-chain available balance and your off-chain unified balance. See [Architecture](./architecture-at-a-glance#how-funds-flow) for the complete flow. -::: - -For example, deposit 50 USDC on Polygon and 50 USDC on Base—after resizing, your unified balance shows 100 USDC. You can then withdraw all 100 USDC to Arbitrum if you choose. - -```mermaid -graph LR - A["Deposit on Polygon
50 USDC"] --> C["Unified Balance
100 USDC"] - B["Deposit on Base
50 USDC"] --> C - C --> D["Withdraw to Arbitrum
100 USDC"] - - style C fill:#90EE90,stroke:#333,color:#111 -``` - ---- - -## Real-World Applications - -### Payment Applications - -- **Micropayments**: Pay-per-article, API usage billing, content monetization -- **Streaming payments**: Subscription services, hourly billing, real-time payroll -- **P2P transfers**: Instant remittances without intermediaries - -### Gaming Applications - -- **Turn-based games**: Chess, poker, strategy games with wagers -- **Real-time multiplayer**: In-game economies with instant transactions -- **Tournaments**: Prize pools and automated payouts - -### DeFi Applications - -- **High-frequency trading**: Execute trades without MEV concerns -- **Prediction markets**: Real-time betting with instant settlement -- **Escrow services**: Multi-party coordination with dispute resolution - ---- - -## Security Model - -Yellow Network maintains blockchain-level security despite operating off-chain: - -| Guarantee | How It's Achieved | -|-----------|-------------------| -| **Fund Safety** | All funds locked in audited smart contracts | -| **Dispute Resolution** | Challenge period allows contesting incorrect states | -| **Cryptographic Proof** | Every state transition is signed by participants | -| **Recovery Guarantee** | Users can always recover funds via on-chain contracts | - -If a Clearnode becomes unresponsive or malicious, you can submit your latest signed state to the blockchain and recover your funds after a challenge period. - ---- - -## Next Steps - -Now that you understand what Yellow solves, continue to: - -- **[Architecture at a Glance](./architecture-at-a-glance.mdx)** — See how the protocol layers work together -- **[Quickstart](../getting-started/quickstart.mdx)** — Create your first state channel in minutes diff --git a/docs/learn/protocol-flows/architecture.mdx b/docs/learn/protocol-flows/architecture.mdx deleted file mode 100644 index c21c1ed..0000000 --- a/docs/learn/protocol-flows/architecture.mdx +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: "Protocol Architecture (Petal Diagram)" -description: "A visual and technical breakdown of the Nitrolite Protocol architecture, covering the four core petals and three operational layers." -sidebar_position: 1 ---- - -# Protocol Architecture (Petal Diagram) - -The **Petal Diagram** is the central architectural reference for the Nitrolite Protocol. It maps how different actions -- Deposits, Withdrawals, Transfers, and App Sessions -- interact with various blockchains, all centered on a user's channel ("My Channel"). - -Nitrolite Protocol Petal Diagram - -*The diagram represents the architecture centered on a user's channel and maps how different actions interact with various blockchains.* - ---- - -## The Four Petals - -### Petal 1: My Home Chain (Deposits and Withdrawals) - -The **Home Chain** is the blockchain chosen by the user when they first create their channel or deposit a specific token. - -:::info Important Choice -When selecting your Home Chain, consider that this is the blockchain where your funds will be **enforced on-chain**. If you are unable to agree with the Node on a next state (e.g., in a dispute scenario), you can withdraw your funds on this specific blockchain. Choose a chain where you have easy access and are comfortable transacting. -::: - -**The Flow:** - -- **Deposit:** When a user deposits for the first time, that specific chain (e.g., Polygon) becomes the "Home Chain" for that token. -- **Optimization:** This process improves upon the previous version by **skipping the custody ledger step**, reducing one step and improving UX. -- **Mechanism:** Users transfer funds directly from their ERC-20 token balance via approvals, saving one transaction compared to the old protocol. -- **Withdrawal:** The withdrawal process follows the exact same direct logic as the deposit. - -**Key Benefit:** Direct fund transfers without intermediate custody steps result in reduced gas costs and improved UX. - ---- - -### Petal 2: Another Chain (Cross-Chain Support) - -When a user already has a Home Chain (e.g., Polygon) with funds and wants to deposit funds from a *different* chain (e.g., Linea or Base), the protocol uses a specialized bridge mechanism. - -**The Challenge:** Since the user's Home Chain is already defined, the protocol needs a different mechanism to handle deposits from a non-home chain. - -**The Solution -- The Bridge:** - -- The protocol implements a specialized bridge solution. -- Cross-chain deposits work similarly to cross-chain transfers, but with **atomic properties** -- ensuring the action either fully completes or doesn't happen at all. -- Atomicity is achieved not via smart contract infrastructure alone, but with the help of the participants. Both the User and the Node can see whether the process is failing on one chain and can cancel the process on another chain. - -**Key Property:** Atomic execution guarantees -- no partial states or stuck funds. - ---- - -### Petal 3: Transfers (Sender and Receiver State) - -Unlike state changes within a single channel, transfers between *two different users* are not strictly "atomic" because the two users have **unrelated states**. You cannot update the receiver's state on-chain immediately when the sender initiates the transfer. - -**The Solution -- Aggregated State Updates:** - -- **Sender Action:** The sender submits their new state (showing funds sent). -- **Verification:** If the Clearnode accepts this state as valid, it prepares a "pending state" for the Receiver. -- **Receiver Action (Aggregation):** If a receiver gets multiple transfers from different people, they do **not** need to sign a state update for every single transfer. -- **Efficiency:** The "receive" updates are **aggregated**. Only the Node signs the aggregated states; the Receiver doesn't need to. However, when such states may be aggregated with a "send", "lock", or "withdraw", then the Receiver needs to sign these new states. -- It is in the Receiver's security interest to own the latest state (to be able to challenge with it if the network goes down). The main challenge for the Receiver is to obtain the "receive" state signed by the Node if they are offline or not connected to the Node. -- In v1.0, not only the receiver but anyone can subscribe for state updates, enabling users to hire **User Watchtowers** that listen to "receive" state updates and store such states for the User. - -**Key Optimization:** Batch processing reduces signature overhead and improves throughput. - ---- - -### Petal 4: App Sessions (The "Virtual Layer") - -App Sessions function as a **Virtual Layer** within the protocol. - -**The Node Mechanism:** - -- When a user enters an App Session (locks funds), those funds move to the Node. -- These funds are technically locked on the Node's wallet and are **not directly enforced by the State Channel** during the session. - -**Security and Risk:** - -- If the Node misbehaves, the user can currently only recover funds that are *not* locked in an active session. - -:::note -This is a limitation in the current version of the protocol. Future work to improve App Session security through Node Watchtowers and cryptographic proofs is planned. -::: - -- To simplify security, the protocol restricts App Sessions to **one participant deposit of one token per session update**. - -**Future Validation -- Node Watchtowers:** - -- **Third-Party Node Observers (Node Watchtowers)** will validate actions on the Virtual Layer. -- If they detect misbehavior by the Node, they can intervene or slash, ensuring the system remains trustless. - -**UX Philosophy:** The Virtual Layer stays flexible (allowing flexible app sessions, settlements, and swaps), while the **Fundamental Layer** (Deposits/Withdrawals/Transfers) remains strictly enforced on-chain. - ---- - -## Protocol Layers - -| Layer | Description | Security Level | -| --- | --- | --- | -| **Fundamental Layer** | Deposits/Withdrawals (on-chain) and Transfers (off-chain) | Strictly enforced on-chain (High Security) | -| **Virtual Layer** | App Sessions and internal movements | Broker/Watchtower model (High Flexibility) | -| **Cross-Chain** | Managed via specialized bridge | Atomic swap properties | diff --git a/docs/learn/protocol-flows/escrow-deposit.mdx b/docs/learn/protocol-flows/escrow-deposit.mdx deleted file mode 100644 index a4b8f8c..0000000 --- a/docs/learn/protocol-flows/escrow-deposit.mdx +++ /dev/null @@ -1,542 +0,0 @@ ---- -title: "Escrow Channel Deposit Flow" -description: "A comprehensive breakdown of the Escrow Channel Deposit flow for cross-chain deposits via short-lived escrow channels in the Nitrolite v1.0 protocol." -sidebar_position: 8 ---- - -# Escrow Channel Deposit Flow - -This document provides a comprehensive breakdown of the **Escrow Channel Deposit** flow as defined in the Nitrolite v1.0 protocol. This operation allows a user to deposit funds from a **Non-Home Chain** (a blockchain different from where their home channel exists) into their unified balance through a **short-lived Escrow Channel**. - -This is a **cross-chain bridging operation** that uses a two-phase approach (Preparation + Execution) to move liquidity across blockchains without requiring atomic cross-chain verification. - -:::caution Cross-Chain Status -Cross-chain functionality is not yet fully implemented. While channels can be created on any chain with a Nitro deployment, cross-chain operations like escrow deposit and withdrawal are planned for shortly after launch. -::: - ---- - -## Actors in the Flow - -```mermaid -graph LR - U["User"] --> C["Client"] - C <--> N["Node (Clearnode)"] - N <--> HC["HomeChain"] - N <--> EC["EscrowChain"] - - style U stroke:#333 - style C stroke:#333 - style N stroke:#333 - style HC stroke:#4CAF50 - style EC stroke:#FF9800 -``` - -| Actor | Role | -| --- | --- | -| **User** | The human user initiating the cross-chain deposit | -| **Client** | SDK/Application managing states on behalf of the user | -| **Node** | The Clearnode that validates, coordinates, and bridges state transitions | -| **HomeChain** | The blockchain where the user's home channel exists | -| **EscrowChain** | The non-home blockchain where the user is depositing funds from | - ---- - -## Prerequisites - -Before the escrow deposit flow begins: - -1. **User already has a home channel** on the HomeChain. -2. **Node** contains the user's state with Home Channel information. -3. **Client** is connected to the Node via WebSocket. - -This flow handles the "Another Chain" petal from the Nitrolite architecture diagram. When a user wants to deposit from a chain that is NOT their home chain, they cannot directly deposit -- instead, they use an escrow mechanism. - ---- - -## Key Concepts - -### What is an Escrow Entity? - -An **Escrow Entity** is a short-lived channel created on a non-home chain specifically for cross-chain deposits. It acts as a bridge (note: "entity" is used instead of "channel" to avoid confusion with the channel concept and lifecycle): - -- User locks funds on the **Escrow Chain** (non-home). -- Node provides equivalent liquidity on the **Home Chain**. -- **Happy case**: The escrow is finalized once User and Node sign the execution phase state and submit it on-chain. -- **Unhappy case**: Either party challenges the escrow if the opposite party decides not to continue cooperating. After challenge, escrow funds are distributed back. - -### Two-Phase Cross-Chain Operations - -Since one chain cannot directly observe or verify another chain's state, cross-chain actions are **two-phase** and **optimistic**: - -| Phase | Purpose | -| --- | --- | -| **Preparation Phase** | Lock liquidity on both chains, create escrow object | -| **Execution Phase** | Update allocations and net flows, finalize the operation | - -### Transition Types Used - -| Transition | Description | -| --- | --- | -| `mutual_lock` | Initial lock of funds preparing for cross-chain movement | -| `escrow_deposit` | Finalizes the escrow deposit, updating allocations | - ---- - -## Phase 1: Deposit Initiation - -```mermaid -sequenceDiagram - actor User - actor Client - - User->>Client: async deposit(blockchainId, asset, amount) - Note over Client: User initiates cross-chain deposit -``` - -The **User** calls the `deposit` function on the **Client** SDK with three parameters: - -| Parameter | Description | Example | -| --- | --- | --- | -| `blockchainId` | The blockchain ID where funds are coming FROM (non-home chain) | `59144` (Linea) | -| `asset` | The asset symbol to deposit | `usdc` | -| `amount` | The amount to deposit | `100.0` | - ---- - -## Phase 2: Fetching Current State - -```mermaid -sequenceDiagram - actor Client - actor Node - - Client->>Node: GetLastState(UserWallet, asset) - Note right of Node: GetLastState(userWallet, asset) - Node->>Client: Returns state -``` - -1. **Client** requests the **latest state** from the Node. -2. The Node looks up the state using `UserWallet` and `asset`. -3. The Node returns the current **state** object containing the **Home Channel** information. - ---- - -## Phase 3: Building the Preparation State (Mutual Lock) - -```mermaid -sequenceDiagram - actor Client - - Note over Client: createNextState(currentState) returns state - Note over Client: state.setID(CalculateStateID(state.userWallet,
state.asset, state.cycleId, state.version)) - Note over Client: GetTokenAddress(blockchainId, asset) - Note over Client: state.setEscrowToken(blockchainId, tokenAddress) - Note over Client: GetEscrowChannelID(homeChannelDef, state.version) - Note over Client: NewTransition(mutualLockT, state.ID(),
homeChannelID, amount) - Note over Client: state.applyTransitions(transitions) returns true - Note over Client: signState(state) returns userSig -``` - -### 3.1 Create Next State - -``` -createNextState(currentState) -> state -``` - -The Client creates a new state object based on the current state with an incremented version. - -### 3.2 Calculate State ID - -``` -state.setID(CalculateStateID(state.userWallet, state.asset, state.cycleId, state.version)) -``` - -The **State ID** is a deterministic hash computed from user wallet, asset, cycle, and version. - -### 3.3 Get Token Address for Escrow Chain - -``` -GetTokenAddress(blockchainId, asset) -> tokenAddress -``` - -The Client resolves the token contract address for the specified asset on the escrow (non-home) chain. - -### 3.4 Set Escrow Token - -``` -state.setEscrowToken(blockchainId, tokenAddress) -``` - -The state is updated to include the escrow chain's token information in the `escrow_ledger`. - -### 3.5 Get Escrow Channel ID - -``` -GetEscrowChannelID(homeChannelDef, state.version) -> escrowChannelID -``` - -A deterministic escrow channel ID is computed based on the home channel definition and current version. - -### 3.6 Create Mutual Lock Transition - -``` -NewTransition(mutual_lock, state.ID(), homeChannelID, amount) -``` - -The **mutual_lock** transition prepares funds for cross-chain movement: - -| Field | Value | -| --- | --- | -| `type` | `mutual_lock` | -| `tx_hash` | State ID reference | -| `account_id` | Home Channel ID | -| `amount` | Amount to lock | - -### 3.7 Apply and Sign - -``` -state.applyTransitions(transitions) -> true -signState(state) -> userSig -``` - -The transition is applied to the state and the user signs it. - ---- - -## Phase 4: Node Validates and Stores Escrow Channel - -```mermaid -sequenceDiagram - actor Client - actor Node - - Client->>Node: SubmitState(state, userSig) - Note right of Node: GetLastState(userWallet, asset) returns currentState - Note right of Node: EnsureNoOngoingTransitions() - Note right of Node: ValidateStateTransition(currentState, state) - Note right of Node: StoreEscrowChannel(escrow_channel) - Note right of Node: StoreState(state) - Node->>Client: Return node signature -``` - -### Node Validation Steps - -| Step | Operation | Purpose | -| --- | --- | --- | -| 1 | `GetLastState(...)` | Fetch current user state | -| 2 | `EnsureNoOngoingTransitions()` | Block other operations during escrow | -| 3 | `ValidateStateTransition(...)` | Verify version, signatures, balances | -| 4 | `StoreEscrowChannel(...)` | Create escrow channel record | -| 5 | `StoreState(state)` | Persist the new state | - -:::warning Atomic Operations -Once an escrow deposit starts with `mutual_lock`, **the Node stops issuing new states** until `escrow_deposit` finalizes. This ensures atomicity of cross-chain operations. -::: - ---- - -## Phase 5: On-Chain Escrow Initiation - -```mermaid -sequenceDiagram - actor Client - actor EscrowChain - actor Node - - Note over Client: PackChannelDefinition(channelDef) - Note over Client: PackState(channelId, state) - Client->>EscrowChain: initiateEscrowDeposit(packedChannelDef, packedState) - EscrowChain->>Client: Return Tx Hash - EscrowChain-->>Node: Emits EscrowDepositInitiated Event - Note right of Node: HandleEscrowDepositInitiated() - Note right of Node: UpdateEscrowChannel(escrow_channel) - Node-->>Client: Sends ChannelUpdate and BalanceUpdate -``` - -### 5.1 Pack Channel Definition and State - -``` -PackChannelDefinition(channelDef) -> packedChannelDef -PackState(channelId, state) -> packedState -``` - -The Client serializes the channel definition and state for on-chain submission. - -### 5.2 On-Chain Transaction - -``` -initiateEscrowDeposit(packedChannelDef, packedState) -``` - -The Client submits a transaction to the **EscrowChain** smart contract, which: - -- Locks the user's funds on the escrow chain -- Creates an escrow object with a timeout -- Emits `EscrowDepositInitiated` event - -### 5.3 Node Event Handling - -The Node listens for blockchain events and: - -1. **HandleEscrowDepositInitiated** -- Processes the event. -2. **UpdateEscrowChannel** -- Updates the escrow channel status. -3. Sends **ChannelUpdate** and **BalanceUpdate** notifications to the Client. - ---- - -## Phase 6: Home Chain Escrow Initiation - -```mermaid -sequenceDiagram - actor Node - actor HomeChain - actor Client - - Node->>HomeChain: initiateEscrowDeposit(homeChannelId, packedState) - HomeChain-->>Node: Emits EscrowDepositInitiatedOnHome Event - Node-->>Node: HandleEscrowDepositInitiatedOnHome() - Node-->>Client: Sends ChannelUpdate and BalanceUpdate -``` - -The **Node** initiates escrow deposit on the **Home Chain**: - -1. Submits `initiateEscrowDeposit(homeChannelId, packedState)` to Home Chain contract. -2. Home Chain emits `EscrowDepositInitiatedOnHome` event. -3. Node handles the event internally. -4. Sends updated notifications to Client. - -The initiation on the home chain ensures that the Node's liquidity commitment is recorded on-chain, providing security guarantees for the cross-chain operation. - ---- - -## Phase 7: Building the Execution State (Escrow Deposit) - -This phase starts when the Client sees the `EscrowDepositInitiatedOnHome` event on the Home Chain. - -```mermaid -sequenceDiagram - actor Client - actor Node - - Client->>Node: GetLastState(UserWallet, asset) - Note right of Node: GetLastState(userWallet, asset) - Node->>Client: Returns state - Note over Client: createNextState(currentState) returns state - Note over Client: state.setID(CalculateStateID(state.userWallet,
state.asset, state.cycleId, state.version)) - Note over Client: NewTransition(escrow_depositT, state.ID(),
homeChannelID, amount) - Note over Client: state.applyTransitions(transitions) returns true - Note over Client: signState(state) returns userSig -``` - -### 7.1 Fetch Updated State - -The Client fetches the latest state which now reflects the escrowed funds. - -### 7.2 Create Escrow Deposit Transition - -``` -NewTransition(escrow_deposit, state.ID(), homeChannelID, amount) -``` - -The **escrow_deposit** transition finalizes the cross-chain deposit: - -| Field | Value | -| --- | --- | -| `type` | `escrow_deposit` | -| `tx_hash` | State ID reference | -| `account_id` | Home Channel ID | -| `amount` | Deposited amount | - ---- - -## Phase 8: Submitting Execution State - -```mermaid -sequenceDiagram - actor Client - actor Node - actor User - - Client->>Node: SubmitState(state, userSig) - Note right of Node: GetLastState(userWallet, asset) returns currentState - Note right of Node: EnsureNoOngoingTransitions() - Note right of Node: ValidateStateTransition(currentState, state) - Note right of Node: StoreState(state) - Node->>Client: Return node signature - - Client-->>User: Returns success -``` - -1. Client submits the execution state with `escrow_deposit` transition. -2. Node validates and stores the state. -3. Client returns success to the User. - -At this point, the user's unified balance is updated to reflect the deposited funds. The escrow mechanism has effectively "bridged" the funds from the non-home chain. - ---- - -## Phase 9: Escrow Finalization (Automatic or Fast Unlock) - -```mermaid -sequenceDiagram - actor Node - actor EscrowChain - actor Client - - Note over Node: PackState(channelId, state) - Node->>EscrowChain: finalizeEscrowDeposit(escrowChannelId, packedState) - EscrowChain->>Node: Return Tx Hash - EscrowChain-->>Node: Emits EscrowDepositFinalized Event - Note right of Node: HandleEscrowDepositFinalized() - Note right of Node: UpdateEscrowChannel(escrow_channel) - Node-->>Client: Sends ChannelUpdate and BalanceUpdate -``` - -### Two Unlock Options - -These are **Node funds** that require an unlock: - -| Option | Description | -| --- | --- | -| **Automatic Release** | Escrowed funds are released after the lock period expires | -| **Fast Unlock** | Node calls `FinalizeEscrowDeposit` on escrow chain to release funds immediately | - -:::warning -"Automatic" unlock means the funds **will be released eventually after the `unlockAt` timestamp is reached**, not exactly when the timestamp is reached. Each on-chain action checks whether Node's funds can be unlocked, and if so, the unlock is performed. There is also a manual method `purgeEscrowDeposits(maxToPurge)` for explicit cleanup. -::: - -### Finalization Steps - -1. **PackState** -- Node prepares the final state. -2. **FinalizeEscrowDeposit** -- Submits to Escrow Chain contract. -3. **EscrowDepositFinalized** event emitted. -4. Node updates internal state and notifies Client. - ---- - -## Complete Flow Diagram - -```mermaid -sequenceDiagram - actor User - actor Client - actor Node - actor HomeChain - actor EscrowChain - - rect rgb(40, 40, 100) - Note over User,Client: Phase 1: Initiation - User->>Client: async deposit(blockchainId, asset, amount) - end - - rect rgb(40, 100, 40) - Note over Client,Node: Phase 2-4: State Preparation - Client->>Node: GetLastState(UserWallet, asset) - Node->>Client: Returns state - Note over Client: Build mutual_lock state - Client->>Node: SubmitState(state, userSig) - Node->>Client: Return node signature - end - - rect rgb(150, 100, 40) - Note over Client,EscrowChain: Phase 5: On-Chain Escrow - Client->>EscrowChain: initiateEscrowDeposit(...) - EscrowChain-->>Node: EscrowDepositInitiated Event - end - - rect rgb(40, 100, 100) - Note over Node,HomeChain: Phase 6: Home Chain Checkpoint - Node->>HomeChain: initiateEscrowDeposit(homeChannelId, packedState) - HomeChain-->>Node: EscrowDepositInitiatedOnHome Event - end - - rect rgb(100, 40, 100) - Note over Client,Node: Phase 7-8: Execution State - Client->>Node: GetLastState(UserWallet, asset) - Node->>Client: Returns state - Note over Client: Build escrow_deposit state - Client->>Node: SubmitState(state, userSig) - Node->>Client: Return node signature - Client-->>User: Returns success - end - - rect rgb(100, 100, 40) - Note over Node,EscrowChain: Phase 9: Finalization - Node->>EscrowChain: finalizeEscrowDeposit(...) - EscrowChain-->>Node: EscrowDepositFinalized Event - Node-->>Client: ChannelUpdate and BalanceUpdate - end -``` - ---- - -## Key Concepts Summary - -### State Transitions Overview - -```mermaid -flowchart LR - subgraph Preparation["Preparation Phase"] - A["Current State"] --> B["mutual_lock Transition"] - B --> C["Escrowed State"] - end - subgraph Execution["Execution Phase"] - C --> D["escrow_deposit Transition"] - D --> E["Final State"] - end - - style Preparation fill:#1a1a2e,stroke:#16213e - style Execution fill:#0f3460,stroke:#16213e -``` - -### On-Chain vs Off-Chain Actions - -| Action | Chain | Purpose | -| --- | --- | --- | -| `SubmitState` (mutual_lock) | Off-chain (Node) | Prepare escrow | -| `initiateEscrowDeposit` | **On-chain (Escrow)** | Lock funds on non-home chain | -| `initiateEscrowDeposit` | **On-chain (Home)** | Record state on home chain | -| `SubmitState` (escrow_deposit) | Off-chain (Node) | Execute escrow | -| `finalizeEscrowDeposit` | **On-chain (Escrow)** | Release locked funds | - -### Security Guarantees - -From the on-chain protocol: - -- **Preparation phase**: User locks funds on the non-home chain. Node locks equal liquidity on the home chain. An escrow object with timeouts is created. -- **Execution phase**: A signed execution state updates allocations and net flows. -- **Recoverability**: Every escrow phase must be completable or revertible via timeout and challenge on at least one chain. - ---- - -## Error Recovery - -### What if the process stalls? - -| Scenario | Recovery | -| --- | --- | -| Node doesn't respond | User can challenge with the last signed state | -| On-chain transaction fails | Retry or wait for timeout to revert | -| Network issues | Escrowed funds released automatically after lock period | - -### Challenge Mechanism - -If an escrow process is challenged and the challenge period expires without resolution, the finalize function: - -1. Does not invoke the channel engine. -2. Manually unlocks the locked funds to the Node. -3. Sets status to `FINALIZED`. - -:::warning -If an escrow was challenged, then the on-chain channel **must also be challenged and closed**. It is not possible to continue operating a channel after any related escrow was challenged. -::: - ---- - -## Related Flows - -- [Transfer Communication Flow](./transfer-flow) -- [App Session Deposit Flow](./app-session-deposit) -- [Home Channel Deposit Flow](./home-channel-deposit) -- [Escrow Channel Withdrawal Flow](./escrow-withdrawal) diff --git a/docs/learn/protocol-flows/escrow-withdrawal.mdx b/docs/learn/protocol-flows/escrow-withdrawal.mdx deleted file mode 100644 index e740011..0000000 --- a/docs/learn/protocol-flows/escrow-withdrawal.mdx +++ /dev/null @@ -1,517 +0,0 @@ ---- -title: "Escrow Channel Withdrawal Flow" -description: "A comprehensive breakdown of the Escrow Channel Withdrawal flow for cross-chain withdrawals via short-lived escrow channels in the Nitrolite v1.0 protocol." -sidebar_position: 9 ---- - -# Escrow Channel Withdrawal Flow - -This document provides a comprehensive breakdown of the **Escrow Channel Withdrawal** flow as defined in the Nitrolite v1.0 protocol. This operation allows a user to withdraw funds from their **unified balance** to a **Non-Home Chain** (a blockchain different from where their home channel exists) through a **short-lived Escrow Channel**. - -This is the reverse operation of Escrow Channel Deposit -- it's a **cross-chain bridging out** operation that uses a two-phase approach (Preparation + Execution) to move liquidity from the home chain to a different blockchain. - -:::caution Cross-Chain Status -Cross-chain functionality is not yet fully implemented. While channels can be created on any chain with a Nitro deployment, cross-chain operations like escrow deposit and withdrawal are planned for shortly after launch. -::: - ---- - -## Actors in the Flow - -```mermaid -graph LR - U["User"] --> C["Client"] - C <--> N["Node (Clearnode)"] - N <--> HC["HomeChain"] - N <--> EC["EscrowChain"] - - style U stroke:#333 - style C stroke:#333 - style N stroke:#333 - style HC stroke:#4CAF50 - style EC stroke:#FF9800 -``` - -| Actor | Role | -| --- | --- | -| **User** | The human user initiating the cross-chain withdrawal | -| **Client** | SDK/Application managing states on behalf of the user | -| **Node** | The Clearnode that validates, coordinates, and bridges state transitions | -| **HomeChain** | The blockchain where the user's home channel exists (funds originate here) | -| **EscrowChain** | The non-home blockchain where the user wants to receive funds | - ---- - -## Prerequisites - -Before the escrow withdrawal flow begins: - -1. **User already has a home channel** on the HomeChain. -2. **Node** contains the user's state with Home Channel information. -3. **Client** is connected to the Node via WebSocket. -4. **User has sufficient balance** in their unified balance to withdraw. - -This flow handles the "bridging out" scenario. When a user wants to receive funds on a chain that is NOT their home chain, they use the escrow withdrawal mechanism where the Node locks liquidity on the target chain. - ---- - -## Key Concepts - -### Escrow Withdrawal vs Escrow Deposit - -| Operation | Direction | User Action | Node Action | -| --- | --- | --- | --- | -| **Escrow Deposit** | Non-Home to Home | User locks funds on escrow chain | Node provides liquidity on home chain | -| **Escrow Withdrawal** | Home to Non-Home | User locks funds on home chain | Node provides liquidity on escrow chain | - -### Transition Types Used - -| Transition | Description | -| --- | --- | -| `escrow_lock` | Lock funds from unified balance, preparing for withdrawal | -| `escrow_withdraw` | Finalize the withdrawal, releasing funds on escrow chain | - ---- - -## Phase 1: Withdrawal Initiation - -```mermaid -sequenceDiagram - actor User - actor Client - - User->>Client: async withdraw(blockchainId, asset, amount) - Note over Client: User initiates cross-chain withdrawal -``` - -The **User** calls the `withdraw` function on the **Client** SDK with three parameters: - -| Parameter | Description | Example | -| --- | --- | --- | -| `blockchainId` | The blockchain ID where funds should be received (non-home chain) | `59144` (Linea) | -| `asset` | The asset symbol to withdraw | `usdc` | -| `amount` | The amount to withdraw | `100.0` | - ---- - -## Phase 2: Fetching Current State - -```mermaid -sequenceDiagram - actor Client - actor Node - - Client->>Node: GetLastState(UserWallet, asset) - Note right of Node: GetLastState(userWallet, asset) - Node->>Client: Returns state -``` - -1. **Client** requests the **latest state** from the Node. -2. The Node looks up the state using `UserWallet` and `asset`. -3. The Node returns the current **state** object containing the **Home Channel** information. - ---- - -## Phase 3: Building the Preparation State (Escrow Lock) - -```mermaid -sequenceDiagram - actor Client - - Note over Client: createNextState(currentState) returns state - Note over Client: state.setID(CalculateStateID(state.userWallet,
state.asset, state.cycleId, state.version)) - Note over Client: GetTokenAddress(blockchainId, asset) - Note over Client: state.setEscrowToken(blockchainId, tokenAddress) - Note over Client: GetEscrowChannelID(homeChannelDef, state.version) - Note over Client: NewTransition(escrowLockT, state.ID(),
escrowChannelID, amount) - Note over Client: state.applyTransitions(transitions) returns true - Note over Client: signState(state) returns userSig -``` - -### 3.1 Create Next State - -``` -createNextState(currentState) -> state -``` - -The Client creates a new state object based on the current state with an incremented version. - -### 3.2 Calculate State ID - -``` -state.setID(CalculateStateID(state.userWallet, state.asset, state.cycleId, state.version)) -``` - -The **State ID** is a deterministic hash computed from user wallet, asset, cycle, and version. - -### 3.3 Get Token Address for Escrow Chain - -``` -GetTokenAddress(blockchainId, asset) -> tokenAddress -``` - -The Client resolves the token contract address for the specified asset on the escrow (non-home) chain. - -### 3.4 Set Escrow Token - -``` -state.setEscrowToken(blockchainId, tokenAddress) -``` - -The state is updated to include the escrow chain's token information in the `escrow_ledger`. - -### 3.5 Get Escrow Channel ID - -``` -GetEscrowChannelID(homeChannelDef, state.version) -> escrowChannelID -``` - -A deterministic escrow channel ID is computed based on the home channel definition and current version. - -### 3.6 Create Escrow Lock Transition - -``` -NewTransition(escrow_lock, state.ID(), escrowChannelID, amount) -``` - -The **escrow_lock** transition locks funds from the user's unified balance: - -| Field | Value | -| --- | --- | -| `type` | `escrow_lock` | -| `tx_hash` | State ID reference | -| `account_id` | Escrow Channel ID | -| `amount` | Amount to lock | - -### 3.7 Apply and Sign - -``` -state.applyTransitions(transitions) -> true -signState(state) -> userSig -``` - -The transition is applied to the state and the user signs it. - ---- - -## Phase 4: Node Validates and Stores Escrow Channel - -```mermaid -sequenceDiagram - actor Client - actor Node - - Client->>Node: SubmitState(state, userSig) - Note right of Node: GetLastState(userWallet, asset) returns currentState - Note right of Node: EnsureNoOngoingTransitions() - Note right of Node: ValidateStateTransition(currentState, state) - Note right of Node: StoreEscrowChannel(escrow_channel) - Note right of Node: StoreState(state) - Node->>Client: Return node signature -``` - -### Node Validation Steps - -| Step | Operation | Purpose | -| --- | --- | --- | -| 1 | `GetLastState(...)` | Fetch current user state | -| 2 | `EnsureNoOngoingTransitions()` | Block other operations during escrow | -| 3 | `ValidateStateTransition(...)` | Verify version, signatures, balances | -| 4 | `StoreEscrowChannel(...)` | Create escrow channel record | -| 5 | `StoreState(state)` | Persist the new state | - -:::warning Atomic Operations -Once an escrow withdrawal starts with `escrow_lock`, **the Node stops issuing new states** until `escrow_withdraw` finalizes. This ensures atomicity of cross-chain operations. -::: - ---- - -## Phase 5: On-Chain Escrow Initiation - -```mermaid -sequenceDiagram - actor Client - actor Node - actor EscrowChain - - Note over Client: PackChannelDefinition(channelDef) - Note over Client: PackState(channelId, state) - Client->>EscrowChain: initiateEscrowWithdrawal(packedChannelDef, packedState) - EscrowChain->>Client: Return Tx Hash - EscrowChain-->>Node: Emits EscrowWithdrawalInitiated Event - Note right of Node: HandleEscrowWithdrawalInitiated() - Note right of Node: UpdateEscrowChannel(escrow_channel) - Node-->>Client: Sends ChannelUpdate and BalanceUpdate -``` - -### 5.1 Pack Channel Definition and State - -``` -PackChannelDefinition(channelDef) -> packedChannelDef -PackState(channelId, state) -> packedState -``` - -The Client serializes the channel definition and state for on-chain submission. - -### 5.2 Client On-Chain Transaction - -``` -initiateEscrowWithdrawal(packedChannelDef, packedState) -``` - -The **Client** submits a transaction to the **EscrowChain** smart contract, which: - -- Locks the Node's liquidity on the escrow chain -- Creates an escrow object with timeouts -- Emits `EscrowWithdrawalInitiated` event - -:::warning Security Measure -In cross-chain operations (escrow deposit, escrow withdrawal, channel migration), the **first on-chain transaction is always submitted by the User/Client**. This guards against DOS attacks where a user would initiate an action, the Node would need to perform a transaction, and the user disappears. -::: - -### 5.3 Node Event Handling - -The Node listens for blockchain events and: - -1. **HandleEscrowWithdrawalInitiated** -- Processes the event. -2. **UpdateEscrowChannel** -- Updates the escrow channel status. -3. Sends **ChannelUpdate** and **BalanceUpdate** notifications to the Client. - ---- - -## Phase 6: Building the Execution State (Escrow Withdrawal) - -```mermaid -sequenceDiagram - actor Client - actor Node - - Client->>Node: GetLastState(UserWallet, asset) - Note right of Node: GetLastState(userWallet, asset) - Node->>Client: Returns state - Note over Client: createNextState(currentState) returns state - Note over Client: state.setID(CalculateStateID(state.userWallet,
state.asset, state.cycleId, state.version)) - Note over Client: NewTransition(escrow_withdrawalT, state.ID(),
escrowChannelID, amount) - Note over Client: state.applyTransitions(transitions) returns true - Note over Client: signState(state) returns userSig -``` - -### 6.1 Fetch Updated State - -The Client fetches the latest state which now reflects the locked funds. - -### 6.2 Create Escrow Withdrawal Transition - -``` -NewTransition(escrow_withdraw, state.ID(), escrowChannelID, amount) -``` - -The **escrow_withdraw** transition finalizes the cross-chain withdrawal: - -| Field | Value | -| --- | --- | -| `type` | `escrow_withdraw` | -| `tx_hash` | State ID reference | -| `account_id` | Escrow Channel ID | -| `amount` | Withdrawn amount | - ---- - -## Phase 7: Submitting Execution State - -```mermaid -sequenceDiagram - actor Client - actor Node - - Client->>Node: SubmitState(state, userSig) - Note right of Node: GetLastState(userWallet, asset) returns currentState - Note right of Node: ValidateStateTransition(currentState, state) - Note right of Node: StoreState(state) - Node->>Client: Return node signature -``` - -1. Client submits the execution state with `escrow_withdraw` transition. -2. Node validates and stores the state. -3. Node returns its signature confirming acceptance. - ---- - -## Phase 8: Escrow Finalization - -```mermaid -sequenceDiagram - actor Client - actor Node - actor EscrowChain - - Note over Node: PackState(channelId, state) - Client->>EscrowChain: finalizeEscrowWithdrawal(escrowChannelId, packedState) - EscrowChain->>Client: Return Tx Hash - EscrowChain-->>Node: Emits EscrowWithdrawalFinalized Event - Note right of Node: HandleEscrowWithdrawalFinalized() - Note right of Node: UpdateEscrowChannel(escrow_channel) - Node-->>Client: Sends ChannelUpdate and BalanceUpdate -``` - -### 8.1 Pack Final State - -``` -PackState(channelId, state) -> packedState -``` - -The Node prepares the final state for on-chain submission. - -### 8.2 Client Finalizes On-Chain - -``` -finalizeEscrowWithdrawal(escrowChannelId, packedState) -``` - -The **Client** submits a transaction to finalize the withdrawal: - -- Releases the Node's locked funds to the User on the escrow chain. -- User receives funds on the non-home chain. -- Emits `EscrowWithdrawalFinalized` event. - -At this point, the user's wallet on the EscrowChain receives the withdrawn funds. - -### 8.3 Node Event Handling - -The Node: - -1. **HandleEscrowWithdrawalFinalized** -- Processes the event. -2. **UpdateEscrowChannel** -- Marks escrow as completed. -3. Sends final **ChannelUpdate** and **BalanceUpdate** notifications. - ---- - -## Complete Flow Diagram - -```mermaid -sequenceDiagram - actor User - actor Client - actor Node - actor HomeChain - actor EscrowChain - - rect rgb(40, 40, 100) - Note over User,Client: Phase 1: Initiation - User->>Client: async withdraw(blockchainId, asset, amount) - end - - rect rgb(40, 100, 40) - Note over Client,Node: Phase 2-4: State Preparation - Client->>Node: GetLastState(UserWallet, asset) - Node->>Client: Returns state - Note over Client: Build escrow_lock state - Client->>Node: SubmitState(state, userSig) - Node->>Client: Return node signature - end - - rect rgb(150, 100, 40) - Note over Client,EscrowChain: Phase 5: On-Chain Escrow - Client->>EscrowChain: initiateEscrowWithdrawal(...) - EscrowChain-->>Node: EscrowWithdrawalInitiated Event - Node-->>Client: ChannelUpdate and BalanceUpdate - end - - rect rgb(100, 40, 100) - Note over Client,Node: Phase 6-7: Execution State - Client->>Node: GetLastState(UserWallet, asset) - Node->>Client: Returns state - Note over Client: Build escrow_withdraw state - Client->>Node: SubmitState(state, userSig) - Node->>Client: Return node signature - end - - rect rgb(100, 100, 40) - Note over Client,EscrowChain: Phase 8: Finalization - Client->>EscrowChain: finalizeEscrowWithdrawal(...) - EscrowChain-->>Node: EscrowWithdrawalFinalized Event - Node-->>Client: ChannelUpdate and BalanceUpdate - end -``` - ---- - -## Key Concepts Summary - -### State Transitions Overview - -```mermaid -flowchart LR - subgraph Preparation["Preparation Phase"] - A["Current State"] --> B["escrow_lock Transition"] - B --> C["Locked State"] - end - subgraph Execution["Execution Phase"] - C --> D["escrow_withdraw Transition"] - D --> E["Final State"] - end - - style Preparation fill:#1a1a2e,stroke:#16213e - style Execution fill:#0f3460,stroke:#16213e -``` - -### Comparison: Escrow Deposit vs Withdrawal - -| Aspect | Escrow Deposit | Escrow Withdrawal | -| --- | --- | --- | -| **Direction** | Non-Home to Home | Home to Non-Home | -| **Preparation Transition** | `mutual_lock` | `escrow_lock` | -| **Execution Transition** | `escrow_deposit` | `escrow_withdraw` | -| **Who initiates on-chain?** | Client (initiateEscrowDeposit) | Client (initiateEscrowWithdrawal) | -| **Who finalizes on-chain?** | Node (finalizeEscrowDeposit) | Client (finalizeEscrowWithdrawal) | -| **Who provides liquidity?** | Node on home chain | Node on escrow chain | - -### On-Chain vs Off-Chain Actions - -| Action | Chain | Who | Purpose | -| --- | --- | --- | --- | -| `SubmitState` (escrow_lock) | Off-chain (Node) | Client | Lock funds in preparation | -| `initiateEscrowWithdrawal` | **On-chain (Escrow)** | Client | Lock Node's liquidity | -| `SubmitState` (escrow_withdraw) | Off-chain (Node) | Client | Execute withdrawal | -| `finalizeEscrowWithdrawal` | **On-chain (Escrow)** | Client | Release funds to User | - -### Security Guarantees - -From the on-chain protocol: - -- **Preparation phase**: Node locks withdrawal liquidity on the non-home chain. -- **Execution phase**: Signed state updates allocations and net flows so that User receives funds on the non-home chain. -- If enforcement stalls, challenges and timeouts guarantee completion or reversion. - ---- - -## Error Recovery - -### What if the process stalls? - -| Scenario | Recovery | -| --- | --- | -| Node doesn't respond | User can challenge with the last signed state | -| On-chain transaction fails | Retry or wait for timeout to revert | -| Network issues | User can challenge or wait for timeout-based recovery | - -### Challenge Resolution - -If an escrow process is challenged and the challenge period expires without resolution: - -1. The finalize function handles this explicitly. -2. Manually unlocks the locked funds to the Node. -3. Sets status to `FINALIZED`. - -:::warning -If an escrow was challenged, then the on-chain channel **must also be challenged and closed**. It is not possible to continue operating a channel after any related escrow was challenged. -::: - ---- - -## Related Flows - -- [Transfer Communication Flow](./transfer-flow) -- [App Session Deposit Flow](./app-session-deposit) -- [Escrow Channel Deposit Flow](./escrow-deposit) -- [Home Channel Withdrawal Flow](./home-channel-withdrawal) diff --git a/docs/api-reference/_category_.json b/docs/nitrolite/api-reference/_category_.json similarity index 100% rename from docs/api-reference/_category_.json rename to docs/nitrolite/api-reference/_category_.json diff --git a/docs/nitrolite/api-reference/app-sessions-v1.md b/docs/nitrolite/api-reference/app-sessions-v1.md new file mode 100644 index 0000000..e5900ab --- /dev/null +++ b/docs/nitrolite/api-reference/app-sessions-v1.md @@ -0,0 +1,172 @@ +--- +title: app_sessions.v1 +description: Nitrolite v1 app-session RPC methods sourced from docs/api.yaml +displayed_sidebar: apiSidebar +sidebar_position: 4 +--- + +# app_sessions.v1 + +App-session methods create sessions, submit signed app-state updates, deposit into sessions, rebalance sessions, and manage app-session session keys. Send requests with the compact envelope described in [Interaction Model](../protocol/interaction-model): `[1, requestId, method, payload, timestamp]`. + +:::important Register the app first +`create_app_session` requires the application to exist in the app registry. If you receive `application_not_registered`, register it with [`apps.v1.submit_app_version`](./apps-v1#submit_app_version), then retry session creation. +::: + +## submit_deposit_state + +Submit an application session deposit state update. +SDK wrapper: `Client.submitAppSessionDeposit(appStateUpdate, quorumSigs, asset, depositAmount)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app_state_update` | [`app_state_update`](./types#app_state_update) | required | Application session state update. | +| `quorum_sigs` | `array` | required | App-session state update signatures. | +| `user_state` | [`state`](./types#state) | required | User state associated with the update. | + +| Response field | Type | Description | +| --- | --- | --- | +| `signature` | `string` | Node signature for the deposit state. | +Errors: `invalid_app_state`, `quorum_not_met`, `channel_not_found`. See [Errors](./errors). + +```json +[1, 2001, "app_sessions.v1.submit_deposit_state", { "app_state_update": { "app_session_id": "0xSession", "intent": "deposit", "version": "2", "allocations": [], "session_data": "{}" }, "quorum_sigs": ["0xSig"], "user_state": { "id": "0xState", "asset": "usdc" } }, 1741344819012] +``` + +## submit_app_state + +Submit an application session state update. +SDK wrapper: `Client.submitAppState(appStateUpdate, quorumSigs)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app_state_update` | [`app_state_update`](./types#app_state_update) | required | Application session state update. | +| `quorum_sigs` | `array` | required | App-session state update signatures required for quorum. | +Response: empty payload. +Errors: `invalid_app_state`, `quorum_not_met`, `ongoing_transition`. See [Errors](./errors). + +```json +[1, 2002, "app_sessions.v1.submit_app_state", { "app_state_update": { "app_session_id": "0xSession", "intent": "operate", "version": "3", "allocations": [], "session_data": "{\"move\":\"e4\"}" }, "quorum_sigs": ["0xSigA", "0xSigB"] }, 1741344819012] +``` + +## rebalance_app_sessions + +Rebalance multiple application sessions atomically. +SDK wrapper: `Client.rebalanceAppSessions(signedUpdates)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `signed_updates` | `array` | required | Signed app-state updates; each update must have intent `rebalance`. | + +| Response field | Type | Description | +| --- | --- | --- | +| `batch_id` | `string` | Unique identifier of the executed rebalance operation. | +Errors: none declared in `docs/api.yaml`. + +```json +[1, 2003, "app_sessions.v1.rebalance_app_sessions", { "signed_updates": [{ "app_state_update": { "app_session_id": "0xSession", "intent": "rebalance", "version": "4", "allocations": [], "session_data": "{}" }, "quorum_sigs": ["0xSig"] }] }, 1741344819012] +``` + +## get_app_definition + +Retrieve the application definition for a specific app session. +SDK wrapper: `Client.getAppDefinition(appSessionId)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app_session_id` | `string` | required | Application session ID. | + +| Response field | Type | Description | +| --- | --- | --- | +| `definition` | [`app_definition`](./types#app_definition) | Application definition. | +Errors: `app_session_not_found`. See [Errors](./errors). + +```json +[1, 2004, "app_sessions.v1.get_app_definition", { "app_session_id": "0xSession" }, 1741344819012] +``` + +## get_app_sessions + +List all application sessions for a participant with optional filtering. +SDK wrapper: `Client.getAppSessions({ appSessionId, wallet, status, page, pageSize })`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app_session_id` | `string` | optional | Filter by application session ID. | +| `participant` | `string` | optional | Filter by participant wallet address. | +| `status` | `string` | optional | Filter by `open` or `closed`. | +| `pagination` | [`pagination_params`](./types#pagination_params) | optional | Pagination parameters. | + +| Response field | Type | Description | +| --- | --- | --- | +| `app_sessions` | `array` | List of application sessions. | +| `metadata` | [`pagination_metadata`](./types#pagination_metadata) | Pagination information. | + +`metadata` is optional and may be absent when the response is not paginated. + +Errors: `invalid_parameters`. See [Errors](./errors). + +```json +[1, 2005, "app_sessions.v1.get_app_sessions", { "participant": "0xUser", "status": "open", "pagination": { "offset": 0, "limit": 20 } }, 1741344819012] +``` + +## create_app_session + +Create a new application session between participants. +SDK wrapper: `Client.createAppSession(definition, sessionData, quorumSigs, { ownerSig })`. + +Before calling this method, register the application with [`apps.v1.submit_app_version`](./apps-v1#submit_app_version). The `application_not_registered` error means the app registry does not know the `definition.application_id`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `definition` | [`app_definition`](./types#app_definition) | required | Application definition including participants and quorum. | +| `session_data` | `string` | required | JSON stringified session data. | +| `quorum_sigs` | `array` | required | Participant signatures for app-session creation. | +| `owner_sig` | `string` | optional | Owner signature, required when creation approval is required. | + +| Response field | Type | Description | +| --- | --- | --- | +| `app_session_id` | `string` | Created application session ID. | +| `version` | `string` | Initial session version. | +| `status` | `string` | Session status, normally `open`. | +Errors: `invalid_definition`, `application_not_registered`, `owner_sig_required`, `invalid_owner_signature`, `insufficient_balance`. See [Errors](./errors). + +```json +[1, 2006, "app_sessions.v1.create_app_session", { "definition": { "application_id": "demo-app", "participants": [{ "wallet_address": "0xUser", "signature_weight": 1 }], "quorum": 1, "nonce": "1" }, "session_data": "{}", "quorum_sigs": ["0xSig"] }, 1741344819012] +``` + +## submit_session_key_state + +Submit the app-session key state for registration and updates. +SDK wrapper: `Client.submitSessionKeyState(state)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `state` | [`app_session_key_state`](./types#app_session_key_state) | required | Session key metadata and delegation information. | +Response: empty payload. +Errors: `invalid_session_key_state`. See [Errors](./errors). + +```json +[1, 2007, "app_sessions.v1.submit_session_key_state", { "state": { "user_address": "0xUser", "session_key": "0xKey", "version": "1", "application_id": ["demo-app"], "app_session_id": ["0xSession"], "expires_at": "1770000000", "user_sig": "0xSig" } }, 1741344819012] +``` + +## get_last_key_states + +Retrieve latest app-session key states for a user, optionally filtered by session key. +SDK wrapper: `Client.getLastKeyStates(userAddress, sessionKey?)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `user_address` | `string` | required | User wallet address. | +| `session_key` | `string` | optional | Optional session key filter. | +| `pagination` | [`pagination_params`](./types#pagination_params) | optional | Pagination parameters. The `sort` field is not supported and must be omitted; maximum `limit` is 10. | + +| Response field | Type | Description | +| --- | --- | --- | +| `states` | `array` | Active app-session key states for the user. | +| `metadata` | [`pagination_metadata`](./types#pagination_metadata) | Pagination information. | +Errors: `account_not_found`. See [Errors](./errors). + +```json +[1, 2008, "app_sessions.v1.get_last_key_states", { "user_address": "0xUser", "session_key": "0xKey", "pagination": { "offset": 0, "limit": 10 } }, 1741344819012] +``` diff --git a/docs/nitrolite/api-reference/apps-v1.md b/docs/nitrolite/api-reference/apps-v1.md new file mode 100644 index 0000000..da10f64 --- /dev/null +++ b/docs/nitrolite/api-reference/apps-v1.md @@ -0,0 +1,54 @@ +--- +title: apps.v1 +description: Nitrolite v1 app registry RPC methods sourced from docs/api.yaml +displayed_sidebar: apiSidebar +sidebar_position: 5 +--- + +# apps.v1 + +App registry methods list registered applications and register new application versions. Send requests with the compact envelope described in [Interaction Model](../protocol/interaction-model): `[1, requestId, method, payload, timestamp]`. + +## get_apps + +Retrieve registered applications with optional filtering by app ID and owner wallet. + +SDK wrapper: `Client.getApps({ appId, ownerWallet, page, pageSize })`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app_id` | `string` | optional | Filter by application ID. | +| `owner_wallet` | `string` | optional | Filter by owner wallet address. | +| `pagination` | [`pagination_params`](./types#pagination_params) | optional | Pagination parameters. | + +| Response field | Type | Description | +| --- | --- | --- | +| `apps` | `array` | Registered applications. | +| `metadata` | [`pagination_metadata`](./types#pagination_metadata) | Pagination information. | + +Errors: `invalid_parameters`. See [Errors](./errors). + +```json +[1, 3001, "apps.v1.get_apps", { "owner_wallet": "0xOwner", "pagination": { "offset": 0, "limit": 20 } }, 1741344819012] +``` + +## submit_app_version + +Register a new application in the app registry. Currently only version `1` creation is supported, and the owner must sign the packed app data. + +SDK wrapper: `Client.registerApp(appID, metadata, creationApprovalNotRequired)`. + +This is the registration prerequisite for [`app_sessions.v1.create_app_session`](./app-sessions-v1#create_app_session). If app-session creation returns `application_not_registered`, submit the app version first and then retry session creation. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app` | [`app`](./types#app) | required | Application definition including ID, owner wallet, metadata, version, and creation approval flag. | +| `owner_sig` | `string` | required | Owner EIP-191 signature over the packed application data. | + +Response: empty payload. + +Errors: `invalid_app_id`, `invalid_version`, `invalid_signature`, `app_already_exists`. See [Errors](./errors). + +```json +[1, 3002, "apps.v1.submit_app_version", { "app": { "id": "demo-app", "owner_wallet": "0xOwner", "metadata": "0xMetadata", "version": "1", "creation_approval_not_required": true }, "owner_sig": "0xSig" }, 1741344819012] +``` diff --git a/docs/nitrolite/api-reference/channels-v1.md b/docs/nitrolite/api-reference/channels-v1.md new file mode 100644 index 0000000..aaab8a8 --- /dev/null +++ b/docs/nitrolite/api-reference/channels-v1.md @@ -0,0 +1,238 @@ +--- +title: channels.v1 +description: Nitrolite v1 channel RPC methods sourced from docs/api.yaml +displayed_sidebar: apiSidebar +sidebar_position: 3 +--- + +# channels.v1 + +Channel methods query home and escrow channels, fetch signed state, submit state transitions, and manage channel session keys. Send requests with the compact envelope described in [Interaction Model](../protocol/interaction-model): `[1, requestId, method, payload, timestamp]`. + +## get_home_channel + +Retrieve current on-chain home channel information. +SDK wrapper: `Client.getHomeChannel(wallet, asset)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `wallet` | `string` | required | User wallet address. | +| `asset` | `string` | required | Asset symbol. | + +| Response field | Type | Description | +| --- | --- | --- | +| `channel` | [`channel`](./types#channel) | On-chain channel information. | +Errors: `channel_not_found`. See [Errors](./errors). + +```json +[1, 1001, "channels.v1.get_home_channel", { "wallet": "0xUser", "asset": "usdc" }, 1741344819012] +``` + +## get_escrow_channel + +Retrieve current on-chain escrow channel information. +SDK wrapper: `Client.getEscrowChannel(escrowChannelId)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `escrow_channel_id` | `string` | required | Escrow channel ID. | + +| Response field | Type | Description | +| --- | --- | --- | +| `channel` | [`channel`](./types#channel) | On-chain channel information. | +Errors: `channel_not_found`. See [Errors](./errors). + +```json +[1, 1002, "channels.v1.get_escrow_channel", { "escrow_channel_id": "0xEscrowChannel" }, 1741344819012] +``` + +## get_channels + +Retrieve all channels for a user with optional filtering. +SDK wrapper: `Client.getChannels(wallet, { asset, status, pagination })`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `wallet` | `string` | required | User wallet address. | +| `asset` | `string` | optional | Filter by asset. | +| `status` | `string` | optional | Filter by status. | +| `pagination` | [`pagination_params`](./types#pagination_params) | optional | Pagination parameters. | + +| Response field | Type | Description | +| --- | --- | --- | +| `channels` | `array` | List of channels. | +| `metadata` | [`pagination_metadata`](./types#pagination_metadata) | Pagination information. | + +`metadata` is optional and may be absent when the response is not paginated. + +Errors: `invalid_parameters`. See [Errors](./errors). + +```json +[1, 1003, "channels.v1.get_channels", { "wallet": "0xUser", "asset": "usdc", "status": "open" }, 1741344819012] +``` + +## get_latest_state + +Retrieve the current state of the user stored on the Node. +SDK wrapper: `Client.getLatestState(wallet, asset, onlySigned)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `wallet` | `string` | required | User wallet address. | +| `asset` | `string` | required | Asset symbol. | +| `only_signed` | `boolean` | required | When true, returns only the latest signed state. | + +| Response field | Type | Description | +| --- | --- | --- | +| `state` | [`state`](./types#state) | Current state of the user. | +Errors: `channel_not_found`. See [Errors](./errors). + +```json +[1, 1004, "channels.v1.get_latest_state", { "wallet": "0xUser", "asset": "usdc", "only_signed": true }, 1741344819012] +``` + +## request_creation + +Request the creation of a channel from Node. +SDK wrapper: internal path used by `Client.deposit(...)` when the home channel does not exist. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `state` | [`state`](./types#state) | required | Initial state to submit. | +| `channel_definition` | [`channel_definition`](./types#channel_definition) | required | Definition of the channel to create. | + +| Response field | Type | Description | +| --- | --- | --- | +| `signature` | `string` | Node signature for the state. | +Errors: `invalid_channel_definition`, `invalid_state`, `channel_already_exists`. See [Errors](./errors). + +```json +[ + 1, + 1005, + "channels.v1.request_creation", + { + "state": { + "id": "0xState", + "transition": { + "type": "home_deposit", + "tx_id": "tx-1", + "account_id": "0xUser", + "amount": "10.0" + }, + "asset": "usdc", + "user_wallet": "0xUser", + "epoch": "1", + "version": "1", + "home_ledger": { + "token_address": "0xToken", + "blockchain_id": "11155111", + "user_balance": "10.0", + "user_net_flow": "10.0", + "node_balance": "0", + "node_net_flow": "0" + } + }, + "channel_definition": { "nonce": "1", "challenge": 86400 } + }, + 1741344819012 +] +``` + +## submit_state + +Submit a cross-chain channel state. +SDK wrapper: internal path used by `Client.deposit(...)`, `Client.transfer(...)`, `Client.checkpoint(...)`, and other channel-state transitions. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `state` | [`state`](./types#state) | required | Signed state to submit. | + +| Response field | Type | Description | +| --- | --- | --- | +| `signature` | `string` | Node signature for the state. | +Errors: `invalid_transition`, `ongoing_transition`, `channel_not_found`, `denied_until_checkpoint`. See [Errors](./errors). + +```json +[ + 1, + 1006, + "channels.v1.submit_state", + { + "state": { + "id": "0xState", + "transition": { + "type": "transfer_send", + "tx_id": "tx-2", + "account_id": "0xReceiver", + "amount": "1.0" + }, + "asset": "usdc", + "user_wallet": "0xUser", + "epoch": "1", + "version": "2", + "home_ledger": { + "token_address": "0xToken", + "blockchain_id": "11155111", + "user_balance": "9.0", + "user_net_flow": "9.0", + "node_balance": "1.0", + "node_net_flow": "1.0" + }, + "user_sig": "0xUserSig" + } + }, + 1741344819012 +] +``` + +## submit_session_key_state + +Submit the channel session key state for registration and updates. +SDK wrapper: `Client.submitChannelSessionKeyState(state)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `state` | [`channel_session_key_state`](./types#channel_session_key_state) | required | Session key metadata and delegation information. | +Response: empty payload. +Errors: `invalid_session_key_state`. See [Errors](./errors). + +```json +[1, 1007, "channels.v1.submit_session_key_state", { "state": { "user_address": "0xUser", "session_key": "0xKey", "version": "1", "assets": ["usdc"], "expires_at": "1770000000", "user_sig": "0xSig" } }, 1741344819012] +``` + +## get_last_key_states + +Retrieve latest channel session key states for a user, optionally filtered by session key. +SDK wrapper: `Client.getLastChannelKeyStates(userAddress, sessionKey?)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `user_address` | `string` | required | User wallet address. | +| `session_key` | `string` | optional | Optional session key filter. | +| `pagination` | [`pagination_params`](./types#pagination_params) | optional | Pagination parameters. The `sort` field is not supported and must be omitted; maximum `limit` is 10. | + +| Response field | Type | Description | +| --- | --- | --- | +| `states` | `array` | Active channel session key states for the user. | +| `metadata` | [`pagination_metadata`](./types#pagination_metadata) | Pagination information. | +Errors: `account_not_found`. See [Errors](./errors). + +```json +[1, 1008, "channels.v1.get_last_key_states", { "user_address": "0xUser", "session_key": "0xKey", "pagination": { "offset": 0, "limit": 10 } }, 1741344819012] +``` + +## Events + +### home_channel_created + +`docs/api.yaml` declares `home_channel_created` with `channel` and `initial_state` payload fields. + +:::note Future protocol revision +The [Interaction Model](../protocol/interaction-model#message-types) currently marks asynchronous event notifications as reserved for a future protocol revision. Treat `home_channel_created` as documented schema, not a stable event subscription guarantee. +::: + +| Payload field | Type | Description | +| --- | --- | --- | +| `channel` | [`channel`](./types#channel) | Created home channel information. | +| `initial_state` | [`state`](./types#state) | Initial state of the home channel. | diff --git a/docs/nitrolite/api-reference/errors.md b/docs/nitrolite/api-reference/errors.md new file mode 100644 index 0000000..0e632e1 --- /dev/null +++ b/docs/nitrolite/api-reference/errors.md @@ -0,0 +1,37 @@ +--- +title: Errors +description: Aggregated Nitrolite v1 RPC error messages sourced from docs/api.yaml +displayed_sidebar: apiSidebar +sidebar_position: 9 +--- + +# Errors + +RPC failures are returned as error response messages in the envelope described by [Interaction Model](../protocol/interaction-model#error-handling). Surface the message safely to users, log the method and request ID, and guide the user to the next recoverable action where one exists. + +| Error message | Returned by | User-facing handling | +| --- | --- | --- | +| `account_not_found` | `channels.v1.get_last_key_states`, `app_sessions.v1.get_last_key_states`, `user.v1.get_balances` | Ask the user to reconnect or verify the wallet address. | +| `app_already_exists` | `apps.v1.submit_app_version` | Treat registration as complete if the app metadata matches; otherwise choose a new app ID. | +| `app_session_not_found` | `app_sessions.v1.get_app_definition` | Refresh the session list before retrying. | +| `application_not_registered` | `app_sessions.v1.create_app_session` | Register the app with [`apps.v1.submit_app_version`](./apps-v1#submit_app_version), then retry. | +| `channel_already_exists` | `channels.v1.request_creation` | Fetch the existing channel and continue from its latest state. | +| `channel_not_found` | `channels.v1.get_home_channel`, `channels.v1.get_escrow_channel`, `channels.v1.get_latest_state`, `channels.v1.submit_state`, `app_sessions.v1.submit_deposit_state` | Create or fund the required channel, or refresh local channel IDs. | +| `denied_until_checkpoint` | `channels.v1.submit_state` | Run the checkpoint flow before submitting another state. | +| `insufficient_balance` | `app_sessions.v1.create_app_session` | Ask the user to deposit or reduce allocations. | +| `invalid_app_id` | `apps.v1.submit_app_version` | Fix the application ID format. | +| `invalid_app_state` | `app_sessions.v1.submit_deposit_state`, `app_sessions.v1.submit_app_state` | Rebuild the app-state update from the latest session version. | +| `invalid_channel_definition` | `channels.v1.request_creation` | Recompute the channel definition from SDK helpers. | +| `invalid_definition` | `app_sessions.v1.create_app_session` | Validate participants, quorum, nonce, and application ID. | +| `invalid_owner_signature` | `app_sessions.v1.create_app_session` | Re-sign creation approval with the registered owner wallet. | +| `invalid_parameters` | `channels.v1.get_channels`, `app_sessions.v1.get_app_sessions`, `apps.v1.get_apps` | Fix filters and pagination parameters. | +| `invalid_session_key_state` | `channels.v1.submit_session_key_state`, `app_sessions.v1.submit_session_key_state` | Rebuild the session key state and verify expiration, scope, and user signature. | +| `invalid_signature` | `apps.v1.submit_app_version` | Re-sign packed app data with the owner wallet. | +| `invalid_state` | `channels.v1.request_creation` | Rebuild and sign the state from canonical SDK state helpers. | +| `invalid_transition` | `channels.v1.submit_state` | Recompute the next state from the latest node-signed state. | +| `invalid_version` | `apps.v1.submit_app_version` | Submit version `1`; other versions are not supported yet. | +| `ongoing_transition` | `channels.v1.submit_state`, `app_sessions.v1.submit_app_state` | Poll latest state or app session until the pending transition settles. | +| `owner_sig_required` | `app_sessions.v1.create_app_session` | Include `owner_sig` because the registered app requires owner approval. | +| `quorum_not_met` | `app_sessions.v1.submit_deposit_state`, `app_sessions.v1.submit_app_state` | Collect enough participant signatures to satisfy quorum. | +| `retrieval_failed` | `user.v1.get_action_allowances` | Retry after reconnecting; log the failure for operators. | +| `wallet_required` | `user.v1.get_action_allowances` | Request a connected wallet address before calling the method. | diff --git a/docs/nitrolite/api-reference/index.md b/docs/nitrolite/api-reference/index.md new file mode 100644 index 0000000..a108e2e --- /dev/null +++ b/docs/nitrolite/api-reference/index.md @@ -0,0 +1,44 @@ +--- +title: API Reference +description: Canonical Nitrolite v1 RPC reference sourced from docs/api.yaml +displayed_sidebar: apiSidebar +sidebar_position: 1 +--- + +# API Reference + +The **Protocol** tab describes the semantic model: envelope structure, channel lifecycle, state-and-ledger model, enforcement guarantees, and other conceptual pieces. **API Reference** catalogs the concrete RPC methods that bind that model to the wire. Most app builders never read this tab directly: the TypeScript and Go SDKs build the envelopes, sign requests, and surface every method below as a typed function call. Reach for hand-rolled RPC only when you are integrating from a language without an official SDK, building a thin proxy or wire debugger, or generating code against `docs/api.yaml`. + +This is the canonical Nitrolite v1 RPC surface, sourced from `docs/api.yaml` in the Nitrolite repository. Method names are grouped by namespace and version, for example `channels.v1.submit_state`. + +RPC messages use the compact wire envelope described in [Interaction Model](../protocol/interaction-model): `[type, requestId, method, payload, timestamp]`. A request uses type `1`, a successful response uses type `2`, an event uses type `3`, and an error response uses type `4`. + +```json +[1, 1001, "channels.v1.get_home_channel", { "wallet": "0x...", "asset": "usdc" }, 1741344819012] +``` + +:::info Migrating from 0.5.3? +New integrations should use the v1 SDKs, not `@erc7824/nitrolite@0.5.3`. For a full rewrite, start with the [native TypeScript SDK](../build/sdk/typescript/getting-started). For a lower-risk migration that preserves selected 0.5.3 helper shapes while you move to v1, start with the [TypeScript compat SDK](../build/sdk/typescript-compat/overview). This section documents the v1 RPC methods, not the legacy helper surface. +::: + +## Use the SDKs first + +The official SDKs are the recommended way to call every method documented here: + +- **TypeScript:** [`@yellow-org/sdk`](../build/sdk/typescript/getting-started) for new apps, or [`@yellow-org/sdk-compat`](../build/sdk/typescript-compat/overview) for migrations from `@erc7824/nitrolite@0.5.3`. You get IDE autocomplete, parameter checking, discriminated-union response types, and correct amount-unit handling out of the box. +- **Go:** [`github.com/layer-3/nitrolite/sdk/go`](../build/sdk/go/getting-started) for backends and CLI tooling, with typed structs for every request and response. +- **MCP server (`@yellow-org/sdk-mcp`, coming soon):** ask `lookup_rpc_method` for a method by name, for example `channels.v1.submit_state`, and `lookup_method` to find the matching SDK call. The MCP indexes the same `docs/api.yaml` surface and is intended for agentic browsing inside coding tools. + +The SDKs handle the `[type, requestId, method, payload, timestamp]` envelope, signing, response correlation, event streams, auth/session-key plumbing, and amount-unit conversions. Reach for the catalogue below when you genuinely need to inspect or generate against the wire surface. + +## Groups + +| Group | Page | Purpose | +| --- | --- | --- | +| `channels.v1` | [Channels](./channels-v1) | Channel queries, state submission, channel session keys, and channel events. | +| `app_sessions.v1` | [App Sessions](./app-sessions-v1) | App-session creation, updates, deposits, rebalancing, and app-session keys. | +| `apps.v1` | [Apps](./apps-v1) | App registry lookup and application registration. | +| `user.v1` | [User](./user-v1) | Balances, transaction history, and action allowances. | +| `node.v1` | [Node](./node-v1) | Connectivity, node configuration, and supported assets. | +| Types | [Types](./types) | Field tables for all shared request and response types. | +| Errors | [Errors](./errors) | Aggregated error messages and suggested user-facing handling. | diff --git a/docs/nitrolite/api-reference/node-v1.md b/docs/nitrolite/api-reference/node-v1.md new file mode 100644 index 0000000..69246c4 --- /dev/null +++ b/docs/nitrolite/api-reference/node-v1.md @@ -0,0 +1,62 @@ +--- +title: node.v1 +description: Nitrolite v1 node RPC methods sourced from docs/api.yaml +displayed_sidebar: apiSidebar +sidebar_position: 7 +--- + +# node.v1 + +Node methods check connectivity and read the node configuration and supported assets. Send requests with the compact envelope described in [Interaction Model](../protocol/interaction-model): `[1, requestId, method, payload, timestamp]`. + +## ping + +Simple connectivity check. + +SDK wrapper: `Client.ping()`. + +Request: empty payload. Response: empty payload. Errors: none declared in `docs/api.yaml`. + +```json +[1, 5001, "node.v1.ping", {}, 1741344819012] +``` + +## get_config + +Retrieve broker configuration and supported networks. + +SDK wrapper: `Client.getConfig()`. + +Request: empty payload. + +| Response field | Type | Description | +| --- | --- | --- | +| `node_address` | `string` | Node wallet address. | +| `node_version` | `string` | Node software version. | +| `blockchains` | `array` | Supported networks. | + +Errors: none declared in `docs/api.yaml`. + +```json +[1, 5002, "node.v1.get_config", {}, 1741344819012] +``` + +## get_assets + +Retrieve all supported assets with optional blockchain filter. + +SDK wrapper: `Client.getAssets(blockchainId?)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `blockchain_id` | `string` | optional | Filter by blockchain network ID. | + +| Response field | Type | Description | +| --- | --- | --- | +| `assets` | `array` | Supported assets, filtered by blockchain if provided. | + +Errors: none declared in `docs/api.yaml`. + +```json +[1, 5003, "node.v1.get_assets", { "blockchain_id": "11155111" }, 1741344819012] +``` diff --git a/docs/nitrolite/api-reference/types.md b/docs/nitrolite/api-reference/types.md new file mode 100644 index 0000000..06aff56 --- /dev/null +++ b/docs/nitrolite/api-reference/types.md @@ -0,0 +1,260 @@ +--- +title: Types +description: Shared Nitrolite v1 RPC types sourced from docs/api.yaml +displayed_sidebar: apiSidebar +sidebar_position: 2 +--- + +# Types + +These field tables mirror the `types:` section of `docs/api.yaml`. Optional fields are marked in the presence column. + +## channel + +Represents an on-chain channel. + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `channel_id` | `string` | required | Unique identifier for the channel. | +| `user_wallet` | `string` | required | User wallet address. | +| `type` | `string` | required | Type of the channel: `home` or `escrow`. | +| `blockchain_id` | `string` | required | Unique identifier for the blockchain. | +| `token_address` | `string` | required | Address of the token used in the channel. | +| `challenge_duration` | `integer` | required | Challenge period for the channel in seconds. | +| `challenge_expires_at` | `string` | optional | Challenge expiration timestamp in Unix seconds. | +| `nonce` | `string` | required | Nonce for the channel. | +| `status` | `string` | required | Current status: `void`, `open`, `challenged`, or `closed`. | +| `state_version` | `string` | required | On-chain state version of the channel. | + +## transition_type + +Type of state transition. Values: `transfer_receive`, `transfer_send`, `release`, `commit`, `home_deposit`, `home_withdrawal`, `mutual_lock`, `escrow_deposit`, `escrow_lock`, `escrow_withdraw`, `migrate`, `finalize`. + +## transition + +Represents a state transition. + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `type` | `transition_type` | required | Type of state transition. | +| `tx_id` | `string` | required | Transaction ID associated with the transition. | +| `account_id` | `string` | required | Account identifier, varying by transition type. | +| `amount` | `string` | required | Amount involved in the transition. | + +## state + +Represents the current state of the user stored on Node. + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `id` | `string` | required | Deterministic ID hash of the state. | +| `transition` | `transition` | required | State transition that led to this state. | +| `asset` | `string` | required | Asset type of the state. | +| `user_wallet` | `string` | required | User wallet address. | +| `epoch` | `string` | required | User epoch index. | +| `version` | `string` | required | Version of the state. | +| `home_channel_id` | `string` | optional | Identifier for the home channel. | +| `escrow_channel_id` | `string` | optional | Identifier for the escrow channel. | +| `home_ledger` | `ledger` | required | User and node balances for the home channel. | +| `escrow_ledger` | `ledger` | optional | User and node balances for the escrow channel. | +| `user_sig` | `string` | optional | User signature for the state. | +| `node_sig` | `string` | optional | Node signature for the state. | + +## ledger + +Represents ledger balances. + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `token_address` | `string` | required | Address of the token used in this channel. | +| `blockchain_id` | `string` | required | Unique identifier for the blockchain. | +| `user_balance` | `string` | required | User balance in the channel. | +| `user_net_flow` | `string` | required | User net flow in the channel. | +| `node_balance` | `string` | required | Node balance in the channel. | +| `node_net_flow` | `string` | required | Node net flow in the channel. | + +## channel_definition + +Configuration for creating a channel. + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `nonce` | `string` | required | Unique number to prevent replay attacks. | +| `challenge` | `integer` | required | Challenge period for the channel in seconds. | + +## app_participant + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `wallet_address` | `string` | required | Participant wallet address. | +| `signature_weight` | `integer` | required | Signature weight for the participant. | + +## app_definition + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `application_id` | `string` | required | Application identifier from an app registry. | +| `participants` | `array` | required | List of participants in the app session. | +| `quorum` | `integer` | required | Quorum required for the app session. | +| `nonce` | `string` | required | Unique number to prevent replay attacks. | + +## app_allocation + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `participant` | `string` | required | Participant wallet address. | +| `asset` | `string` | required | Asset symbol. | +| `amount` | `string` | required | Amount allocated to the participant. | + +## app_state_update + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app_session_id` | `string` | required | Unique application session identifier. | +| `intent` | `string` | required | Update intent: `operate`, `deposit`, `withdraw`, `close`, or `rebalance`. | +| `version` | `string` | required | Version of the app state. | +| `allocations` | `array` | required | Allocations in the app state. | +| `session_data` | `string` | required | JSON stringified session data. | + +## signed_app_state_update + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app_state_update` | `app_state_update` | required | Application session state update. | +| `quorum_sigs` | `array` | required | Signature quorum for the application session. | + +## token + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `name` | `string` | required | Token name. | +| `symbol` | `string` | required | Token symbol. | +| `address` | `string` | required | Token contract address. | +| `blockchain_id` | `string` | required | Blockchain network ID. | +| `decimals` | `integer` | required | Number of decimal places. | + +## asset + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `name` | `string` | required | Asset name. | +| `symbol` | `string` | required | Asset symbol. | +| `decimals` | `integer` | required | Asset decimal places. | +| `suggested_blockchain_id` | `string` | required | Suggested blockchain network ID for this asset. | +| `tokens` | `array` | required | Supported tokens for the asset. | + +## blockchain_info + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `name` | `string` | required | Blockchain name. | +| `blockchain_id` | `string` | required | Blockchain network ID. | +| `contract_address` | `string` | required | Main contract address on this blockchain. | + +## balance_entry + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `asset` | `string` | required | Asset symbol. | +| `amount` | `string` | required | Balance amount. | +| `enforced` | `string` | required | On-chain enforced balance. | + +## transaction_type + +Type of transaction. Values: `transfer`, `release`, `commit`, `home_deposit`, `home_withdrawal`, `mutual_lock`, `escrow_deposit`, `escrow_lock`, `escrow_withdraw`, `migrate`, `rebalance`, `finalize`. + +## transaction + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `id` | `string` | required | Unique transaction reference. | +| `asset` | `string` | required | Asset symbol. | +| `tx_type` | `transaction_type` | required | Transaction type. | +| `from_account` | `string` | required | Account that sent the funds. | +| `to_account` | `string` | required | Account that received the funds. | +| `sender_new_state_id` | `string` | optional | ID of the sender's new channel state. | +| `receiver_new_state_id` | `string` | optional | ID of the receiver's new channel state. | +| `amount` | `string` | required | Transaction amount. | +| `created_at` | `string` | required | Creation timestamp. | + +## app_session_info + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `app_session_id` | `string` | required | Unique application session identifier. | +| `status` | `string` | required | Session status: `open` or `closed`. | +| `app_definition` | `app_definition` | required | Application definition for this session. | +| `session_data` | `string` | optional | JSON stringified session data. | +| `version` | `string` | required | Current version of the session state. | +| `allocations` | `array` | required | Allocations in the app state. | + +## channel_session_key_state + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `user_address` | `string` | required | User wallet address. | +| `session_key` | `string` | required | Session key address for delegation. | +| `version` | `string` | required | Session key format version. | +| `assets` | `array` | required | Assets associated with this session key. | +| `expires_at` | `string` | required | Unix timestamp in seconds when the key expires. | +| `user_sig` | `string` | required | User signature authorizing the registration or update. | + +## app_session_key_state + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `user_address` | `string` | required | User wallet address. | +| `session_key` | `string` | required | Session key address for delegation. | +| `version` | `string` | required | Session key format version. | +| `application_id` | `array` | required | Application IDs associated with this session key. | +| `app_session_id` | `array` | required | App-session IDs associated with this session key. | +| `expires_at` | `string` | required | Unix timestamp in seconds when the key expires. | +| `user_sig` | `string` | required | User signature authorizing the registration or update. | + +## app + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `id` | `string` | required | Application identifier. | +| `owner_wallet` | `string` | required | Owner wallet address. | +| `metadata` | `string` | required | Application metadata bytes32 hash. | +| `version` | `string` | required | Current application version. | +| `creation_approval_not_required` | `boolean` | required | Whether app sessions can be created without owner approval. | + +## app_info + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `id` | `string` | required | Application identifier. | +| `owner_wallet` | `string` | required | Owner wallet address. | +| `metadata` | `string` | required | Application metadata bytes32 hash. | +| `version` | `string` | required | Current application version. | +| `creation_approval_not_required` | `boolean` | required | Whether app sessions can be created without owner approval. | +| `created_at` | `string` | required | Creation timestamp in Unix seconds. | +| `updated_at` | `string` | required | Last update timestamp in Unix seconds. | + +## action_allowance + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `gated_action` | `string` | required | Gated action: `transfer`, `app_session_deposit`, `app_session_operation`, or `app_session_withdrawal`. | +| `time_window` | `string` | required | Time window for the allowance, for example `24h0m0s`. | +| `allowance` | `string` | required | Total allowance within the window. | +| `used` | `string` | required | Amount already used within the window. | + +## pagination_params + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `offset` | `integer` | optional | Number of items to skip. | +| `limit` | `integer` | optional | Number of items to return. | +| `sort` | `string` | optional | Sort order: `asc` or `desc`. | + +## pagination_metadata + +| Field | Type | Presence | Description | +| --- | --- | --- | --- | +| `page` | `integer` | required | Current page number. | +| `per_page` | `integer` | required | Number of items per page. | +| `total_count` | `integer` | required | Total number of items. | +| `page_count` | `integer` | required | Total number of pages. | diff --git a/docs/nitrolite/api-reference/user-v1.md b/docs/nitrolite/api-reference/user-v1.md new file mode 100644 index 0000000..597971a --- /dev/null +++ b/docs/nitrolite/api-reference/user-v1.md @@ -0,0 +1,78 @@ +--- +title: user.v1 +description: Nitrolite v1 user RPC methods sourced from docs/api.yaml +displayed_sidebar: apiSidebar +sidebar_position: 6 +--- + +# user.v1 + +User methods read balances, transaction history, and action allowances. Send requests with the compact envelope described in [Interaction Model](../protocol/interaction-model): `[1, requestId, method, payload, timestamp]`. + +## get_balances + +Retrieve the balances of the user in YN. + +SDK wrapper: `Client.getBalances(wallet)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `wallet` | `string` | required | User wallet address. | + +| Response field | Type | Description | +| --- | --- | --- | +| `balances` | `array` | List of asset balances. | + +Errors: `account_not_found`. See [Errors](./errors). + +```json +[1, 4001, "user.v1.get_balances", { "wallet": "0xUser" }, 1741344819012] +``` + +## get_transactions + +Retrieve ledger transaction history with optional filtering. + +SDK wrapper: `Client.getTransactions(wallet, options)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `wallet` | `string` | required | User wallet address. | +| `asset` | `string` | optional | Filter by asset symbol. | +| `tx_type` | [`transaction_type`](./types#transaction_type) | optional | Filter by transaction type. | +| `pagination` | [`pagination_params`](./types#pagination_params) | optional | Pagination parameters. | +| `from_time` | `integer` | optional | Start timestamp filter. | +| `to_time` | `integer` | optional | End timestamp filter. | + +| Response field | Type | Description | +| --- | --- | --- | +| `transactions` | `array` | Ledger transactions. | +| `metadata` | [`pagination_metadata`](./types#pagination_metadata) | Pagination information. | + +`metadata` is optional and may be absent when the response is not paginated. + +Errors: none declared in `docs/api.yaml`. + +```json +[1, 4002, "user.v1.get_transactions", { "wallet": "0xUser", "asset": "usdc", "pagination": { "offset": 0, "limit": 20 } }, 1741344819012] +``` + +## get_action_allowances + +Retrieve action allowances for a user based on their staking level. + +SDK wrapper: `Client.getActionAllowances(wallet)`. + +| Request field | Type | Presence | Description | +| --- | --- | --- | --- | +| `wallet` | `string` | required | User wallet address. | + +| Response field | Type | Description | +| --- | --- | --- | +| `allowances` | `array` | Action allowances. | + +Errors: `wallet_required`, `retrieval_failed`. See [Errors](./errors). + +```json +[1, 4003, "user.v1.get_action_allowances", { "wallet": "0xUser" }, 1741344819012] +``` diff --git a/docs/build/_category_.json b/docs/nitrolite/build/_category_.json similarity index 100% rename from docs/build/_category_.json rename to docs/nitrolite/build/_category_.json diff --git a/docs/nitrolite/build/examples.mdx b/docs/nitrolite/build/examples.mdx new file mode 100644 index 0000000..47afe45 --- /dev/null +++ b/docs/nitrolite/build/examples.mdx @@ -0,0 +1,156 @@ +--- +title: Examples +description: Deployed Nitrolite example applications, their source repositories, stacks, and SDK usage. +sidebar_position: 4 +--- + +# Examples + +These deployed examples show different ways to build on Nitrolite. Use the live apps to understand the product flow, then inspect the source repositories for integration details. + +:::note +Stack and SDK details reflect the linked repositories when this page was added. The repositories are the source of truth if an example changes after deployment. +::: + +
+
+
+ + Co-Sign Checkout front page + +
+
+

Co-Sign Checkout

+
+
+

A shared checkout demo where two participants create a cart, co-sign checkout actions, move funds into an app-session cart, propose purchases, close the cart, and withdraw remaining shared-wallet funds.

+
+
+

Functionality

+
    +
  • Two-party shared cart and approval flow.
  • +
  • Shared Wallet plus Checkout Cart app session.
  • +
  • Purchase proposals, cart close, and wallet withdrawal.
  • +
  • YUSD and YELLOW on Ethereum Sepolia.
  • +
+
+
+

Stack

+
    +
  • Next.js, React, TypeScript, Tailwind CSS.
  • +
  • Supabase rooms, proposals, and events.
  • +
  • Vercel deployment with scheduled proposal expiration.
  • +
  • MetaMask SDK and viem.
  • +
+
+
+

SDKs

+
    +
  • @yellow-org/sdk
  • +
  • @yellow-org/sdk-compat
  • +
+
+
+
+ +
+
+
+ +
+
+ + Nitrolite Store front page + +
+
+

Nitrolite Store

+
+
+

A content-store reference app with a Go backend and browser frontend. The shopper connects MetaMask, opens a store app session, adds funds, buys catalog content, reads purchased content, and withdraws the remaining balance.

+
+
+

Functionality

+
    +
  • MetaMask shopper identity with backend app signing.
  • +
  • Frontend-constructed app-session updates.
  • +
  • Catalog purchase and content gating.
  • +
  • YUSD and YELLOW pricing on Ethereum Sepolia.
  • +
+
+
+

Stack

+
    +
  • Go service serving API and built web UI.
  • +
  • React, Vite, TypeScript frontend.
  • +
  • SQLite persistence, Docker, Railway.
  • +
  • MetaMask and viem in the browser.
  • +
+
+
+

SDKs

+
    +
  • github.com/layer-3/nitrolite/sdk/go
  • +
  • @yellow-org/sdk
  • +
+
+
+
+ +
+
+
+ +
+
+ + Nexus P2P Transfer front page + +
+
+

Nexus P2P Transfer

+
+
+

A workshop-style content app that demonstrates wallet connection, Nitrolite session setup, balance polling, and instant peer-to-peer support payments to post authors.

+
+
+

Functionality

+
    +
  • MetaMask connection and Sepolia network selection.
  • +
  • Nitrolite client session with balance polling.
  • +
  • Small support transfers to author wallets.
  • +
  • YUSD and YELLOW asset selection.
  • +
+
+
+

Stack

+
    +
  • Preact with Hooks, TypeScript, and Vite.
  • +
  • CSS Modules for app styling.
  • +
  • viem for wallet access and amount conversion.
  • +
  • Vercel-hosted static frontend.
  • +
+
+
+

SDKs

+
    +
  • @yellow-org/sdk
  • +
  • @yellow-org/sdk-compat
  • +
+
+
+
+ +
+
+
+
diff --git a/docs/learn/getting-started/_category_.json b/docs/nitrolite/build/getting-started/_category_.json similarity index 100% rename from docs/learn/getting-started/_category_.json rename to docs/nitrolite/build/getting-started/_category_.json diff --git a/docs/nitrolite/build/getting-started/key-terms.mdx b/docs/nitrolite/build/getting-started/key-terms.mdx new file mode 100644 index 0000000..82bdbac --- /dev/null +++ b/docs/nitrolite/build/getting-started/key-terms.mdx @@ -0,0 +1,102 @@ +--- +sidebar_position: 3 +title: Key Terms & Mental Models +description: Vocabulary for the v1 Nitrolite SDK, protocol, and docs. +keywords: [terminology, glossary, concepts, state channels, mental models] +--- + +# Key Terms & Mental Models + +Nitrolite uses one shared vocabulary across the SDK, protocol docs, and on-chain contracts. Learn these terms first and the rest of the docs become much easier to scan. + +:::info Canonical source +The protocol source of truth is [Protocol Terminology](../../protocol/terminology). Use this page as the builder-facing quick reference for the SDK docs. +::: + +## Core mental model + +Nitrolite lets a user and Nitronode exchange signed channel states off-chain, then enforce the latest mutually signed state on-chain when needed. The user experience is fast because most updates are signatures, not blockchain transactions. The safety model still has an on-chain exit through ChannelHub. + +## Essential vocabulary + +| Term | What it means | Builder cue | +|------|---------------|-------------| +| **Channel** | A state container shared by a user and Nitronode for one unified asset. | The thing your deposits, transfers, withdrawals, and checkpoints update. | +| **Home Channel** (for a specific Asset) | The default user-Nitronode channel whose home ledger for the Asset is enforced on its home chain. | Most apps start here. `deposit()`, `transfer()`, and `checkpoint()` act on the home channel for an asset. | +| **Escrow Channel** | A temporary cross-chain channel derived from a home channel for escrow operations. | You meet this when funds move between chains. | +| **Channel State** | The current agreed channel configuration: ledgers, version, and transition data. | SDK state operations return a state before you decide whether to checkpoint it. | +| **Channel Definition** | The immutable channel parameters: user, node, asset, nonce, challenge duration, and signature validators. | Fixed at channel creation. Do not model it as app-level mutable config. | +| **Ledger** | A record of asset allocations and net flows for a specific blockchain inside a channel state. | Ledger math must balance exactly. | +| **Home Ledger** | The ledger tied to the chain where the current channel state is enforced. | This is the authoritative ledger for checkpoint and challenge flows. | +| **Non-Home Ledger** | A secondary ledger for a blockchain other than the home chain. | Used by escrow and migration flows, not as a synonym for home channel. | +| **(Node) Vault** | Nitronode's per-chain pool of available funds used to cover required locking during transitions. | Cross-chain availability depends on node liquidity in the right vault. | +| **Asset** | A logical value unit, such as USDC, with a symbol and decimal precision independent of any one chain. | The SDK takes asset symbols like `'usdc'`, not token addresses, for high-level channel operations. | +| **Token** | The per-blockchain contract representation of an Asset. | Nitronode asset config maps an asset symbol to the token address used on each supported chain. | +| **Decimal** | An exact decimal amount represented with `decimal.js`. | Use `new Decimal(5)`, not JavaScript floating-point numbers, for SDK amounts. | +| **App Session** | An application extension that commits channel funds into a multi-party off-chain session. | Games, matching, escrow, and other multi-party flows live here. | +| **Quorum** | The minimum weighted approval needed for an app session state update. | If weights are `[40, 40, 50]` and quorum is `80`, two participants may be enough. | +| **Session Key** | A delegated signing key authorized by a participant's primary key for a scoped time window. | Useful for repeated app signatures without prompting the primary wallet every time. | +| **Challenge** | An on-chain dispute where a participant submits a signed state and opens the challenge window. | Lets an honest party enforce a newer valid state before timeout. | +| **Checkpoint** | The operation of submitting a signed state to the blockchain layer for enforcement. | `client.checkpoint('usdc')` settles the latest agreed state for that asset. | +| **Nitronode** | The v1 off-chain coordinator that serves RPC, co-signs valid states, tracks channels, and coordinates app sessions. | This is the node your SDK client connects to over WebSocket. | +| **ChannelHub** | The v1 on-chain entrypoint for channel create, deposit, withdraw, checkpoint, challenge, and close operations. | The SDK hides most contract calls, but this is the contract enforcing the state. | +| **Migration** | A transition that moves the home-chain channel of a specific Asset to another blockchain. | Treat it as protocol state enforcement configuration, not a package upgrade. | +| **Transition** | A typed operation explaining why the channel state changed. | Every state advancement carries one transition type. | + +## Channel lifecycle statuses + +`Channel.status` uses these v1 values: + +| Status | Meaning | +|--------|---------| +| `void` | No channel exists yet. | +| `open` | The channel is active. | +| `challenged` | A challenge is active on-chain. | +| `closed` | The channel has been finalized. | + +## Transition types + +These are the v1 `transition_type` literals from `docs/api.yaml`: + +| Literal | Meaning | +|---------|---------| +| `transfer_receive` | Receive side of an off-chain transfer. | +| `transfer_send` | Send side of an off-chain transfer. | +| `release` | Return funds from an extension back to the channel. | +| `commit` | Move funds from the channel into an extension. | +| `home_deposit` | Add funds to the home channel. | +| `home_withdrawal` | Remove funds from the home channel. | +| `mutual_lock` | Lock funds through mutual agreement. | +| `escrow_deposit` | Start or advance cross-chain escrow deposit flow. | +| `escrow_lock` | Lock escrow funds while a cross-chain flow is pending. | +| `escrow_withdraw` | Withdraw through an escrow flow. | +| `migrate` | Move the channel home-chain relationship. | +| `finalize` | Finalize and close the channel state. | + +## Mental models that prevent bugs + +### States first, settlement second + +High-level SDK calls such as `deposit()`, `withdraw()`, and `transfer()` prepare signed state updates. `checkpoint()` is the separate step that submits a state on-chain. + +### Amounts are decimals, ledgers are exact + +The builder-facing SDK uses `Decimal` values. The protocol then preserves exact ledger accounting using the asset's configured precision. This avoids rounding bugs when the same logical asset exists on multiple chains. + +### App sessions spend committed channel funds + +An app session does not replace a channel. It is an extension that receives funds through `commit` transitions and returns funds through `release` transitions. + +### Challenges are the escape hatch + +If cooperation fails, a participant can use a challenge to force the latest valid state into the on-chain flow. The challenge window gives the counterparty time to respond with a newer valid state. + +## v0.5.x -> v1 term map + +| v0.5.x term | v1 term to use now | +|-------------|--------------------| +| Clearnode | Nitronode | +| VirtualApp | App Session, or the specific SDK/client API you are using | +| Custody Contract | ChannelHub | +| Adjudicator | ChannelHub validators and engine logic | +| NitroRPC/0.4 | v1 RPC | diff --git a/docs/nitrolite/build/getting-started/prerequisites.mdx b/docs/nitrolite/build/getting-started/prerequisites.mdx new file mode 100644 index 0000000..d73d9fb --- /dev/null +++ b/docs/nitrolite/build/getting-started/prerequisites.mdx @@ -0,0 +1,252 @@ +--- +sidebar_position: 2 +title: Prerequisites & Environment +description: Set up a Node 20 TypeScript environment for @yellow-org/sdk. +keywords: [prerequisites, setup, development, environment, Node.js, viem] +--- + +# Prerequisites & Environment + +Use this checklist to prepare a local TypeScript project for the v1 Nitrolite SDK. + +:::tip Choosing your SDK +New v1 app: use [`@yellow-org/sdk`](../sdk/typescript/getting-started). Migrating from 0.5.3: read the [migration overview](../sdk/typescript-compat/overview) before moving native flows over. See the [SDK overview](../sdk/) for the full decision tree. +::: + +## System Requirements + +| Requirement | Minimum | Recommended | +|-------------|---------|-------------| +| **Node.js** | 20.x | 20.x or later | +| **npm/yarn/pnpm** | Latest stable | Latest stable | +| **Operating System** | macOS, Linux, Windows | macOS or Linux | + +The package declares `engines.node >=20.0.0`. Some package managers only warn on an older runtime, but the SDK and its dependencies are tested against Node 20 or later. + +## Required Knowledge + +Before building on Yellow Network, you should be comfortable with: + +| Topic | Why It Matters | +|-------|----------------| +| **JavaScript/TypeScript** | SDK and examples are in TypeScript | +| **Async/await patterns** | All network operations are asynchronous | +| **Basic Web3 concepts** | Wallets, transactions, signatures | +| **ERC-20 tokens** | Fund management involves token operations | + +:::tip New to Web3? +If you're new to blockchain development, start with the [Ethereum Developer Documentation](https://ethereum.org/developers) to understand wallets, transactions, and smart contract basics. +::: + +## Step 1: Install Node.js + +### macOS + +```bash +brew install node@20 +node --version +npm --version +``` + +### Linux + +```bash +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt-get install -y nodejs +node --version +npm --version +``` + +### Windows + +Download and run the current LTS installer from [nodejs.org](https://nodejs.org/), then verify `node --version` in a new terminal. + +## Step 2: Install Core Dependencies + +Create a project and install the v1 SDK: + +```bash +mkdir yellow-app +cd yellow-app +npm init -y +npm install @yellow-org/sdk decimal.js viem dotenv +npm install -D typescript @types/node +``` + +| Package | Purpose | +|---------|---------| +| `@yellow-org/sdk` | v1 client for Nitronode payment channels | +| `decimal.js` | Exact asset amounts without floating-point rounding | +| `viem` | Wallet, address, and blockchain RPC utilities | +| `dotenv` | Local `.env` loading for development scripts | +| `typescript` | Type checking and JavaScript output | + +## Step 3: Configure TypeScript + +Create `tsconfig.json`: + +```json +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "./dist", + "rootDir": "." + }, + "include": ["src/**/*", "scripts/**/*"] +} +``` + +This config matches the checked-in Node.js lifecycle example. If your app is built by Vite, webpack, or another bundler, `moduleResolution: "bundler"` can be valid there, but do not use it for a plain `tsc` + `node dist/...` script. + +Update `package.json`: + +```json +{ + "type": "module", + "scripts": { + "build": "tsc", + "start": "node dist/src/index.js" + } +} +``` + +## Step 4: Set Up Environment Variables + +Create `.env` for sensitive values: + +```bash +# .env - never commit this file +USER_PRIVATE_KEY=0x... +APP_PRIVATE_KEY=0x... +NITRONODE_WS_URL= +RPC_URL=https://ethereum-sepolia-rpc.publicnode.com +CHAIN_ID=11155111 +ASSET=yellow +``` + +:::info Test endpoint +`NITRONODE_WS_URL` should point at a v1 Nitronode endpoint for the environment you are testing against. Use the current sandbox or test endpoint provided for your environment until the public sandbox URL is pinned. +::: + +Add local files to `.gitignore`: + +```bash +.env +.env.local +node_modules/ +dist/ +``` + +## Step 5: Wallet Setup + +Use a dedicated development wallet. Never reuse a wallet that holds mainnet funds. + +```typescript title="scripts/create-wallet.ts" +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; + +const privateKey = generatePrivateKey(); +const account = privateKeyToAccount(privateKey); + +console.log('New development wallet'); +console.log('Address:', account.address); +console.log('Private key:', privateKey); +console.log('Save this key in .env as USER_PRIVATE_KEY or APP_PRIVATE_KEY.'); +``` + +Run it: + +```bash +npm run build +node dist/scripts/create-wallet.js +``` + +### Get Test Tokens + +{/* coming soon: faucet endpoint pending + +```bash +curl -XPOST https://clearnet-sandbox.yellow.com/faucet/requestTokens \ + -H "Content-Type: application/json" \ + -d '{"userAddress":""}' +``` + +*/} + +If you need on-chain gas or token balances before the sandbox faucet is pinned, use the relevant testnet faucet for your chain and RPC endpoint. + +| Network | Faucet | +|---------|--------| +| Polygon Amoy | [Polygon Faucet](https://faucet.polygon.technology/) | +| Sepolia | [Ethereum Sepolia Faucet](https://sepoliafaucet.com/) | +| Base Sepolia | [Base Faucet](https://www.coinbase.com/faucets/base-ethereum-sepolia-faucet) | + +## Step 6: Verify Setup + +Create `src/index.ts`: + +```typescript title="src/index.ts" +import 'dotenv/config'; +import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; +import { createPublicClient, http } from 'viem'; + +const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY as `0x${string}`; +const RPC_URL = process.env.RPC_URL; +const NITRONODE_WS_URL = process.env.NITRONODE_WS_URL; +const CHAIN_ID = BigInt(process.env.CHAIN_ID || '11155111'); + +if (!USER_PRIVATE_KEY) throw new Error('USER_PRIVATE_KEY not set in .env'); +if (!RPC_URL) throw new Error('RPC_URL not set in .env'); + +const publicClient = createPublicClient({ + transport: http(RPC_URL), +}); + +const blockNumber = await publicClient.getBlockNumber(); +const { stateSigner, txSigner } = createSigners(USER_PRIVATE_KEY); + +console.log('Wallet loaded:', stateSigner.getAddress()); +console.log('RPC connected, block:', blockNumber.toString()); + +if (!NITRONODE_WS_URL) { + console.log('Nitronode URL not set; wallet and RPC verified.'); +} else { + const client = await Client.create( + NITRONODE_WS_URL, + stateSigner, + txSigner, + withBlockchainRPC(CHAIN_ID, RPC_URL), + ); + + try { + console.log('Nitronode connected as:', client.getUserAddress()); + } finally { + await client.close(); + } +} +``` + +Run the verification: + +```bash +npm run build +npm start +``` + +Without a Nitronode URL, the script stops after confirming the wallet and blockchain RPC. With a v1 Nitronode URL, it also connects through `Client.create()` and prints `client.getUserAddress()`. + +## When to add client options + +Use `withBlockchainRPC(chainId, rpcURL)` when you call any method that needs on-chain settlement, including `checkpoint()`, `approveToken()`, token allowance checks, deposits, or withdrawals. + +Use `withApplicationID(appID)` when you want Nitronode records tagged with the application that produced them. This is optional metadata, but useful once multiple apps share the same node or wallet. + +## Next Steps + +- [Quickstart](./quickstart) - run the native v1 channel and app-session lifecycle. +- [Key Terms & Mental Models](./key-terms) - learn the vocabulary used across the docs. +- [TypeScript SDK](../sdk/typescript/getting-started) - read the full SDK guide. diff --git a/docs/nitrolite/build/getting-started/quickstart.mdx b/docs/nitrolite/build/getting-started/quickstart.mdx new file mode 100644 index 0000000..b829e1b --- /dev/null +++ b/docs/nitrolite/build/getting-started/quickstart.mdx @@ -0,0 +1,225 @@ +--- +title: Quickstart +description: Run a native v1 TypeScript SDK channel and app-session lifecycle. +sidebar_position: 1 +displayed_sidebar: buildSidebar +--- + +# Quickstart + +Run a complete native v1 Nitrolite flow with `@yellow-org/sdk`: connect to Nitronode, prepare a home channel, create an app session, deposit into it, operate, withdraw, and close the session. + +The runnable source lives in [`examples/nitrolite-v1-lifecycle`](https://github.com/layer-3/docs/tree/main/examples/nitrolite-v1-lifecycle). The snippets below explain the stages; the checked-in example is the source of truth. + +## Prerequisites + +You need: + +- Node.js 20 or later. +- A disposable user wallet with Sepolia gas and the selected test asset. +- A separate app signer wallet. It can be unfunded for this example. +- A Sepolia RPC URL. +- A v1 Nitronode WebSocket URL. + +See [Prerequisites & Environment](./prerequisites) for the full setup checklist. + +## Step 1: Install and configure + +Clone the docs repo, enter the tested example package, and install its isolated dependencies: + +```bash +git clone https://github.com/layer-3/docs.git +cd docs/examples/nitrolite-v1-lifecycle +cp .env.example .env +npm install +``` + +Edit `.env`: + +```bash title=".env" +USER_PRIVATE_KEY=0x... +APP_PRIVATE_KEY=0x... +NITRONODE_WS_URL= +RPC_URL=https://ethereum-sepolia-rpc.publicnode.com +CHAIN_ID=11155111 +ASSET=yellow + +CHANNEL_DEPOSIT_AMOUNT=0.01 +APP_DEPOSIT_AMOUNT=0.005 +OPERATE_AMOUNT=0.001 +WITHDRAW_AMOUNT=0.002 +``` + +`CHANNEL_DEPOSIT_AMOUNT` is the amount available in the user's home channel. `APP_DEPOSIT_AMOUNT` is the smaller amount committed from that home-channel balance into the app session. + +Use a dedicated test wallet. Do not reuse a wallet that holds mainnet funds. + +## Step 2: Connect clients + +The example creates separate SDK clients for the user and the app signer: + +```typescript +const userSigners = createSigners(config.userPrivateKey); +const appSigners = createSigners(config.appPrivateKey); + +const user = await Client.create( + config.wsURL, + userSigners.stateSigner, + userSigners.txSigner, + withBlockchainRPC(config.chainId, config.rpcURL), +); +``` + +`createSigners()` derives both signer types the SDK needs: off-chain state signing and on-chain transaction signing. + +The checked-in Node.js script also applies `enableNodeLocalAccountTransactions()` after client creation. Public RPC transports need the local account preserved on `writeContract()` calls, otherwise `approveToken()` and `checkpoint()` can fail with viem account errors. Browser wallet apps do not need this shim. + +## Step 3: Prepare the home channel + +The script loads Nitronode config, checks the configured asset, sets the asset home blockchain, and prepares the user home channel. + +If the wallet already has a signed funded state, the script continues. Otherwise it runs: + +```typescript +await client.approveToken(chainId, asset, depositAmount); + +const depositState = await client.deposit(chainId, asset, depositAmount); +console.log('Deposit state version:', depositState.version); + +const txHash = await client.checkpoint(asset); +console.log('Deposit checkpoint tx:', txHash); +``` + +`deposit()` creates a co-signed channel state. `checkpoint()` submits that state to ChannelHub so the deposit is enforced on-chain. + +## Step 4: Create the app session + +The app session has two participants: the user wallet and the app signer. Both sign the packed session definition. + +```typescript +const definition: AppDefinitionV1 = { + applicationId: appID, + participants: [ + { walletAddress: userAddress, signatureWeight: 1 }, + { walletAddress: appAddress, signatureWeight: 1 }, + ], + quorum: 2, + nonce: BigInt(Date.now()) * 1_000_000n + BigInt(Math.floor(Math.random() * 1_000_000)), +}; + +const payload = packCreateAppSessionRequestV1(definition, sessionData); +const userSig = await userSessionSigner.signMessage(payload); +const appSig = await appSessionSigner.signMessage(payload); + +const created = await client.createAppSession(definition, sessionData, [userSig, appSig]); +``` + +## Step 5: Deposit into the app session + +The user commits part of the home-channel balance into the app session with a deposit intent: + +```typescript +const { sessions } = await client.getAppSessions({ appSessionId: created.appSessionId }); +const session = sessions[0]; + +const update: AppStateUpdateV1 = { + appSessionId: created.appSessionId, + intent: AppStateUpdateIntent.Deposit, + version: session.version + 1n, + allocations: [ + { participant: userAddress, asset, amount: appDepositAmount }, + { participant: appAddress, asset, amount: new Decimal(0) }, + ], + sessionData: JSON.stringify({ intent: 'user_deposit' }), +}; + +const updatePayload = packAppStateUpdateV1(update); +const updateUserSig = await userSessionSigner.signMessage(updatePayload); +const updateAppSig = await appSessionSigner.signMessage(updatePayload); + +await client.submitAppSessionDeposit(update, [updateUserSig, updateAppSig], asset, appDepositAmount); +``` + +## Step 6: Operate, withdraw, and close + +After the app session is funded, the remaining stages use `submitAppState()` with different intents: + +```typescript +await client.submitAppState(operateUpdate, operateSignatures); +await client.submitAppState(withdrawUpdate, withdrawSignatures); +await client.submitAppState(closeUpdate, closeSignatures); +``` + +- `Operate` moves a small purchase amount from the user allocation to the app signer allocation. +- `Withdraw` releases part of the user allocation back to the home channel. +- `Close` finalizes the app session and returns the remaining allocations. + +The example leaves the home channel open by default. Set `CLOSE_HOME_CHANNEL=true` only when you intentionally want to close the example channel after the app-session lifecycle. + +## Step 7: Run it + +Build and run the lifecycle: + +```bash +npm run build +npm run lifecycle +``` + +A successful run prints each stage. + +
+Sanitized sample output + +```text +== Connect clients == +user=0xUSER_WALLET +app=0xAPP_SIGNER + +== Inspect Nitronode config and set home blockchain == +node=0xNITRONODE_SIGNER version=13a90cd +chain=ethereum_sepolia id=11155111 +asset=yellow decimals=18 + +== Prepare user home channel == +signed state before prepare: version=2 transition=40 homeBalance=0.005 homeChannel=0xHOME_CHANNEL_ID +pending state before prepare: version=4 transition=41 homeBalance=0.009 homeChannel=0xHOME_CHANNEL_ID +acknowledging pending yellow channel state +ack checkpoint tx=0xCHECKPOINT_TX_HASH +home channel ready: version=5 transition=1 homeBalance=0.009 homeChannel=0xHOME_CHANNEL_ID + +== Run app-session lifecycle == +registering app=docs-v1-lifecycle-... owner=0xAPP_SIGNER +app registry is disabled on this node; continuing with app-session creation +created app session=0xAPP_SESSION_ID version=1 status=open +after create: version=1 closed=false user=0 app=0 +app-session deposit nodeSig=0xNODE_SIGNATURE +after app deposit: version=2 closed=false user=0.005 app=0 +after operate: version=3 closed=false user=0.004 app=0.001 +after withdraw: version=4 closed=false user=0.002 app=0.001 +after close: version=5 closed=true user=0 app=0 + +== Final queries == +final signed channel state: version=6 transition=40 homeBalance=0.004 homeChannel=0xHOME_CHANNEL_ID +final app session: version=5 closed=true user=0 app=0 + +== Optional cleanup == +home-channel close skipped; set CLOSE_HOME_CHANNEL=true to run cleanup + +Lifecycle complete. +``` + +
+ +## What just happened + +- `Client.create()` opened v1 Nitronode WebSocket connections for both signers. +- `setHomeBlockchain()` selected the settlement chain for the asset. +- `approveToken()`, `deposit()`, and `checkpoint()` prepared and enforced the user's home channel. +- `packCreateAppSessionRequestV1()` and `packAppStateUpdateV1()` produced the exact hashes participants signed. +- `createAppSession()`, `submitAppSessionDeposit()`, and `submitAppState()` moved through the app-session lifecycle. + +## Next steps + +- [Configuration](../sdk/typescript/configuration) - timeouts, RPC endpoints, application IDs, and error handling. +- [Multi-party app sessions](../sdk/multi-party-app-sessions) - expand the same lifecycle to more participants. +- [Key Terms & Mental Models](./key-terms) - learn the vocabulary used across the docs. diff --git a/docs/build/sdk/_category_.json b/docs/nitrolite/build/sdk/_category_.json similarity index 79% rename from docs/build/sdk/_category_.json rename to docs/nitrolite/build/sdk/_category_.json index 32cf882..4a71e07 100644 --- a/docs/build/sdk/_category_.json +++ b/docs/nitrolite/build/sdk/_category_.json @@ -1,6 +1,6 @@ { "label": "SDK", - "position": 2, + "position": 3, "collapsed": false, "collapsible": false } diff --git a/docs/build/sdk/go/_category_.json b/docs/nitrolite/build/sdk/go/_category_.json similarity index 80% rename from docs/build/sdk/go/_category_.json rename to docs/nitrolite/build/sdk/go/_category_.json index 3a2be2d..593eb15 100644 --- a/docs/build/sdk/go/_category_.json +++ b/docs/nitrolite/build/sdk/go/_category_.json @@ -1,6 +1,6 @@ { "label": "Go SDK", - "position": 4, + "position": 6, "collapsed": false, "collapsible": false } diff --git a/docs/nitrolite/build/sdk/go/api-reference.mdx b/docs/nitrolite/build/sdk/go/api-reference.mdx new file mode 100644 index 0000000..742bb9f --- /dev/null +++ b/docs/nitrolite/build/sdk/go/api-reference.mdx @@ -0,0 +1,153 @@ +--- +title: API Reference +description: Complete API reference for the Nitrolite Go SDK +sidebar_position: 2 +--- + +# Go SDK API Reference + +The Go SDK package is `github.com/layer-3/nitrolite/sdk/go`. It wraps Nitronode v1 RPC methods and ChannelHub settlement helpers in typed Go methods. + +## App-Builder Methods + +These are the methods most builders use in normal applications. + +### Channel State + +```go +client.Deposit(ctx, blockchainID, asset, amount) // (*core.State, error) +client.Withdraw(ctx, blockchainID, asset, amount) // (*core.State, error) +client.Transfer(ctx, recipientWallet, asset, amount) // (*core.State, error) +client.CloseHomeChannel(ctx, asset) // (*core.State, error) +client.Acknowledge(ctx, asset) // (*core.State, error) +``` + +Use `Checkpoint` when the latest prepared state needs on-chain settlement. + +### Settlement + +```go +client.Checkpoint(ctx, asset) // (string, error) +client.Challenge(ctx, state) // (string, error) +client.ApproveToken(ctx, chainID, asset, amount) // (string, error) +client.GetOnChainBalance(ctx, chainID, asset, wallet) // (decimal.Decimal, error) +``` + +`Checkpoint` routes by transition type and channel status: + +| Latest state | Effect | +| --- | --- | +| Void channel plus deposit state | Creates the channel and locks funds. | +| Deposit or withdrawal state | Checkpoints and applies the fund movement. | +| Finalize state | Closes the channel. | + +### Node and Asset Discovery + +```go +client.Ping(ctx) +client.GetConfig(ctx) +client.GetBlockchains(ctx) +client.GetAssets(ctx, &blockchainID) +client.SetHomeBlockchain(asset, chainID) +``` + +### User and Channel Queries + +```go +client.GetBalances(ctx, wallet) +client.GetTransactions(ctx, wallet, opts) +client.GetHomeChannel(ctx, wallet, asset) +client.GetEscrowChannel(ctx, escrowChannelID) +client.GetLatestState(ctx, wallet, asset, onlySigned) +``` + +`GetEscrowChannel` is a query helper. Escrow operation guides are not public app-builder docs yet. + +## App Registry and Sessions + +```go +apps, meta, err := client.GetApps(ctx, &sdk.GetAppsOptions{ + AppID: &appID, + OwnerWallet: &wallet, +}) + +err := client.RegisterApp(ctx, "my-app", `{"name":"My App"}`, false) +``` + +```go +sessions, meta, err := client.GetAppSessions(ctx, opts) +def, err := client.GetAppDefinition(ctx, appSessionID) +sessionID, version, status, err := client.CreateAppSession(ctx, def, data, sigs) +nodeSig, err := client.SubmitAppSessionDeposit(ctx, update, sigs, asset, amount) +err := client.SubmitAppState(ctx, update, sigs) +batchID, err := client.RebalanceAppSessions(ctx, signedUpdates) +``` + +Apps are responsible for collecting quorum signatures before calling `CreateAppSession`, `SubmitAppSessionDeposit`, or `SubmitAppState`. + +## Session Keys + +### App Sessions + +```go +state := app.AppSessionKeyStateV1{ + UserAddress: client.GetUserAddress(), + SessionKey: "0xSessionKey...", + Version: 1, + ApplicationIDs: []string{"app1"}, + AppSessionIDs: []string{}, + ExpiresAt: time.Now().Add(24 * time.Hour), +} + +sig, err := client.SignSessionKeyState(state) +state.UserSig = sig +err = client.SubmitAppSessionKeyState(ctx, state) + +states, err := client.GetLastAppKeyStates(ctx, userAddress, nil) +``` + +### Channels + +```go +state := core.ChannelSessionKeyStateV1{ + UserAddress: client.GetUserAddress(), + SessionKey: "0xSessionKey...", + Version: 1, + Assets: []string{"yellow"}, + ExpiresAt: time.Now().Add(24 * time.Hour), +} + +sig, err := client.SignChannelSessionKeyState(state) +state.UserSig = sig +err = client.SubmitChannelSessionKeyState(ctx, state) + +states, err := client.GetLastChannelKeyStates(ctx, userAddress, nil) +``` + +## Utilities + +```go +client.Close() +client.WaitCh() +client.SignState(state) +client.GetUserAddress() +``` + +## Common Types + +```go +core.State +core.Channel +core.Transition +core.Transaction +core.Asset +core.Blockchain +core.ChannelSessionKeyStateV1 + +app.AppV1 +app.AppInfoV1 +app.AppSessionInfoV1 +app.AppDefinitionV1 +app.AppStateUpdateV1 +app.AppSessionKeyStateV1 +``` diff --git a/docs/nitrolite/build/sdk/go/examples.mdx b/docs/nitrolite/build/sdk/go/examples.mdx new file mode 100644 index 0000000..ced72a5 --- /dev/null +++ b/docs/nitrolite/build/sdk/go/examples.mdx @@ -0,0 +1,66 @@ +--- +title: Examples +description: Go SDK examples for channels and app sessions +sidebar_position: 3 +--- + +# Go SDK Examples + +Use these examples as starting points for backend services and CLIs that call Nitronode with `github.com/layer-3/nitrolite/sdk/go`. + +## Channel Funding and Transfer + +```go +ctx := context.Background() +chainID := uint64(11155111) +asset := "yellow" +amount := decimal.NewFromFloat(0.01) + +if err := client.SetHomeBlockchain(asset, chainID); err != nil { + return err +} + +if _, err := client.ApproveToken(ctx, chainID, asset, amount); err != nil { + return err +} + +if _, err := client.Deposit(ctx, chainID, asset, amount); err != nil { + return err +} + +if _, err := client.Checkpoint(ctx, asset); err != nil { + return err +} + +if _, err := client.Transfer(ctx, "0xRecipient...", asset, decimal.NewFromFloat(0.001)); err != nil { + return err +} +``` + +## App Session Update + +```go +update := app.AppStateUpdateV1{ + AppSessionID: appSessionID, + Version: 2, + Intent: app.AppStateUpdateIntentOperate, + Allocations: allocations, + SessionData: `{"purchase":"content-1"}`, +} + +// The application gathers quorum signatures before submitting. +if err := client.SubmitAppState(ctx, update, quorumSigs); err != nil { + return err +} +``` + +## Query Latest State + +```go +state, err := client.GetLatestState(ctx, client.GetUserAddress(), "yellow", true) +if err != nil { + return err +} + +fmt.Printf("version=%d transition=%s\n", state.Version, state.Transition.Type) +``` diff --git a/docs/nitrolite/build/sdk/go/getting-started.mdx b/docs/nitrolite/build/sdk/go/getting-started.mdx new file mode 100644 index 0000000..b625b72 --- /dev/null +++ b/docs/nitrolite/build/sdk/go/getting-started.mdx @@ -0,0 +1,177 @@ +--- +title: Getting Started +description: Install and set up the Nitrolite Go SDK +sidebar_position: 1 +--- + +# Getting Started with the Go SDK + +The Go SDK lives in the Nitrolite module at `github.com/layer-3/nitrolite/sdk/go`. Use it for backend services, workers, and CLI tools that need typed access to Nitronode v1 RPC plus ChannelHub settlement helpers. + +## Requirements + +- Go 1.25.7 or later +- A Nitronode WebSocket endpoint +- A blockchain RPC endpoint for chains where you call `Checkpoint`, `Challenge`, or `ApproveToken` +- A signer for channel states and a signer for blockchain transactions + +## Installation + +```bash +go get github.com/layer-3/nitrolite/sdk/go +``` + +## Quick Start + +:::info Sandbox URL - coming soon +Use your Nitronode WebSocket URL in `sdk.NewClient()`. The public sandbox URL is intentionally shown as `` until the canonical host is pinned. +::: + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/layer-3/nitrolite/pkg/core" + "github.com/layer-3/nitrolite/pkg/sign" + sdk "github.com/layer-3/nitrolite/sdk/go" + "github.com/shopspring/decimal" +) + +func main() { + ctx := context.Background() + privateKeyHex := "0x..." + chainID := uint64(11155111) + asset := "yellow" + + msgSigner, err := sign.NewEthereumMsgSigner(privateKeyHex) + if err != nil { + log.Fatal(err) + } + stateSigner, err := core.NewChannelDefaultSigner(msgSigner) + if err != nil { + log.Fatal(err) + } + txSigner, err := sign.NewEthereumRawSigner(privateKeyHex) + if err != nil { + log.Fatal(err) + } + + client, err := sdk.NewClient( + "", + stateSigner, + txSigner, + sdk.WithBlockchainRPC(chainID, "https://ethereum-sepolia-rpc.example"), + ) + if err != nil { + log.Fatal(err) + } + defer client.Close() + + if err := client.SetHomeBlockchain(asset, chainID); err != nil { + log.Fatal(err) + } + + if _, err := client.ApproveToken(ctx, chainID, asset, decimal.NewFromFloat(0.01)); err != nil { + log.Fatal(err) + } + + state, err := client.Deposit(ctx, chainID, asset, decimal.NewFromFloat(0.01)) + if err != nil { + log.Fatal(err) + } + fmt.Printf("deposit state version: %d\n", state.Version) + + txHash, err := client.Checkpoint(ctx, asset) + if err != nil { + log.Fatal(err) + } + fmt.Printf("checkpoint tx: %s\n", txHash) +} +``` + +## Creating a Client + +```go +msgSigner, err := sign.NewEthereumMsgSigner(privateKeyHex) +stateSigner, err := core.NewChannelDefaultSigner(msgSigner) +txSigner, err := sign.NewEthereumRawSigner(privateKeyHex) + +client, err := sdk.NewClient( + wsURL, + stateSigner, + txSigner, + sdk.WithBlockchainRPC(chainID, rpcURL), + sdk.WithHandshakeTimeout(10*time.Second), + sdk.WithPingInterval(5*time.Second), +) +``` + +The constructor takes: + +| Parameter | Purpose | +| --- | --- | +| `wsURL` | Nitronode v1 WebSocket endpoint. | +| `stateSigner` | `core.ChannelSigner` used for channel-state signatures. | +| `txSigner` | Blockchain transaction signer used for settlement calls. | +| Options | RPC URLs, handshake timeout, ping interval, and error handler. | + +## Channel Signers + +The Go SDK wraps raw signers with `core.ChannelSigner` so signatures carry the v1 validator type byte. + +| Type | Byte | Struct | Usage | +| --- | --- | --- | --- | +| Default | `0x00` | `core.ChannelDefaultSigner` | Main wallet signs directly. | +| Session key | `0x01` | `core.ChannelSessionKeySignerV1` | Delegated channel session key. | + +```go +msgSigner, err := sign.NewEthereumMsgSigner(privateKeyHex) +channelSigner, err := core.NewChannelDefaultSigner(msgSigner) +client, err := sdk.NewClient(wsURL, channelSigner, txSigner, opts...) +``` + +## Builder Flow + +For normal app-builder integrations: + +1. Connect with `sdk.NewClient`. +2. Call `GetConfig`, `GetBlockchains`, or `GetAssets` to inspect the node. +3. Set the asset home chain with `SetHomeBlockchain`. +4. Approve the ChannelHub spender with `ApproveToken` before first funding. +5. Use `Deposit`, `Withdraw`, `Transfer`, and `CloseHomeChannel` to prepare signed states. +6. Use `Checkpoint` when a state needs on-chain settlement. +7. Use app-session methods for application-specific state. + +## Configuration Options + +```go +sdk.WithBlockchainRPC(chainID, rpcURL) +sdk.WithHandshakeTimeout(duration) +sdk.WithPingInterval(duration) +sdk.WithErrorHandler(func(error)) +``` + +## Error Handling + +```go +state, err := client.Deposit(ctx, chainID, asset, amount) +if err != nil { + log.Printf("state error: %v", err) +} + +txHash, err := client.Checkpoint(ctx, asset) +if err != nil { + log.Printf("checkpoint error: %v", err) +} +``` + +Common errors: + +- `"home blockchain not set for asset"`: call `SetHomeBlockchain` first. +- `"blockchain RPC not configured for chain"`: pass `sdk.WithBlockchainRPC`. +- `"no channel exists for asset"`: call `Deposit` before checkpointing. +- `"insufficient balance"`: fund the wallet or channel before retrying. diff --git a/docs/nitrolite/build/sdk/index.md b/docs/nitrolite/build/sdk/index.md new file mode 100644 index 0000000..536bd01 --- /dev/null +++ b/docs/nitrolite/build/sdk/index.md @@ -0,0 +1,51 @@ +--- +title: SDK Overview +description: Yellow Network SDKs for building state channel applications +sidebar_position: 1 +--- + +# SDK Overview + +Yellow Network provides official SDKs for building applications on top of Nitrolite payment channels. All SDKs share the same two-step architecture: **build and co-sign states off-chain**, then **settle on-chain when needed**. + +## Choose your SDK + +| Builder type | Package | Import | Notes | +|---|---|---|---| +| New TypeScript app | [`@yellow-org/sdk@1.2.1`](./typescript/getting-started) | `import { Client, createSigners } from '@yellow-org/sdk'` | Native v1 SDK; uses `Decimal` amounts. | +| Migrating from 0.5.3 (TS) | [`@yellow-org/sdk-compat@1.2.1`](./typescript-compat/overview) | `import { NitroliteClient } from '@yellow-org/sdk-compat'` | Minimal-diff bridge for `@erc7824/nitrolite@0.5.3`; see the compat codemod workflow. | +| Go integration | [`github.com/layer-3/nitrolite/sdk/go`](./go/getting-started) | `import "github.com/layer-3/nitrolite/sdk/go"` | Standard Go package; run `go get github.com/layer-3/nitrolite/sdk/go`. | +| Agentic IDE / AI agents | `@yellow-org/sdk-mcp@1.2.1` | `npx -y @yellow-org/sdk-mcp` | MCP server with binary `yellow-sdk-mcp`; coming soon on npm. | +| 0.5.3 codemod | `@yellow-org/nitrolite-codemod@1.0.0` | `npx -y @yellow-org/nitrolite-codemod` | One-time migration tool; use the source workflow until the npm package publishes. | + +## Architecture + +All SDKs follow a unified design: + +- **State Operations** (off-chain): `deposit()`, `withdraw()`, `transfer()`, `closeHomeChannel()`, `acknowledge()`. Build and co-sign channel states without touching the blockchain. +- **Blockchain Settlement**: `checkpoint()`. The single entry point for all on-chain transactions. Routes to the correct contract method based on transition type and channel status. +- **Low-Level Operations**: Direct RPC access for app sessions, session keys, queries, and custom flows. + +```mermaid +sequenceDiagram + participant App + participant SDK + participant Node as Nitronode + participant Chain as Blockchain + + App->>SDK: deposit(chain, asset, amount) + SDK->>Node: Build & co-sign state + Node-->>SDK: Co-signed state + SDK-->>App: State object + + App->>SDK: checkpoint(asset) + SDK->>Chain: Create/checkpoint/close channel + Chain-->>SDK: Transaction hash + SDK-->>App: tx hash +``` + +## Choosing an SDK + +- **New TypeScript projects**: Use [`@yellow-org/sdk`](./typescript/getting-started) directly. +- **Migrating from v0.5.3**: Use [`@yellow-org/sdk-compat`](./typescript-compat/overview) to minimise code changes, then migrate to the main SDK at your own pace. +- **Go projects**: Use the [Go SDK](./go/getting-started) for backend services and CLI tooling. diff --git a/docs/nitrolite/build/sdk/mcp.mdx b/docs/nitrolite/build/sdk/mcp.mdx new file mode 100644 index 0000000..d2cd17e --- /dev/null +++ b/docs/nitrolite/build/sdk/mcp.mdx @@ -0,0 +1,94 @@ +--- +title: MCP Server +description: Use the Nitrolite SDK MCP server with AI coding tools. +sidebar_position: 5 +--- + +# MCP Server + +The Nitrolite SDK MCP server gives AI coding tools structured access to the TypeScript SDK, Go SDK, protocol docs, v1 RPC catalogue, examples, and migration guidance. + +:::caution Coming soon on npm +`@yellow-org/sdk-mcp` is not published on npm yet. `npm view @yellow-org/sdk-mcp version` currently returns `404`. Until the package is published, run the server from the Nitrolite source checkout. +::: + +## What It Provides + +| Capability | Tools and resources | +| --- | --- | +| SDK lookup | `lookup_method`, `lookup_type`, `search_api`, and import validation for TypeScript and Go. | +| RPC lookup | `lookup_rpc_method` reads the same `docs/api.yaml` surface as the API Reference. | +| Protocol help | Protocol resources for channel lifecycle, state model, enforcement, cross-chain notes, and interactions. | +| Scaffolding | Starter project prompts for TypeScript and Go flows. | +| Migration help | Guidance for moving from `@erc7824/nitrolite@0.5.3` to the v1 package family. | + +## Source Setup + +From a local Nitrolite source checkout: + +```bash +cd sdk/mcp +npm install +npm run build +npm run start +``` + +For stdio clients, configure the server command from the repository root: + +```json +{ + "mcpServers": { + "nitrolite": { + "command": "npm", + "args": ["--prefix", "/path/to/nitrolite/sdk/mcp", "run", "start"] + } + } +} +``` + +## Tool Setup Shapes + +The exact config file location depends on the AI tool. Use the same stdio command shape in each tool until the npm package is published. + +### Claude Code + +Add the `nitrolite` server to the project's MCP configuration and point it at the source checkout command above. Restart Claude Code so it reloads MCP servers. + +### Claude Desktop + +Add the `nitrolite` entry under `mcpServers` in Claude Desktop's MCP config. Use an absolute path to the Nitrolite repo if Claude Desktop is not launched from the repo root. + +### Cursor + +Add a project MCP server named `nitrolite` with the stdio command above. Keep the working directory at the Nitrolite repo root or use absolute paths in `args`. + +### VS Code and Copilot-Compatible MCP Clients + +Use a workspace MCP configuration that starts `npm --prefix /path/to/nitrolite/sdk/mcp run start`. Restart the MCP client after changing the config. + +### Generic Stdio Clients + +Use: + +```bash +npm --prefix /path/to/nitrolite/sdk/mcp run start +``` + +The server communicates over stdio, so the client must keep the process open for the duration of the session. + +## After NPM Publish + +Once `@yellow-org/sdk-mcp` is published, this page should switch from source setup to a package command, for example: + +```json +{ + "mcpServers": { + "nitrolite": { + "command": "npx", + "args": ["-y", "@yellow-org/sdk-mcp"] + } + } +} +``` + +Leave the source setup available as the local-development fallback. diff --git a/docs/nitrolite/build/sdk/migration-guide.md b/docs/nitrolite/build/sdk/migration-guide.md new file mode 100644 index 0000000..8ff4896 --- /dev/null +++ b/docs/nitrolite/build/sdk/migration-guide.md @@ -0,0 +1,412 @@ +--- +sidebar_position: 2 +title: Migration Guide +description: Guide to migrate to newer versions of Nitrolite +keywords: [migration, upgrade, breaking changes, nitrolite, erc7824] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Migration Guide + +If you are coming from an earlier version of the Nitrolite protocol, first choose the migration surface: + +- If you're using `@yellow-org/sdk-compat`, use this page for legacy protocol semantics and the [compat overview](./typescript-compat/overview) for the codemod workflow. +- If you're going straight to `@yellow-org/sdk`, use this page as a semantics checklist, then implement new code with the native [TypeScript SDK guide](./typescript/getting-started). + +The `@yellow-org/nitrolite-codemod` package is planned for npm. Until it publishes, the [compat overview codemod workflow](./typescript-compat/overview) shows how to clone the source repo, build the CLI, scan your app, and apply the migration transforms. It rewrites package imports, updates dependencies, and leaves `TODO [codemod]` markers where a native v1 decision is required. + +## What semantics changed + +Amount units are the most important migration boundary: + +- If you're using `@yellow-org/sdk-compat`, channel transfers use raw asset-unit strings, such as `'5000000'` for 5 USDC with 6 decimals. See the amount-units table in the [compat overview](./typescript-compat/overview). +- If you're updating app-session allocations, keep human-decimal strings in the allocation payloads because app state is signed and shared by participants. +- If you're going straight to `@yellow-org/sdk`, use `Decimal` values for native v1 calls such as `deposit()`, `withdraw()`, `transfer()`, and app-session allocation amounts. + +## 0.5.x Breaking changes + +If you're using `@yellow-org/sdk-compat`, treat the snippets in this section as legacy-shape examples to compare with your current app. If you're going straight to `@yellow-org/sdk`, use the native SDK pages for replacement code. + +The 0.5.x release includes fundamental protocol changes affecting session keys, channel operations, state signatures, and channel resize rules. The main objective of these changes is to enhance security, and provide better experience for developers and users by ability to limit allowances for specific applications. + +**Not ready to migrate?** Unfortunately, at this time Yellow Network does not provide Nitronode instances running the previous version of the protocol, so you will need to migrate to the latest version to continue using the Network. + +### Protocol Changes + +These protocol-level changes affect all implementations and integrations with the Yellow Network. + +#### Session Keys: Applications, Allowances, and Expiration + +Session keys now have enhanced properties that define their access levels and capabilities: + +- **Application field**: Determines the scope of session key permissions. Setting this to an application name (e.g., "My Trading App") grants application-scoped access with enforced allowances. Legacy root-access examples may use a broker-specific root marker because that is what older protocol versions expected. + +- **Allowances field**: Defines spending limits for application-scoped session keys. These limits are tracked cumulatively across all operations and are enforced by the protocol. + +- **Expires_at field**: Uses a bigint timestamp (seconds since epoch). Once expired, session keys are permanently frozen and cannot be reactivated. This is particularly critical for legacy root access keys: if they expire, you lose the ability to perform channel operations. + +#### Channel Creation: Separate Create and Fund Steps + +Nitronode no longer supports creating channels with an initial deposit. All channels must be created with zero balance and funded separately through a resize operation. This two-step process ensures cleaner state management and prevents edge cases in channel initialization. + +#### State Signatures: Wallet vs Session Key Signing + +A fundamental change in how channel states are signed: + +- **Channels created before v0.5.0**: The participant address is the session key, and all states must be signed by that session key. + +- **Channels created after v0.5.0**: The participant address is the wallet address, and all states must be signed by the wallet. + +This change improves security and aligns with standard practices, but requires careful handling during the transition period. + +#### Resize Operations: Strict Channel Balance Rules + +The protocol now enforces strict rules about channel balances and their impact on other operations: + +- **Blocked operations**: Users with any channel containing non-zero amounts cannot perform transfers, submit app states with deposit intent, or create app sessions with non-zero allocations. + +- **Resizing state**: After a resize request, channels enter a "resizing" state with locked funds until the on-chain transaction is confirmed. If a channel remains stuck in this state for an extended period, the recommended action is to close the channel and create a new one. + +- **Allocate amount semantics**: The resize operation uses `allocate_amount` where negative values withdraw from the channel to unified balance, and positive values deposit to the channel. + +:::warning +**Legacy channel migration**: Users with existing channels containing non-zero amounts must either resize them to zero (by providing "resize_amount" as 0 and "allocate_amount" as your **negative** on-chain balance) or close them to enable full protocol functionality. If you are unsure how to adjust resize parameters, the safe option is to close the old on-chain channel entirely, and open a new one. +::: + +#### Non-Zero Channel Allocations: Operation Restrictions + +The following operations will return errors if the user has any channel with non-zero amount: + +- **Transfer**: Returns error code indicating blocked due to non-zero channel balance +- **Submit App State** (with deposit intent): Rejected if attempting to deposit +- **Create App Session** (with allocations): Rejected if attempting to allocate + +The returned error has the following format: `operation denied: non-zero allocation in channel(s) detected owned by wallet
"` + +### Nitrolite SDK + +You should read this section if you are using the Nitrolite SDK. + +#### Update Authentication + +Implementing the new session key protocol changes: + + + + + ```typescript + const authRequest = { + address: '0x...', + session_key: '0x...', + application: 'My Trading App', // Application name for confined access + allowances: [ + { asset: 'usdc', amount: '1000.0' }, + { asset: 'eth', amount: '0.5' } + ], + scope: 'app.create', + expires_at: BigInt(Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60) // 7 days + }; + ``` + + + + + ```typescript + const authRequest = { + address: '0x...', + session_key: '0x...', + // The legacy root-access value was 'clearnode'; check your broker docs if you used a custom value. + application: '', + allowances: [], // Not enforced for root access + scope: 'app.create', + expires_at: BigInt(Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60) // Long expiration recommended + }; + ``` + + + + +**Important considerations:** +- Root access keys that use a legacy root-application marker cannot perform channel operations after expiration +- Plan expiration times based on your operational needs +- Application-scoped keys track cumulative spending against allowances + +#### Auth Verify Changes + +The `createEIP712AuthMessageSigner` function signature has changed to align with the new session key structure. + +```typescript +const eip712SigningFunction = createEIP712AuthMessageSigner( + walletClient, + { + scope: authMessage.scope, + // remove-next-line + application: authMessage.application, + // remove-next-line + participant: authMessage.session_key, + // remove-next-line + expire: authMessage.expire, + // add-next-line + session_key: authMessage.session_key, + // add-next-line + expires_at: authMessage.expires_at, + allowances: authMessage.allowances, + }, + getAuthDomain(), +); +``` + +#### Migrate Channel Creation + +Channels must now be created with zero initial deposit and funded separately via the `resizeChannel` method: + +```typescript +const { channelId } = await client.createChannel({ + chain_id: 1, + token: tokenAddress, + // remove-next-line + amount: BigInt(1000000), // Initial deposit + // remove-next-line + session_key: '0x...' // Optional +}); + +// add-start +// Step 2: Fund the channel separately +await client.resizeChannel({ + channel_id: channelId, + amount: BigInt(1000000), +}); +// add-end +``` + +#### Resize correctly + +Channel resizing must be negotiated with Nitronode through WebSocket. Use `resize_amount` and `allocate_amount` with correct sign convention (`resize_amount = -allocate_amount`) and help users with non-zero channel balances migrate by resizing to zero or reopening channels. + +Channel resize can be requested as follows: + +```typescript +const resizeMessage = await createResizeChannelMessage(messageSigner, { + channel_id: channelId, + resize_amount: BigInt(50), // Positive = deposit to channel, negative = withdraw from channel to custody ledger + allocate_amount: BigInt(-50), // Negative = deposit to unified balance, negative = withdraw from unified balance to channel + funds_destination: walletAddress, +}); + +const resizeResponse = {}; // send the message and wait for Nitronode's response + +const { params: resizeResponseParams } = parseResizeChannelResponse(resizeResponse); +const resizeParams = { + resizeState: { + channelId, + ...resizeResponseParams.state, + serverSignature: resizeResponseParams.serverSignature, + data: resizeResponseParams.state.stateData as Hex, + version: BigInt(resizeResponseParams.state.version), + }, + // `previousState` is either initial or previous resizing state, depending on which has higher version number + // can be obtained with `await (client.getChannelData(channelId)).lastValidState` + proofStates: [previousState], +} + +const {txHash} = await client.resizeChannel(resizeParams); +``` + +Here is how you can migrate your channels: + +```typescript +// Check and migrate channels with non-zero amounts +const channels = await client.getOpenChannels(); + +for (const channel of channels) { + if (channel.amount > 0) { + // Must empty channel to enable transfers/app operations + const resizeMessage = await createResizeChannelMessage(messageSigner, { + channel_id: channel.channelId, + resize_amount: BigInt(0), + allocate_amount: -BigInt(channel.amount), + funds_destination: walletAddress, + }); + + // perform the resize as shown above + } +} +``` + + +**Critical:** Operations blocked when any channel has non-zero amount: +- Off-chain transfers +- App state submissions with deposit intent +- Creating app sessions with allocations + +#### Test State Signatures + +If you plan to work with on-chain channels opened PRIOR to v0.5.0, then on Nitrolite client initialization the `stateSigner` you specify must be based on a Session Key used in the channel as participant. Even if this session key is or will expire, you still need to provide a `stateSigner` based on it. + +On the other hand, if you plan to work with channels created SINCE v0.5.0, you can specify the `stateSigner` based on the `walletClient` you have specified. + +#### Manage Session Keys + +New methods have been added for comprehensive session key management, including retrieval and revocation. + +```typescript +// Get all active session keys +const sessionKeys = await client.getSessionKeys(); + +// Revoke a specific session key +await client.revokeSessionKey({ + session_key: '0x...' +}); + +// Session key data structure +interface RPCSessionKey { + id: string; + sessionKey: Address; + application: string; + allowances: RPCAllowanceUsage[]; // Includes usage tracking + scope: string; + expiresAt: bigint; + createdAt: bigint; +} +``` +#### EIP-712 Signatures: String-based Amounts + +EIP-712 signature types now use string values for amounts instead of numeric types to support better precision with decimal values. + +```typescript +const types = { + Allowance: [ + { name: 'asset', type: 'string' }, + // remove-next-line + { name: 'amount', type: 'uint256' }, + // add-next-line + { name: 'amount', type: 'string' }, + ] +}; +``` + +### Nitronode RPC + +You should read this section only if you are using the Nitronode RPC API directly. + +#### Update Authentication + +Use the new session key parameters with proper `application`, `allowances`, and `expires_at` fields: + + + + + ```json + { + "req": [1, "auth_request", { + "address": "0x1234567890abcdef...", + "session_key": "0x9876543210fedcba...", + "application": "My Trading App", + "allowances": [ + { "asset": "usdc", "amount": "1000.0" }, + { "asset": "eth", "amount": "0.5" } + ], + "scope": "app.create", + "expires_at": 1719123456789 + }, 1619123456789], + "sig": ["0x..."] + } + ``` + + + + + ```json + { + "req": [1, "auth_request", { + "address": "0x1234567890abcdef...", + "session_key": "0x9876543210fedcba...", + "application": "", + "allowances": [], + "scope": "app.create", + "expires_at": 1750659456789 + }, 1619123456789], + "sig": ["0x..."] + } + ``` + + + + +#### Migrate Channel Creation + +Implement the two-step process (create empty, then resize to fund) + +The `create_channel` method no longer accepts `amount` and `session_key` parameters: + +```json +{ + "req": [1, "create_channel", { + "chain_id": 137, + "token": "0xeeee567890abcdef...", + // remove-next-line + "amount": "100000000", + // remove-next-line + "session_key": "0x1234567890abcdef..." + }, 1619123456789], + "sig": ["0x9876fedcba..."] +} +``` + +#### Manage Session Keys + +New methods for session key operations have been added. + +##### Get Session Keys + +Request: +```json +{ + "req": [1, "get_session_keys", {}, 1619123456789], + "sig": ["0x..."] +} +``` + +Response: +```json +{ + "res": [1, "get_session_keys", { + "session_keys": [{ + "id": "sk_123", + "session_key": "0x9876543210fedcba...", + "application": "My Trading App", + "allowances": [ + { "asset": "usdc", "amount": "1000.0", "used": "250.0" } + ], + "scope": "app.create", + "expires_at": 1719123456789, + "created_at": 1619123456789 + }] + }, 1619123456789], + "sig": ["0x..."] +} +``` + +##### Revoke Session Key Request + +Request: +```json +{ + "req": [1, "revoke_session_key", { + "session_key": "0x1234567890abcdef..." + }, 1619123456789], + "sig": ["0x..."] +} +``` + +Response: +```json +{ + "res": [1, "revoke_session_key", { + "session_key": "0x1234567890abcdef..." + }, 1619123456789], + "sig": ["0x..."] +} +``` diff --git a/docs/nitrolite/build/sdk/multi-party-app-sessions.mdx b/docs/nitrolite/build/sdk/multi-party-app-sessions.mdx new file mode 100644 index 0000000..ce73797 --- /dev/null +++ b/docs/nitrolite/build/sdk/multi-party-app-sessions.mdx @@ -0,0 +1,313 @@ +--- +sidebar_position: 3 +title: Multi-Party Application Sessions +description: Create, update, withdraw from, and close a native v1 Nitrolite application session +keywords: [app sessions, multi-party, state channels, quorum, signatures, allocations] +--- + +# Multi-Party Application Sessions + +:::warning Native v1 API +This page demonstrates the v1 native API. If you're on `@erc7824/nitrolite@0.5.3` and want a minimal-diff migration, see the [`@yellow-org/sdk-compat` overview](./typescript-compat/overview) first. +::: + +Application sessions are shared off-chain states owned by an app. Participants sign each app-session creation and update. Nitronode verifies quorum, stores the latest state, and connects deposits or withdrawals to each participant's home channel. + +The native v1 TypeScript SDK uses: + +- `Client.create()` for each participant connection. +- `Client.registerApp()` for the `apps.v1.submit_app_version` prerequisite. +- `Client.createAppSession()` for `app_sessions.v1.create_app_session`. +- `Client.submitAppSessionDeposit()` for `app_sessions.v1.submit_deposit_state`. +- `Client.submitAppState()` for `Operate`, `Withdraw`, `Close`, and `Rebalance` updates. +- `packCreateAppSessionRequestV1()` and `packAppStateUpdateV1()` to hash the exact payload participants sign. + +There is no separate `closeAppSession()` helper in `@yellow-org/sdk@1.2.1`; closing is an app-state update with `AppStateUpdateIntent.Close`. + +## Prerequisites + +You need two disposable wallets, a Nitronode WebSocket URL, a chain RPC URL, and enough test funds in the first user's home channel to fund the session. + +```bash +USER_1_PRIVATE_KEY=0x... +USER_2_PRIVATE_KEY=0x... +NITRONODE_WS_URL= +RPC_URL=https://polygon-amoy.g.alchemy.com/v2/YOUR_KEY +CHAIN_ID=80002 +ASSET=usdc +``` + +:::info Sandbox URL - coming soon +Use your Nitronode WebSocket URL for `NITRONODE_WS_URL`. The public sandbox URL is intentionally shown as `` until the canonical host is pinned. +::: + +## Step 1: Connect both participants + +Each participant has a channel signer, a transaction signer, and an app-session signer. The channel signer signs home-channel states. The app-session signer signs app-session payloads and prefixes signatures with the v1 app-session wallet signer byte. + +```typescript +import { + AppSessionWalletSignerV1, + AppStateUpdateIntent, + Client, + EthereumMsgSigner, + createSigners, + packAppStateUpdateV1, + packCreateAppSessionRequestV1, + withBlockchainRPC, + type AppDefinitionV1, + type AppStateUpdateV1, +} from '@yellow-org/sdk'; +import Decimal from 'decimal.js'; + +const wsURL = process.env.NITRONODE_WS_URL ?? ''; +const chainId = BigInt(process.env.CHAIN_ID ?? '80002'); +const asset = process.env.ASSET ?? 'usdc'; + +const user1Key = process.env.USER_1_PRIVATE_KEY as `0x${string}`; +const user2Key = process.env.USER_2_PRIVATE_KEY as `0x${string}`; + +const user1 = createSigners(user1Key); +const user2 = createSigners(user2Key); + +const client1 = await Client.create( + wsURL, + user1.stateSigner, + user1.txSigner, + withBlockchainRPC(chainId, process.env.RPC_URL!) +); + +const client2 = await Client.create( + wsURL, + user2.stateSigner, + user2.txSigner, + withBlockchainRPC(chainId, process.env.RPC_URL!) +); + +const appSigner1 = new AppSessionWalletSignerV1(new EthereumMsgSigner(user1Key)); +const appSigner2 = new AppSessionWalletSignerV1(new EthereumMsgSigner(user2Key)); +``` + +Always close both clients in a `finally` block when the flow ends. + +## Step 2: Prepare the user home channel + +The session deposit comes from `client1`'s home channel. If the channel does not exist yet, create and checkpoint it first. + +```typescript +await client1.setHomeBlockchain(asset, chainId); +await client2.setHomeBlockchain(asset, chainId); + +await client1.approveToken(chainId, asset, new Decimal('20')); +await client1.deposit(chainId, asset, new Decimal('10')); +await client1.checkpoint(asset); +``` + +If the home channel already has enough signed balance, skip the approve/deposit/checkpoint block and continue. + +## Step 3: Define the app session + +The native v1 app definition uses `AppDefinitionV1`, not the legacy RPC app-definition and protocol-version shape. + +```typescript +const appId = `checkout-demo-${Date.now()}`; + +const definition: AppDefinitionV1 = { + applicationId: appId, + participants: [ + { walletAddress: client1.getUserAddress(), signatureWeight: 1 }, + { walletAddress: client2.getUserAddress(), signatureWeight: 1 }, + ], + quorum: 2, + nonce: BigInt(Date.now()), +}; + +const sessionData = JSON.stringify({ cartId: 'cart-001' }); +``` + +## Step 4: Register the application via `apps.v1.submit_app_version` + +`app_sessions.v1.create_app_session` requires the application to exist in the app registry. If the app is missing, Nitronode returns `application_not_registered`. + +The SDK wrapper is `Client.registerApp(appID, metadata, creationApprovalNotRequired)`. It signs the packed app data with the transaction signer and submits `apps.v1.submit_app_version`. + +```typescript +await client1.registerApp( + appId, + JSON.stringify({ name: 'Checkout demo', version: 1 }), + true +); +``` + +If `creationApprovalNotRequired` is `false`, pass the owner's approval signature to `createAppSession()` through the optional `{ ownerSig }` argument. + +## Step 5: Create the session + +Every participant signs the same create-session hash. + +```typescript +const createHash = packCreateAppSessionRequestV1(definition, sessionData); + +const createSigs = [ + await appSigner1.signMessage(createHash), + await appSigner2.signMessage(createHash), +]; + +const { appSessionId, version, status } = await client1.createAppSession( + definition, + sessionData, + createSigs +); + +console.log({ appSessionId, version, status }); +``` + +## Step 6: Deposit into the session + +A deposit update moves funds from the user's home channel into the app session. + +```typescript +const depositUpdate: AppStateUpdateV1 = { + appSessionId, + intent: AppStateUpdateIntent.Deposit, + version: 2n, + allocations: [ + { participant: client1.getUserAddress(), asset, amount: new Decimal('10') }, + { participant: client2.getUserAddress(), asset, amount: new Decimal('0') }, + ], + sessionData, +}; + +const depositHash = packAppStateUpdateV1(depositUpdate); +const depositSigs = [ + await appSigner1.signMessage(depositHash), + await appSigner2.signMessage(depositHash), +]; + +const nodeSig = await client1.submitAppSessionDeposit( + depositUpdate, + depositSigs, + asset, + new Decimal('10') +); + +console.log('Deposit state node signature:', nodeSig); +``` + +## Step 7: Operate on app state + +An `Operate` update changes the session's off-chain state without touching the blockchain. + +```typescript +const operateUpdate: AppStateUpdateV1 = { + appSessionId, + intent: AppStateUpdateIntent.Operate, + version: 3n, + allocations: [ + { participant: client1.getUserAddress(), asset, amount: new Decimal('8') }, + { participant: client2.getUserAddress(), asset, amount: new Decimal('2') }, + ], + sessionData: JSON.stringify({ cartId: 'cart-001', purchase: 'item-123' }), +}; + +const operateHash = packAppStateUpdateV1(operateUpdate); +await client1.submitAppState(operateUpdate, [ + await appSigner1.signMessage(operateHash), + await appSigner2.signMessage(operateHash), +]); +``` + +## Step 8: Withdraw remaining funds + +Use `Withdraw` when participants move session funds back to home channels. + +Setting each allocation to zero in a `Withdraw` update tells the node to release those amounts back to the participant's home channel. + +```typescript +const withdrawUpdate: AppStateUpdateV1 = { + appSessionId, + intent: AppStateUpdateIntent.Withdraw, + version: 4n, + allocations: [ + { participant: client1.getUserAddress(), asset, amount: new Decimal('0') }, + { participant: client2.getUserAddress(), asset, amount: new Decimal('0') }, + ], + sessionData: operateUpdate.sessionData, +}; + +const withdrawHash = packAppStateUpdateV1(withdrawUpdate); +await client1.submitAppState(withdrawUpdate, [ + await appSigner1.signMessage(withdrawHash), + await appSigner2.signMessage(withdrawHash), +]); +``` + +## Step 9: Close the session + +Close is another signed app-state update. Submit it after final withdrawals or with the final allocation state your app requires. + +This example reuses `withdrawUpdate` intentionally: Close carries forward the same final allocations and session data from the Withdraw step. + +```typescript +const closeUpdate: AppStateUpdateV1 = { + ...withdrawUpdate, + intent: AppStateUpdateIntent.Close, + version: 5n, +}; + +const closeHash = packAppStateUpdateV1(closeUpdate); +await client1.submitAppState(closeUpdate, [ + await appSigner1.signMessage(closeHash), + await appSigner2.signMessage(closeHash), +]); +``` + +## Step 10: Query final state and close clients + +```typescript +const { sessions } = await client1.getAppSessions({ + wallet: client1.getUserAddress(), + status: 'closed', +}); + +const finalDefinition = await client1.getAppDefinition(appSessionId); + +console.log({ sessions, finalDefinition }); + +await client1.close(); +await client2.close(); +``` + +## Compat alternative for migrators + +If you are still using `@yellow-org/sdk-compat`, do not copy the native snippets above into a compat-only client. Use the compat app-session helpers while migrating call sites, then move to the native `Client` flow when you are ready to own app definitions and signed `AppStateUpdateV1` payloads directly. + +```typescript +// Compat path: keep the 0.5.3-facing surface while the app migrates. +import { NitroliteClient } from '@yellow-org/sdk-compat'; + +// See the compat overview for walletClient and blockchainRPCs setup. +const compatClient = await NitroliteClient.create({ + wsURL, + walletClient, + chainId: Number(chainId), + blockchainRPCs, +}); +``` + +## Troubleshooting + +| Symptom | Likely cause | Fix | +|---|---|---| +| `application_not_registered` | `createAppSession()` ran before app registration. | Call `registerApp()` first; it submits `apps.v1.submit_app_version`. | +| `quorum_not_met` | Not enough participant signatures for the app definition's quorum. | Sign the same packed hash with enough app-session signers. | +| `invalid_app_state` | The signed payload does not match the submitted update. | Recompute `packAppStateUpdateV1(update)` and collect fresh signatures. | +| `channel_not_found` | The depositing user has no ready home channel for `asset`. | Deposit and checkpoint the home channel before `submitAppSessionDeposit()`. | +| `ongoing_transition` | A prior channel transition is still pending. | Acknowledge or checkpoint the pending state, then retry the app-session update. | + +## Further Reading + +- [TypeScript SDK examples](./typescript/examples) +- [TypeScript SDK API reference](./typescript/api-reference) +- App-session RPC catalogue: `app_sessions.v1` in the API Reference rebuild. +- App registry RPC catalogue: `apps.v1` in the API Reference rebuild. diff --git a/docs/build/sdk/typescript-compat/_category_.json b/docs/nitrolite/build/sdk/typescript-compat/_category_.json similarity index 83% rename from docs/build/sdk/typescript-compat/_category_.json rename to docs/nitrolite/build/sdk/typescript-compat/_category_.json index 5139e8f..28b66b7 100644 --- a/docs/build/sdk/typescript-compat/_category_.json +++ b/docs/nitrolite/build/sdk/typescript-compat/_category_.json @@ -1,6 +1,6 @@ { "label": "TypeScript Compat SDK", - "position": 3, + "position": 5, "collapsed": false, "collapsible": false } diff --git a/docs/nitrolite/build/sdk/typescript-compat/errors.mdx b/docs/nitrolite/build/sdk/typescript-compat/errors.mdx new file mode 100644 index 0000000..4bfbf50 --- /dev/null +++ b/docs/nitrolite/build/sdk/typescript-compat/errors.mdx @@ -0,0 +1,183 @@ +--- +title: "Errors and recovery (compat)" +description: Typed compat errors and recovery recipes for migrated apps +sidebar_position: 6 +--- + +# Errors and Recovery + +At app boundaries, classify raw wallet or SDK failures and show only UI-safe copy: + +```typescript +import { NitroliteClient, getUserFacingMessage } from '@yellow-org/sdk-compat'; + +try { + await client.deposit(tokenAddress, 11_000_000n); +} catch (err) { + const typed = NitroliteClient.classifyError(err); + showToast(getUserFacingMessage(typed)); + throw typed; +} +``` + +## Error Catalogue + +| Error class | Code | Trigger | Recovery | +|---|---|---|---| +| `CompatError` | varies | Base compat failure | Log `error.code`; show `getUserFacingMessage(error)` | +| `AllowanceError` | `ALLOWANCE_INSUFFICIENT` | ChannelHub needs ERC-20 allowance | Approve, then retry | +| `UserRejectedError` | `USER_REJECTED` | Wallet rejected a signature or transaction | Show retry UI; do not auto-loop prompts | +| `InsufficientFundsError` | `INSUFFICIENT_FUNDS` | Not enough gas or token balance | Show funding guidance | +| `NotInitializedError` | `NOT_INITIALIZED` | Wallet/client is disconnected | Reconnect and recreate the client | +| `OngoingStateTransitionError` | `ONGOING_STATE_TRANSITION` | Previous transition still finalizing | Poll status, then retry | + +## Allowance + +Compat token helpers use token addresses and raw token units: + +```typescript +import { AllowanceError, NitroliteClient, getUserFacingMessage } from '@yellow-org/sdk-compat'; + +try { + await client.deposit(tokenAddress, 11_000_000n); +} catch (err) { + const typed = NitroliteClient.classifyError(err); + showToast(getUserFacingMessage(typed)); + + if (typed instanceof AllowanceError) { + await client.approveTokens(tokenAddress, 11_000_000n); + await client.deposit(tokenAddress, 11_000_000n); + } +} +``` + +When you fall through to native v1, use `client.innerClient.approveToken(chainId, asset, amount)` with `Decimal`: + +```typescript +import Decimal from 'decimal.js'; +import { getUserFacingMessage } from '@yellow-org/sdk-compat'; + +try { + await client.innerClient.checkpoint('usdc'); +} catch (err) { + showToast(getUserFacingMessage(err)); + // Native v1 approveToken expects a bigint chain ID. + await client.innerClient.approveToken(11155111n, 'usdc', new Decimal(11)); + await client.innerClient.checkpoint('usdc'); +} +``` + +## Prompt and Reconnect + +For `UserRejectedError`, show `getUserFacingMessage(error)` and expose an explicit retry button. Do not auto-loop wallet prompts. For `NotInitializedError`, reconnect the wallet and recreate `NitroliteClient` before retrying. + +## Ongoing Transition + +Use `EventPoller` to wait for channel status to settle before enabling another write: + +```typescript +import { + EventPoller, + OngoingStateTransitionError, + NitroliteClient, + RPCChannelStatus, + getUserFacingMessage, +} from '@yellow-org/sdk-compat'; + +try { + await client.deposit(tokenAddress, 11_000_000n); +} catch (err) { + const typed = NitroliteClient.classifyError(err); + showToast(getUserFacingMessage(typed)); + + if (typed instanceof OngoingStateTransitionError) { + const poller = new EventPoller(client, { + onChannelUpdate: (channels) => { + if (!channels.some((channel) => channel.status === RPCChannelStatus.Resizing)) { + poller.stop(); + enableRetry(); + } + }, + onError: (pollError) => showToast(getUserFacingMessage(pollError)), + }, 3000); + poller.start(); + } +} +``` + +## Insufficient Funds + +```typescript +import { InsufficientFundsError, NitroliteClient, getUserFacingMessage } from '@yellow-org/sdk-compat'; + +try { + await client.deposit(tokenAddress, 11_000_000n); +} catch (err) { + const typed = NitroliteClient.classifyError(err); + showToast(getUserFacingMessage(typed)); + + if (typed instanceof InsufficientFundsError) { + const tokenBalance = await client.getTokenBalance(tokenAddress); + renderFundingHelp({ tokenBalance, chainId: 11155111 }); + } +} +``` + +## Missing RPC URL + +On-chain helpers throw `No RPC URL configured for chain ...` when `blockchainRPCs` is missing. Fix the client config with `blockchainRPCs: { 80002: process.env.POLYGON_AMOY_RPC_URL! }`. + +```typescript +import { NitroliteClient, getUserFacingMessage } from '@yellow-org/sdk-compat'; + +try { + await client.getOpenChannels(); +} catch (err) { + const typed = NitroliteClient.classifyError(err); + showToast(getUserFacingMessage(typed)); + if (err instanceof Error && err.message.includes('No RPC URL configured')) promptForRpcUrl(); +} +``` + +## Unsupported Asset or Wrong Amount Unit + +```typescript +try { + await client.transfer(destination, [{ asset: 'usdc', amount: '5000000' }]); +} catch (err) { + showToast(getUserFacingMessage(err)); + + if (err instanceof Error && err.message.includes('Unknown asset')) { + renderSupportedAssets(await client.getAssetsList()); + } +} +``` + +If a transfer uses `5.0`, fix it before retrying. Compat transfer uses raw asset-unit strings. Keep the [amount-units table](./overview#amount-units) next to migrated payment code reviews. + +## Failed Checkpoint + +Retry with capped backoff only after approval and state preconditions are satisfied. Keep the visible message behind `getUserFacingMessage(err)`: + +```typescript +import { NitroliteClient, getUserFacingMessage } from '@yellow-org/sdk-compat'; + +let lastError: unknown; + +for (const waitMs of [1000, 3000, 5000]) { + try { + await client.innerClient.checkpoint('usdc'); + lastError = undefined; + break; + } catch (err) { + const typed = NitroliteClient.classifyError(err); + lastError = typed; + showToast(getUserFacingMessage(typed)); + await new Promise((resolve) => setTimeout(resolve, waitMs)); + } +} + +if (lastError) { + throw lastError; +} +``` diff --git a/docs/nitrolite/build/sdk/typescript-compat/event-poller.mdx b/docs/nitrolite/build/sdk/typescript-compat/event-poller.mdx new file mode 100644 index 0000000..be571e1 --- /dev/null +++ b/docs/nitrolite/build/sdk/typescript-compat/event-poller.mdx @@ -0,0 +1,99 @@ +--- +title: "EventPoller: bridging the dropped server-push model" +description: Polling bridge for migrated v0.5.3 event handlers +sidebar_position: 5 +--- + +# EventPoller + +`EventPoller` is the compat bridge for apps that used 0.5.3-style pushed channel, balance, or asset updates. The v1 runtime does not preserve that server-push event model, so compat polls the query methods and calls your existing UI update hooks. + +Use it when the rest of your app still expects callbacks. If you are already rewriting the state layer, call `client.getChannels()`, `client.getBalances()`, and `client.getAssetsList()` directly. + +## Public Surface + +```typescript +import { EventPoller } from '@yellow-org/sdk-compat'; + +const poller = new EventPoller(client, callbacks, intervalMs); + +poller.start(); +poller.stop(); +poller.setInterval(10000); +``` + +The constructor takes the compat `NitroliteClient`, optional `EventPollerCallbacks`, and an optional interval in milliseconds. The default interval is `5000`. + +```typescript +interface EventPollerCallbacks { + onChannelUpdate?: (channels: LedgerChannel[]) => void; + onBalanceUpdate?: (balances: LedgerBalance[]) => void; + onAssetsUpdate?: (assets: ClearNodeAsset[]) => void; + onError?: (error: Error) => void; +} +``` + +`onAssetsUpdate` keeps the legacy asset shape for backwards compatibility. New native v1 code should read the asset surface directly from `@yellow-org/sdk`. + +## Full Example + +```typescript +import { + EventPoller, + NitroliteClient, + blockchainRPCsFromEnv, + getUserFacingMessage, +} from '@yellow-org/sdk-compat'; + +const client = await NitroliteClient.create({ + wsURL: '', + walletClient, + chainId: 11155111, + blockchainRPCs: blockchainRPCsFromEnv(), +}); + +const poller = new EventPoller(client, { + onChannelUpdate: (channels) => { + channelStore.replace(channels); + }, + onBalanceUpdate: (balances) => { + balanceStore.replace(balances); + }, + onAssetsUpdate: (assets) => { + assetStore.replace(assets); + }, + onError: (err) => { + showToast(getUserFacingMessage(err)); + }, +}, 5000); + +poller.start(); + +window.addEventListener('beforeunload', () => { + poller.stop(); +}); +``` + +## Intervals + +Use `2000` to `5000` ms during active deposits, withdrawals, or app-session transitions. Use `5000` to `10000` ms for normal dashboards, and `15000` to `30000` ms for background views. Switch intervals as the user moves between active and passive flows: + +```typescript +poller.setInterval(isTransitionPending ? 3000 : 10000); +``` + +## Failure Behavior + +Each poll cycle calls: + +- `client.getChannels()` +- `client.getBalances()` +- `client.getAssetsList()` + +Those requests run independently. If one fails, `EventPoller` calls `onError` for that failure and still delivers fulfilled results from the other requests. + +`EventPoller` does not renew auth tokens, persist state, or replace your app cache. It gives migrated apps a single start/stop lifecycle and normalizes channel, balance, and asset polling failures into one `onError` path while they move away from pushed WebSocket events. + +## Moving to Native v1 + +When you remove compat, remove `EventPoller` too. Native v1 apps should own their own refresh policy and call SDK query methods such as `client.getChannels(wallet)`, `client.getBalances(wallet)`, and `client.getAssets()` from the screen that needs the data. diff --git a/docs/nitrolite/build/sdk/typescript-compat/migration-offchain.mdx b/docs/nitrolite/build/sdk/typescript-compat/migration-offchain.mdx new file mode 100644 index 0000000..08aec5f --- /dev/null +++ b/docs/nitrolite/build/sdk/typescript-compat/migration-offchain.mdx @@ -0,0 +1,178 @@ +--- +title: "Migration: Off-Chain" +description: Off-chain migration from v0.5.3 to the compat layer +sidebar_position: 4 +--- + +# Off-Chain Migration Guide + +This page covers authentication, app sessions, transfers, ledger queries, and event polling when moving 0.5.3 app code to `@yellow-org/sdk-compat`. + +## Authentication + +Native `NitroliteClient` usage handles connection authentication inside the client. Legacy auth helper imports are split into three buckets. + +### Available and Functional + +These helpers are exported and implemented for migration code that still owns a WebSocket auth flow: + +```typescript +import { + createAuthRequestMessage, + createAuthVerifyMessage, + createAuthVerifyMessageWithJWT, + createEIP712AuthMessageSigner, +} from '@yellow-org/sdk-compat'; +``` + +The v1 auth request fields are `application`, `expires_at`, `session_key`, `scope`, and `allowances`. The codemod flags older `app_name`, `expire`, `participant`, and `challenge` usage for review. + +### Exported but Fail Fast + +Workflow helpers that cannot map honestly to a single live v1 RPC remain exported so old imports compile, but throw with migration guidance: + +```typescript +createTransferMessage; +createCreateChannelMessage; +createCloseChannelMessage; +createResizeChannelMessage; +``` + +Use `NitroliteClient` methods instead. + +### Parse v1 Payloads Into 0.5.x Shapes + +The `parse*Response` helpers normalize known v1 response payloads into the older app-facing response shape: + +```typescript +parseGetChannelsResponse; +parseGetLedgerBalancesResponse; +parseGetLedgerEntriesResponse; +parseGetAppSessionsResponse; +parseCreateAppSessionResponse; +parseCloseAppSessionResponse; +parseSubmitAppStateResponse; +parseGetAppDefinitionResponse; +parseAnyRPCResponse; +``` + +These are migration aids, not a reason to keep an app-owned RPC transport indefinitely. + +## App Sessions + +### List + +**Before:** `createGetAppSessionsMessage` + `sendRequest` + `parseGetAppSessionsResponse`. + +**After:** + +```typescript +const sessions = await client.getAppSessionsList(); +``` + +### Create + +App-session allocation strings remain human-readable decimal strings: + +```typescript +const appAmount = '5.0'; // human-readable app-session decimal string + +await client.createAppSession(definition, [ + { participant: userAddress, asset: 'usdc', amount: appAmount }, +], quorumSigs); +``` + +### Submit State + +```typescript +import { RPCAppStateIntent } from '@yellow-org/sdk-compat'; + +await client.submitAppState({ + app_session_id: appSessionId, + intent: RPCAppStateIntent.Operate, + version, + allocations, + quorum_sigs: quorumSigs, +}); +``` + +### Close + +```typescript +await client.closeAppSession(appSessionId, allocations, quorumSigs); +``` + +## Transfers + +Transfer allocations are raw asset-unit strings, not human-readable decimal strings. + +**Before:** helper workflow. + +```typescript +const msg = await createTransferMessage(signer.sign, { destination, allocations }); +await sendRequest(msg); +``` + +**After:** compat method. + +```typescript +await client.transfer(destination, [ + { asset: 'usdc', amount: '5000000' }, +]); +``` + +That example means 5 USDC when the asset has 6 decimals. See the [amount-units table](./overview#amount-units). + +## Ledger Queries + +**Before:** create, send, parse. + +```typescript +const msg = await createGetLedgerBalancesMessage(signer.sign, accountId); +const raw = await sendRequest(msg); +const balances = parseGetLedgerBalancesResponse(raw).params.ledgerBalances; +``` + +**After:** direct client calls. + +```typescript +const balances = await client.getBalances(); +const entries = await client.getLedgerEntries(); +const channels = await client.getChannels(); +``` + +## Event Polling + +0.5.3 apps often listened for pushed channel and balance updates. Compat replaces that with `EventPoller`, which polls the same query methods your app can call directly. + +```typescript +import { EventPoller } from '@yellow-org/sdk-compat'; + +const poller = new EventPoller(client, { + onChannelUpdate: (channels) => updateChannels(channels), + onBalanceUpdate: (balances) => updateBalances(balances), + onAssetsUpdate: (assets) => updateAssets(assets), + onError: (err) => reportError(err), +}, 5000); + +poller.start(); +``` + +See the [EventPoller cookbook](./event-poller) for lifecycle, interval, and cleanup guidance. + +## Error Handling + +Wrap migrated calls with typed error handling instead of string-matching raw wallet or RPC errors: + +```typescript +import { getUserFacingMessage, NitroliteClient } from '@yellow-org/sdk-compat'; + +try { + await client.transfer(destination, [{ asset: 'usdc', amount: '5000000' }]); +} catch (err) { + const typed = NitroliteClient.classifyError(err); + showToast(getUserFacingMessage(typed)); +} +``` + +See [Errors and recovery](./errors) for recovery recipes. diff --git a/docs/nitrolite/build/sdk/typescript-compat/migration-onchain.mdx b/docs/nitrolite/build/sdk/typescript-compat/migration-onchain.mdx new file mode 100644 index 0000000..f87d801 --- /dev/null +++ b/docs/nitrolite/build/sdk/typescript-compat/migration-onchain.mdx @@ -0,0 +1,123 @@ +--- +title: "Migration: On-Chain" +description: On-chain migration from v0.5.3 to the compat layer +sidebar_position: 3 +--- + +# On-Chain Migration Guide + +This page covers deposits, withdrawals, channel operations, amount handling, and contract-address migration for apps moving from `@erc7824/nitrolite@0.5.3` to `@yellow-org/sdk-compat`. + +## Deposits + +In v1, home-channel creation is implicit on first deposit. Apps should stop sending a separate `createChannel` workflow. + +**Before:** manual approve, deposit, and channel creation. + +```typescript +await approveToken(custody, tokenAddress, amount); +await sendRequest(createDepositMessage(signer.sign, { token: tokenAddress, amount })); +await sendRequest(createCreateChannelMessage(signer.sign, { token: tokenAddress, amount })); +``` + +**After:** one compat call with raw token units. + +```typescript +const amount = 11_000_000n; // 11 USDC when the token has 6 decimals +await client.deposit(tokenAddress, amount); +``` + +`deposit()` resolves the token to a v1 asset, sets the home blockchain, calls the native deposit path, and checkpoints. If token allowance is missing, the compat client retries after approval through the v1 SDK. + +## Withdrawals + +**Before:** close or resize workflow plus a manual withdrawal request. + +```typescript +const closeMsg = await createCloseChannelMessage(signer.sign, channelId, destination); +await sendRequest(closeMsg); +await sendRequest(createWithdrawMessage(signer.sign, { token, amount })); +``` + +**After:** one compat call with raw token units. + +```typescript +const amount = 5_000_000n; // 5 USDC +await client.withdrawal(tokenAddress, amount); +``` + +## Channel Operations + +| Operation | v0.5.3 habit | Compat behavior | +|---|---|---| +| Create | `createChannel()` | Unsupported as a standalone flow; first `deposit()` creates the home channel when needed | +| Close | `createCloseChannelMessage` + send + parse | `client.closeChannel()` or `client.closeChannel({ tokenAddress })` | +| Resize | `createResizeChannelMessage` + send + parse | `client.resizeChannel({ allocate_amount, token })`, implemented through deposit | +| Checkpoint | `checkpointChannel(...)` | Fail-fast stub; use `client.innerClient.checkpoint(asset)` | +| Challenge | `challengeChannel({ state })` | Delegates to the v1 challenge path | + +## Amount Handling + +Compat does not accept one universal amount shape. Use the canonical [amount-units table](./overview#amount-units) before changing production amounts. + +```typescript +// Raw token-unit bigint for deposit and withdrawal. +await client.deposit(usdcTokenAddress, 11_000_000n); +await client.withdrawal(usdcTokenAddress, 5_000_000n); + +// Helpers for display conversion. +const formatted = client.formatAmount(usdcTokenAddress, 11_000_000n); // "11" +const parsed = client.parseAmount(usdcTokenAddress, '11.0'); // 11_000_000n +``` + +Transfers are different. A transfer allocation uses a raw asset-unit string: + +```typescript +await client.transfer(recipientAddress, [ + { asset: 'usdc', amount: '5000000' }, +]); +``` + +App-session allocations are different again: they use human-readable decimal strings. Keep these three cases separate during review. + +## Contract Addresses + +**Before:** apps often carried manual contract maps. + +```typescript +const addresses = { custody: '0x...', adjudicator: '0x...' }; +``` + +**After:** contract and asset metadata comes from Nitronode config and asset discovery. The `addresses` config field is deprecated and ignored. + +```typescript +const config = await client.getConfig(); +const assets = await client.getAssetsList(); +``` + +If an on-chain helper needs an RPC URL for a chain, pass `blockchainRPCs` when creating `NitroliteClient` or use `blockchainRPCsFromEnv()`. + +```typescript +const client = await NitroliteClient.create({ + wsURL: '', + walletClient, + chainId: 11155111, + blockchainRPCs: { + 11155111: process.env.SEPOLIA_RPC_URL!, + }, +}); +``` + +## Approval Helpers + +Compat exposes allowance helpers for migration code that still thinks in raw token units: + +```typescript +const allowance = await client.getTokenAllowance(tokenAddress); + +if (allowance < 11_000_000n) { + await client.approveTokens(tokenAddress, 11_000_000n); +} +``` + +For native v1-only flows, use `client.innerClient.approveToken(chainId, asset, amount)` with `Decimal`. diff --git a/docs/nitrolite/build/sdk/typescript-compat/migration-overview.mdx b/docs/nitrolite/build/sdk/typescript-compat/migration-overview.mdx new file mode 100644 index 0000000..1568395 --- /dev/null +++ b/docs/nitrolite/build/sdk/typescript-compat/migration-overview.mdx @@ -0,0 +1,147 @@ +--- +title: Migration Overview +description: Migrating from v0.5.3 to the compat layer +sidebar_position: 2 +--- + +# Migrating from v0.5.3 to Compat + +Use this guide when an existing app depends on `@erc7824/nitrolite@0.5.3` and needs a staged path onto the v1 runtime. Compat preserves the supported app-facing surface, but it does not promise full 0.5.3 package parity. + +## Start With the Codemod + +Run the codemod before hand-editing. It rewrites imports, migrates client constructors, renames auth fields, updates dependencies, and leaves `TODO [codemod]` comments where a human needs to collapse a workflow. + +```bash +git clone https://github.com/layer-3/yellow-sdk-codemod.git +cd yellow-sdk-codemod +npm install +npm run build + +node dist/cli.js scan --path /path/to/your-app/src +node dist/cli.js run --path /path/to/your-app --update-deps +``` + +Then search for the manual markers: + +```bash +rg "TODO \\[codemod\\]" /path/to/your-app +``` + +See the [overview](./overview) for the command sequence and `node dist/cli.js list` for available transforms. + +## MCP-Assisted Migration + +:::info Coming soon +The Yellow SDK MCP server is proposed for npm publication as `@yellow-org/sdk-mcp`. Until that package is published, treat this as a preview of the assisted workflow, not a command that should work today. +::: + +After publication, connect the MCP server to your MCP-compatible client and use it as a second review pass after the codemod: + +1. Run `server_info` to confirm the MCP content version matches the SDK version you are migrating to. +2. Use the `migrate-from-v053` prompt to walk through changed files and unresolved `TODO [codemod]` markers. +3. Use `validate_import` when you are unsure whether a symbol belongs to `@yellow-org/sdk-compat` or native `@yellow-org/sdk`. +4. Use `get_rpc_method` to map old `create*Message` + `sendRequest` + `parse*Response` flows onto the v1 wire method. +5. Use `lookup_method`, `lookup_type`, or `search_api` to verify direct v1 SDK replacements before reaching for `client.innerClient`. + +MCP assistance does not replace the manual checklist below. Tool-specific setup belongs with the package publish so the documented commands are runnable on the day they appear here. + +## Install Compat + +```bash +npm install @yellow-org/sdk-compat +npm install @yellow-org/sdk viem +``` + +## Client Construction + +**Before:** app-owned WebSocket plus helper calls. + +```typescript +const msg = await createGetChannelsMessage(signer.sign, address); +const raw = await sendRequest(msg); +const parsed = parseGetChannelsResponse(raw); +``` + +**After:** one compat client backed by the v1 SDK. + +```typescript +import { NitroliteClient, blockchainRPCsFromEnv } from '@yellow-org/sdk-compat'; + +const client = await NitroliteClient.create({ + wsURL: '', + walletClient, + chainId: 11155111, + blockchainRPCs: blockchainRPCsFromEnv(), +}); + +const channels = await client.getChannels(); +``` + +:::info Sandbox URL - coming soon +Use the same Nitronode URL placeholder pattern as the native v1 quickstart. Replace `` only when the canonical sandbox host is pinned. +::: + +## Migration Matrix + +| Legacy use | Compat status | Direct v1 replacement | +|---|---|---| +| Auth helpers | Available: `createAuthRequestMessage`, `createAuthVerifyMessage`, `createAuthVerifyMessageWithJWT`, `createEIP712AuthMessageSigner` | SDK client auth flow | +| Channel create or resize helpers | Unsupported or fail-fast where no honest v1 mapping exists | Native `client.innerClient.deposit(...)`, `client.innerClient.checkpoint(asset)`, lifecycle methods | +| Transfer | Available with raw-amount caveat | `Client.transfer(recipient, asset, Decimal)` | +| App sessions | Available with v1 remapping | Native app-session methods on `Client` | + +## Amount Rules + +Do not flatten all amounts into one convention. Compat intentionally keeps separate units for separate method families. + +- `deposit`, `withdrawal`, and token allowance helpers use raw token-unit `bigint`. +- `transfer` uses raw asset-unit strings, for example `{ asset: 'usdc', amount: '5000000' }` for 5 USDC with 6 decimals. +- App-session allocations use human-readable decimal strings, for example `'5.0'`. +- Direct native v1 calls use `Decimal`. + +See the canonical [amount-units table](./overview#amount-units). + +## What Changes + +| Concern | v0.5.3 habit | Compat behavior | +|---|---|---| +| WebSocket ownership | App creates and manages `WebSocket` | `NitroliteClient` manages the connection | +| Signing | App passes `signer.sign` into each helper | Client methods sign internally with `walletClient` | +| Contract addresses | App hard-codes custody and adjudicator addresses | Compat reads current node config and asset metadata | +| Channel creation | App calls `createChannel()` | First `deposit()` creates the home channel when needed | +| Server-push events | App handles pushed channel and balance events | Use [EventPoller](./event-poller) | +| Errors | App matches raw strings | Use typed errors and [Errors and recovery](./errors) | + +## Manual Review Checklist + +After the codemod runs, review each changed area before shipping: + +| Area | What to check | +|---|---| +| Imports | `@erc7824/nitrolite`, `@layer-3/nitrolite`, and vendored tarballs are gone from app source | +| Client setup | `new NitroliteClient(...)` and `createNitroliteClient(...)` are replaced with `NitroliteClient.create({ wsURL, walletClient, chainId, blockchainRPCs })` | +| Auth | `app_name`, `expire`, `participant`, and `challenge` are migrated to `application`, `expires_at`, `session_key`, `scope`, and `allowances` | +| RPC chains | `create*Message` + `sendRequest` + `parse*Response` call chains are replaced with client methods or consciously kept as transitional helpers | +| Channel operations | `createChannel`, `depositToChannel`, and old `approveToken` wrappers are replaced with `deposit`, `withdrawal`, `approveTokens`, or a direct `innerClient` call | +| Events | WebSocket push handlers are replaced with [EventPoller](./event-poller) or app-owned polling | +| Contract config | `custody`, `adjudicator`, `CUSTODIES`, and `ADJUDICATORS` constants are removed; config comes from Nitronode | +| Amounts | Transfer amounts are raw asset-unit strings; app-session allocations are human-readable decimal strings | +| Errors | Raw string matching is replaced with `NitroliteClient.classifyError()` and `getUserFacingMessage()` | +| SSR | Do not import `Client` from `@yellow-org/sdk-compat`; import SDK classes directly from `@yellow-org/sdk` | + +## Unsupported Surfaces + +The following names can still appear in migrated code, but they are not full live replacements: + +- `checkpointChannel(...)` fails fast; use `client.innerClient.checkpoint(asset)`. +- Workflow-only `create*Message` helpers such as `createTransferMessage`, `createCreateChannelMessage`, `createCloseChannelMessage`, and `createResizeChannelMessage` fail fast with migration guidance. +- `parse*Response` helpers normalize known v1 payloads into 0.5.x-shaped responses, but they are not a replacement for an app-owned RPC transport. +- `get_session_keys` directory-style APIs and `get_rpc_history` are not preserved. + +## Next Steps + +- [On-Chain Changes](./migration-onchain) - deposits, withdrawals, channel operations, and raw token units +- [Off-Chain Changes](./migration-offchain) - auth helpers, app sessions, transfers, and ledger queries +- [EventPoller](./event-poller) - replace server-push handlers with polling callbacks +- [Errors and recovery](./errors) - classify wallet, allowance, funds, and transition errors diff --git a/docs/nitrolite/build/sdk/typescript-compat/overview.mdx b/docs/nitrolite/build/sdk/typescript-compat/overview.mdx new file mode 100644 index 0000000..46ae303 --- /dev/null +++ b/docs/nitrolite/build/sdk/typescript-compat/overview.mdx @@ -0,0 +1,170 @@ +--- +title: Overview +description: Compatibility layer bridging selected Nitrolite SDK v0.5.3 APIs to the v1 runtime +sidebar_position: 1 +--- + +# TypeScript Compat SDK + +`@yellow-org/sdk-compat` is a migration layer for apps already built on the published `@erc7824/nitrolite@0.5.3` API. It keeps selected app-facing calls available while routing them through the native v1 `@yellow-org/sdk` runtime. + +:::note Package naming +The published npm package audited for this migration path is `@erc7824/nitrolite@0.5.3`. Some older internal docs refer to the same 0.5.3 codebase as `@layer-3/nitrolite`. +::: + +
+Already integrated with @erc7824/nitrolite? + +Speed up the migration with the `yellow-sdk-codemod` repository: + +```bash +# Clone the codemod and install its dependencies +git clone https://github.com/layer-3/yellow-sdk-codemod.git +cd yellow-sdk-codemod +npm install +npm run build + +# Scan your codebase for migrate-able patterns (no files modified) +node dist/cli.js scan --path /path/to/your-app/src + +# Apply transforms (and update package.json deps) +node dist/cli.js run --path /path/to/your-app --update-deps +``` + +After running, search for `TODO [codemod]` in your codebase and resolve them manually. The codemod surfaces every place a chained `create*Message` + `sendRequest` + `parse*Response` pattern needs context. See `node dist/cli.js list` for the full transform list. + +:::info MCP-assisted migration - coming soon +The Yellow SDK MCP server is planned for npm as `@yellow-org/sdk-mcp`, but it is not published yet. After it lands, use it as a second review pass for unresolved codemod markers, uncertain imports, old RPC helper chains, and direct v1 SDK replacements. See the [migration overview](./migration-overview#mcp-assisted-migration) for the planned workflow. +::: + +
+ +## Why Use Compat + +Use compat when a live app needs to move off the 0.5.3 package without rewriting every transport, auth, event, and amount-handling call at once. New apps should start with the native [`@yellow-org/sdk`](../typescript/getting-started) instead. + +Compat is a bridge, not full package parity. The underlying v1 client is available as `client.innerClient` for flows that the migration surface cannot model honestly. + +## Installation + +```bash +npm install @yellow-org/sdk-compat +# peer dependencies +npm install @yellow-org/sdk viem +``` + +:::info Sandbox URL - coming soon +Use your Nitronode WebSocket URL in the `wsURL` field below. The public sandbox URL is intentionally shown as `` until the canonical host is pinned. +::: + +## Quick Start + +```typescript +import { NitroliteClient, blockchainRPCsFromEnv } from '@yellow-org/sdk-compat'; + +const client = await NitroliteClient.create({ + wsURL: '', + walletClient, // viem WalletClient with account + chainId: 11155111, // Sepolia + blockchainRPCs: blockchainRPCsFromEnv(), +}); + +await client.deposit(tokenAddress, 11_000_000n); + +const channels = await client.getChannels(); +const balances = await client.getBalances(); +const sessions = await client.getAppSessionsList(); + +// 5 USDC (6 decimals) as raw asset-unit string: +await client.transfer(recipientAddress, [{ asset: 'usdc', amount: '5000000' }]); + +await client.close(); +``` + +`TransferAllocation.amount` is a raw asset-unit string using the asset's canonical decimals; the compat layer divides by decimals before delegating to the v1 SDK. + +## Amount Units + +| Method area | Amount format | Example | +|---|---|---| +| `deposit`, `withdrawal`, token allowance helpers | raw token-unit `bigint` | `11_000_000n` | +| `transfer` allocations | raw asset-unit string | `'5000000'` | +| App-session allocations | human-readable decimal string | `'5.0'` | +| Direct `@yellow-org/sdk` channel and app amounts | `Decimal` | `new Decimal(5)` | + +## Method Cheat Sheet + +| Area | Covered surface | +|---|---| +| Channel operations | `deposit`, `depositAndCreateChannel`, `withdrawal`, `closeChannel`, `resizeChannel`, `challengeChannel`, `acknowledge` | +| Query methods | `getChannels`, `getChannelData`, `getBalances`, `getLedgerEntries`, `getAppSessionsList`, `getAssetsList`, `getAccountInfo`, `getConfig`, `getBlockchains`, `getActionAllowances` | +| On-chain helpers | `getOpenChannels`, `approveTokens`, `getTokenAllowance`, `getTokenBalance` | +| Transfers | `transfer(destination, allocations)` with raw asset-unit strings | +| App sessions | `createAppSession`, `closeAppSession`, `submitAppState`, `getAppDefinition`, `registerApp`, `getApps` | +| App-session signing | `packCreateAppSessionHash`, `packSubmitAppStateHash`, `toWalletQuorumSignature`, `toSessionKeyQuorumSignature` | +| Auth helpers | `createAuthRequestMessage`, `createAuthVerifyMessage`, `createAuthVerifyMessageWithJWT`, `createEIP712AuthMessageSigner` | +| Event polling | `EventPoller` polls `getChannels()`, `getBalances()`, and `getAssetsList()` and emits legacy-shaped callbacks | + +`checkpointChannel(...)` is exported as a fail-fast migration shim; use `client.innerClient.checkpoint(asset)` instead. + +See [EventPoller](./event-poller) for the cookbook. + +Lifecycle helpers include `ping()`, `close()`, `waitForClose()`, and `refreshAssets()`. + +## What Compat Does Not Preserve + +:::warning Compat is curated, not a drop-in +`@yellow-org/sdk-compat` preserves selected `@erc7824/nitrolite@0.5.3` app-facing APIs. It does not preserve: + +- the legacy server-push event model; use [EventPoller](./event-poller) +- `checkpointChannel(...)`; it is a fail-fast stub, use `client.innerClient.checkpoint(asset)` +- `get_session_keys` directory-style APIs +- `get_rpc_history` +- full root-export parity with `@erc7824/nitrolite@0.5.3` + +If your code touches these surfaces, run the codemod first, then use the migration guides below to finish the manual changes. +::: + +## Configuration + +```typescript +interface NitroliteClientConfig { + wsURL: string; // Nitronode WebSocket URL + walletClient: WalletClient; // viem WalletClient with account + chainId: number; // Chain ID, for example 11155111 + blockchainRPCs?: Record; // Chain ID -> RPC URL map + channelSessionKeySigner?: { + sessionKeyPrivateKey: Hex; + walletAddress: Address; + metadataHash: Hex; + authSig: Hex; + }; +} +``` + +`blockchainRPCsFromEnv()` reads from `NEXT_PUBLIC_BLOCKCHAIN_RPCS`: + +```text +NEXT_PUBLIC_BLOCKCHAIN_RPCS=11155111:https://rpc.sepolia.io,1:https://mainnet.infura.io/v3/KEY +``` + +## Accessing the v1 SDK + +`Client` is not re-exported from `@yellow-org/sdk-compat` for SSR safety. Import SDK classes directly from `@yellow-org/sdk`, or use the wrapped instance exposed on the compat client: + +```typescript +const v1Client = client.innerClient; +await v1Client.getHomeChannel(wallet, 'usdc'); +await v1Client.checkpoint('usdc'); +await v1Client.approveToken(chainId, 'usdc', amount); +``` + +## Error Handling + +The compat package exports typed errors plus `getUserFacingMessage(error)` for UI-safe messages. See [Errors and recovery](./errors) for the cookbook. + +## Migration Guides + +- [Migration Overview](./migration-overview) - choose the compat path and run the codemod +- [On-Chain Changes](./migration-onchain) - deposits, withdrawals, channel operations, and amount units +- [Off-Chain Changes](./migration-offchain) - auth, app sessions, transfers, ledger queries, and polling diff --git a/docs/build/sdk/typescript/_category_.json b/docs/nitrolite/build/sdk/typescript/_category_.json similarity index 82% rename from docs/build/sdk/typescript/_category_.json rename to docs/nitrolite/build/sdk/typescript/_category_.json index 9fdfa4a..d58d148 100644 --- a/docs/build/sdk/typescript/_category_.json +++ b/docs/nitrolite/build/sdk/typescript/_category_.json @@ -1,6 +1,6 @@ { "label": "TypeScript SDK", - "position": 2, + "position": 4, "collapsed": false, "collapsible": false } diff --git a/docs/build/sdk/typescript/api-reference.mdx b/docs/nitrolite/build/sdk/typescript/api-reference.mdx similarity index 79% rename from docs/build/sdk/typescript/api-reference.mdx rename to docs/nitrolite/build/sdk/typescript/api-reference.mdx index 830eee2..e82f173 100644 --- a/docs/build/sdk/typescript/api-reference.mdx +++ b/docs/nitrolite/build/sdk/typescript/api-reference.mdx @@ -15,6 +15,7 @@ These methods build and co-sign a state off-chain. Use [`checkpoint()`](#checkpo ### `deposit(blockchainId, asset, amount)` Prepares a deposit state. Creates a new channel if none exists, otherwise advances the existing state. +Backing RPC: creates new channels through `channels.v1.request_creation`, then submits signed states through `channels.v1.submit_state`. ```typescript const state = await client.deposit(80002n, 'usdc', new Decimal(100)); @@ -38,6 +39,7 @@ const txHash = await client.checkpoint('usdc'); ### `withdraw(blockchainId, asset, amount)` Prepares a withdrawal state to remove funds from the channel. +Backing RPC: submits the signed withdrawal state through `channels.v1.submit_state`. ```typescript const state = await client.withdraw(80002n, 'usdc', new Decimal(25)); @@ -59,6 +61,7 @@ const txHash = await client.checkpoint('usdc'); ### `transfer(recipientWallet, asset, amount)` Prepares an off-chain transfer to another wallet. For existing channels, no checkpoint is needed. +Backing RPC: submits the sender state through `channels.v1.submit_state`; the node creates the receiver state and returns the signed pair. ```typescript const state = await client.transfer('0xRecipient...', 'usdc', new Decimal(50)); @@ -79,6 +82,7 @@ const state = await client.transfer('0xRecipient...', 'usdc', new Decimal(50)); ### `closeHomeChannel(asset)` Prepares a finalize state to close the user's channel for a specific asset. +Backing RPC: submits the finalize transition through `channels.v1.submit_state`. ```typescript const state = await client.closeHomeChannel('usdc'); @@ -90,6 +94,7 @@ const txHash = await client.checkpoint('usdc'); ### `acknowledge(asset)` Acknowledges a received state (e.g., after receiving a transfer). +Backing RPC: submits the acknowledgement transition through `channels.v1.submit_state`. ```typescript const state = await client.acknowledge('usdc'); @@ -109,6 +114,8 @@ Settles the latest co-signed state on-chain. Routes to the correct contract meth - **Deposit/Withdrawal** on existing channel → checkpoints the state - **Finalize** → closes the channel +Backing RPC: reads channel context through `channels.v1.get_latest_state` and `channels.v1.get_home_channel`, then sends the on-chain ChannelHub transaction. + ```typescript const txHash = await client.checkpoint('usdc'); ``` @@ -120,6 +127,7 @@ const txHash = await client.checkpoint('usdc'); ### `challenge(state)` Submits an on-chain challenge for a channel. Initiates a dispute period. +Backing RPC: none for submission; the SDK verifies the signed state locally and sends the on-chain ChannelHub challenge transaction. ```typescript const state = await client.getLatestState(wallet, 'usdc', true); @@ -131,6 +139,7 @@ const txHash = await client.challenge(state); ### `approveToken(chainId, asset, amount)` Approves the ChannelHub contract to spend ERC-20 tokens. Required before depositing. +Backing RPC: none; this is an ERC-20 approval transaction against the token contract resolved from Nitronode asset config. ```typescript const txHash = await client.approveToken(80002n, 'usdc', new Decimal(1000)); @@ -141,6 +150,7 @@ const txHash = await client.approveToken(80002n, 'usdc', new Decimal(1000)); ### `checkTokenAllowance(chainId, tokenAddress, owner)` Checks the current token allowance for the ChannelHub contract. +Backing RPC: none; this reads the ERC-20 allowance through the configured blockchain RPC. ```typescript const allowance = await client.checkTokenAllowance(80002n, '0xToken...', '0xOwner...'); @@ -157,6 +167,8 @@ const chains = await client.getBlockchains(); // Supported blockchains const assets = await client.getAssets(); // All assets (or pass blockchainId) ``` +Backing RPC: `node.v1.ping`, `node.v1.get_config`, and `node.v1.get_assets`. `getBlockchains()` reads the blockchains returned by `node.v1.get_config`. + --- ## User Queries @@ -174,6 +186,8 @@ const { transactions, metadata } = await client.getTransactions(wallet, { const allowances = await client.getActionAllowances(wallet); ``` +Backing RPC: `user.v1.get_balances`, `user.v1.get_transactions`, and `user.v1.get_action_allowances`. + --- ## Channel Queries @@ -193,6 +207,8 @@ const escrow = await client.getEscrowChannel(escrowChannelId); const state = await client.getLatestState(wallet, asset, true); ``` +Backing RPC: `channels.v1.get_channels`, `channels.v1.get_home_channel`, `channels.v1.get_escrow_channel`, and `channels.v1.get_latest_state`. + --- ## App Registry @@ -208,6 +224,8 @@ const { apps, metadata } = await client.getApps({ await client.registerApp('my-app', '{"name": "My App"}', false); ``` +Backing RPC: `apps.v1.get_apps` and `apps.v1.submit_app_version`. Register the app before creating sessions for its `applicationId`. + --- ## App Sessions @@ -233,6 +251,8 @@ await client.submitAppState(appUpdate, quorumSigs); const batchId = await client.rebalanceAppSessions(signedUpdates); ``` +Backing RPC: `app_sessions.v1.create_app_session`, `app_sessions.v1.submit_deposit_state`, `app_sessions.v1.submit_app_state`, and `app_sessions.v1.rebalance_app_sessions`. + ### Query ```typescript @@ -240,6 +260,8 @@ const { sessions, metadata } = await client.getAppSessions(opts); const definition = await client.getAppDefinition(appSessionId); ``` +Backing RPC: `app_sessions.v1.get_app_sessions` and `app_sessions.v1.get_app_definition`. + --- ## App Session Keys @@ -260,6 +282,8 @@ await client.submitSessionKeyState({ ...state, user_sig: sig }); const states = await client.getLastKeyStates('0x1234...'); ``` +Backing RPC: `app_sessions.v1.submit_session_key_state` and `app_sessions.v1.get_last_key_states`. + --- ## Channel Session Keys @@ -279,6 +303,8 @@ await client.submitChannelSessionKeyState({ ...state, user_sig: sig }); const states = await client.getLastChannelKeyStates('0x1234...'); ``` +Backing RPC: `channels.v1.submit_session_key_state` and `channels.v1.get_last_key_states`. + --- ## Utilities diff --git a/docs/build/sdk/typescript/configuration.mdx b/docs/nitrolite/build/sdk/typescript/configuration.mdx similarity index 78% rename from docs/build/sdk/typescript/configuration.mdx rename to docs/nitrolite/build/sdk/typescript/configuration.mdx index 675f2a2..9a19d9d 100644 --- a/docs/build/sdk/typescript/configuration.mdx +++ b/docs/nitrolite/build/sdk/typescript/configuration.mdx @@ -10,6 +10,10 @@ sidebar_position: 3 Configuration is passed as variadic options to `Client.create()`: +:::info Sandbox URL - coming soon +Use your Nitronode WebSocket URL for `wsURL`. The public sandbox URL is intentionally shown as `` until the canonical host is pinned. +::: + ```typescript import { Client, @@ -20,7 +24,7 @@ import { } from '@yellow-org/sdk'; const client = await Client.create( - wsURL, + '', stateSigner, txSigner, withBlockchainRPC(chainId, rpcURL), // Blockchain RPC (required for checkpoint) @@ -57,10 +61,29 @@ withErrorHandler((error) => { ## Home Blockchain -`setHomeBlockchain(asset, blockchainId)` sets the default blockchain for an asset. Required before `transfer()` on a new channel (where no chain context exists yet). +`setHomeBlockchain(asset, blockchainId)` sets the default blockchain for an asset. It is required when `transfer()` may need to create a new home channel, because `transfer(recipientWallet, asset, amount)` does not take a chain ID. It is not required for transfers within an existing home channel. ```typescript +import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; +import Decimal from 'decimal.js'; + +const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY as `0x${string}`); + +const client = await Client.create( + '', + stateSigner, + txSigner, + withBlockchainRPC(80002n, process.env.POLYGON_AMOY_RPC!), +); + +// Required only if this transfer could create the USDC home channel. await client.setHomeBlockchain('usdc', 80002n); + +await client.transfer( + '0xRecipient000000000000000000000000000000000000', + 'usdc', + new Decimal('1.25'), +); ``` :::warning diff --git a/docs/build/sdk/typescript/examples.mdx b/docs/nitrolite/build/sdk/typescript/examples.mdx similarity index 66% rename from docs/build/sdk/typescript/examples.mdx rename to docs/nitrolite/build/sdk/typescript/examples.mdx index 84494e5..bf7c8d0 100644 --- a/docs/build/sdk/typescript/examples.mdx +++ b/docs/nitrolite/build/sdk/typescript/examples.mdx @@ -6,6 +6,10 @@ sidebar_position: 4 # Examples +:::info Sandbox URL - coming soon +Use your Nitronode WebSocket URL in each `Client.create()` call below. The public sandbox URL is intentionally shown as `` until the canonical host is pinned. +::: + ## Basic Deposit and Transfer ```typescript @@ -13,10 +17,10 @@ import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; import Decimal from 'decimal.js'; async function basicExample() { - const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!); + const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY as `0x${string}`); const client = await Client.create( - 'wss://clearnode.example.com/ws', + '', stateSigner, txSigner, withBlockchainRPC(80002n, process.env.RPC_URL!) @@ -59,10 +63,10 @@ import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; import Decimal from 'decimal.js'; async function multiChainExample() { - const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!); + const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY as `0x${string}`); const client = await Client.create( - 'wss://clearnode.example.com/ws', + '', stateSigner, txSigner, withBlockchainRPC(80002n, process.env.POLYGON_RPC!), @@ -94,9 +98,9 @@ async function multiChainExample() { import { Client, createSigners } from '@yellow-org/sdk'; async function queryTransactions() { - const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!); + const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY as `0x${string}`); const client = await Client.create( - 'wss://clearnode.example.com/ws', + '', stateSigner, txSigner ); @@ -122,42 +126,63 @@ async function queryTransactions() { ## App Session Workflow +This example uses a single participant and `quorum: 1` to keep the native SDK wiring short. Use the multi-party app-session guide when you need multiple participants to co-sign the same app definition and state updates. + ```typescript -import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; +import { + AppSessionWalletSignerV1, + AppStateUpdateIntent, + Client, + EthereumMsgSigner, + createSigners, + packAppStateUpdateV1, + packCreateAppSessionRequestV1, + withBlockchainRPC, + type AppDefinitionV1, + type AppStateUpdateV1, +} from '@yellow-org/sdk'; import Decimal from 'decimal.js'; async function appSessionExample() { - const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!); + const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY as `0x${string}`); + const appSigner = new AppSessionWalletSignerV1( + new EthereumMsgSigner(process.env.PRIVATE_KEY as `0x${string}`) + ); + const client = await Client.create( - 'wss://clearnode.example.com/ws', + '', stateSigner, txSigner, withBlockchainRPC(80002n, process.env.RPC_URL!) ); try { - const definition = { + const definition: AppDefinitionV1 = { applicationId: 'chess-v1', participants: [ { walletAddress: client.getUserAddress(), signatureWeight: 1 }, - { walletAddress: '0xOpponent...', signatureWeight: 1 }, ], - quorum: 2, + quorum: 1, nonce: 1n, }; + await client.registerApp('chess-v1', '{"name":"Chess"}', true); + + const createHash = packCreateAppSessionRequestV1(definition, '{}'); + const createSig = await appSigner.signMessage(createHash); + const { appSessionId } = await client.createAppSession( definition, '{}', - ['sig1', 'sig2'] + [createSig] ); console.log('Session created:', appSessionId); // Deposit to app session - const appUpdate = { + const appUpdate: AppStateUpdateV1 = { appSessionId, - intent: 1, - version: 1n, + intent: AppStateUpdateIntent.Deposit, + version: 2n, allocations: [{ participant: client.getUserAddress(), asset: 'usdc', @@ -166,9 +191,12 @@ async function appSessionExample() { sessionData: '{}', }; + const depositHash = packAppStateUpdateV1(appUpdate); + const depositSig = await appSigner.signMessage(depositHash); + const nodeSig = await client.submitAppSessionDeposit( appUpdate, - ['sig1'], + [depositSig], 'usdc', new Decimal(50) ); @@ -196,56 +224,77 @@ import { type TransactionSigner, withBlockchainRPC, } from '@yellow-org/sdk'; -import { createWalletClient, custom, type WalletClient } from 'viem'; +import { + createWalletClient, + custom, + type Address, + type Hex, + type WalletClient, +} from 'viem'; import { sepolia } from 'viem/chains'; // Adapt viem WalletClient to SDK's StateSigner (EIP-191 signatures) -class WalletStateSigner implements StateSigner { +class BrowserStateSigner implements StateSigner { constructor(private wc: WalletClient) {} - async sign(payload: Uint8Array): Promise { - return this.wc.signMessage({ - account: this.wc.account!, - message: { raw: payload }, - }); + getAddress(): Address { + return this.wc.account!.address; } - getAddress(): string { - return this.wc.account!.address; + async signMessage(hash: Hex): Promise { + return await this.wc.signMessage({ + account: this.wc.account!, + message: { raw: hash }, + }); } } // Adapt viem WalletClient to SDK's TransactionSigner -class WalletTransactionSigner implements TransactionSigner { +class BrowserTransactionSigner implements TransactionSigner { constructor(private wc: WalletClient) {} - async sign(payload: Uint8Array): Promise { - return this.wc.signTypedData({ + getAddress(): Address { + return this.wc.account!.address; + } + + async sendTransaction(tx: Parameters[0]): Promise { + return await this.wc.sendTransaction(tx); + } + + async signMessage({ raw }: { raw: Hex }): Promise { + return await this.wc.signMessage({ account: this.wc.account!, - domain: { name: 'Nitrolite', version: '1', chainId: 1 }, - types: { Nitrolite: [{ name: 'operation', type: 'bytes' }] }, - primaryType: 'Nitrolite', - message: { operation: `0x${Buffer.from(payload).toString('hex')}` }, + message: { raw }, }); } - getAddress(): string { - return this.wc.account!.address; + async signPersonalMessage(hash: Hex): Promise { + return await this.wc.signMessage({ + account: this.wc.account!, + message: { raw: hash }, + }); } } async function connectWithWallet() { + const transport = custom(window.ethereum!); + const requestClient = createWalletClient({ + chain: sepolia, + transport, + }); + + const [address] = await requestClient.requestAddresses(); const walletClient = createWalletClient({ + account: address, chain: sepolia, - transport: custom(window.ethereum!), + transport, }); - const [address] = await walletClient.requestAddresses(); - const stateSigner = new ChannelDefaultSigner(new WalletStateSigner(walletClient)); - const txSigner = new WalletTransactionSigner(walletClient); + const stateSigner = new ChannelDefaultSigner(new BrowserStateSigner(walletClient)); + const txSigner = new BrowserTransactionSigner(walletClient); const client = await Client.create( - 'wss://clearnode.example.com/ws', + '', stateSigner, txSigner, withBlockchainRPC(11155111n, 'https://rpc.sepolia.io'), @@ -257,7 +306,7 @@ async function connectWithWallet() { ## Allowance Handling -On-chain operations like `checkpoint()` require ERC-20 token approval. The example-app pattern detects allowance errors and retries after approval: +On-chain operations like `checkpoint()` require ERC-20 token approval. This pattern assumes `deposit()` succeeded and only retries the checkpoint transaction. The recovery example below wraps the full deposit plus checkpoint flow. ```typescript import { Client } from '@yellow-org/sdk'; @@ -287,6 +336,52 @@ async function depositWithApproval( } ``` +## Errors and Recovery + +```typescript +import { Client } from '@yellow-org/sdk'; +import Decimal from 'decimal.js'; + +async function recoverableDeposit( + client: Client, + chainId: bigint, + asset: string, + amount: Decimal, +) { + try { + await client.deposit(chainId, asset, amount); + return await client.checkpoint(asset); + } catch (error) { + const message = error instanceof Error ? error.message.toLowerCase() : ''; + + if (message.includes('allowance') || message.includes('insufficient')) { + await client.approveToken(chainId, asset, amount); + return await client.checkpoint(asset); + } + + if (message.includes('ongoing')) { + await client.acknowledge(asset); + return await client.checkpoint(asset); + } + + if (message.includes('blockchain client')) { + throw new Error(`Missing withBlockchainRPC(${chainId}n, rpcURL) for ${asset}`); + } + + if (message.includes('asset')) { + const assets = await client.getAssets(chainId); + throw new Error(`Unsupported asset ${asset}. Supported assets: ${assets.map((a) => a.symbol).join(', ')}`); + } + + if (message.includes('channel')) { + throw new Error(`No ready ${asset} channel. Deposit first or call setHomeBlockchain('${asset}', ${chainId}n).`); + } + + throw error; + } +} +``` + ## Channel Session Keys Session keys let users delegate signing authority so operations don't require repeated wallet prompts. From the example-app: @@ -353,10 +448,10 @@ async function setupSessionKey(client: Client) { import { Client, createSigners, withErrorHandler } from '@yellow-org/sdk'; async function monitorConnection() { - const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!); + const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY as `0x${string}`); const client = await Client.create( - 'wss://clearnode.example.com/ws', + '', stateSigner, txSigner, withErrorHandler((error) => { diff --git a/docs/build/sdk/typescript/getting-started.mdx b/docs/nitrolite/build/sdk/typescript/getting-started.mdx similarity index 61% rename from docs/build/sdk/typescript/getting-started.mdx rename to docs/nitrolite/build/sdk/typescript/getting-started.mdx index 23b3758..0555a18 100644 --- a/docs/build/sdk/typescript/getting-started.mdx +++ b/docs/nitrolite/build/sdk/typescript/getting-started.mdx @@ -6,10 +6,10 @@ sidebar_position: 1 # Getting Started with @yellow-org/sdk -The TypeScript SDK for Clearnode payment channels provides both high-level and low-level operations in a unified client. +The TypeScript SDK for Nitronode payment channels provides both high-level and low-level operations in a unified client. :::tip Migrating from v0.5.3? -If you are migrating from `@layer-3/nitrolite@v0.5.3`, consider using the [`@yellow-org/sdk-compat`](../typescript-compat/overview) package first. It maps the familiar v0.5.3 API to the v1.0.0 runtime with minimal code changes. +If you are migrating from the published `@erc7824/nitrolite@0.5.3` package, you do not have to use the compat layer. Migrate directly to `@yellow-org/sdk` when you can update call sites now, or use [`@yellow-org/sdk-compat`](../typescript-compat/overview) first when you need a smaller staged migration. Older internal docs may refer to the same 0.5.3 codebase as `@layer-3/nitrolite`; use the published package name when auditing app dependencies. ::: ## Installation @@ -26,11 +26,15 @@ pnpm add @yellow-org/sdk - **Node.js** 20.0.0 or later - **TypeScript** 5.3.0 or later (for development) -- A running **Clearnode** instance or access to a public node +- A running **Nitronode** instance or access to a public node - A **blockchain RPC endpoint** for on-chain operations via `checkpoint()` ## Quick Start +:::info Sandbox URL - coming soon +Use your Nitronode WebSocket URL in the `Client.create()` call below. The public sandbox URL is intentionally shown as `` until the canonical host is pinned. +::: + ```typescript import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; import Decimal from 'decimal.js'; @@ -43,7 +47,7 @@ async function main() { // 2. Create unified client const client = await Client.create( - 'wss://clearnode.example.com/ws', + '', stateSigner, txSigner, withBlockchainRPC(80002n, 'https://polygon-amoy.alchemy.com/v2/KEY') @@ -81,7 +85,12 @@ main().catch(console.error); The `Client` is the single entry point for all operations. ```typescript -import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; +import { + Client, + createSigners, + withBlockchainRPC, + withHandshakeTimeout, +} from '@yellow-org/sdk'; // Step 1: Create signers from private key const { stateSigner, txSigner } = createSigners('0x1234...'); @@ -100,6 +109,18 @@ const client = await Client.create( await client.setHomeBlockchain('usdc', 80002n); ``` +## Errors and Recovery + +The SDK README keeps the [full error catalogue](https://github.com/layer-3/nitrolite/tree/main/sdk/ts#error-handling). These are the failures most builders should handle in their first integration: + +| Error | Common cause | Recovery | +|---|---|---| +| Insufficient allowance | `checkpoint()` needs ERC-20 approval before the first deposit. | Call `approveToken(chainId, asset, amount)`, then retry `checkpoint(asset)`. | +| Ongoing transition | A previous state transition is waiting for acknowledgement or settlement. | Query the latest state, acknowledge pending funds when needed, or wait for the in-flight checkpoint. | +| Missing RPC | `withBlockchainRPC(chainId, rpcURL)` was not configured for an on-chain operation. | Add the RPC option for every chain you deposit, withdraw, approve, or checkpoint on. | +| Asset not supported | The Nitronode config does not list the requested asset on the selected chain. | Call `getAssets(chainId)` and use one of the returned asset symbols. | +| Channel not found | The wallet has no home channel or no signed state for that asset yet. | Deposit first, or call `setHomeBlockchain(asset, chainId)` before a transfer that may create the home channel. | + ## Signer Types The SDK provides two signer types: @@ -156,11 +177,11 @@ const txHash = await client.checkpoint('usdc'); ### Channel Lifecycle -1. **Void** — No channel exists -2. **Create** — `deposit()` creates channel on-chain via `checkpoint()` -3. **Open** — Channel active; can deposit, withdraw, transfer -4. **Challenged** — Dispute initiated (advanced) -5. **Closed** — Channel finalized (advanced) +1. **Void**: No channel exists +2. **Create**: `deposit()` creates channel on-chain via `checkpoint()` +3. **Open**: Channel active; can deposit, withdraw, transfer +4. **Challenged**: Dispute initiated (advanced) +5. **Closed**: Channel finalized (advanced) ### TypeScript Conventions @@ -179,6 +200,6 @@ const wallet: Address = '0x1234...'; ## Next Steps -- [API Reference](./api-reference) — Full method documentation -- [Configuration](./configuration) — Client options and error handling -- [Examples](./examples) — Complete working examples +- [API Reference](./api-reference): Full method documentation +- [Configuration](./configuration): Client options and error handling +- [Examples](./examples): Complete working examples diff --git a/docs/nitrolite/builder-toolkit.mdx b/docs/nitrolite/builder-toolkit.mdx new file mode 100644 index 0000000..7433fd8 --- /dev/null +++ b/docs/nitrolite/builder-toolkit.mdx @@ -0,0 +1,130 @@ +--- +title: Builder Toolkit +description: Live Nitrolite examples, source repositories, deployments, and upcoming builder tools. +--- + +# Builder Toolkit + +Start here when you want to see Nitrolite working before reading every page. Try the live apps, inspect their source, then use the SDK and migration docs when you are ready to build. + +## Live Examples + +
+
+ + Co-Sign Checkout front page + +
+
Shared checkout
+

Co-Sign Checkout

+

Two participants create a shared cart, co-sign checkout actions, move funds into an app session, propose purchases, close the cart, and withdraw remaining shared-wallet funds.

+
+ Next.js + Supabase + MetaMask SDK + @yellow-org/sdk + @yellow-org/sdk-compat +
+
+ +
+ +
+ + Nitrolite Store front page + +
+
Content store
+

Nitrolite Store

+

A Go-backed reference app where a shopper connects MetaMask, opens a store app session, adds funds, buys catalog content, reads purchased content, and withdraws the remaining balance.

+
+ Go backend + React + Vite + go-sdk + @yellow-org/sdk +
+
+ +
+ +
+ + Nexus P2P Transfer front page + +
+
Peer payments
+

Nexus P2P Transfer

+

A workshop-style content app that demonstrates wallet connection, Nitrolite setup, balance polling, and instant peer-to-peer support payments to post authors.

+
+ Preact + Vite + viem + @yellow-org/sdk + @yellow-org/sdk-compat +
+
+ +
+
+ +## Upcoming Tools + +
+
+
+
Coming soon
+

Nitrolite Playground

+

Manage channels and inspect active app sessions from one UI. App-session management will be available after the playground deployment is ready for builders.

+ Deployment pending +
+
+ +
+
+
Coming soon
+

Yellow SDK MCP

+

Structured SDK, protocol, RPC, examples, and migration context for AI coding tools. This should help teams build with Yellow SDK with fewer hallucinated method shapes.

+ NPM package pending +
+
+ +
+
+
Coming soon
+

Faucet App

+

Get testnet assets for the v1 builder flow without leaving the Nitrolite docs path. This will be linked here once the faucet deployment is ready.

+ Deployment pending +
+
+
+ +## Start Building + + diff --git a/docs/learn/_category_.json b/docs/nitrolite/learn/_category_.json similarity index 100% rename from docs/learn/_category_.json rename to docs/nitrolite/learn/_category_.json diff --git a/docs/learn/core-concepts/_category_.json b/docs/nitrolite/learn/core-concepts/_category_.json similarity index 100% rename from docs/learn/core-concepts/_category_.json rename to docs/nitrolite/learn/core-concepts/_category_.json diff --git a/docs/nitrolite/learn/core-concepts/challenge-response.mdx b/docs/nitrolite/learn/core-concepts/challenge-response.mdx new file mode 100644 index 0000000..93ad486 --- /dev/null +++ b/docs/nitrolite/learn/core-concepts/challenge-response.mdx @@ -0,0 +1,120 @@ +--- +sidebar_position: 4 +title: Challenge & Recovery +description: How Yellow Network lets participants recover funds when a counterparty stops cooperating +keywords: [challenge, dispute, security, settlement, fund recovery] +--- + +import Tooltip from '@site/src/components/Tooltip'; +import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; + +# Challenge & Recovery + +A challenge is the on-chain fallback that lets a participant recover from an unresponsive or broken counterparty. + +**Goal**: understand why off-chain updates remain recoverable even when Nitronode is unavailable. + +--- + +## Why Challenges Matter + +In any off-chain system, a critical question arises: what if the counterparty stops signing, stops responding, or tries to use an on-chain state you disagree with? + +State channels answer this with versioned, signed states: + +1. Old states cannot replace newer states because versions must increase. +2. A participant can enforce a signed state on-chain when cooperation fails. +3. If a challenge is active, the counterparty has a response window to enforce a newer valid state. +4. If no newer state is accepted before the window expires, the challenged state can be closed. + +## Trust Model + +| Guarantee | How it is achieved | +| --- | --- | +| **Fund custody** | ChannelHub holds channel funds on-chain, not Nitronode. | +| **State validity** | Only states signed by the required participants, with a greater version and correct intent, can advance the channel. | +| **Dispute resolution** | A participant can force the latest valid state on-chain. | +| **Recovery** | Funds are recoverable from the latest enforceable state. | + +## Channel Challenge Flow + +### Scenario: Nitronode becomes unresponsive + +You have a channel with 100 USDC. Nitronode stops responding. + +Your options: + +1. Wait for Nitronode to recover. +2. Submit the latest mutually signed state on-chain and start the challenge path. + +### Process + +1. **Initiate challenge**: submit a mutually signed state to ChannelHub with a challenger signature. +2. **Challenge period**: ChannelHub sets the response deadline. +3. **Response window**: a counterparty can enforce a newer mutually signed state. +4. **Resolution**: a newer state returns the channel to `open`; after timeout, the challenged state can be closed. + +```mermaid +stateDiagram-v2 + [*] --> open + open --> challenged: challenge + challenged --> open: enforce newer state + challenged --> closed: close after timeout + closed --> [*] + + note right of challenged: A newer valid state
can defeat the challenge +``` + +## Why This Works + +### States are ordered + +Every channel state has a version number. ChannelHub rejects stale submissions that do not advance the version beyond the state already recorded on-chain. + +### States are signed + +The default v1 channel path requires both user and node signatures for enforceable states. A party that signed a state cannot later deny that signature. + +### Challenge period provides fairness + +The waiting window gives honest parties time to respond. Network delays should not cause immediate loss of funds. + +### ChannelHub is neutral + +ChannelHub validates signatures, state version, status, and ledger invariants. It does not rely on Nitronode cooperation once a valid state is submitted. + +## Enforcement vs Challenge + +| Operation | Purpose | Channel status | +| --- | --- | --- | +| Enforce a signed state | Record a valid state without starting the dispute path. | Stays `open`. | +| `challenge()` | Start the on-chain dispute path. | Changes to `challenged`. | + +Use ordinary state enforcement when you only need the chain to record a newer state. Use challenge when you need to force recovery because a counterparty is not cooperating. + +## What Happens If... + +| Scenario | Outcome | +| --- | --- | +| **Nitronode goes offline** | Challenge with the latest signed state, then close after timeout. | +| **You lose state history** | An older state can be defeated by a newer valid state if the counterparty has it. | +| **Counterparty submits a stale state** | ChannelHub rejects it if it does not have a higher version than the current on-chain state. | +| **Counterparty challenges with a stale state** | Enforce a newer valid state during the challenge window to resolve the challenge. | +| **Block reorg occurs** | Wait for the transaction to be included in the canonical chain again. No special challenge action is required only because of a reorg. | + +## Key Takeaways + +| Concept | Remember | +| --- | --- | +| **Challenge** | Starts on-chain dispute resolution. | +| **Response** | A newer mutually signed state resolves an older challenged state. | +| **Timeout** | The time window during which a challenge can be resolved. After expiry, the challenged state can be closed. | +| **Enforcement** | Records state without starting the dispute path. | + +:::success Security guarantee +You can recover funds according to the latest enforceable state even if the off-chain counterparty stops cooperating. +::: + +## Deep Dive + +For protocol details, see [Enforcement and Settlement](/nitrolite/protocol/enforcement-and-settlement). diff --git a/docs/nitrolite/learn/core-concepts/state-channels-vs-l1-l2.mdx b/docs/nitrolite/learn/core-concepts/state-channels-vs-l1-l2.mdx new file mode 100644 index 0000000..dfed213 --- /dev/null +++ b/docs/nitrolite/learn/core-concepts/state-channels-vs-l1-l2.mdx @@ -0,0 +1,114 @@ +--- +sidebar_position: 1 +title: State Channels vs L1/L2 +description: Compare state channels with Layer 1 and Layer 2 scaling solutions +keywords: [state channels, L1, L2, scaling, comparison, rollups] +--- + +import Tooltip from '@site/src/components/Tooltip'; +import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; + +# State Channels vs L1/L2 + +State channels are a scaling pattern for repeated interactions between known parties. Yellow Network uses channels, Nitronode coordination, and ChannelHub enforcement to make high-frequency asset movement fast while preserving an on-chain fallback. + +**Goal**: understand where state channels fit beside Layer 1 chains and Layer 2 rollups. + +--- + +## Solution Comparison + +| Solution | Throughput | Latency | Cost per operation | Best for | +| --- | --- | --- | --- | --- | +| **Layer 1** | Chain-limited | Block time | Native gas cost | Settlement, contracts, global ordering | +| **Layer 2** | Rollup or sequencer-limited | Sequencer plus settlement latency | L2 fees | General applications with many unknown users | +| **State channels** | Limited by signing, networking, and app logic | Sub-second off-chain path | No gas for ordinary off-chain updates | Repeated interactions between identified parties | + +## How State Channels Work + +State channels operate on a simple pattern: + +1. **Lock funds** in ChannelHub on-chain. +2. **Exchange signed states** off-chain. +3. **Enforce, challenge, or close** on-chain when needed. + +Most interactions do not need immediate on-chain settlement. The chain is used for funding, state enforcement, disputes, and exits. + +## State Channel Advantages + +### Low-latency updates + +| Solution | Transaction flow | +| --- | --- | +| L1 | Transaction -> mempool -> block -> confirmation | +| L2 | Transaction -> sequencer -> L2 block -> L1 data path | +| Channels | Signature -> validation -> accepted state | + +### No gas for ordinary off-chain operations + +Once the channel is funded, transfers and app-session updates are signed off-chain. Gas is paid only for on-chain funding, state enforcement, disputes, and exits. + +### Enforceable settlement + +If cooperation fails, the latest enforceable state can be submitted to ChannelHub. The on-chain path validates signatures, versions, and ledger invariants. + +## State Channel Limitations + +### Identified counterparties + +Channels work between defined participants. Yellow Network uses Nitronode as an off-chain coordinator so applications can work with channel balances without each app directly managing every counterparty path. + +### Liquidity requirements + +Funds must be locked before they can be spent. A user cannot spend more than the balance available in the relevant channel or app session. + +### Liveness requirements + +Participants need a way to monitor and respond during challenge periods. Users can enforce the latest signed state, but they must retain state data and react before a challenge expires. + +### Cross-chain trust + +Cross-chain behavior currently relies on Nitronode liquidity and relay behavior. Trustless cross-chain enforcement is not the default public builder path yet. + +## When to Use Each + +| Choose | When | +| --- | --- | +| **L1** | You need global settlement, contract deployment, or one-time high-value operations. | +| **L2** | You need general smart-contract execution for many unknown users. | +| **State channels** | You need fast repeated interactions between identified parties with an on-chain fallback. | + +## Decision Framework + +```mermaid +flowchart TD + A[Interaction] --> B{Known participants?} + B -->|No| C[Use L1 or L2] + B -->|Yes| D{Repeated updates?} + D -->|Yes| E[Use state channels] + D -->|No| F{Needs custom global contract logic?} + F -->|Yes| C + F -->|No| E + + style E fill:#9999ff,stroke:#333,color:#111 + style C fill:#99ff99,stroke:#333,color:#111 +``` + +## How Yellow Network Addresses Limitations + +| Limitation | Yellow Network approach | +| --- | --- | +| Identified counterparties | Nitronode coordinates off-chain channel state advancement. | +| Liquidity | Home-channel balances and app sessions keep app funds available off-chain. | +| Enforcement | ChannelHub provides state enforcement, challenge, and close paths. | +| Developer ergonomics | SDKs build signed requests and typed app-session updates. | + +## Key Takeaways + +State channels work best for real-time applications where the same participants update state repeatedly. They are not a replacement for every L1 or L2 use case, but they are a strong fit for payments, trading, gaming, checkout, and app-session workflows that benefit from signed off-chain updates. + +## Deep Dive + +- [Protocol Overview](/nitrolite/protocol/introduction) +- [State and Ledger Model](/nitrolite/protocol/state-and-ledger-model) +- [Enforcement and Settlement](/nitrolite/protocol/enforcement-and-settlement) diff --git a/docs/learn/core-concepts/yellow-token.mdx b/docs/nitrolite/learn/core-concepts/yellow-token.mdx similarity index 100% rename from docs/learn/core-concepts/yellow-token.mdx rename to docs/nitrolite/learn/core-concepts/yellow-token.mdx diff --git a/docs/nitrolite/learn/glossary.mdx b/docs/nitrolite/learn/glossary.mdx new file mode 100644 index 0000000..c5f3ab6 --- /dev/null +++ b/docs/nitrolite/learn/glossary.mdx @@ -0,0 +1,74 @@ +--- +title: Glossary +description: Core Nitrolite v1 terms for builders and protocol readers. +sidebar_position: 5 +--- + +# Glossary + +This page defines the terms used across the Nitrolite v1 Learn, Build, Protocol, and API Reference docs. + +## Core Terms + +| Term | Meaning | +| --- | --- | +| **App session** | An app-specific off-chain state where users can commit channel funds, exchange app-state signatures, and later release balances back to their home channels. | +| **Asset** | A unified value unit that Nitronode configures across supported chains, such as one canonical `usdc` asset spanning chain-specific USDC tokens. | +| **Channel** | A user-node relationship for one asset, with signed state updates and an on-chain enforcement path. | +| **Channel state** | The signed record of channel version, transition, ledgers, and participant signatures. | +| **ChannelHub** | The v1 on-chain enforcement layer for channel creation, deposits, withdrawals, state enforcement, challenges, and close operations. | +| **Checkpoint / enforcement** | An on-chain submission that records a valid signed state without necessarily closing the channel. `checkpoint()` is an SDK or contract entry point for specific enforcement paths. | +| **Challenge** | An on-chain dispute path where a participant submits a signed state and gives the counterparty time to respond with a newer valid state. | +| **Home channel** | The user's primary channel for a configured asset. | +| **Home ledger** | The authoritative ledger for the channel's current enforcement chain. | +| **Nitronode** | The open-source off-chain entrypoint that validates state transitions, signs states, and serves v1 RPC. | +| **Non-home ledger** | The secondary ledger used when a cross-chain operation is in progress. It is empty during ordinary same-chain operation. | +| **Pending state** | A proposed state that is not enforceable yet because it does not have every required signature. Node-issued pending states commonly wait for user acknowledgement. | +| **Quorum** | The signature threshold required by an app session before an app-state update is accepted. | +| **Session key** | A scoped signing key authorized by a participant. It can apply to channel state or app-session state, depending on its signature-validation scope. | +| **Token** | A chain-specific contract or native token instance. A single asset can map to different tokens on different supported chains. | +| **Vault** | Node-controlled liquidity used by enforcement and cross-chain accounting paths. | + +## Channel Statuses + +The v1 RPC status surface uses: + +| Status | Meaning | +| --- | --- | +| `void` | No channel exists yet. | +| `open` | The channel exists and can accept normal state updates. | +| `challenged` | A challenge is active and the response window is running. | +| `closing` | The Node has issued the finalization state; the channel is in the close path. | +| `closed` | Funds have been released and the channel lifecycle is complete. | + +## Transition Literals + +These are the current `transition_type` literals from the v1 API schema. + +| Literal | Meaning | +| --- | --- | +| `transfer_send` | Sender-side off-chain transfer. | +| `transfer_receive` | Receiver-side off-chain transfer. | +| `release` | Return funds from an app session or extension to the channel. | +| `commit` | Move funds from the channel into an app session or extension. | +| `home_deposit` | Same-chain home-channel deposit. | +| `home_withdrawal` | Same-chain home-channel withdrawal. | +| `finalize` | Finalize and close the channel state. | + +Escrow and migration operation names are documented in the Protocol section because they are protocol-level concepts, not current app-builder how-to flows. + +## Amount Units + +Use the SDK method signature as the source of truth for amount shape: + +| Surface | Typical amount shape | +| --- | --- | +| Native TypeScript SDK | `Decimal` for app-facing method calls. | +| Compat TypeScript SDK | Raw asset-unit strings for transfer-style methods and raw base units for on-chain-style methods. | +| API Reference | Wire payload shape from `docs/api.yaml`. | + +## Related Pages + +- [Architecture at a Glance](./introduction/architecture-at-a-glance) +- [State and Ledger Model](/nitrolite/protocol/state-and-ledger-model) +- [Key Terms](/nitrolite/build/getting-started/key-terms) diff --git a/docs/nitrolite/learn/index.mdx b/docs/nitrolite/learn/index.mdx new file mode 100644 index 0000000..983dac0 --- /dev/null +++ b/docs/nitrolite/learn/index.mdx @@ -0,0 +1,76 @@ +--- +title: Learn +description: Master Yellow Network and state channel technology +sidebar_position: 1 +displayed_sidebar: learnSidebar +--- + +import Tooltip from '@site/src/components/Tooltip'; +import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; + +# Learn + +Welcome to the Yellow Network learning path. This section builds your understanding from fundamentals to advanced concepts. + +Yellow Network is decentralized clearing and settlement infrastructure that operates as a Layer 3 overlay on existing blockchains. It lets businesses, exchanges, and application developers move digital assets through signed channel states. Independent node operators run open-source Nitronode software supplied by Layer3 Fintech Ltd. The SDK and developer tools are open-source and free. + +:::info What changed in v1 +The v1 protocol uses ChannelHub, Nitronode, channel states, home ledgers, and app sessions. If you are coming from `@erc7824/nitrolite@0.5.3`, start with [What Changed Since 0.5.3](./whats-new-from-0.5.3) before opening the code migration guide. +::: + +## Introduction + +**[What Yellow Solves](./introduction/what-yellow-solves.mdx)**: Understand the core problems: scaling, cost, and speed. Learn why state channels are the answer for high-frequency applications. + +**[Architecture at a Glance](./introduction/architecture-at-a-glance.mdx)**: See how the protocol, off-chain, and blockchain layers work together to enable fast channel updates with enforceable settlement. + +## Core Concepts + +**[State Channels vs L1/L2](./core-concepts/state-channels-vs-l1-l2.mdx)**: Compare state channels with Layer 1 and Layer 2 solutions. Understand when each approach is the right choice. + +**[Challenge & Recovery](./core-concepts/challenge-response.mdx)**: How Yellow Network handles disputes and ensures your funds are recoverable through the latest mutually signed state. + +**[YELLOW Token](./core-concepts/yellow-token.mdx)**: The utility token powering network services, node operator collateral, and dispute resolution. + +**[Glossary](./glossary.mdx)**: Core v1 terms, statuses, transition literals, and amount-unit reminders. + +## Migration + +**[What Changed Since 0.5.3](./whats-new-from-0.5.3.mdx)**: Learn the v1 mental model before changing code: Clearnode is now Nitronode, ChannelHub replaces Custody, and app sessions sit on top of home channels. + +**[Migrating from 0.5.3](./migrating-from-0.5.3.mdx)**: Choose the compat-first or native v1 code migration path from `@erc7824/nitrolite@0.5.3`. + +## Protocol Flows + +**[Protocol Architecture](./protocol-flows/architecture.mdx)**: The Petal Diagram: home chain, cross-chain, transfers, and app sessions. + +**[Transfer Flow](./protocol-flows/transfer-flow.mdx)**: Off-chain transfers between users via Nitronode. + +**[Home Channel Flows](./protocol-flows/home-channel-creation.mdx)**: Channel creation, deposits, and withdrawals on the home chain. + +**[App Session Deposit](./protocol-flows/app-session-deposit.mdx)**: Depositing funds into app sessions. + +## Next Steps + +After completing the Learn section, continue to: + +- **[Build](/nitrolite/build/getting-started/quickstart)**: Implement complete Yellow applications. +- **[Protocol](/nitrolite/protocol/introduction)**: Read the authoritative protocol specification. +- **MCP tools**: Agent-assisted protocol and SDK lookup documentation is coming soon. + +--- + +## Quick Reference + +| Topic | Time | Difficulty | +|-------|------|------------| +| [What Yellow Solves](./introduction/what-yellow-solves) | 5 min | Beginner | +| [Architecture at a Glance](./introduction/architecture-at-a-glance) | 8 min | Beginner | +| [State Channels vs L1/L2](./core-concepts/state-channels-vs-l1-l2) | 12 min | Intermediate | +| [Challenge & Recovery](./core-concepts/challenge-response) | 6 min | Intermediate | +| [YELLOW Token](./core-concepts/yellow-token) | 8 min | Intermediate | +| [Glossary](./glossary) | 5 min | Beginner | +| [What Changed Since 0.5.3](./whats-new-from-0.5.3) | 8 min | Beginner | +| [Migrating from 0.5.3](./migrating-from-0.5.3) | 10 min | Intermediate | +| [Protocol Architecture](./protocol-flows/architecture) | 10 min | Intermediate | +| [Transfer Flow](./protocol-flows/transfer-flow) | 12 min | Advanced | diff --git a/docs/learn/introduction/_category_.json b/docs/nitrolite/learn/introduction/_category_.json similarity index 100% rename from docs/learn/introduction/_category_.json rename to docs/nitrolite/learn/introduction/_category_.json diff --git a/docs/nitrolite/learn/introduction/architecture-at-a-glance.mdx b/docs/nitrolite/learn/introduction/architecture-at-a-glance.mdx new file mode 100644 index 0000000..d351636 --- /dev/null +++ b/docs/nitrolite/learn/introduction/architecture-at-a-glance.mdx @@ -0,0 +1,156 @@ +--- +sidebar_position: 2 +title: Architecture at a Glance +description: High-level overview of Yellow Network's v1 architecture +keywords: [architecture, state channels, Nitrolite, Nitronode, ChannelHub, smart contracts] +--- + +# Architecture at a Glance + +Yellow Network v1 combines application code, SDK clients, off-chain state validation, and ChannelHub enforcement so most updates happen off-chain while the latest mutually signed state remains enforceable on-chain. + +--- + +## The Four Surfaces + +```mermaid +flowchart TB + app["Application
Games, payments, exchanges"] + sdk["Client SDK + v1 RPC
typed calls and signed envelopes"] + node["Nitronode
state validation and coordination"] + chain["Blockchain layer
ChannelHub + engines"] + + app --> sdk + sdk <-->|"v1 RPC over WebSocket"| node + sdk -->|"deposit, enforce, challenge, close"| chain + node -->|"state enforcement coordination"| chain + chain -->|"events and enforced state"| node +``` + +| Surface | Purpose | Typical speed | Cost | +|-------|---------|---------------|------| +| **Application** | Business logic, user interface, app-session rules | User interaction speed | App-defined | +| **Client SDK + v1 RPC** | Builds signed envelopes and calls Nitronode methods | Network round trip | No gas | +| **Nitronode** | Validates transitions, signs channel states, coordinates off-chain updates | Sub-second in normal operation | No gas | +| **Blockchain layer** | ChannelHub state enforcement, locked funds, challenge and close paths | Block time | Gas fees | + +--- + +## ChannelHub + +ChannelHub is the v1 on-chain enforcement layer. It works with ChannelEngine, EscrowDepositEngine, EscrowWithdrawalEngine, and signature validators. Builders interact with ChannelHub as the contract that creates channels, processes deposits and withdrawals, enforces signed states, accepts challenges, and closes channels. + +ChannelHub validates: + +- channel definitions and channel identifiers +- approved signature validation modes +- state versions and intents +- home-ledger and non-home-ledger invariants +- locked-funds accounting +- challenge and close status transitions + +Routine transfers and app-session updates are signed off-chain. The blockchain is used for create, deposit, withdraw, state enforcement, challenge, close, escrow, and migration operations. + +--- + +## Off-chain layer + +The off-chain layer handles frequent state advancement without blockchain transactions. + +### Nitronode + +Nitronode is operated by an independent node operator using open-source software developed and maintained by Layer3 Fintech Ltd. It: + +- validates v1 channel transitions +- signs mutually agreed channel states +- issues pending receive and release states for user acknowledgement +- coordinates app-session flows and, where supported, cross-chain escrow flows +- serves v1 RPC methods over WebSocket + +### v1 RPC + +v1 RPC is the wire surface used by SDKs and low-level clients. Builders should prefer the SDK unless they are implementing an SDK, proxy, debugger, or language integration. + +--- + +## How Funds Flow + +Funds are represented by channel states with a home ledger and, when needed, a non-home ledger. + +```mermaid +flowchart TB + wallet["User wallet"] + hub["ChannelHub"] + home["Home channel
home ledger"] + nonhome["Non-home ledger
escrow in progress"] + session["App session
extension state"] + + wallet -->|"approve + home deposit"| hub + hub -->|"locked funds"| home + home <-->|"transfer / acknowledge"| home + home -->|"commit"| session + session -->|"release"| home + home <-->|"escrow initiate / finalize"| nonhome + home -->|"home withdrawal / close"| wallet +``` + +| Concept | What it means | +|-------|---------------| +| **Home channel** | The user and Node channel for a unified asset. | +| **Home ledger** | The authoritative single-token ledger for the enforcement chain. | +| **Non-home ledger** | A temporary single-token ledger for cross-chain escrow, withdrawal, or migration. | +| **App session** | An extension state funded by commit transitions and settled by release transitions. | + +--- + +## Channel Lifecycle + +The v1 RPC exposes channel status as `void`, `open`, `challenged`, `closing`, and `closed`. `closing` appears when the Node has issued the finalization state and the channel is in the close path. After a challenge expires, a separate close call can move the channel to `closed`. + +```mermaid +stateDiagram-v2 + [*] --> VOID + VOID --> OPEN: create or deposit + OPEN --> OPEN: signed off-chain updates + OPEN --> OPEN: enforce higher-version state + OPEN --> CHALLENGED: challenge + CHALLENGED --> OPEN: higher-version response + CHALLENGED --> CLOSED: close after timeout + OPEN --> CLOSED: cooperative close + CLOSED --> [*] + + note right of OPEN: Normal operation happens here +``` + +`CHALLENGED` means a participant has submitted a candidate state on-chain and the challenge duration is running. During that window, another participant may respond with a higher-version mutually signed state. If no higher-version response is accepted before expiry, a separate close call is still required before funds are released according to that enforced state. See [Channel Lifecycle](/nitrolite/protocol/channel-lifecycle) and [Enforcement and Settlement](/nitrolite/protocol/enforcement-and-settlement) for the formal rules. + +### Typical Flow + +1. **Create or deposit**: The user and Node sign an initial channel state; a deposit state may be submitted to ChannelHub to lock funds. +2. **Operate**: The user and Node exchange signed states off-chain for transfers, acknowledgements, commits, releases, and other valid transitions. +3. **Enforce or challenge**: Any party may enforce the latest mutually signed state if needed. +4. **Close**: A final signed state or expired challenge releases funds according to the enforced allocation. + +## Key Takeaways + +| Concept | What to Remember | +|---------|------------------| +| **ChannelHub** | The v1 contract entrypoint for enforcement and locked funds. | +| **Nitronode** | The off-chain coordinator for state advancement and v1 RPC. | +| **Channel state** | The signed record of allocations, version, transition, and ledgers. | +| **Home ledger** | The enforceable ledger for the channel's current home chain. | +| **App session** | An extension funded by commit transitions and settled through release transitions. | + +:::success Security Guarantee +For the home chain, assets can be recovered through ChannelHub with the latest mutually signed state even if Nitronode becomes unresponsive. +::: + +--- + +## Next Steps + +Ready to start building? Continue to: + +- **[Quickstart](/nitrolite/build/getting-started/quickstart)**: Create your first channel in minutes. +- **[Prerequisites](/nitrolite/build/getting-started/prerequisites)**: Set up your development environment. +- **[Core Concepts](../core-concepts/state-channels-vs-l1-l2.mdx)**: Deep dive into state channels. diff --git a/docs/nitrolite/learn/introduction/supported-chains.mdx b/docs/nitrolite/learn/introduction/supported-chains.mdx new file mode 100644 index 0000000..0bae5e7 --- /dev/null +++ b/docs/nitrolite/learn/introduction/supported-chains.mdx @@ -0,0 +1,80 @@ +--- +sidebar_position: 3 +title: Supported Chains & Assets +description: Discover supported blockchains, assets, and ChannelHub addresses at runtime +keywords: [supported chains, blockchains, assets, tokens, ChannelHub, Ethereum Sepolia, Polygon Amoy, Base Sepolia] +--- + +# Supported Chains & Assets + +Yellow Network nodes advertise their supported blockchains, ChannelHub addresses, and assets through v1 RPC. Treat runtime discovery as the source of truth. + +--- + +## Runtime Discovery + +Do not hardcode v1 contract addresses in application docs or app configuration. The active Nitronode returns current network and asset configuration: + +- `client.getConfig()` wraps `node.v1.get_config` and returns the node address, node version, supported signature validators, supported blockchains, and each blockchain's ChannelHub address. +- `client.getAssets()` wraps `node.v1.get_assets` and returns asset symbols, decimals, suggested blockchain IDs, token addresses, and token chain IDs. + +```typescript +import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; + +const wsUrl = process.env.NITRONODE_WS_URL!; +const rpcUrl = process.env.RPC_URL!; +const chainId = BigInt(process.env.CHAIN_ID!); +const privateKey = process.env.PRIVATE_KEY as `0x${string}`; + +const { stateSigner, txSigner } = createSigners(privateKey); +const client = await Client.create( + wsUrl, + stateSigner, + txSigner, + withBlockchainRPC(chainId, rpcUrl), +); + +const config = await client.getConfig(); +const assets = await client.getAssets(); + +for (const chain of config.blockchains) { + console.log(`${chain.name} (${chain.id}) ChannelHub=${chain.channelHubAddress}`); +} + +for (const asset of assets) { + console.log(`${asset.symbol} decimals=${asset.decimals}`); + for (const token of asset.tokens) { + console.log(` chain=${token.blockchainId} token=${token.address}`); + } +} +``` + +Raw RPC clients can call `node.v1.get_config` for blockchain and ChannelHub data and `node.v1.get_assets` for asset and token data. + +--- + +## Deployed v1 Contract Folders + +The Nitrolite repository currently contains v1 deployment artifacts under [`contracts/deployments`](https://github.com/layer-3/nitrolite/tree/main/contracts/deployments). + +| Chain ID | Deployment folder | +| ---: | --- | +| `11155111` | `contracts/deployments/11155111/` | +| `80002` | `contracts/deployments/80002/` | +| `84532` | `contracts/deployments/84532/` | + +Each folder contains ChannelHub, ChannelEngine, escrow engines, signature validators, and test token deployment records for that chain. Use the runtime RPC response for the active endpoint you are connected to, because a node may support only a subset of deployed chains. + +--- + +## Asset Symbols + +Asset symbols are node configuration, not protocol constants. The current v1 sandbox examples use symbols such as `yusd` and `yellow`, all represented in lower case, but applications should discover the available list through `client.getAssets()` or `node.v1.get_assets`. + +--- + +## See Also + +- [Quick Start Guide](/nitrolite/build/getting-started/quickstart) — Get started building with Yellow SDK +- [Multi-Party App Sessions](/nitrolite/build/sdk/multi-party-app-sessions) — Create multi-party application sessions +- [API Reference](/nitrolite/api-reference) — Complete SDK documentation diff --git a/docs/nitrolite/learn/introduction/what-yellow-solves.mdx b/docs/nitrolite/learn/introduction/what-yellow-solves.mdx new file mode 100644 index 0000000..7950309 --- /dev/null +++ b/docs/nitrolite/learn/introduction/what-yellow-solves.mdx @@ -0,0 +1,123 @@ +--- +sidebar_position: 1 +title: What Yellow Solves +description: "Understand the core problems Yellow Network addresses: scaling, cost, and speed" +keywords: [Yellow Network, state channels, blockchain scaling, off-chain, Web3] +--- + +import Tooltip from '@site/src/components/Tooltip'; +import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; + +# What Yellow Solves + +Yellow Network lets applications move high-frequency asset updates off-chain while keeping the latest mutually signed state enforceable on-chain. + +--- + +## The Blockchain Scalability Problem + +Every blockchain transaction requires global consensus. While this guarantees security and decentralization, it creates three fundamental limitations: + +| Challenge | Impact on Users | +|-----------|-----------------| +| **High Latency** | Transactions take 15 seconds to several minutes for confirmation | +| **High Costs** | Gas fees spike during network congestion, making microtransactions impractical | +| **Limited Throughput** | Networks like Ethereum process ~15-30 transactions per second | + +For applications requiring real-time interactions, such as gaming, trading, and micropayments, these constraints make traditional blockchain execution a poor backend for every user action. + +--- + +## How Yellow Network Solves This + +Yellow Network uses **state channels** to move frequent operations off-chain while preserving blockchain-level enforcement for the latest mutually signed state. + +### The Core Insight + +Most interactions between parties do not need immediate on-chain settlement. Consider a chess game with a 10 USDC wager: + +| Approach | Result | +| --- | --- | +| On-chain moves | Every move requires a transaction. A 40-move game becomes 40+ transactions and repeated gas fees. | +| State channel moves | Players lock funds once, sign moves off-chain, and settle the final allocation through one enforceable state. | + +State channels let applications execute many signed off-chain operations between occasional on-chain enforcement actions. + +### What You Get + +- **Fast updates:** Users exchange signed states without waiting for block confirmation. +- **No gas per off-chain update:** Routine channel operations do not send a transaction. +- **High throughput:** Throughput is bounded by signing, networking, and application logic rather than global chain consensus. +- **On-chain enforcement:** A participant can submit the latest mutually signed state to ChannelHub if cooperation fails. + +:::tip When to Use Yellow Network +Choose Yellow Network when your application needs: + +- Real-time interactions between users +- Microtransactions or streaming payments +- High transaction volume without gas per update +- Multi-party coordination with enforceable settlement + +::: + +--- + +## Chain Abstraction with Nitronode + +A **Nitronode** is operated by an independent node operator using open-source software. It coordinates v1 RPC, validates state transitions, signs channel updates, and tracks the channel state each user shares with the Node. + +In v1, the user's main balance lives in **home channels**, one for each asset. The home channel has a **home ledger** tied to the chain where the latest state is enforceable. When cross-chain movement is needed, a **non-home ledger** tracks the temporary escrow state until the operation is finalized. + +Typical flow: + +1. **Create or deposit into a home channel.** The user approves the ChannelHub spender and submits a signed deposit state. ChannelHub locks the deposited ERC-20 or native ETH amount for that channel. +2. **Transact off-chain.** The user and Nitronode exchange signed channel states for transfers, acknowledgements, commits, releases, and other v1 transitions. +3. **Move across chains through escrow where supported.** The protocol defines two-phase escrow states that coordinate the home ledger and non-home ledger, but public builder workflows for escrow are not documented yet. +4. **Settle when needed.** Any party may enforce, challenge, or close with an enforceable mutually signed state. + +```mermaid +flowchart LR + wallet["User wallet"] + hub["ChannelHub"] + home["Home channel state"] + escrow["Escrow state
(when cross-chain)"] + app["App session"] + + wallet -->|"approve + deposit"| hub + hub --> home + home <-->|"signed updates"| home + home -->|"commit"| app + app -->|"release"| home + home <-->|"escrow initiate/finalize"| escrow +``` + +--- + +## Real-World Applications + +- **Payments:** Micropayments, streaming payments, P2P transfers, API billing, and content monetization. +- **Gaming:** Turn-based wagers, tournament payouts, and in-game economies with instant updates. +- **DeFi:** High-frequency trading, prediction markets, and escrow-backed flows once the public builder workflow is available. + +--- + +## Security Model + +Yellow Network maintains blockchain-level enforcement despite operating off-chain: + +| Guarantee | How It Works | +|-----------|--------------| +| **Fund safety** | Participants cannot lose assets without signing a state that authorizes the change. | +| **Dispute resolution** | A challenge period lets participants respond with a higher-version state. | +| **Cryptographic proof** | Every enforceable channel state is signed by the required participants. | +| **Recovery path** | Home-chain assets can be recovered through ChannelHub using the latest mutually signed state. | + +The current protocol still has trust assumptions around Node liveness, cross-chain liquidity, and cross-chain relay. Read [Security and Limitations](/nitrolite/protocol/security-and-limitations) for the formal boundary. + +--- + +## Where to next + +- **[Architecture at a Glance](./architecture-at-a-glance.mdx)**: See the v1 layers, state model, and lifecycle. +- **[Supported Chains & Assets](./supported-chains.mdx)**: Learn how to discover supported chains, assets, and contract addresses at runtime. +- **[Protocol terminology](/nitrolite/protocol/terminology)**: Use the formal v1 term definitions until the Learn glossary lands. diff --git a/docs/nitrolite/learn/migrating-from-0.5.3.mdx b/docs/nitrolite/learn/migrating-from-0.5.3.mdx new file mode 100644 index 0000000..f09ba25 --- /dev/null +++ b/docs/nitrolite/learn/migrating-from-0.5.3.mdx @@ -0,0 +1,65 @@ +--- +title: Migrating from 0.5.3 +description: Migration paths from @erc7824/nitrolite 0.5.3 to the Yellow v1 SDKs. +sidebar_position: 6 +--- + +# Migrating from 0.5.3 + +If your application uses `@erc7824/nitrolite@0.5.3`, choose one of two migration paths: + +| Path | Use when | +| --- | --- | +| **Compat first** | You need the smallest migration and want familiar helper shapes while moving to the v1 package family. Treat it as a bridge, not the long-term endpoint. | +| **Native v1** | You are building new flows or can afford to adopt the v1 SDK model directly. | + +## Path 1: Compat First + +Use `@yellow-org/sdk-compat` when you need to move existing 0.5.3 code with minimal application churn. + +Start here: + +- [Compat overview](/nitrolite/build/sdk/typescript-compat/overview) +- [Migration overview](/nitrolite/build/sdk/typescript-compat/migration-overview) +- [Off-chain migration](/nitrolite/build/sdk/typescript-compat/migration-offchain) +- [On-chain migration](/nitrolite/build/sdk/typescript-compat/migration-onchain) +- [Errors and recovery](/nitrolite/build/sdk/typescript-compat/errors) + +Compat does not preserve every old behavior. In particular, fail-fast calls, polling, and server-push assumptions need deliberate review. It formats known compat surfaces for v1, but you should still review every amount boundary where your app crosses between compat calls, native SDK calls, RPC payloads, and UI display. Follow the migration pages before changing production code. + +## Path 2: Native v1 + +Use `@yellow-org/sdk` for new applications and for migrations that can adopt the v1 lifecycle directly. + +Start here: + +- [Quickstart](/nitrolite/build/getting-started/quickstart) +- [Prerequisites](/nitrolite/build/getting-started/prerequisites) +- [Native TypeScript SDK](/nitrolite/build/sdk/typescript/getting-started) +- [Lifecycle example source](/nitrolite/build/getting-started/quickstart#step-7-run-it) + +Native v1 is the default long-term path. It exposes the current channel and app-session lifecycle directly instead of preserving the 0.5.3 helper vocabulary. + +## Terminology Map + +| 0.5.3 term | v1 term | +| --- | --- | +| `@erc7824/nitrolite` | `@yellow-org/sdk` or `@yellow-org/sdk-compat` | +| Clearnode | Nitronode | +| Custody | ChannelHub | +| Adjudicator | Channel engine and signature validators | +| Virtual app | App session | + +## Migration Checklist + +1. Pick compat-first or native v1. Prefer native v1 as the final model once the staged migration is stable. +2. Replace package imports. +3. Review amount units for every transfer, deposit, withdrawal, and app-session call. +4. Replace server-push assumptions with explicit queries or the supported polling helper. +5. Run the updated flow against a disposable test wallet before updating docs or production examples. + +## Related Pages + +- [Glossary](./glossary) +- [API Reference](/nitrolite/api-reference/) +- [Protocol Overview](/nitrolite/protocol/introduction) diff --git a/docs/learn/protocol-flows/_category_.json b/docs/nitrolite/learn/protocol-flows/_category_.json similarity index 100% rename from docs/learn/protocol-flows/_category_.json rename to docs/nitrolite/learn/protocol-flows/_category_.json diff --git a/docs/learn/protocol-flows/app-session-deposit.mdx b/docs/nitrolite/learn/protocol-flows/app-session-deposit.mdx similarity index 90% rename from docs/learn/protocol-flows/app-session-deposit.mdx rename to docs/nitrolite/learn/protocol-flows/app-session-deposit.mdx index c74e615..6ccff0b 100644 --- a/docs/learn/protocol-flows/app-session-deposit.mdx +++ b/docs/nitrolite/learn/protocol-flows/app-session-deposit.mdx @@ -15,7 +15,7 @@ This document provides a comprehensive breakdown of the **App Session Deposit** ```mermaid graph LR U["User"] --> SC["SenderClient"] - SC <--> N["Node (Clearnode)"] + SC <--> N["Nitronode"] style U stroke:#333 style SC stroke:#333 @@ -26,7 +26,7 @@ graph LR | --- | --- | | **User** | The human user initiating the deposit | | **SenderClient** | SDK/Application managing states on behalf of the user | -| **Node** | The Clearnode that validates and coordinates both app session and channel states | +| **Node** | The Nitronode that validates and coordinates both app session and channel states | --- @@ -39,6 +39,10 @@ Before the deposit flow begins: 3. An **App Session** already exists (created via `create_app_session`). 4. User is a **participant** in the target App Session. +:::info Application signature collection +The application is responsible for collecting the app-session quorum signatures before it submits an app-state update. Nitronode validates the quorum, but it does not gather participant signatures for the app. +::: + --- ## Key Data Structures @@ -213,7 +217,7 @@ From the on-chain protocol: --- -## Phase 5: Notifications and Completion +## Phase 5: Confirmation and Completion ```mermaid sequenceDiagram @@ -221,16 +225,17 @@ sequenceDiagram actor SenderClient actor User - Node->>SenderClient: Sends AppSessionUpdate Node->>SenderClient: Return node signature - SenderClient->>User: Returns success and tx hash + SenderClient->>Node: Query latest app session if needed + Node->>SenderClient: Returns latest app session state + SenderClient->>User: Returns success ``` ### What Gets Returned -1. **AppSessionUpdate** -- Notification of the updated app session state. -2. **Node signature** -- Confirms the Node has accepted both states. -3. **Success and tx hash** -- User-facing confirmation. +1. **Node signature** -- Confirms Nitronode has accepted both states. +2. **Latest app session state** -- Retrieved by query if the application wants a fresh read after submission. +3. **Success** -- User-facing confirmation. --- @@ -269,8 +274,10 @@ sequenceDiagram rect rgb(0, 0, 230) Note over Node,User: Phase 5: Complete - Node->>SenderClient: AppSessionUpdate + node signature - SenderClient->>User: Returns success and tx hash + Node->>SenderClient: Return node signature + SenderClient->>Node: Query latest app session if needed + Node->>SenderClient: Returns latest app session state + SenderClient->>User: Returns success end ``` @@ -290,7 +297,7 @@ sequenceDiagram | Transition | From / To | Purpose | | --- | --- | --- | | `commit` | Unified Balance to App Session | Lock funds for deposit | -| `release` | App Session to Unified Balance | Unlock funds on withdraw | +| `release` | App Session to Unified Balance | Return funds on withdraw | ### Why Two States? @@ -302,12 +309,6 @@ This is solved by: 2. **Verifiable accounting** -- Channel state tracks fund flows, app session tracks allocations. 3. **Linked validation** -- `EnsureSameDepositAssetAmount` validates that both states reflect the same deposit amount before either is stored. -:::info Note on Quorum -The `quorum_sigs` parameter must be assembled by the **application itself**. This means the application is responsible for collecting signatures from each participant (based on their signature weights) to meet the quorum threshold before submitting the deposit state to the Node. -::: - ---- - ## Related Flows - [Transfer Communication Flow](./transfer-flow) diff --git a/docs/nitrolite/learn/protocol-flows/architecture.mdx b/docs/nitrolite/learn/protocol-flows/architecture.mdx new file mode 100644 index 0000000..daf1020 --- /dev/null +++ b/docs/nitrolite/learn/protocol-flows/architecture.mdx @@ -0,0 +1,69 @@ +--- +title: "Protocol Architecture (Petal Diagram)" +description: "A visual and technical breakdown of the Nitrolite Protocol architecture, covering the four core petals and three operational layers." +sidebar_position: 1 +--- + +# Protocol Architecture (Petal Diagram) + +The **Petal Diagram** is the central architectural reference for the Nitrolite Protocol. It maps how deposits, withdrawals, transfers, and app sessions interact with blockchains, all centered on a user's channel. + +Nitrolite Protocol Petal Diagram + +*The diagram represents the architecture centered on a user's channel and maps how different actions interact with various blockchains.* + +--- + +## The Four Petals + +### Petal 1: My Home Chain (Deposits and Withdrawals) + +The **Home Chain** is the blockchain where the user's channel state is enforced for a specific asset. + +:::info Important Choice +When selecting your Home Chain, choose the blockchain where you are most comfortable depositing, enforcing and withdrawing a specific asset if cooperation with the Node fails. +::: + +**The Flow:** The user approves the ChannelHub spender, submits a signed home-deposit state, and ChannelHub pulls ERC-20 tokens or native ETH into the home channel. A home-withdrawal state releases funds from ChannelHub back to the user. + +--- + +### Petal 2: Another Chain (Cross-Chain Support) + +When a user already has a Home Chain with funds and wants to deposit from a different chain, the protocol uses a two-phase escrow mechanism. + +**The Challenge:** The home ledger is authoritative for enforcement, but the user's funds may start on a non-home chain. + +**The Solution:** A signed escrow-deposit initiate state creates a non-home escrow record, and a signed finalize state completes the deposit and updates the home ledger. Cross-chain withdrawals mirror this flow. Token decimal normalization validates cross-chain amounts when token precisions differ. + +**Key Property:** Escrow coordinates the home ledger and non-home ledger so cross-chain operations can be challenged or finalized according to the protocol rules. + +--- + +### Petal 3: Transfers (Sender and Receiver State) + +Transfers update two unrelated user channels. The sender signs a `transfer_send` state. The receiver later acknowledges a Node-issued `transfer_receive` state. + +**The Solution:** The sender signs a new state showing funds sent through the Node. Nitronode prepares a receive state for the receiver's channel. The receiver signs an acknowledgement to make the pending state mutually signed and enforceable. + +**Key Efficiency:** Receive states can be batched before acknowledgement, reducing signature overhead while preserving the enforceable-state boundary. + +--- + +### Petal 4: App Sessions + +App sessions are extension states funded by commit transitions from the user's home channel. + +**The Mechanism:** A `commit` transition moves assets from the user's home channel ledger into the app session extension. The app session tracks its own state and participant allocations. A `release` transition returns assets from the app session back into the user's channel ledger. + +**Security and Risk:** + +- Home-channel assets outside app sessions remain recoverable through the latest mutually signed channel state. +- In-session app funds depend on the app-session participants and current extension-layer trust assumptions. +- The protocol roadmap includes stronger extension-layer enforcement and watchtower support. + +:::note Current trust boundary +App-session funds are not fully enforced on-chain in the current protocol version. Core channel assets remain recoverable through ChannelHub; extension-layer enforcement is still evolving. +::: + +**UX Philosophy:** Core deposits, withdrawals, transfers, and challenges stay tied to channel-state enforcement, while app sessions provide flexible application logic through the extension layer. diff --git a/docs/nitrolite/learn/protocol-flows/escrow-deposit.mdx b/docs/nitrolite/learn/protocol-flows/escrow-deposit.mdx new file mode 100644 index 0000000..63cdaa4 --- /dev/null +++ b/docs/nitrolite/learn/protocol-flows/escrow-deposit.mdx @@ -0,0 +1,41 @@ +--- +title: "Escrow Channel Deposit Flow" +description: "Current documentation status for non-home-chain escrow deposits in Nitrolite v1." +sidebar_position: 8 +--- + +# Escrow Channel Deposit Flow + +Escrow deposits are the protocol path for bringing funds from a non-home chain into a user's home-channel balance. The mechanism can be supported by Nitrolite, but docs.yellow.org does not yet document escrow deposits as a builder-facing workflow. + +:::caution Not documented as an app workflow yet +Escrow operation mechanics are intentionally deferred until the Nitronode automation and product surface are ready for public builder docs. Use the home-channel deposit and app-session lifecycle docs for the current v1 builder path. +::: + +## What is stable + +The conceptual model is still useful when reading protocol material: + +- A user has one home chain for a given asset. +- A non-home-chain deposit needs a temporary escrow path because one chain cannot directly verify another chain's latest channel state. +- Nitronode coordinates signed off-chain state updates and on-chain settlement paths. +- The home ledger remains the user's canonical app balance for that asset. + +## Protocol reference + +Protocol-level operation names and invariants live in [Cross-chain and Assets](/nitrolite/protocol/cross-chain-and-assets). This Learn page intentionally avoids escrow state construction steps until the public Nitronode workflow is documented. + +## Current builder guidance + +For applications today: + +- Use home-channel deposit for same-chain funding. +- Use app-session deposit to move available home-channel funds into an app session. +- Treat escrow deposit automation as a future product path until the public Nitronode workflow is documented. + +## Related pages + +- [Architecture at a Glance](../introduction/architecture-at-a-glance) +- [Home Channel Deposit Flow](./home-channel-deposit) +- [App Session Deposit Flow](./app-session-deposit) +- [Cross-chain and Assets](/nitrolite/protocol/cross-chain-and-assets) diff --git a/docs/nitrolite/learn/protocol-flows/escrow-withdrawal.mdx b/docs/nitrolite/learn/protocol-flows/escrow-withdrawal.mdx new file mode 100644 index 0000000..3ac4d73 --- /dev/null +++ b/docs/nitrolite/learn/protocol-flows/escrow-withdrawal.mdx @@ -0,0 +1,41 @@ +--- +title: "Escrow Channel Withdrawal Flow" +description: "Current documentation status for non-home-chain escrow withdrawals in Nitrolite v1." +sidebar_position: 9 +--- + +# Escrow Channel Withdrawal Flow + +Escrow withdrawals are the protocol path for moving value from a user's home-channel balance to a non-home chain. The mechanism can be supported by Nitrolite, but docs.yellow.org does not yet document escrow withdrawals as a builder-facing workflow. + +:::caution Not documented as an app workflow yet +Escrow operation mechanics are intentionally deferred until the Nitronode automation and product surface are ready for public builder docs. Use the home-channel withdrawal and app-session lifecycle docs for the current v1 builder path. +::: + +## What is stable + +The conceptual model is still useful when reading protocol material: + +- A user's home ledger remains the canonical balance for an asset. +- A withdrawal to a non-home chain needs an escrow path because the target chain cannot directly verify the home chain's latest channel state. +- Nitronode coordinates signed off-chain state updates and on-chain settlement paths. +- Applications should not ask users to run escrow withdrawal state construction manually. + +## Protocol reference + +Protocol-level operation names and invariants live in [Cross-chain and Assets](/nitrolite/protocol/cross-chain-and-assets). This Learn page intentionally avoids escrow state construction steps until the public Nitronode workflow is documented. + +## Current builder guidance + +For applications today: + +- Use home-channel withdrawal for same-chain exits. +- Use app-session withdrawal to return app-session funds to the user's home channel. +- Treat escrow withdrawal automation as a future product path until the public Nitronode workflow is documented. + +## Related pages + +- [Architecture at a Glance](../introduction/architecture-at-a-glance) +- [Home Channel Withdrawal Flow](./home-channel-withdrawal) +- [App Session Deposit Flow](./app-session-deposit) +- [Cross-chain and Assets](/nitrolite/protocol/cross-chain-and-assets) diff --git a/docs/learn/protocol-flows/home-channel-creation.mdx b/docs/nitrolite/learn/protocol-flows/home-channel-creation.mdx similarity index 96% rename from docs/learn/protocol-flows/home-channel-creation.mdx rename to docs/nitrolite/learn/protocol-flows/home-channel-creation.mdx index bfcde3c..f1936b3 100644 --- a/docs/learn/protocol-flows/home-channel-creation.mdx +++ b/docs/nitrolite/learn/protocol-flows/home-channel-creation.mdx @@ -17,7 +17,7 @@ This flow is triggered when a user attempts to deposit for the first time and ha ```mermaid graph LR U["User"] --> C["Client"] - C <--> N["Node (Clearnode)"] + C <--> N["Nitronode"] C --> HC["HomeChain"] HC -.-> N @@ -31,7 +31,7 @@ graph LR | --- | --- | | **User** | The human user initiating their first deposit | | **Client** | SDK/Application managing states on behalf of the user | -| **Node** | The Clearnode that validates, stores, and coordinates state transitions | +| **Node** | The Nitronode that validates, stores, and coordinates state transitions | | **HomeChain** | The blockchain where the user's home channel will be created | --- @@ -318,7 +318,7 @@ The **Client** submits a transaction to the **HomeChain** smart contract, which: --- -## Phase 6: Event Handling and Completion +## Phase 6: Confirmation and Completion ```mermaid sequenceDiagram @@ -328,20 +328,16 @@ sequenceDiagram Note right of Node: HandleChannelCreated() Note right of Node: UpdateChannel(channel) - Node-->>Client: Sends ChannelUpdate and BalanceUpdate + Client->>Node: Query latest channel and balance + Node-->>Client: Returns latest channel state and balance Client-->>User: Returns success ``` -The Node listens for blockchain events and: +Nitronode listens for blockchain events internally and updates its stored view: 1. **HandleChannelCreated()** -- Processes the creation event. 2. **UpdateChannel(channel)** -- Updates channel status to reflect on-chain state. -| Event | Description | -| --- | --- | -| `ChannelUpdate` | Notifies client of new channel status | -| `BalanceUpdate` | Notifies client of new balance | - The Client returns success to the User, confirming: - Home channel created successfully @@ -392,7 +388,8 @@ sequenceDiagram rect rgb(40, 100, 100) Note over Node,User: Phase 6: Completion - Node-->>Client: ChannelUpdate and BalanceUpdate + Client->>Node: Query latest channel and balance + Node-->>Client: Returns latest channel state and balance Client-->>User: Returns success end ``` @@ -439,7 +436,7 @@ flowchart LR The Nitrolite architecture improves upon the previous version by **skipping the custody ledger step**. Users transfer funds directly from their ERC-20 token balance via approvals, saving one transaction compared to the old protocol. This means: -1. User approves the Custody contract. +1. User approves the ChannelHub. 2. Channel creation pulls funds directly. 3. No intermediate custody step required. diff --git a/docs/learn/protocol-flows/home-channel-deposit.mdx b/docs/nitrolite/learn/protocol-flows/home-channel-deposit.mdx similarity index 93% rename from docs/learn/protocol-flows/home-channel-deposit.mdx rename to docs/nitrolite/learn/protocol-flows/home-channel-deposit.mdx index 71b528b..6890a2c 100644 --- a/docs/learn/protocol-flows/home-channel-deposit.mdx +++ b/docs/nitrolite/learn/protocol-flows/home-channel-deposit.mdx @@ -21,7 +21,7 @@ This flow is for **subsequent deposits** to an existing channel. For the initial ```mermaid graph LR U["User"] --> C["Client"] - C <--> N["Node (Clearnode)"] + C <--> N["Nitronode"] C --> HC["HomeChain"] HC -.-> N @@ -35,7 +35,7 @@ graph LR | --- | --- | | **User** | The human user initiating the deposit | | **Client** | SDK/Application managing states on behalf of the user | -| **Node** | The Clearnode that validates and stores state transitions | +| **Node** | The Nitronode that validates and stores state transitions | | **HomeChain** | The blockchain where the user's home channel exists | --- @@ -48,10 +48,10 @@ Before the home channel deposit flow begins: 2. **User already has a home channel** on the HomeChain. 3. **Node** contains the user's state with Home Channel information. 4. **User has funds** on the HomeChain to deposit (ERC-20 tokens). -5. **No ongoing operation** exists for this channel (the Clearnode will deny the request otherwise). +5. **No ongoing operation** exists for this channel (the Nitronode will deny the request otherwise). :::note -The "no ongoing operation" requirement applies to ALL operations except `finalize_escrow_deposit`, `finalize_escrow_withdrawal`, and `finalize_migration`. +The "no ongoing operation" requirement applies to ALL operations except the RPC operation names `finalize_escrow_deposit`, `finalize_escrow_withdrawal`, and `finalize_migration`. Those names describe protocol/RPC surfaces, not current app-builder escrow or migration how-to steps. ::: --- @@ -64,7 +64,6 @@ The "no ongoing operation" requirement applies to ALL operations except `finaliz | --- | --- | --- | | **home_deposit** | External to Channel | `createChannel` (first deposit) or `depositToChannel` | | **home_withdrawal** | Channel to External | `withdrawFromChannel` (can also be done with `createChannel`) | -| **escrow_withdraw** | Channel to Non-Home Chain | `initiateEscrowWithdrawal` | ### The depositToChannel Mechanism @@ -248,7 +247,7 @@ The **Client** submits a transaction to the **HomeChain** smart contract, which: --- -## Phase 6: Event Handling and Completion +## Phase 6: Confirmation and Completion ```mermaid sequenceDiagram @@ -258,20 +257,16 @@ sequenceDiagram Note right of Node: HandleChannelDeposited() Note right of Node: UpdateChannel(channel) - Node-->>Client: Sends ChannelUpdate and BalanceUpdate + Client->>Node: Query latest channel and balance + Node-->>Client: Returns latest channel state and balance Client-->>User: Returns success ``` -The Node listens for blockchain events and: +Nitronode listens for blockchain events internally and updates its stored view: 1. **HandleChannelDeposited()** -- Processes the deposit event. 2. **UpdateChannel(channel)** -- Updates the channel's on-chain state version and locked funds. -| Event | Description | -| --- | --- | -| `ChannelUpdate` | Notifies client of new channel state | -| `BalanceUpdate` | Notifies client of new balance | - --- ## Complete Flow Diagram @@ -316,7 +311,8 @@ sequenceDiagram rect rgb(100, 40, 40) Note over Node,User: Phase 6: Completion - Node-->>Client: ChannelUpdate and BalanceUpdate + Client->>Node: Query latest channel and balance + Node-->>Client: Returns latest channel state and balance Client-->>User: Returns success end ``` diff --git a/docs/learn/protocol-flows/home-channel-withdraw-on-create.mdx b/docs/nitrolite/learn/protocol-flows/home-channel-withdraw-on-create.mdx similarity index 97% rename from docs/learn/protocol-flows/home-channel-withdraw-on-create.mdx rename to docs/nitrolite/learn/protocol-flows/home-channel-withdraw-on-create.mdx index 5187395..5ea0284 100644 --- a/docs/learn/protocol-flows/home-channel-withdraw-on-create.mdx +++ b/docs/nitrolite/learn/protocol-flows/home-channel-withdraw-on-create.mdx @@ -17,7 +17,7 @@ This scenario commonly occurs when a user receives a transfer before ever deposi ```mermaid graph LR U["User"] --> C["Client"] - C <--> N["Node (Clearnode)"] + C <--> N["Nitronode"] C --> HC["HomeChain"] HC -.-> N @@ -31,7 +31,7 @@ graph LR | --- | --- | | **User** | The human user initiating the withdrawal | | **Client** | SDK/Application managing states on behalf of the user | -| **Node** | The Clearnode that contains the user's pending state | +| **Node** | The Nitronode that contains the user's pending state | | **HomeChain** | The blockchain where the channel will be created | --- @@ -280,7 +280,7 @@ Since this is a withdrawal without deposit: --- -## Phase 6: Event Handling and Completion +## Phase 6: Confirmation and Completion ```mermaid sequenceDiagram @@ -290,7 +290,8 @@ sequenceDiagram Note right of Node: HandleChannelCreated() Note right of Node: UpdateChannel(channel) - Node-->>Client: Sends ChannelUpdate and BalanceUpdate + Client->>Node: Query latest channel and balance + Node-->>Client: Returns latest channel state and balance Client-->>User: Returns success ``` @@ -344,7 +345,8 @@ sequenceDiagram rect rgb(40, 100, 100) Note over Node,User: Phase 6: Completion - Node-->>Client: ChannelUpdate and BalanceUpdate + Client->>Node: Query latest channel and balance + Node-->>Client: Returns latest channel state and balance Client-->>User: Returns success end ``` diff --git a/docs/learn/protocol-flows/home-channel-withdrawal.mdx b/docs/nitrolite/learn/protocol-flows/home-channel-withdrawal.mdx similarity index 91% rename from docs/learn/protocol-flows/home-channel-withdrawal.mdx rename to docs/nitrolite/learn/protocol-flows/home-channel-withdrawal.mdx index 3b5d455..480c0dd 100644 --- a/docs/learn/protocol-flows/home-channel-withdrawal.mdx +++ b/docs/nitrolite/learn/protocol-flows/home-channel-withdrawal.mdx @@ -8,7 +8,7 @@ sidebar_position: 6 This document provides a comprehensive breakdown of the **Home Channel Withdrawal** flow as defined in the Nitrolite v1.0 protocol. This operation allows a user to withdraw funds from their **unified balance** back to their wallet on the **Home Chain** -- the blockchain where their channel currently exists. -This is a **single-chain operation** that updates the state and checkpoints it on-chain to release funds. +This is a **single-chain operation** that updates the state and enforces it on-chain to release funds. :::note The Home Chain may differ from where the channel was originally created, as users can migrate their Home Chain explicitly. @@ -21,7 +21,7 @@ The Home Chain may differ from where the channel was originally created, as user ```mermaid graph LR U["User"] --> C["Client"] - C <--> N["Node (Clearnode)"] + C <--> N["Nitronode"] C --> HC["HomeChain"] HC -.-> N @@ -35,7 +35,7 @@ graph LR | --- | --- | | **User** | The human user initiating the withdrawal | | **Client** | SDK/Application managing states on behalf of the user | -| **Node** | The Clearnode that validates and stores state transitions | +| **Node** | The Nitronode that validates and stores state transitions | | **HomeChain** | The blockchain where the user's home channel exists | --- @@ -48,10 +48,10 @@ Before the home channel withdrawal flow begins: 2. **User already has a home channel** on the HomeChain. 3. **Node** contains the user's state with Home Channel information. 4. **User has sufficient balance** in their unified balance to withdraw. -5. **No ongoing operation** exists for this channel (the Clearnode will deny the request otherwise). +5. **No ongoing operation** exists for this channel (the Nitronode will deny the request otherwise). :::note -The "no ongoing operation" requirement applies to ALL operations except `finalize_escrow_deposit`, `finalize_escrow_withdrawal`, and `finalize_migration`. +The "no ongoing operation" requirement applies to ALL operations except the RPC operation names `finalize_escrow_deposit`, `finalize_escrow_withdrawal`, and `finalize_migration`. Those names describe protocol/RPC surfaces, not current app-builder escrow or migration how-to steps. ::: --- @@ -64,7 +64,6 @@ The "no ongoing operation" requirement applies to ALL operations except `finaliz | --- | --- | --- | | **home_deposit** | External to Channel | `createChannel` (first deposit) or `depositToChannel` | | **home_withdrawal** | Channel to External | `withdrawFromChannel` (can also be done with `createChannel`) | -| **escrow_withdraw** | Channel to Non-Home Chain | `finalizeEscrowWithdrawal` or `initiateEscrowWithdrawal` | ### The withdrawFromChannel Mechanism @@ -211,7 +210,7 @@ A state with intent `WITHDRAW` must include a negative user net-flow delta and m --- -## Phase 5: On-Chain Checkpoint +## Phase 5: On-Chain Enforcement ```mermaid sequenceDiagram @@ -258,7 +257,7 @@ The **Client** submits a transaction to the **HomeChain** smart contract, which: --- -## Phase 6: Event Handling and Completion +## Phase 6: Confirmation and Completion ```mermaid sequenceDiagram @@ -268,20 +267,16 @@ sequenceDiagram Note right of Node: HandleChannelWithdrawn() Note right of Node: UpdateChannel(channel) - Node-->>Client: Sends ChannelUpdate and BalanceUpdate + Client->>Node: Query latest channel and balance + Node-->>Client: Returns latest channel state and balance Client-->>User: Returns success ``` -The Node listens for blockchain events and: +Nitronode listens for blockchain events internally and updates its stored view: 1. **HandleChannelWithdrawn()** -- Processes the withdrawal event. 2. **UpdateChannel(channel)** -- Updates the channel's on-chain state version. -| Event | Description | -| --- | --- | -| `ChannelUpdate` | Notifies client of new channel state | -| `BalanceUpdate` | Notifies client of new balance | - --- ## Complete Flow Diagram @@ -326,7 +321,8 @@ sequenceDiagram rect rgb(100, 40, 40) Note over Node,User: Phase 6: Completion - Node-->>Client: ChannelUpdate and BalanceUpdate + Client->>Node: Query latest channel and balance + Node-->>Client: Returns latest channel state and balance Client-->>User: Returns success end ``` @@ -341,14 +337,14 @@ sequenceDiagram flowchart LR A["Current State
(balance: 100)"] --> B["home_withdrawal
Transition (50)"] B --> C["New State
(balance: 50)"] - C --> D["Checkpoint
On-Chain"] + C --> D["Enforce
On-Chain"] D --> E["Funds Released
to User Wallet"] style A fill:#4caf50 style E fill:#2196f3 ``` -### Withdrawal vs Checkpoint +### Withdrawal vs Enforcement | Aspect | Description | | --- | --- | @@ -379,7 +375,7 @@ Without the on-chain withdrawal call, the withdrawal is only recorded off-chain. ### What Protects the User? 1. **Dual signatures** -- Both User and Node must agree to the state. -2. **On-chain enforcement** -- Checkpoint validates all signatures. +2. **On-chain enforcement** -- ChannelHub validates all signatures. 3. **Challenge mechanism** -- User can challenge if Node misbehaves. --- @@ -392,7 +388,7 @@ Without the on-chain withdrawal call, the withdrawal is only recorded off-chain. | **Ongoing transition** | Atomic operation in progress | Wait for completion | | **Invalid signature** | Corrupted or wrong key | Regenerate signature | | **Stale version** | Race condition | Refetch state and retry | -| **Checkpoint revert** | On-chain validation failure | Check state validity | +| **Enforcement revert** | On-chain validation failure | Check state validity | --- diff --git a/docs/learn/protocol-flows/transfer-flow.mdx b/docs/nitrolite/learn/protocol-flows/transfer-flow.mdx similarity index 90% rename from docs/learn/protocol-flows/transfer-flow.mdx rename to docs/nitrolite/learn/protocol-flows/transfer-flow.mdx index 94201d8..753082b 100644 --- a/docs/learn/protocol-flows/transfer-flow.mdx +++ b/docs/nitrolite/learn/protocol-flows/transfer-flow.mdx @@ -6,7 +6,7 @@ sidebar_position: 2 # Transfer Communication Flow -This document provides a comprehensive breakdown of the **off-chain transfer flow** as defined in the Nitrolite v1.0 protocol. The transfer operation moves funds between users instantly without blockchain transactions, leveraging **state transitions** managed through the **Clearnode** (Node). +This document provides a comprehensive breakdown of the **off-chain transfer flow** as defined in the Nitrolite v1.0 protocol. The transfer operation moves funds between users instantly without blockchain transactions, leveraging **state transitions** managed through the **Nitronode** (Node). --- @@ -15,7 +15,7 @@ This document provides a comprehensive breakdown of the **off-chain transfer flo ```mermaid graph LR SU["SenderUser"] --> SC["SenderClient"] - SC <--> N["Node (Clearnode)"] + SC <--> N["Nitronode"] N <--> RC["ReceiverClient"] style SU stroke:#333 @@ -28,8 +28,8 @@ graph LR | --- | --- | | **SenderUser** | The human user initiating the transfer | | **SenderClient** | SDK/Application that manages states on behalf of the sender | -| **Node** | The Clearnode that validates, stores, and coordinates state transitions | -| **ReceiverClient** | SDK/Application that receives notifications for the recipient | +| **Node** | The Nitronode that validates, stores, and coordinates state transitions | +| **ReceiverClient** | SDK/Application that can query the recipient's latest state | --- @@ -204,9 +204,9 @@ EnsureNoOngoingTransitions() The Node checks that there are no pending/incomplete transitions for this user. This prevents race conditions and ensures state consistency. :::warning -If there's an ongoing transition, the Clearnode will return a relevant error and the submission is rejected. +If there's an ongoing transition, the Nitronode will return a relevant error and the submission is rejected. -**Atomic Operations**: `escrow_deposit`, `escrow_withdrawal`, and `home-chain migration` operations are considered as one atomic operation. This means that if, for example, an escrow deposit was started with `initiate_escrow_deposit` transition, then no other states will be issued apart from `finalize_escrow_deposit`. Only after finalization can a `transfer` (or other non-`finalize_escrow_deposit` transition) be accepted by the Node. +**Serialized operations**: Nitronode advances one state at a time for the same user and asset. It rejects a transfer if an earlier state transition is still pending, because accepting both would make the final version order ambiguous. Escrow and migration are protocol-defined but not current builder-facing workflows; the same sequencing rule applies when those paths are enabled. ::: :::info @@ -219,7 +219,7 @@ Even in case of `home_deposit`, the Node won't accept a new state until it knows ValidateAdvancement(currentState, proposedState) ``` -State advancement validation is done by applying effects from the proposed state to the current one. If there is any difference between the proposed state and the one built by the Clearnode, it will return a relevant error (e.g., `version should be X`, `asset shouldn't change`, etc.). +State advancement validation is done by applying effects from the proposed state to the current one. If there is any difference between the proposed state and the one built by the Nitronode, it will return a relevant error (e.g., `version should be X`, `asset shouldn't change`, etc.). The Node validates: @@ -244,7 +244,7 @@ StoreState(state) ``` :::info 2-Signature Rule -The Node also signs the sender's state. In v1, only a 2-signature state (both User and Node signatures) can be valid. After signing, the Node stores the state locally and sends it as a notification to subscribers. +The Node also signs the sender's state. In v1, only a 2-signature state (both User and Node signatures) can be valid. After signing, Nitronode stores the state locally and returns the signature in the transfer response. ::: The validated and dual-signed state is stored in the Node's database. At this point, the sender's side of the transfer is complete. @@ -324,9 +324,9 @@ The Node supports receiving funds by users who don't have a channel or are not o --- -## Phase 6: Notifications and Completion +## Phase 6: Confirmation and Completion -After the receiver state is created, the Node sends notifications to both the sender and receiver clients. +After the receiver state is created, Nitronode returns the sender-side signature. Applications that need fresh balances should query the latest channel or balance state after the transfer response. --- @@ -367,11 +367,13 @@ sequenceDiagram end rect rgb(0, 0, 0) - Note over Node,ReceiverClient: Phase 6: Notify - Node->>SenderClient: ChannelUpdate and BalanceUpdate - Node->>ReceiverClient: ChannelUpdate and BalanceUpdate + Note over Node,ReceiverClient: Phase 6: Confirm and query Node->>SenderClient: Return node signature - SenderClient->>SenderUser: Returns success and tx hash + SenderClient->>Node: Query latest state if needed + ReceiverClient->>Node: Query latest state if online + Node->>SenderClient: Returns latest state + Node->>ReceiverClient: Returns latest state + SenderClient->>SenderUser: Returns success end ``` @@ -411,8 +413,7 @@ sequenceDiagram | `commit` | Commit funds to lock | | `home_deposit` | Deposit to home channel | | `home_withdrawal` | Withdraw from home channel | -| `escrow_deposit/lock/withdraw` | Escrow channel operations | -| `migrate` | Cross-chain migration | +| Escrow and migration operations | Protocol-defined, not current builder-facing workflows | --- diff --git a/docs/nitrolite/learn/whats-new-from-0.5.3.mdx b/docs/nitrolite/learn/whats-new-from-0.5.3.mdx new file mode 100644 index 0000000..b176b4a --- /dev/null +++ b/docs/nitrolite/learn/whats-new-from-0.5.3.mdx @@ -0,0 +1,115 @@ +--- +title: What Changed Since 0.5.3 +description: Mental model changes for builders moving from @erc7824/nitrolite 0.5.3 to Nitrolite v1. +sidebar_position: 5 +--- + +# What Changed Since 0.5.3 + +If you built with `@erc7824/nitrolite@0.5.3`, v1 can feel like a lot changed at once. The important shift is not that the old model was wrong. v1 moves responsibilities into clearer layers so app code can work closer to the channel and app-session lifecycle. + +This page is a no-code orientation. Read it before the code migration guide if you want the new vocabulary and product shape to feel familiar first. + +## You are still building the same kind of app + +The goal is the same: move value through fast off-chain updates while keeping an on-chain recovery path. Your users should not wait for a blockchain transaction for every app action, and the latest mutually signed state should still be enforceable if cooperation fails. + +In 0.5.3, many apps learned this through Clearnode, unified balances, explicit auth helpers, channel resize flows, and WebSocket notifications. In v1, the same safety goal is expressed through Nitronode, home channels, app sessions, signed state advancement, and ChannelHub enforcement. + +## Names changed, roles got clearer + +| 0.5.3 term | v1 term | How to think about it | +| --- | --- | --- | +| Clearnode | Nitronode | The off-chain coordinator your SDK connects to. It serves v1 RPC, validates transitions, co-signs valid states, and coordinates app sessions. | +| Custody | ChannelHub | The on-chain enforcement layer for channel create, deposit, withdraw, state enforcement, challenge, and close operations. | +| Adjudicator | ChannelHub validators and engine logic | Validation moved into the v1 engine and signature-validator model instead of app code naming an adjudicator contract directly. | +| Virtual app | App session | The app-specific off-chain state extension funded from channel balances. | +| NitroRPC 0.4 helper methods | v1 RPC namespaces | Wire methods are grouped as `channels.v1.*`, `app_sessions.v1.*`, `apps.v1.*`, `user.v1.*`, and `node.v1.*`. Most builders should call them through an SDK. | + +The biggest practical rename is Clearnode to Nitronode. If old docs said "connect to Clearnode", the v1 equivalent is "connect to Nitronode". + +## What moved into the SDK + +In many 0.5.3 integrations, app code owned the whole pipeline: open a WebSocket, build a helper message, sign it, send it, parse the response, and listen for pushed updates. + +In v1, the SDK owns more of that plumbing: + +- connection setup and request correlation +- envelope framing and method names +- state signing and transaction signing +- Nitronode config and asset metadata lookup +- amount conversion for high-level calls +- typed errors and safer recovery paths + +That means app code can focus on business intent: prepare a home channel, commit funds into an app session, operate, withdraw, close, and query final state. + +## Authentication did not disappear + +The old front door was explicit: `auth_request`, `auth_challenge`, `auth_verify`, JWT reuse, and a session key that signed later RPC requests. That flow still matters for legacy and compat migrations, but it is not the native v1 app front door. + +Native v1 treats authorization as part of signed protocol state. A channel update is accepted because the right participant signed the right state. Session keys still exist, but they are expressed as signed key-state and signature-validation mode, not as a separate auth handshake every app must implement before it can call the SDK. + +The user-facing result should be better: fewer application-owned security decisions, less copied auth code, and clearer boundaries between wallet authority, delegated signing, Nitronode validation, and on-chain enforcement. + +## Home channels come before app sessions + +In 0.5.3, it was natural to think in terms of unified balance plus app-level helper workflows. In v1, start with the channel state: + +| Concept | Role | +| --- | --- | +| Home channel | The user and Nitronode channel for one asset. Deposits, transfers, acknowledgements, enforcement submissions, and withdrawals update this state. | +| Home ledger | The ledger tied to the chain where the channel state is enforced. | +| App session | An extension that receives committed channel funds and tracks app-specific allocations. | +| Commit and release | The movements between channel funds and app-session funds. | + +An app session does not replace the home channel. It sits on top of channel funds. That is why the native lifecycle first prepares the home channel, then creates and funds an app session. + +## State first, settlement when needed + +v1 separates state advancement from blockchain settlement. A high-level SDK operation can produce a state that both the user and Nitronode have signed. That signed state is useful before it is posted on-chain. + +State enforcement is the moment you ask ChannelHub to record the latest signed state on-chain. For routine app activity, most updates stay off-chain. You enforce a state when the lifecycle requires on-chain recovery or settlement. SDK and contract surfaces may still call a specific enforcement path `checkpoint()`. + +This is the mental model to keep: first agree on state, then settle only when needed. + +## Events become refresh policy + +0.5.3 docs talked about pushed balance, channel, transfer, and app-session updates. In current v1 docs, asynchronous events are reserved for a future protocol revision. Do not build a new v1 app around server-push assumptions. + +Use explicit queries for the state your UI needs. If you are migrating through compat and your old app expects event-shaped callbacks, use the compat polling helper as a bridge while you decide your final refresh policy. + +## Amounts are a boundary + +v1 makes amount boundaries more explicit. Native SDK calls use exact decimal values for builder-facing amounts. RPC payloads use strings. Compat preserves some 0.5.3 amount shapes so older code can move in stages. + +That is intentional. Treat every amount conversion as a boundary between layers, not a formatting detail. During migration, review deposits, withdrawals, transfers, app-session allocations, and security-token calls separately. + +## Compat is scaffolding + +`@yellow-org/sdk-compat` is useful when you need a smaller first step from `@erc7824/nitrolite@0.5.3`. It preserves selected app-facing shapes and routes them through the v1 runtime. + +Compat is not meant to preserve every old behavior forever. Some helper workflows fail fast because there is no honest one-to-one mapping to v1. That is a safety rail: it points you to the place where the app needs a real lifecycle decision. + +Use compat to reduce migration risk. Use native `@yellow-org/sdk` when you are ready to align the app with the v1 model directly. + +## What not to carry forward + +Avoid carrying these assumptions into new v1 code: + +- hard-coded Clearnode, Custody, or Adjudicator addresses in app config +- app-owned WebSocket helper chains as the primary integration model +- server-push updates as the only way to keep UI state fresh +- one global amount format across every method family +- escrow or migration operation guides as current builder flows + +Escrow and migration are protocol concepts, and contract support exists in lower layers. They are not documented as app-builder how-to flows on docs.yellow.org yet. + +## Ready to migrate your code? + +Start with the code migration page when the mental model is clear: + +- [Migrating from 0.5.3](./migrating-from-0.5.3) - choose compat-first or native v1. +- [TypeScript compat SDK](/nitrolite/build/sdk/typescript-compat/overview) - staged migration for existing 0.5.3 apps. +- [Native TypeScript SDK](/nitrolite/build/sdk/typescript/getting-started) - the long-term SDK path for new v1 flows. +- [Quickstart](/nitrolite/build/getting-started/quickstart) - run the tested channel and app-session lifecycle. +- [Key Terms & Mental Models](/nitrolite/build/getting-started/key-terms) - quick vocabulary reference for the v1 SDK docs. diff --git a/docs/protocol/_category_.json b/docs/nitrolite/protocol/_category_.json similarity index 100% rename from docs/protocol/_category_.json rename to docs/nitrolite/protocol/_category_.json diff --git a/docs/nitrolite/protocol/channel-lifecycle.mdx b/docs/nitrolite/protocol/channel-lifecycle.mdx new file mode 100644 index 0000000..85b1891 --- /dev/null +++ b/docs/nitrolite/protocol/channel-lifecycle.mdx @@ -0,0 +1,140 @@ +--- +title: "Channel Lifecycle" +description: "Channel definition, identifiers, lifecycle actions, signing categories, transition families, and enforcement-relevant transitions." +--- + +# Channel Lifecycle + +Channels are the primary mechanism for off-chain interaction in the Nitrolite protocol. They allow participants to exchange assets and update state without on-chain transactions. + +## Channel Definition + +A channel is defined by immutable parameters fixed at creation time. + +| Field | Description | +| --- | --- | +| `User` | Identifier of the user participant. | +| `Node` | Identifier of the node participant. | +| `Asset` | Identifier of the asset operated within the channel. | +| `Nonce` | Unique nonce distinguishing channels with identical parameters. | +| `ChallengeDuration` | Challenge period duration in seconds. | +| `ApprovedSignatureValidators` | Bitmask of approved signature validation modes. | + +The channel definition MUST NOT change after creation. + +## Channel Identifier + +The channel identifier is derived deterministically from the channel definition using canonical encoding and hashing. + +The derivation produces a 32-byte identifier where: + +- the first byte encodes the smart contract version. +- the remaining bytes are derived from the hash of the canonical encoded channel definition parameters. + +This ensures that each channel definition produces a unique identifier, that the identifier can be independently computed, and that identifiers are scoped to a specific protocol version. + +## Lifecycle Actions + +```mermaid +stateDiagram-v2 + [*] --> Create + Create --> Enforced: enforce signed state + Create --> Close: cooperative close + Enforced --> Enforced: higher-version state + Enforced --> Challenge: participant dispute + Challenge --> Enforced: higher-version response + Challenge --> Close: challenge expires + Enforced --> Close: final signed state + Close --> [*] +``` + +| Action | Context | Rule | +| --- | --- | --- | +| Create | Off-chain, then optionally on-chain | The Node validates and stores the channel definition. An initial state is constructed and signed by all participants. The initial state, or any later higher-version state, MAY be submitted for on-chain enforcement. | +| State enforcement | Off-chain, then optionally on-chain | The Node validates and stores a new state off-chain. Depending on transition type or participant initiative, the state MAY also be submitted to the blockchain layer. | +| Challenge | On-chain only | A participant submits a signed state plus challenger signature. The challenge duration begins, and other participants MAY respond with a higher-version state. | +| Close | Off-chain for cooperative agreement, on-chain for execution | A close finalizes the channel and releases funds according to final state allocations. | + +## State Signing Categories + +:::info Enforceable state boundary +Only a mutually signed state is enforceable on-chain. A node-issued pending state MUST NOT be treated as the latest authoritative state until the User acknowledges it. +::: + +| Category | Rule | +| --- | --- | +| Mutually signed state | Carries valid signatures from both the User and the Node. It is the authoritative off-chain state and enforceable on-chain. | +| Node-issued pending state | Produced by the Node and carries only the Node's signature. It becomes mutually signed only after User acknowledgement. | + +The off-chain and enforcement representations encode the same logical state. A mutually signed off-chain state is directly enforceable on-chain if the enforcement representation is derived correctly. + +## State Advancement Rules + +When a new state is proposed during off-chain advancement: + +- **Version validation**: the state version MUST equal the current version plus one. +- **Signature validation**: a valid signature from the proposing participant MUST be present, and the signature validation mode MUST be approved for the channel. +- **Channel binding**: the channel identifier MUST be present and MUST match the channel definition. +- **Transition admissibility**: the transition type MUST be valid for the current channel state, and transition-specific validation rules MUST hold. +- **Ledger admissibility**: ledger invariants MUST hold, allocation values MUST be non-negative, declared decimal precision MUST match the asset's actual precision, and transition-specific ledger validations apply. + +## Transition Families + +| Family | Transitions | +| --- | --- | +| Local channel transitions | Home Deposit, Home Withdrawal, Finalize | +| Transfer transitions | TransferSend, TransferReceive, Acknowledgement | +| Extension bridge transitions | Commit, Release | +| Cross-chain escrow transitions | Escrow Deposit Initiate, Escrow Deposit Finalize, Escrow Withdrawal Initiate, Escrow Withdrawal Finalize | +| Migration transitions | Migration Initiate, Migration Finalize | + +## Transition Rules + +For all transitions that do not modify the non-home ledger, the non-home ledger MUST be empty. + +| Transition | Core rule | +| --- | --- | +| Acknowledgement | Allows the User to acknowledge a pending Node-issued state. Valid only when the current state has no User signature. | +| Home Deposit | Records an asset deposit from the home chain. `AccountId` MUST reference the home channel identifier. Requires on-chain enforcement to lock deposited assets. | +| Home Withdrawal | Records an asset withdrawal to the home chain. `AccountId` MUST reference the home channel identifier. Requires on-chain enforcement to release withdrawn assets. | +| TransferSend | Transfers assets from the User to a counterparty through the Node. `TxId` correlates with the receiver's TransferReceive. | +| TransferReceive | Records inbound transfer from a counterparty through the Node. Amount and `TxId` MUST match the sender's TransferSend. It is a Node-issued pending state. | +| Commit | Moves assets from the channel into an extension. `AccountId` MUST reference the extension object identifier. | +| Release | Returns assets from an extension back to channel allocations. The extension state MUST authorize the release. It is a Node-issued pending state. | +| Escrow Deposit Initiate | Initiates cross-chain deposit by creating escrow between home and non-home chains. A non-home ledger MUST be provided and use a different blockchain identifier than the home ledger. | +| Escrow Deposit Finalize | Completes a previously initiated cross-chain deposit. `Amount` MUST match the initiating transition. | +| Escrow Withdrawal Initiate | Initiates cross-chain withdrawal by creating escrow on the non-home chain. A non-home ledger MUST be provided and use a different blockchain identifier. | +| Escrow Withdrawal Finalize | Completes a previously initiated cross-chain withdrawal. `Amount` MUST match the initiating transition. | +| Migration Initiate | Begins migration of the channel to a different chain. A non-home ledger MUST be provided. | +| Migration Finalize | Completes a migration. The version MUST immediately succeed the migration initiate state. | +| Finalize | Indicates cooperative intent to close. `AccountId` MUST reference the home channel identifier, all participants MUST sign, and open escrows or incomplete migrations MUST be resolved before finalization. | + +:::note Migration version note +Migration transitions are functional but may be refined in future protocol versions. +::: + +## Atomicity and Dependent State Changes + +Certain transitions produce side effects that create or modify states in other channels. The entire advancement, including dependent state changes, MUST succeed or fail as a whole. + +- **TransferSend**: when the Node accepts a TransferSend, it MUST atomically create the corresponding TransferReceive state on the receiver's channel. +- **Release**: when an extension releases assets, the Node MUST atomically create the Release state on the User's channel. +- **Cross-chain escrow transitions**: escrow initiate and finalize operations MAY trigger on-chain actions that MUST be coordinated with the off-chain state change. + +## Enforcement-Relevant Transitions + +These transitions require or MAY trigger on-chain enforcement because their intent does not map to `OPERATE`: + +| Transition | Intent | Enforcement behavior | +| --- | --- | --- | +| Home Deposit | `DEPOSIT` | Required to lock deposited assets. | +| Home Withdrawal | `WITHDRAW` | Required to release withdrawn assets. | +| Escrow Deposit Initiate | `INITIATE_ESCROW_DEPOSIT` | Required to create escrow on non-home chain. | +| Escrow Deposit Finalize | `FINALIZE_ESCROW_DEPOSIT` | Required to complete cross-chain deposit. | +| Escrow Withdrawal Initiate | `INITIATE_ESCROW_WITHDRAWAL` | Required to create escrow for withdrawal. | +| Escrow Withdrawal Finalize | `FINALIZE_ESCROW_WITHDRAWAL` | Required to release assets on non-home chain. | +| Migration Initiate | `INITIATE_MIGRATION` | Required to begin chain migration. | +| Migration Finalize | `FINALIZE_MIGRATION` | Required to complete chain migration. | +| Finalize | `CLOSE` | Required to settle and release funds. | + +Any transition MAY also be enforced at a participant's discretion to record the current state on-chain. Any party MAY independently submit a validly signed state to the blockchain layer. diff --git a/docs/nitrolite/protocol/cross-chain-and-assets.mdx b/docs/nitrolite/protocol/cross-chain-and-assets.mdx new file mode 100644 index 0000000..a842871 --- /dev/null +++ b/docs/nitrolite/protocol/cross-chain-and-assets.mdx @@ -0,0 +1,132 @@ +--- +title: "Cross-Chain and Assets" +description: "Unified assets, amount normalization, home and non-home ledger roles, escrow, migration, and replay protection." +--- + +# Cross-Chain and Assets + +The unified asset model allows participants to operate on assets from multiple blockchains within a single channel. This eliminates the need for separate channels per blockchain and enables cross-chain interactions. + +## Unified Asset Concept + +Assets are identified independently of any specific blockchain. + +| Field | Description | +| --- | --- | +| `Symbol` | Human-readable canonical asset identifier. | +| `Decimals` | Decimal precision of the asset. | + +## Canonical Asset Identification + +The protocol identifies a unified asset by symbol. Within channel metadata, the symbol is represented as the first 8 bytes of its Keccak-256 hash. Two chain-specific tokens are recognized as the same unified asset if they share the same symbol-derived identifier and are configured as such by the Node. + +Symbol collisions are prevented by the Node's asset configuration. The protocol does not maintain a global on-chain registry of unified assets. + +## Amount Normalization + +Assets on different blockchains MAY have different decimal precisions. The protocol normalizes amounts for cross-chain comparisons using WAD normalization: + +```text +NormalizedAmount = Amount * 10^(18 - ChainDecimals) +``` + +Rules: + +- Normalization is used only for cross-chain comparisons, such as validating escrow amounts across chains. +- Normalization is not used for storage or accounting; stored values remain in chain-native precision. +- The asset's configured decimal precision acts as the base, while 18 is the upscaling target. +- Maximum supported decimal precision is 18. +- Normalization is exact and lossless when scaling up. +- The blockchain layer validates that declared decimals match actual token decimals on the current chain. + +## Home Chain + +The home chain is the blockchain against which a channel state is enforced. It is identified by the chain identifier in the home ledger. + +The home chain determines: + +- where enforcement operations execute. +- which blockchain holds locked funds for the channel. +- the authoritative source for state validation. + +The home chain MAY change through a migration operation. After migration, the new home chain becomes the authoritative enforcement target. + +## Home and Non-Home Ledger Roles + +| Ledger | Responsibilities | +| --- | --- | +| Home Ledger | Tracks authoritative asset allocations, receives state enforcement submissions, and holds deposited assets in the enforcement contract. | +| Non-Home Ledger | Tracks assets involved in cross-chain escrow operations, reflects cross-chain deposit and withdrawal allocations, and coordinates with the home ledger. | + +When no cross-chain operation is in progress, the non-home ledger MUST be empty. + +## Escrow Model + +Cross-chain operations use escrow to coordinate fund movement across two independent blockchains. An escrow is a temporary on-chain record that locks funds on one chain while a corresponding state update is finalized on another chain. + +Each escrow is identified by an escrow channel identifier derived deterministically from the home channel identifier and the state version at initiation. + +| Property | Description | +| --- | --- | +| Identifier | 32-byte hash derived from home channel identifier and state version. | +| Hosting chain | Non-home chain. For deposits, this is where User funds are locked. For withdrawals, this is where Node funds are locked. | +| Tracked amount | Amount locked in escrow, corresponding to non-home ledger allocations. | +| Release delay | Deposit escrows include a release delay after which funds are automatically returned to the Node if not challenged. | +| Challenge duration | Period after challenge initiation that allows resolution. If no finalization state is supplied, the initiate state is finalized and funds are returned. | + +An escrow is not a separate protocol entity with its own state. It is an on-chain record derived from a channel state transition. + +:::caution Builder workflow not documented yet +Escrow and migration are defined by the protocol, but the current Nitronode public app-builder workflow does not implement them yet. The sections below define the semantic model only. +::: + +## Cross-Chain Deposit + +Cross-chain deposit is represented as a two-phase escrow family: + +| Phase | Protocol operation | Meaning | +| --- | --- | --- | +| Initiate | `initiate_escrow_deposit` | Escrow deposit initiate. | +| Finalize | `finalize_escrow_deposit` | Escrow deposit finalize or complete. | + +Cross-chain amounts are validated using WAD normalization. + +## Cross-Chain Withdrawal + +Cross-chain withdrawal is represented as a two-phase escrow family: + +| Phase | Protocol operation | Meaning | +| --- | --- | --- | +| Initiate | `initiate_escrow_withdrawal` | Escrow withdrawal initiate. | +| Finalize | `finalize_escrow_withdrawal` | Escrow withdrawal finalize or complete. | + +## Home Chain Migration + +The home chain may change through migration states, represented by the `initiate_migration` and `finalize_migration` operation family. Migration is not implemented as a public builder workflow today. Conceptually, after migration: + +- home chain identifier updates. +- home token address updates. +- the new home ledger becomes the authoritative ledger. +- subsequent enforcement operations execute against the new home chain. +- user allocation is preserved, normalized by decimal precision. + +## Cross-Chain Replay Protection + +The protocol prevents cross-chain replay through: + +- **Chain identifier binding**: each ledger is bound to a chain identifier, and the blockchain layer validates that the home ledger chain identifier matches the current blockchain. +- **Channel identifier scoping**: channel identifiers incorporate a protocol version byte. +- **Escrow identifier uniqueness**: escrow identifiers are derived from home channel identifier and state version at initiation. +- **Ledger validation**: enforcement validates declared decimals against token decimals on the current execution chain and applies security invariants. + +## Current Version Notes + +:::warning Current trust boundary +In the current protocol version, cross-chain operations require trust in the Node to relay state correctly between chains. Full cross-chain enforcement is a planned future improvement. +::: + +Current constraints: + +- The Node is responsible for submitting escrow initiation and finalization transactions on the appropriate chains. +- Each channel state supports exactly two ledgers: one home ledger and one non-home ledger. +- Future protocol versions MAY support additional ledger configurations. diff --git a/docs/nitrolite/protocol/cryptography-and-signing.mdx b/docs/nitrolite/protocol/cryptography-and-signing.mdx new file mode 100644 index 0000000..e779ec5 --- /dev/null +++ b/docs/nitrolite/protocol/cryptography-and-signing.mdx @@ -0,0 +1,111 @@ +--- +title: "Cryptography and Signing" +description: "Canonical encoding, digest construction, signature envelopes, validation modes, session keys, and replay protection." +--- + +# Cryptography and Signing + +Protocol cryptography defines how signable objects are encoded, hashed, and signed. The rules are algorithms and canonical procedures independent of any specific programming language. + +## Purpose + +Cryptography in the Nitrolite protocol serves three functions: + +1. **Authentication**: prove that a participant authorized a state update. +2. **Integrity**: ensure signed data has not been modified. +3. **Replay protection**: prevent previously signed states from being reused in unintended contexts. + +## Cryptographic Algorithms + +| Primitive | Protocol rule | +| --- | --- | +| Signature algorithm | ECDSA over the secp256k1 curve, producing a 65-byte signature `(r, s, v)`. | +| Hash function | Keccak-256, producing a 32-byte digest. | + +## Canonical Encoding + +Protocol objects that require signing MUST be encoded into a canonical binary representation before hashing. The canonical encoding uses Solidity ABI encoding (`abi.encode`), which produces deterministic 32-byte-word-aligned byte sequences regardless of implementation language. + +## Message Digest Construction + +The digest of a signable payload is constructed as follows: + +1. Encode the object using canonical encoding. +2. Prepend the EIP-191 personal message prefix: the ASCII string `"\x19Ethereum Signed Message:\n"`, followed by the decimal length of the encoded bytes, then the encoded bytes. +3. Compute the Keccak-256 hash of the prefixed message. + +The resulting 32-byte digest is the value that is signed. + +## ECDSA Signature Format + +| Field | Size | Description | +| --- | --- | --- | +| `R` | 32 bytes | ECDSA r component. | +| `S` | 32 bytes | ECDSA s component. | +| `V` | 1 byte | Recovery identifier. | + +The signer's address is recovered from the signature and the message digest. The protocol does not transmit the signer's public key or address alongside the signature. + +## Protocol Signature Envelope + +A protocol signature wraps the raw ECDSA signature with a validation mode prefix: + +```text +ProtocolSignature = ValidationMode || SignatureData +``` + +The first byte (`ValidationMode`) determines the validation method. The remaining bytes (`SignatureData`) contain mode-specific data, including the raw signature. + +## Signature Validation Modes + +| Mode | Rule | +| --- | --- | +| Default mode (`0x00`) | `SignatureData` contains the raw ECDSA signature `(R, S, V)`. The recovered address MUST match the expected participant address. | +| Session key mode (`0x01`) | `SignatureData` contains a session key authorization and the session key's ECDSA signature over the state data, ABI-encoded as a tuple. | + +For session key mode, the validator first verifies that the participant authorized the session key, then verifies that the session key produced a valid signature over the state. The authorization MUST be associated with the same address as the channel's User or Node participant. The recovered session key address MUST match the authorized session key address. + +:::info Session keys and enforcement +Session-key signatures are valid for both off-chain state advancement and on-chain enforcement, provided the session key validation mode is among the channel's approved signature validators. +::: + +## Signable Object Classes + +The signing framework accommodates multiple classes of signable objects: + +- **Channel objects**: primarily channel state, session key registration, and challenger signature. +- **Extension objects**: primarily extension entity state, such as application session state, signed by the relevant session participants. + +Channel and extension states are identified by unique entity identifiers and follow the same canonical encoding and digest construction rules. Future protocol extensions MAY introduce additional signable object classes without changing the core signing rules. + +## Session Key Authorization + +A participant MAY delegate signing authority to a session key. Authorization is constructed as follows: + +1. The participant signs a message containing the session key address and authorization metadata hash. +2. The authorization signature is produced using the participant's primary key. +3. The session key MAY then produce signatures on behalf of the participant within the authorized scope. + +Session key signatures MUST include the authorization proof alongside the session key signature. The authorization proof is canonically encoded as a tuple containing the session key authorization and the raw signature bytes. + +### Authorization Metadata + +The authorization metadata defines the scope of a session key. It MUST contain at least: + +| Field | Purpose | +| --- | --- | +| `version` | Replay and revocation guard for the participant's session-key delegations. A higher version supersedes prior versions. | +| `expires_at` | Bounds the delegation lifetime. Signatures produced after expiry MUST be rejected. | + +Implementations MAY extend the metadata with additional restrictions, for example authorized assets, allowed transitions, channels, recipients, or per-asset spending limits. Restrictions that an implementation does not encode and enforce are not applied. + +Only the `keccak256` hash of the canonically encoded metadata is bound into the authorization signature. The full metadata is supplied alongside the signature at validation time. This allows off-chain metadata extensions without changing the on-chain validator, while requiring the off-chain layer to decode and enforce every field it understands. + +## Replay Protection + +The protocol prevents replay attacks through: + +- **Entity identifier**: each signable entity has a unique identifier derived from its definition. +- **State version**: each state includes a monotonically increasing version number. The blockchain layer MUST reject states whose version is less than or equal to the currently enforced version. +- **Blockchain identifier**: states include blockchain-specific identifiers to prevent cross-chain replay. +- **Smart contract version**: the channel entity identifier incorporates a contract version, currently as the first byte, preventing replay across deployments. diff --git a/docs/nitrolite/protocol/enforcement-and-settlement.mdx b/docs/nitrolite/protocol/enforcement-and-settlement.mdx new file mode 100644 index 0000000..7e47dff --- /dev/null +++ b/docs/nitrolite/protocol/enforcement-and-settlement.mdx @@ -0,0 +1,145 @@ +--- +title: "Enforcement and Settlement" +description: "On-chain enforceability, state submissions, challenges, close paths, validation rules, and failure conditions." +--- + +# Enforcement and Settlement + +Enforcement is the mechanism by which off-chain state is reflected on-chain. It provides regular state synchronization and dispute resolution. + +The blockchain layer is the ultimate arbiter of channel state and provides security guarantees that do not depend on participant cooperation. + +## Enforceable State Requirements + +A state is enforceable on-chain if and only if: + +- It is mutually signed by both the User and the Node. +- Its signatures use validation modes approved by the channel. +- It has passed off-chain state advancement validation. +- The Node has sufficient balance on the target chain to cover any required fund locking. + +:::warning Pending states are not enforceable +Node-issued pending states carry only the Node's signature. They are NOT enforceable until the User acknowledges them, producing a mutually signed state. +::: + +## Enforcement Model + +- Participants advance state off-chain through signed updates. +- At any time, any party MAY submit the latest mutually signed state to the blockchain layer. +- The blockchain layer validates the submitted state and updates its record. +- On-chain state always reflects the latest successfully enforced state. + +The on-chain state MAY lag behind the off-chain state during normal operation for transitions with the `OPERATE` intent. + +## Locked Funds Model + +The blockchain layer tracks locked funds for each channel. Locked funds represent assets held by the enforcement contract on behalf of the channel. + +Rules: + +- Locked funds increase when assets are pulled from the User or from the Node's vault into the channel. +- Locked funds decrease when assets are released to the User or the Node. +- Unless the channel is being closed, `UserAllocation + NodeAllocation` MUST equal locked funds. +- Locked funds MUST never be negative. + +The Node maintains a vault balance per token on each chain. When a transition requires the Node to lock additional funds, the required amount is deducted from the Node's vault and added to the channel's locked funds. + +| Operation | User fund effect | Node fund effect | Locked funds effect | +| --- | --- | --- | --- | +| `DEPOSIT` | Pull from User | Adjust by Node net flow delta | Increase by total deltas | +| `WITHDRAW` | Release to User | Adjust by Node net flow delta | Decrease by total deltas | +| `OPERATE` | No User fund movement | Adjust by Node delta | Adjust by Node delta only | +| `CLOSE` | Release `UserAllocation` to User | Release `NodeAllocation` to Node | Set to zero | +| Challenge | No fund movement | No fund movement | Unchanged; status changes | + +## Channel Creation + +Channels are created through an enforcement operation. A channel does not need to be created on-chain with its initial off-chain-created state. Any validly signed state MAY be used for on-chain creation if the channel does not yet exist on-chain. + +The state submitted for channel creation MAY carry a `DEPOSIT`, `WITHDRAW`, or `OPERATE` intent. + +## State Submission + +State submission covers state enforcement, deposit, and withdrawal operations: + +1. A participant constructs the enforcement representation of a signed state. +2. The participant submits the enforcement representation with all required signatures. +3. The blockchain layer validates the submission. +4. If valid, on-chain state is updated and fund movements are applied. + +Intent-specific behavior: + +| Intent | Validation and effect | +| --- | --- | +| `OPERATE` | User net flow has not changed and Node allocation is zero. No User fund movement occurs. | +| `DEPOSIT` | User net flow delta is positive. Deposited amount is pulled from the User and added to locked funds. | +| `WITHDRAW` | User net flow delta is negative. Withdrawn amount is released from locked funds to the User. | + +In all cases, the Node's fund delta is adjusted according to the Node net flow change. + +## Challenge Operation + +A challenge lets a participant dispute the current on-chain state by submitting a signed state and a separate challenger signature. + +The challenger signature is distinct from state signatures. It is produced by signing the enforcement representation of the candidate state with `"challenge"` appended to the signing data. Only the User or Node MAY act as challenger. A participant MAY share a valid challenger signature with a third party that initiates the challenge. + +Challenge process: + +1. The challenger submits a candidate state, state signatures, challenger signature, and challenger participant index. +2. The channel MUST NOT already be `challenged` or `closed`. +3. The candidate version MUST be greater than or equal to the current on-chain version. +4. If the candidate version is strictly greater, the blockchain layer validates and applies the new state. +5. The channel status becomes `challenged`, and challenge expiry is set to current time plus challenge duration. + +During the challenge period, any participant MAY respond with a valid state whose version is strictly greater than the challenged state. It is NOT possible to file another challenge while a channel is already challenged. + +After the challenge period expires without resolution, the challenged state becomes final. A separate close call is still required to release locked funds. + +## Close Operation + +Two close paths exist: + +| Path | Rule | +| --- | --- | +| Cooperative close | A participant submits a state with the `CLOSE` intent, signed by all participants. | +| Unilateral close | After challenge expiry, any party MAY call close without additional signatures. Funds are released according to the last enforced state's allocations. | + +It is not possible to close an already `closed` channel. In both close paths, locked funds are set to zero and the channel lifecycle ends. + +## Enforcement Validation + +The blockchain layer applies common validation rules when processing enforcement operations: + +1. The submitted state MUST reference the correct channel identifier. +2. The home ledger chain identifier MUST match the current blockchain. +3. The state version MUST be strictly greater than the currently recorded version. +4. All required signatures MUST be present and valid. +5. Approved signature validation modes MUST be respected. +6. The ledger invariant MUST hold: `UserAllocation + NodeAllocation == UserNetFlow + NodeNetFlow`. +7. Resulting locked funds MUST be non-negative. +8. Unless the channel is being closed, the sum of allocations MUST equal resulting locked funds. +9. The Node MUST have sufficient available vault funds when required to lock additional assets. + +## Escrow and Migration Enforcement + +Escrow and migration operations are part of the protocol model, but the current Nitronode public app-builder workflow does not implement them yet. Treat the enforcement details as protocol reference material, not as instructions for constructing escrow or migration states by hand. + +| Operation family | Enforcement note | +| --- | --- | +| Escrow deposit | Initiate and finalize states coordinate home-ledger accounting with a non-home-chain escrow record. | +| Escrow withdrawal | Initiate and finalize states coordinate home-ledger accounting with non-home-chain release. | +| Migration | Initiate and finalize states move the home-chain enforcement target while preserving normalized user allocation. | + +## Failure Conditions + +Enforcement MAY fail due to: + +- Invalid signatures. +- Stale state version. +- Inconsistent allocations. +- Allocation and locked-funds mismatch. +- Unknown channel, except during channel creation. +- Insufficient Node funds. +- Invalid intent. +- Chain mismatch. +- Incorrect channel status. diff --git a/docs/nitrolite/protocol/interaction-model.mdx b/docs/nitrolite/protocol/interaction-model.mdx new file mode 100644 index 0000000..f074696 --- /dev/null +++ b/docs/nitrolite/protocol/interaction-model.mdx @@ -0,0 +1,115 @@ +--- +title: "Interaction Model" +description: "Transport-independent message envelope, core operations, response correlation, error handling, and ordering." +--- + +# Interaction Model + +The interaction model defines the logical communication protocol between participants. Operations are semantic protocol operations and are independent of transport technologies. + +## Purpose + +Participants exchange protocol messages to advance state, manage channels, and coordinate operations. This page defines the structure and semantics of those messages. + +## Connection Assumptions + +The protocol assumes the communication channel: + +- delivers messages reliably, without silent loss. +- delivers messages in order between any two participants. +- supports bidirectional message exchange. + +The protocol does not require a specific transport technology. + +## Message Envelope + +All protocol messages share a common envelope: + +| Field | Description | +| --- | --- | +| `Type` | Message type: request, response, event, or error. | +| `RequestId` | Numeric identifier unique within the connection. | +| `Method` | Operation name identifying the requested action. | +| `Payload` | Type-specific message data. | +| `Timestamp` | Message creation time in milliseconds. | + +Messages are encoded as compact ordered arrays: + +```text +[Type, RequestId, Method, Payload, Timestamp] +``` + +## Message Types + +| Type | +| --- | +| Request | +| Successful response | +| Reserved event notification | +| Error response | + +:::note Events +Asynchronous event notifications are reserved for a future protocol revision. They will be defined alongside the relevant operations once specced. +::: + +## Core Operations + +| Operation | Direction | Description | +| --- | --- | --- | +| `RequestCreation` | User -> Node | Request to create a new channel. | +| `SubmitState` | User -> Node | Submit a signed state transition. | +| `GetLatestState` | User -> Node | Retrieve the current state for a channel. | +| `GetHomeChannel` | User -> Node | Retrieve on-chain home channel data. | +| `GetEscrowChannel` | User -> Node | Retrieve on-chain escrow channel data. | + +### RequestCreation + +Creates a new channel with an initial state. The request MUST include channel definition parameters, initial state, and the User's signature. The Node validates the channel definition, computes the channel identifier, verifies the User signature, co-signs the state, and stores the channel record. + +The response includes the Node's signature over the submitted state. + +### SubmitState + +Submits a User-signed state transition for processing. The request MUST include the signed state with a valid transition. The Node validates the state against advancement rules, verifies the User signature, co-signs the state, and applies side effects such as scheduling blockchain operations for non-`OPERATE` intents or creating receiver states for transfers. + +The response includes the Node's signature over the submitted state. + +### GetLatestState + +Retrieves the current state for a given User and asset. The response includes the latest state. Implementations MAY support filtering to return only mutually signed states. + +### GetHomeChannel + +Retrieves on-chain home channel data for a given User and asset. + +### GetEscrowChannel + +Retrieves on-chain escrow channel data for a given escrow channel identifier. + +## Correlation and Identifiers + +Responses are correlated with requests using `RequestId`. + +Rules: + +- Each request MUST include a `RequestId` unique within the connection. +- The corresponding response MUST include the same `RequestId`. + +## Error Handling + +Errors are communicated through error response messages. + +Rules: + +- Every failed operation MUST return an error response. +- The error payload MUST contain a human-readable error message. +- Errors MUST NOT expose internal implementation details. + +## Message Ordering + +Message ordering requirements MAY depend on implementation. These protocol-level constraints apply: + +- `RequestId` values MUST NOT be reused within a single connection. +- Future event notifications, once specified, MUST NOT block request processing. + +State update ordering is governed by channel state versioning and advancement rules, not by the message transport layer. diff --git a/docs/nitrolite/protocol/introduction.mdx b/docs/nitrolite/protocol/introduction.mdx new file mode 100644 index 0000000..ae43168 --- /dev/null +++ b/docs/nitrolite/protocol/introduction.mdx @@ -0,0 +1,83 @@ +--- +title: "Overview" +description: "Nitrolite protocol roles, layers, design goals, and version scope." +--- + +# Overview + +Nitrolite is a state channel protocol for high-speed off-chain interaction while preserving on-chain security guarantees. Users exchange signed state updates off-chain with Nodes, and any user can enforce the latest agreed state on the blockchain layer at any time. + +:::info Language-agnostic specification +This section describes protocol rules and invariants. It is not tied to a particular programming language, package, or transport implementation. +::: + +## Design Goals + +The protocol is designed to achieve: + +- **Off-chain scalability**: minimize on-chain transactions by moving state advancement off-chain. +- **Blockchain security guarantees**: allow any user to fall back to the blockchain layer to enforce the latest state. +- **Cross-chain asset interaction**: operate on assets across multiple blockchains through a unified model. +- **Extensibility**: support additional functionality through protocol extensions without modifying the core protocol. + +## System Roles + +| Role | Protocol responsibility | +| --- | --- | +| User | Opens channels, signs state updates, and holds assets within the protocol. | +| Node | Facilitates off-chain state advancement, manages channels, and syncs with the blockchain layer. | +| Blockchain | Validates enforceable incoming states, stores states, holds assets, and resolves disputes. | + +## High-Level Architecture + +The system operates in three conceptual layers: + +```mermaid +flowchart TB + protocol["Protocol layer: validity, advancement, enforcement rules"] + offchain["Off-chain layer: signed state update exchange"] + chain["Blockchain layer: asset custody and state enforcement"] + + protocol --> offchain + offchain --> chain + chain --> protocol +``` + +| Layer | Scope | +| --- | --- | +| Protocol layer | Rules for state validity, advancement, and enforcement. | +| Off-chain layer | Signed state update exchange with a Node. | +| Blockchain layer | Contracts that hold assets and enforce states. | + +## Core Concepts + +**Channels** are state containers shared between a User and a Node. Each channel holds user asset allocations and supports off-chain state updates. A channel is defined by immutable parameters including participants, asset, challenge duration, and approved signature validators. + +**States** represent the current agreed asset allocations and metadata shared between a User and a Node. Each state contains a home ledger, a non-home ledger, a version number, and the transition that produced it. + +**State advancement** is the off-chain process by which a User and a Node exchange signed state transitions. Each new state MUST have a version exactly one greater than the previous state. + +**State enforcement** is the process by which any party MAY submit the latest signed state to the blockchain layer. The blockchain layer validates signatures, version ordering, and ledger invariants before accepting a state. + +**Unified assets** represent the same asset across multiple blockchains. The protocol normalizes amounts by decimal precision when comparing allocations across chains. + +**Extensions** provide additional protocol functionality, such as application sessions. Extensions interact with channels through commit and release transitions. + +## Protocol Layers + +| Layer | Defines | +| --- | --- | +| Core Protocol | Channels, states, state advancement rules, and enforcement mechanisms. | +| Extension Layer | Additional functionality that interacts with the core protocol through defined interfaces. | +| Blockchain Layer | Channel creation, deposits, state enforcement, escrow operations, and fund release. | + +## Version Scope + +This documentation describes the current Nitrolite protocol. + +Compatibility expectations: + +- State structures and signing rules defined in this version are stable. +- Extension interfaces may evolve in future versions. +- Blockchain layer contracts are version-specific. +- Channel identifiers include the protocol version in their hashing function to prevent cross-version replay. diff --git a/docs/nitrolite/protocol/security-and-limitations.mdx b/docs/nitrolite/protocol/security-and-limitations.mdx new file mode 100644 index 0000000..bc692d9 --- /dev/null +++ b/docs/nitrolite/protocol/security-and-limitations.mdx @@ -0,0 +1,93 @@ +--- +title: "Security and Limitations" +description: "Nitrolite security goals, off-chain safety guarantees, enforcement guarantees, trust assumptions, known limitations, and future improvements." +--- + +# Security and Limitations + +This page describes the security guarantees of the Nitrolite protocol, its current trust assumptions, and known limitations. + +## Protocol Maturity + +The core protocol functionality is implemented and operational. A User MAY operate over a unified asset, deposit and withdraw on any supported blockchain, and conduct most interactions without direct blockchain involvement. + +The protocol protects against unauthorized state changes from the User side: no User can unilaterally alter state without valid signatures from all required participants. + +:::warning Current trust minimization status +The protocol is not fully trust-minimized in its current form. The primary remaining trust assumption concerns Node behavior and liquidity. +::: + +## Security Goals + +The protocol aims to guarantee: + +- **Asset safety**: participants MUST NOT lose assets without signing a state authorizing the change. +- **State finality**: the latest mutually signed state can always be enforced on-chain. +- **Non-repudiation**: a participant cannot deny having signed a state. +- **Censorship resistance**: any party MAY independently enforce state on the blockchain layer. + +## Off-Chain Safety + +The protocol protects against invalid or malicious state submissions through: + +| Mechanism | Rule | +| --- | --- | +| Signature requirements | Every state update requires valid signatures from all required participants. No participant can unilaterally change state. | +| Version ordering | State versions are strictly increasing. Old states cannot replace newer states. | +| Asset conservation | State transitions MUST preserve total asset amounts within each ledger. No assets can be created or destroyed through state updates. | +| Transition validation | Each state update MUST satisfy transition-specific rules. Invalid transitions are rejected. | + +## Enforcement Guarantees + +The blockchain layer provides: + +- Any party MAY submit the latest signed state at any time. +- Only states with valid signatures and a higher version than current on-chain state are accepted. +- After the challenge period, enforced state becomes final. +- Final state allocations determine asset distribution. + +## Node Liquidity and Cross-Chain Trust + +Each User channel is opened with a Node. Cross-chain functionality depends on the Node operator maintaining enough liquidity on each supported blockchain to satisfy off-chain state allocations. + +When a User with home chain A transfers assets to a User with home chain B, the Node receives the amount on chain A and allocates from its own balance to the recipient on chain B. If the recipient later enforces state on chain B and the Node lacks sufficient liquidity there, on-chain enforcement will fail. + +Single-chain operation can also depend on timing. During an off-chain transfer, the sender's funds are not returned to the Node's available vault immediately. They become available to the Node only after the sender's latest state is enforced on-chain, so temporary liquidity gaps can exist even when both users operate on the same chain. + +:::caution Cross-chain liquidity trust +In the current protocol version, users rely on the Node operator to maintain adequate liquidity across supported chains. Users cannot independently verify that this condition holds at all times. +::: + +## Current Trust Assumptions + +Participants currently rely on Nodes for: + +- **Liveness**: Nodes need to be online to facilitate off-chain state advancement. +- **Cross-chain liquidity**: Node operators need sufficient funds on each supported chain to honor off-chain allocations. +- **Cross-chain relay**: Nodes relay cross-chain state updates; trustless cross-chain enforcement is not yet implemented. +- **Timely enforcement**: Nodes are expected to submit state enforcement transactions when requested. +- **Off-chain funds integrity**: Until every state path is independently enforceable on-chain, participants trust the Node not to advance unsupported off-chain balances that do not correspond to locked funds. + +Participants do not need to trust Nodes for: + +- **Single-chain asset custody**: assets on the home chain can always be recovered through on-chain enforcement. +- **State validity**: invalid states are rejected by signature and validation rules. + +## Known Limitations + +The following capabilities are not yet implemented: + +- Trustless off-chain state operations through Node liquidity enforcement. +- Validator network for monitoring Node behavior and enforcing correctness. +- Watchtower services for automated enforcement. +- Non-EVM blockchain support. +- Formal verification of protocol rules. + +## Future Improvements + +The protocol roadmap includes: + +- **Validator network**: independent validation of off-chain state advancement and monitoring of on-chain actions. +- **Extension layer on-chain enforcement**: reducing reliance on Node liquidity trust for extension layer operations. +- **Non-EVM blockchain support**: redesigning the protocol to support blockchains beyond the EVM ecosystem in a future major protocol version. +- **Watchtower integration**: automated monitoring and enforcement on behalf of users. diff --git a/docs/nitrolite/protocol/state-and-ledger-model.mdx b/docs/nitrolite/protocol/state-and-ledger-model.mdx new file mode 100644 index 0000000..ff911f1 --- /dev/null +++ b/docs/nitrolite/protocol/state-and-ledger-model.mdx @@ -0,0 +1,158 @@ +--- +title: "State and Ledger Model" +description: "Nitrolite state structure, channel ledgers, off-chain and enforcement representations, intent mapping, and consistency rules." +--- + +# State and Ledger Model + +States represent the current agreed configuration of protocol entities. The state model defines what information a state contains, how states are identified and versioned, and how states are represented for off-chain and on-chain use. + +## Common State Fields + +All protocol states share: + +| Field | Description | +| --- | --- | +| `EntityId` | 32-byte unique identifier of the entity this state belongs to. | +| `Version` | 64-bit unsigned integer, monotonically increasing. | + +In addition to these common fields, each state contains entity-specific data defined by the respective entity specification. + +## State Identification and Versioning + +Each state is identified by the combination of its entity identifier and version number. + +Rules: + +- The entity identifier is derived from the entity definition and is immutable. +- The version MUST start at 1 for the initial state. +- Versions are strictly increasing. +- Off-chain state advancement requires each new version to be exactly the previous version plus one. +- On-chain enforcement requires only that the submitted version be strictly greater than the currently recorded on-chain version. + +## Channel State + +The channel state is the primary protocol state. It represents the current configuration of a channel. + +| Field | Description | +| --- | --- | +| `ChannelId` | 32-byte identifier derived from the channel definition. | +| `Metadata` | 32-byte hash of channel metadata. | +| `Version` | 64-bit unsigned integer state version. | +| `HomeLedger` | Asset allocations on the home chain. | +| `NonHomeLedger` | Asset allocations on the non-home chain. | +| `Transition` | Operation that produced this state. | +| `UserSig` | User signature for the state. | +| `NodeSig` | Node signature for the state. | + +The channel identifier encodes a protocol version byte as its first byte, followed by the hash of the channel definition parameters. + +## Ledger + +A ledger records asset allocations for a specific blockchain within a channel. Each channel state contains exactly two ledgers: a home ledger and a non-home ledger. + +| Field | Description | +| --- | --- | +| `ChainId` | Identifier of the blockchain this ledger is associated with. | +| `Token` | Token contract address on this chain. | +| `Decimals` | Decimal precision of the token on this chain. | +| `UserAllocation` | Amount allocated to the User. | +| `UserNetFlow` | Cumulative net flow for the User; MAY be negative. | +| `NodeAllocation` | Amount allocated to the Node. | +| `NodeNetFlow` | Cumulative net flow for the Node; MAY be negative. | + +The ledger invariant MUST hold at all times: + +```text +UserAllocation + NodeAllocation == UserNetFlow + NodeNetFlow +``` + +All allocation values MUST be non-negative. Net flow values MAY be negative, reflecting outbound transfers or withdrawals that exceed inbound flows. + +![State ledger advancement](/img/protocol/state-ledger-advancement.png) + +## Empty Non-Home Ledger + +When a channel state does not involve cross-chain operations, the non-home ledger MUST be empty. + +| Field | Empty value | +| --- | --- | +| `ChainId` | `0` | +| `Token` | Zero address (`0x0000...0000`) | +| `Decimals` | `0` | +| `UserAllocation` | `0` | +| `UserNetFlow` | `0` | +| `NodeAllocation` | `0` | +| `NodeNetFlow` | `0` | + +An empty non-home ledger is structurally present but zeroed. A non-home ledger with metadata, such as non-zero `ChainId` or `Token`, but zero balances is NOT considered empty. + +## Off-Chain Representation + +The off-chain representation is the primary operational format of a channel state. It is exchanged between participants during state advancement and is the representation that is signed. + +The off-chain representation contains all channel state fields directly, including full transition data: type, transaction identifier, account identifier, and amount. + +## Enforcement Representation + +The off-chain and enforcement representations depict the same logical state. The enforcement representation is derived deterministically from the off-chain representation. + +Preserved fields: + +- Version. +- Home and non-home ledger fields: `ChainId`, `Token`, `Decimals`, `UserAllocation`, `UserNetFlow`, `NodeAllocation`, `NodeNetFlow`. + +Derived fields: + +- **Intent**: derived from transition type through the intent mapping table. +- **MetadataHash**: Keccak-256 hash of ABI-encoded transition data: type, transaction identifier, account identifier, and amount. + +The enforcement representation is constructed as: + +```text +SignablePayload = AbiEncode(ChannelId, AbiEncode(Version, Intent, MetadataHash, HomeLedger, NonHomeLedger)) +``` + +Because this mapping is deterministic, signatures over off-chain state are valid for enforcement and vice versa. + +## Intent Mapping + +| On-chain intent | Protocol operation | Meaning | +| --- | --- | --- | +| `OPERATE` | `transfer_send` | Sender-side off-chain transfer. | +| `OPERATE` | `transfer_receive` | Receiver-side off-chain transfer. | +| `OPERATE` | `commit` | Move channel funds into an app session or extension. | +| `OPERATE` | `release` | Return app-session or extension funds to the channel. | +| `OPERATE` | acknowledgement | Acknowledge a Node-issued pending state. | +| `CLOSE` | `finalize` | Finalize and close the channel. | +| `DEPOSIT` | `home_deposit` | Deposit on the home chain. | +| `WITHDRAW` | `home_withdrawal` | Withdraw on the home chain. | +| `INITIATE_ESCROW_DEPOSIT` | `initiate_escrow_deposit` | Escrow deposit initiate. | +| `FINALIZE_ESCROW_DEPOSIT` | `finalize_escrow_deposit` | Escrow deposit finalize or complete. | +| `INITIATE_ESCROW_WITHDRAWAL` | `initiate_escrow_withdrawal` | Escrow withdrawal initiate. | +| `FINALIZE_ESCROW_WITHDRAWAL` | `finalize_escrow_withdrawal` | Escrow withdrawal finalize or complete. | +| `INITIATE_MIGRATION` | `initiate_migration` | Home-chain migration initiate. | +| `FINALIZE_MIGRATION` | `finalize_migration` | Home-chain migration finalize. | + +Transitions that map to `OPERATE` do not require on-chain enforcement under normal operation. + +Escrow and migration operations are included for protocol accuracy. The protocol defines them, but the current Nitronode public app-builder workflow does not implement them yet. + +## Transition Field + +Each state update includes a transition describing the operation that produced the new state. + +| Field | Description | +| --- | --- | +| `Type` | Transition type identifier. | +| `TxId` | Transaction identifier hash. | +| `AccountId` | Context-dependent account identifier; semantics vary by transition type. | +| `Amount` | Amount involved in the transition. | + +## State Consistency Rules + +State validity requirements differ between off-chain advancement and on-chain enforcement. In both contexts, these invariants MUST hold: + +- The entity identifier MUST match the entity definition. +- The version MUST be strictly greater than the previously accepted version. +- Ledger invariants MUST be satisfied: allocations equal net flows, and allocation values are non-negative. diff --git a/docs/nitrolite/protocol/terminology.mdx b/docs/nitrolite/protocol/terminology.mdx new file mode 100644 index 0000000..dd1bff0 --- /dev/null +++ b/docs/nitrolite/protocol/terminology.mdx @@ -0,0 +1,95 @@ +--- +title: "Terminology" +description: "Canonical Nitrolite protocol terms used throughout the App Layer specification." +--- + +# Terminology + +This page defines protocol terms used throughout the App Layer specification. Each term is defined once, and other pages in this section use these terms consistently. + +## Naming Conventions + +- Protocol entities use CamelCase, for example `ChannelState` and `AppSession`. +- Field names use CamelCase, for example `ChannelId` and `StateVersion`. +- Operations use lowercase with hyphens in document references, for example `state-advancement`. + +## Core Entities + +| Term | Definition | +| --- | --- | +| Channel | A state container shared between a User and a Node that allows off-chain state updates while maintaining on-chain security guarantees. Each channel operates on a single unified asset. | +| Channel Definition | The immutable parameters that define a channel: User, Node, asset, nonce, challenge duration, and approved signature validators. A channel definition is fixed at creation time and MUST NOT change during the channel lifecycle. | +| Channel State | The current agreed configuration of a channel, including home and non-home ledger allocations, a version number, and a transition field. | +| Participant | An entity that holds a signing key and participates in a channel. Each channel has exactly two participants: a User and a Node. | +| Asset | A representation of value identified by a human-readable symbol and decimal precision. Assets are identified independently of any specific blockchain. | + +## State Concepts + +| Term | Definition | +| --- | --- | +| State | An abstract data structure representing the current configuration of a protocol entity at a specific version. | +| State Version | A monotonically increasing integer that identifies the order of state updates. During off-chain advancement, each new state MUST have a version exactly one greater than the previous state. | +| State Advancement | The process of updating a protocol entity's state off-chain through signed transitions exchanged between participants. | +| State Enforcement | The process of submitting a signed state to the blockchain layer for on-chain validation and enforcement. | +| Transition | A typed operation that describes the reason and parameters for a state update. Each transition carries a type, transaction identifier, account identifier, and amount. | +| Intent | A value derived from the transition type that determines how the blockchain layer processes an enforced state. | + +## Cryptographic Concepts + +| Term | Definition | +| --- | --- | +| Signature | A cryptographic proof that a specific key holder authorized a specific message. The protocol uses ECDSA over secp256k1. | +| Signer | An entity capable of producing signatures. Each signer is associated with a specific key. | +| Session Key | A delegated signing key authorized by a participant's primary key to sign state updates on their behalf within an authorization scope. | +| Signature Validation Mode | A mechanism that determines how a signature is verified. The protocol currently defines default mode (`0x00`) and session key mode (`0x01`). | + +:::warning Session key authority +Session key authorization MUST be associated with the same address as the channel's User or Node participant. Participants SHOULD treat session-key compromise as equivalent to control of the participant's authority within the scope they issued, for the duration of the validity window. +::: + +## Ledger Concepts + +| Term | Definition | +| --- | --- | +| Ledger | A record of asset allocations within a channel, associated with a specific blockchain. Each ledger tracks User and Node allocations and net flows, and MUST satisfy the allocation/net-flow invariant. | +| Home Ledger | The primary ledger of a channel state, associated with the blockchain where the state is enforced. | +| Non-Home Ledger | A secondary ledger tracking asset allocations on a blockchain other than the home chain. Used for cross-chain escrow operations and migrations. | +| Home Chain | The blockchain identified by the home ledger's chain identifier. The home chain determines where enforcement operations are executed and MAY change through migration. | +| Locked Funds | The total assets held by the blockchain enforcement contract on behalf of a channel. Unless the channel is being closed, `UserAllocation + NodeAllocation` MUST equal the locked funds. | +| Vault | A pool of available funds maintained by the Node on a specific blockchain, separate from any specific channel. | +| WAD Normalization | Scaling chain-specific asset amounts to the asset's configured decimal precision for exact, lossless cross-chain comparisons. | + +WAD normalization is: + +```text +NormalizedAmount = Amount * 10^(18 - ChainDecimals) +``` + +Each unified asset defines a canonical decimal precision used during protocol interactions. The maximum supported decimal precision is 18. + +## State Signing Categories + +| Term | Definition | +| --- | --- | +| Mutually Signed State | A state that carries valid signatures from both the User and the Node. Only mutually signed states are enforceable on-chain. | +| Node-Issued Pending State | A state produced by the Node that carries only the Node's signature. A pending state is NOT enforceable on-chain and becomes mutually signed only after the User acknowledges it. | +| Channel Status | A specific on-chain channel data configuration throughout the lifecycle. The current RPC status surface is `void`, `open`, `challenged`, `closing`, and `closed`; `closing` means the Node has issued the finalization state and the channel is in the close path. | +| Escrow Channel Identifier | A 32-byte hash derived deterministically from the home channel identifier and state version. It uniquely identifies each escrow operation. | + +## Protocol Operations + +| Operation | Definition | +| --- | --- | +| State Enforcement | Submitting a signed state to the blockchain layer so the latest agreed state is recorded on-chain. SDK or contract surfaces may expose a narrower `checkpoint` entry point for specific enforcement paths. | +| Challenge | An on-chain operation where a participant disputes the current enforced state by submitting a signed state and challenger signature. | +| Commit | Moving assets from a channel into an extension, such as an application session. | +| Release | Returning assets from an extension back to channel allocations. | +| Escrow | A two-phase mechanism for cross-chain operations: initiate locks funds, finalize releases them upon cooperative completion or after a timeout. | + +## Extension Concepts + +| Term | Definition | +| --- | --- | +| Extension | An additional protocol module that provides functionality beyond the core channel protocol. | +| Application Session | An extension that enables off-chain application functionality. Application sessions hold committed assets and maintain their own state. | +| Application State | The state associated with an application session, tracking committed assets and application-specific data. | diff --git a/docs/protocol/app-layer/_category_.json b/docs/protocol/app-layer/_category_.json deleted file mode 100644 index bcc7d72..0000000 --- a/docs/protocol/app-layer/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "App Layer (VirtualApp)", - "position": 5, - "collapsible": false, - "collapsed": false -} diff --git a/docs/protocol/app-layer/off-chain/_category_.json b/docs/protocol/app-layer/off-chain/_category_.json deleted file mode 100644 index a6fb890..0000000 --- a/docs/protocol/app-layer/off-chain/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Off-Chain RPC Protocol", - "position": 2, - "collapsible": false, - "collapsed": false -} diff --git a/docs/protocol/app-layer/off-chain/app-sessions.mdx b/docs/protocol/app-layer/off-chain/app-sessions.mdx deleted file mode 100644 index 4180deb..0000000 --- a/docs/protocol/app-layer/off-chain/app-sessions.mdx +++ /dev/null @@ -1,761 +0,0 @@ ---- -sidebar_position: 6 -title: App Session Methods ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# App Session Methods - -App sessions enable multi-party applications with custom governance rules, allowing complex interactions on top of payment channels. - ---- - -## Overview - -App sessions are off-chain channels built on top of the unified balance, intended for app developers to create application-specific interactions. They act as a "box" or shared account where multiple participants can transfer funds and execute custom logic with governance rules. - -### Key Features - -**Multi-Party Governance**: Define custom voting weights and quorum rules for state updates. - -**Application-Specific State**: Store arbitrary application data (game state, escrow conditions, etc.). - -**Flexible Fund Management**: Transfer, redistribute, add, or withdraw funds during session lifecycle. - -**Instant Updates**: All state changes happen off-chain with zero gas fees. - -:::info For App Developers -App sessions are specifically designed for app developers building trustless multi-party applications like games, prediction markets, escrow, and collaborative finance. -::: - ---- - -## Protocol Versions - -App sessions support multiple protocol versions for backward compatibility. - -### Version Comparison - -| Feature | NitroRPC/0.2 (Legacy) | NitroRPC/0.4 (Current) | -|---------|----------------------|------------------------| -| **State Updates** | Basic only | Intent-based (OPERATE, DEPOSIT, WITHDRAW) | -| **Add Funds to Active Session** | ❌ No | ✅ Yes (DEPOSIT intent) | -| **Remove Funds from Active Session** | ❌ No | ✅ Yes (WITHDRAW intent) | -| **Fund Redistribution** | ✅ Yes | ✅ Yes (OPERATE intent) | -| **Error Handling** | Basic | Enhanced validation | -| **Modify Total Funds** | Must close & recreate | Can update during session | -| **Recommended For** | Legacy support only | All new implementations | - -:::caution Protocol Version Selection -The protocol version is specified in the app definition during creation and **cannot be changed** for an existing session. Always use **NitroRPC/0.4** for new app sessions. -::: - ---- - -## create_app_session - -### Name - -`create_app_session` - -### Usage - -Creates a new virtual application session on top of the unified balance. An app session is a "box" or shared account where multiple participants can transfer funds and execute application-specific logic with custom governance rules. The app definition specifies participants, their voting weights, quorum requirements for state updates, and the protocol version. Funds are transferred from participants' unified balance accounts to a dedicated App Session Account for the duration of the session. App sessions enable complex multi-party applications like games, prediction markets, escrow, and collaborative finance—all operating off-chain with instant state updates and zero gas fees. - -### When to Use - -When multiple participants need to interact with shared funds and application state in a trustless manner. Examples include turn-based games, betting pools, escrow arrangements, multi-signature treasuries, prediction markets, and any application requiring multi-signature state management. - -### Prerequisites - -- All participants with non-zero initial allocations must be [authenticated](./authentication) -- All such participants must have sufficient available balance -- All such participants must sign the creation request -- Protocol version must be supported (NitroRPC/0.2 or NitroRPC/0.4) - -### Request - -:::tip Quick Reference -Common structures: [AppDefinition](#appdefinition) • [Allocation](#allocation) -::: - -| Parameter | Type | Required | Description | See Also | -|-----------|------|----------|-------------|----------| -| `definition` | AppDefinition | Yes | Configuration defining the app session rules and participants | [↓ Structure](#appdefinition) | -| `allocations` | Allocation[] | Yes | Initial funds to transfer from participants' unified balance accounts | [↓ Structure](#allocation) | -| `session_data` | string | No | Application-specific initial state (JSON string, max 64KB recommended)
This is application-specific; protocol doesn't validate content | — | - -#### Session Identifier - -`app_session_id` is derived deterministically from the entire App definition: - -```javascript -appSessionId = keccak256(JSON.stringify({ - application: "...", - protocol: "NitroRPC/0.4", - participants: [...], - weights: [...], - quorum: 100, - challenge: 86400, - nonce: 123456 -})) -``` - -- Includes `application`, `protocol`, `participants`, `weights`, `quorum`, `challenge`, and `nonce` -- Does **not** include `chainId` because sessions live entirely off-chain -- Client can recompute locally to verify clearnode responses -- `nonce` uniqueness is critical: same definition ⇒ same ID - -Implementation reference: `clearnode/app_session_service.go`. - -#### AppDefinition - -| Field | Type | Required | Description | Default | Allowed Values | Notes | -|-------|------|----------|-------------|---------|----------------|-------| -| `protocol` | string | Yes | Protocol version for this app session | — | `"NitroRPC/0.2"` \| `"NitroRPC/0.4"` | Version cannot be changed after creation; use 0.4 for new sessions | -| `participants` | address[] | Yes | Array of all participant wallet addresses | — | Min: 2 participants | Order is important - indices used for signatures and weights
Last participant often represents the application/judge | -| `weights` | int64[] | Yes | Voting power for each participant | — | — | Length must match participants array
Order corresponds to participants array
Absolute values matter for quorum; don't need to sum to 100 | -| `quorum` | uint64 | Yes | Minimum total weight required to approve state updates | — | — | Sum of signers' weights must be ≥ quorum | -| `challenge` | uint64 | No | Challenge period in seconds for disputes | 86400 (24 hours) | — | Only relevant if app session state is ever checkpointed on-chain | -| `nonce` | uint64 | Yes | Unique identifier | — | — | Typically timestamp; ensures uniqueness | - -**Example**: -```json -{ - "protocol": "NitroRPC/0.4", - "participants": ["0x742d35Cc...", "0x8B3192f2...", "0x456789ab..."], - "weights": [50, 50, 100], - "quorum": 100, - "challenge": 3600, - "nonce": 1699123456789 -} -``` - -#### Allocation - -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `participant` | address | Yes | Participant wallet address (must be in `definition.participants`) | -| `asset` | string | Yes | Asset identifier (e.g., `"usdc"`) | -| `amount` | string | Yes | Amount in human-readable format (e.g., `"100.0"`) | - -**Example**: -```json -[ - {"participant": "0x742d35Cc...", "asset": "usdc", "amount": "100.0"}, - {"participant": "0x8B3192f2...", "asset": "usdc", "amount": "100.0"}, - {"participant": "0x456789ab...", "asset": "usdc", "amount": "0.0"} - ] -``` - -**Note**: Participants with zero allocation don't need to sign creation. - -### Response - -| Parameter | Type | Description | Format/Structure | Example | Notes | -|-----------|------|-------------|------------------|---------|-------| -| `app_session_id` | string | Unique identifier for the created app session | 0x-prefixed hex string (32 bytes) | `"0x9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba"` | Use this for all subsequent operations on this session | -| `status` | string | App session status | `"open"` | `"open"` | Values: `"open"` or `"closed"` | -| `version` | number | Current state version | `1` | `1` | Always starts at 1 | - -The Go service returns only these fields on creation. To fetch full metadata (application, participants, quorum, weights, session_data, protocol, challenge, nonce, timestamps), call [`get_app_sessions`](./queries#get_app_sessions) after creation. - ---- - -## Governance Models - -App sessions support flexible governance through custom weights and quorum configurations. - -### Example 1: Simple Two-Player Game - -``` -Participants: [Alice, Bob] -Weights: [1, 1] -Quorum: 2 - -Result: Both players must sign every state update -Use case: Chess, poker, betting between two parties -``` - -**Governance**: Cooperative - both parties must agree to all changes. - -### Example 2: Game with Judge - -``` -Participants: [Alice, Bob, Judge] -Weights: [0, 0, 100] -Quorum: 100 - -Result: Only judge can update state -Use case: Games where application determines outcome -``` - -**Governance**: Authoritative - application/judge has full control. - -### Example 3: Multi-Party Escrow - -``` -Participants: [Buyer, Seller, Arbiter] -Weights: [40, 40, 50] -Quorum: 80 - -Result: Any 2 parties can approve - - Buyer + Seller (80) - - Buyer + Arbiter (90) - - Seller + Arbiter (90) -Use case: Escrowed transactions with dispute resolution -``` - -**Governance**: Flexible 2-of-3 - any two can proceed, preventing single-party blocking. - -### Example 4: Weighted Multi-Party Voting - -``` -Participants: [User1, User2, User3, User4, Contract] -Weights: [20, 25, 30, 25, 0] -Quorum: 51 - -Result: Majority of weighted votes required (51 out of 100) -Use case: Collaborative funds management -``` - -**Governance**: Weighted majority - decisions require majority approval by weight. - -### Example 5: Watch Tower - -``` -Participants: [Alice, Bob, WatchTower] -Weights: [40, 40, 100] -Quorum: 80 - -Result: - - Normal operation: Alice + Bob (80) - - Emergency: WatchTower alone (100) -Use case: Automated monitoring and intervention -``` - -**Governance**: Dual-mode - normal requires cooperation, emergency allows automated action. - -:::tip Governance Flexibility -By adjusting weights and quorum, you can implement any governance model from fully cooperative (all must sign) to fully authoritative (single party controls) to complex weighted voting systems. -::: - ---- - -## Fund Transfer Mechanics - -When an app session is created, funds are transferred from the unified balance account to a dedicated App Session Account: - -```mermaid -graph TB - A["Alice's Unified Account
Balance: 200 USDC"] - - B["Create App Session
Alice transfers 100 USDC"] - - C["Alice's Unified Account
Balance: 100 USDC"] - - D["App Session Account
Balance: 100 USDC
(Beneficiary: Alice)"] - - A -->|create_app_session| B - B --> C - B --> D - - style A fill:#e1f5ff - style B fill:#fff5e1 - style C fill:#e1ffe1 - style D fill:#ffe1f5 -``` - -**Balance State Changes**: - -``` -Before Creation: - Alice's Unified Account: - Balance: 200 USDC - -After Creating Session with 100 USDC: - Alice's Unified Account: - Balance: 100 USDC - - App Session Account: - Balance: 100 USDC (Beneficiary: Alice) -``` - -### Signature Requirements - -All participants with non-zero initial allocations MUST sign the create_app_session request. The clearnode validates that: - -1. All required signatures are present -2. Signatures are valid for respective participants -3. Total weight of signers >= quorum (must be met for creation) - ---- - -## submit_app_state - -### Name - -`submit_app_state` - -### Usage - -Submits a state update for an active app session. State updates can redistribute funds between participants (OPERATE intent), add funds to the session (DEPOSIT intent), or remove funds from the session (WITHDRAW intent). The intent system is only available in NitroRPC/0.4; version 0.2 sessions only support fund redistribution without explicit intent. Each state update increments the version number, and must be signed by participants whose combined weights meet the quorum requirement. The allocations field always represents the FINAL state after the operation, not the delta. - -### When to Use - -During app session lifecycle to update the state based on application logic. Examples include recording game moves, updating scores, reallocating funds based on outcomes, adding stakes, or partially withdrawing winnings. - -### Prerequisites - -- App session must exist and be in "open" status -- Signers must meet quorum requirement -- For DEPOSIT intent: Depositing participant must sign (in addition to quorum) -- For DEPOSIT intent: Depositing participant must have sufficient available balance -- For WITHDRAW intent: Session must have sufficient funds to withdraw -- NitroRPC/0.4: `version` must be **exactly current_version + 1** -- NitroRPC/0.2: **omit** `intent` and `version` (service rejects them); only OPERATE-style redistribution is supported -- If using a session key, spending allowances for that key are enforced - -### Request - -| Parameter | Type | Required | Description | Format | Example | Notes / See Also | -|-----------|------|----------|-------------|--------|---------|------------------| -| `app_session_id` | string | Yes | Identifier of the app session to update | 0x-prefixed hex string (32 bytes) | `"0x9876543210fedcba..."` | - | -| `intent` | string | Yes for v0.4, No for v0.2 | Type of operation (NitroRPC/0.4 only) | Allowed: `"operate"` \| `"deposit"` \| `"withdraw"` | `"operate"` | Omit for NitroRPC/0.2 sessions (treated as operate) | -| `version` | number | Yes | Expected next version number | - | `2` | Must be exactly currentVersion + 1; prevents conflicts | -| `allocations` | Allocation[] | Yes | **FINAL allocation state after this update**

⚠️ **IMPORTANT**: This is the target state, NOT the delta | See [Allocation](#allocation) above | After operate from [100, 100] where Alice loses 25 to Bob:
`[{"participant": "0xAlice", "asset": "usdc", "amount": "75.0"}, {"participant": "0xBob", "asset": "usdc", "amount": "125.0"}]` | Clearnode validates based on intent rules (see below) | -| `session_data` | string | No | Updated application-specific state | JSON string | `"{\"currentMove\":\"e2e4\",\"turn\":\"black\"}"` | Can be updated independently of allocations | - -### Response - -| Parameter | Type | Description | Format/Structure | Example | Notes | -|-----------|------|-------------|------------------|---------|-------| -| `app_session_id` | string | Session identifier (echoed) | - | - | - | -| `version` | number | Confirmed new version number | - | `2` | - | -| `status` | string | Updated session status | `"open"` | `"open"` | Minimal response (no metadata echoed) | - -The Go handler returns an `AppSessionResponse` type, but for state submissions it only includes `app_session_id`, `version`, and `status` (and does not echo session metadata). Use [`get_app_sessions`](./queries#get_app_sessions) to read the full session record. - ---- - -## Intent System (NitroRPC/0.4) - -The intent system defines the type of operation being performed. Each intent has specific validation rules. - -### Intent: OPERATE (Redistribute Existing Funds) - -**Purpose**: Move funds between participants without changing total amount in session. - -**Rules**: -- Sum of allocations MUST equal sum before operation -- No funds added or removed from session -- Quorum requirement MUST be met -- Depositing participant signature NOT required - -**Example**: - -``` -Current state (version 1): - Alice: 100 USDC - Bob: 100 USDC - Total: 200 USDC - -Update (version 2, intent: "operate"): - Allocations: [ - {"participant": "0xAlice", "asset": "usdc", "amount": "75.0"}, - {"participant": "0xBob", "asset": "usdc", "amount": "125.0"} - ] - -Result: - Alice: 75 USDC (-25) - Bob: 125 USDC (+25) - Total: 200 USDC (unchanged) ✓ - -Validation: Sum before (200) == Sum after (200) ✓ -``` - -**Use Cases**: -- Record game outcome (winner gets opponent's stake) -- Update prediction market positions -- Rebalance shared pool -- Penalize or reward participants - -:::tip OPERATE Intent -Use OPERATE for simple fund redistributions within the session. The total amount remains constant—funds just move between participants. -::: - ---- - -### Intent: DEPOSIT (Add Funds to Session) - -**Purpose**: Add funds from a participant's unified balance into the session. - -**Rules**: -- Sum of allocations MUST be greater or equal to sum before operation -- Increase MUST come from available balance of depositing participant -- Depositing participant MUST sign (even if quorum is met without them) -- Quorum requirement MUST still be met -- Allocations show FINAL amounts (not delta) -- If signed via a session key, spending caps for that key are enforced - -**Example**: - -``` -Current state (version 1): - Alice: 100 USDC - Bob: 100 USDC - Total: 200 USDC - -Alice's Unified Balance: - Available: 50 USDC - -Update (version 2, intent: "deposit"): - Allocations: [ - {"participant": "0xAlice", "asset": "usdc", "amount": "150.0"}, - {"participant": "0xBob", "asset": "usdc", "amount": "100.0"} - ] - Signatures: [AliceSig, QuorumSigs...] - -Calculation: - Alice deposit amount = 150 (new) - 100 (old) = 50 USDC - -Result: - Alice: 150 USDC (100 + 50 deposited) - Bob: 100 USDC (unchanged) - Total: 250 USDC (+50) ✓ - -Alice's Unified Balance After: - Available: 0 USDC (50 transferred to App Session Account) - -App Session Account After: - Balance: 250 USDC (increased by 50) - -Validation: - - Sum after (250) > Sum before (200) ✓ - - Alice signed ✓ - - Alice had 50 available ✓ -``` - -**Use Cases**: -- Top up game stake mid-game -- Add collateral to escrow -- Increase position in prediction market -- Buy into ongoing game - -:::caution DEPOSIT Intent -**Critical Understanding**: The allocations array shows FINAL amounts, not the deposit amount. The clearnode calculates the deposit by comparing previous and new allocations for each participant. -::: - ---- - -### Intent: WITHDRAW (Remove Funds from Session) - -**Purpose**: Remove funds from session back to a participant's unified balance. - -**Rules**: -- Sum of allocations MUST be less or equal to sum before operation -- Decrease is returned to participant's available balance -- Withdrawing participant signature NOT specifically required (quorum sufficient) -- Quorum requirement MUST be met -- Allocations show FINAL amounts (not delta) - -**Example**: - -``` -Current state (version 1): - Alice: 150 USDC - Bob: 100 USDC - Total: 250 USDC - -Update (version 2, intent: "withdraw"): - Allocations: [ - {"participant": "0xAlice", "asset": "usdc", "amount": "150.0"}, - {"participant": "0xBob", "asset": "usdc", "amount": "75.0"} - ] - Signatures: [QuorumSigs...] - -Calculation: - Bob withdrawal amount = 100 (old) - 75 (new) = 25 USDC - -Result: - Alice: 150 USDC (unchanged) - Bob: 75 USDC (100 - 25 withdrawn) - Total: 225 USDC (-25) ✓ - -Bob's Unified Balance After: - Available: +25 USDC - -App Session Account After: - Balance: 225 USDC (decreased by 25) - -Validation: - - Sum after (225) < Sum before (250) ✓ - - Quorum met ✓ -``` - -**Use Cases**: -- Cash out partial winnings mid-game -- Remove collateral when no longer needed -- Withdraw partial winnings from a shared session -- Reduce wager in ongoing game - ---- - -## Version Management - -- NitroRPC/0.4: each update MUST be exactly `previous_version + 1`, or it is rejected. -- NitroRPC/0.2: omit `intent` and `version`; providing either results in `"incorrect request: specified parameters are not supported in this protocol"`. - ---- - -## Quorum Validation - -For every update, the clearnode validates quorum: - -```mermaid -graph TD - A[Receive State Update] --> B{Calculate Total Weight} - B --> C[Sum weights of all signers] - C --> D{Total Weight >= Quorum?} - D -->|Yes| E[✓ Update Accepted] - D -->|No| F[✗ Reject: Quorum Not Met] - - style A fill:#e1f5ff - style E fill:#e1ffe1 - style F fill:#ffe1e1 -``` - -**Validation Logic**: - -``` -totalWeight = sum of weights for all signers -if (totalWeight >= definition.quorum) { - ✓ Update accepted -} else { - ✗ Reject: "Quorum not met" -} -``` - -**Example** (using Game with Judge scenario): - -``` -Participants: [Alice, Bob, Judge] -Weights: [0, 0, 100] -Quorum: 100 - -Valid signature combinations: - - Judge alone: weight = 100 >= 100 ✓ - - Alice + Bob: weight = 0 >= 100 ✗ - - Alice + Bob + Judge: weight = 100 >= 100 ✓ -``` - ---- - -## close_app_session - -### Name - -`close_app_session` - -### Usage - -Closes an active app session and distributes all funds from the App Session Account according to the final allocations. Once closed, the app session cannot be reopened; participants must create a new session if they want to continue. The final allocations determine how funds are returned to each participant's unified balance account. Closing requires quorum signatures. The final session_data can record the outcome or final state of the application. All funds in the App Session Account are released immediately. - -### When to Use - -When application logic has completed and participants want to finalize the outcome and retrieve their funds. Examples include game ending, escrow condition met, prediction market settled, or any application reaching its natural conclusion. - -### Prerequisites - -- App session must exist and be in "open" status -- Signers must meet quorum requirement -- Final allocations must not exceed total funds in session -- Sum of final allocations must equal total session funds - -### Request - -| Parameter | Type | Required | Description | Format/Structure | Example | Notes | -|-----------|------|----------|-------------|------------------|---------|-------| -| `app_session_id` | string | Yes | Identifier of the app session to close | 0x-prefixed hex string (32 bytes) | `"0x9876543210fedcba..."` | - | -| `allocations` | Allocation[] | Yes | Final distribution of all funds in the session

**IMPORTANT**: Must account for ALL funds; sum must equal session total

**Structure (per allocation)**:
• `participant` (address) - Participant wallet address
• `asset` (string) - Asset identifier
• `amount` (string) - Final amount for this participant | See structure | 200 USDC total, winner takes most:
`[{"participant": "0xAlice", "asset": "usdc", "amount": "180.0"}, {"participant": "0xBob", "asset": "usdc", "amount": "15.0"}, {"participant": "0xJudge", "asset": "usdc", "amount": "5.0"}]` | Can allocate zero to participants (they get nothing) | -| `session_data` | string | No | Final application state or outcome record | JSON string | `"{\"result\":\"Alice wins\",\"finalScore\":\"3-1\"}"` | Useful for recording outcome for history/analytics | - -### Response - -| Parameter | Type | Description | Format/Structure | Example | Notes | -|-----------|------|-------------|------------------|---------|-------| -| `app_session_id` | string | Session identifier (echoed) | - | - | - | -| `status` | string | Final status | Value: "closed" | `"closed"` | Minimal response | -| `version` | number | New session version | - | `2` | Incremented on close | - -:::note close_app_session response -The handler returns an `AppSessionResponse` type in Go, but on close it only populates `app_session_id`, `status`, and `version`. For full metadata after closure, query [`get_app_sessions`](./queries#get_app_sessions). -::: ---- - -## Fund Distribution on Closure - -When an app session closes, funds return to participants' unified balances: - -``` -Before Closure: - Alice's Unified Account: - Balance: 100 USDC - -App Session Account 0x98765: - Alice: 100 USDC - Bob: 100 USDC - Total: 200 USDC - -Close with final allocations: - Alice: 180 USDC - Bob: 20 USDC - -After Closure: - Alice's Unified Account: - Balance: 280 USDC (100 + 180 received from session) - - Bob's Unified Account: - Balance: 20 USDC (received from session) - - App Session Account 0x98765: - Closed (Balance: 0 USDC) -``` - -### Allocation Rules - -1. **Must Sum to Total**: - - `sum(final_allocations) MUST equal sum(current_allocations)` - - Clearnode validates this; cannot create or destroy funds during close - -2. **Can Be Zero**: - - Participants can receive zero in final allocation (lost everything) - - Example: Losing player in a winner-takes-all game - -3. **Accounting for Participants**: - - It is recommended to include an entry for every participant (use zero for losers). - - If you omit a participant, the service treats them as receiving zero, as long as per-asset totals still match the session balance. - -4. **Can Include Non-Financial Participants**: - - Example: Judge/application can receive commission - - `{"participant": "0xJudge", "asset": "usdc", "amount": "5.0"}` - ---- - -## Closure Examples - -### Example 1: Chess Game - -``` -Initial: - White: 100 USDC - Black: 100 USDC - Judge: 0 USDC - Total: 200 USDC - -Final (White wins): - White: 190 USDC (won 90) - Black: 0 USDC (lost 100) - Judge: 10 USDC (5% commission) - Total: 200 USDC ✓ -``` - -### Example 2: Escrow (Buyer Satisfied) - -``` -Initial: - Buyer: 100 USDC - Seller: 0 USDC - Arbiter: 0 USDC - Total: 100 USDC - -Final (Successful delivery): - Buyer: 0 USDC - Seller: 99 USDC (payment) - Arbiter: 1 USDC (fee) - Total: 100 USDC ✓ -``` - -### Example 3: Escrow (Dispute, Buyer Refunded) - -``` -Initial: - Buyer: 100 USDC - Seller: 0 USDC - Arbiter: 0 USDC - Total: 100 USDC - -Final (Arbiter ruled for buyer): - Buyer: 95 USDC (refund minus fee) - Seller: 0 USDC - Arbiter: 5 USDC (dispute fee) - Total: 100 USDC ✓ -``` - -### Example 4: Prediction Market - -``` -Initial: - User1: 50 USDC (bet YES) - User2: 50 USDC (bet YES) - User3: 40 USDC (bet NO) - Oracle: 0 USDC - Total: 140 USDC - -Final (Outcome: YES): - User1: 68.25 USDC (split pot proportionally) - User2: 68.25 USDC - User3: 0 USDC (lost) - Oracle: 3.50 USDC (2.5% fee) - Total: 140 USDC ✓ -``` - -:::success Final Distribution -All participants receive funds according to the final allocations, whether they won, lost, or served as neutral parties (judges, arbiters, oracles). The total is always preserved. -::: - ---- - -{/* TODO: Document actual error codes from implementation. Currently removed as placeholder errors were inaccurate. */} - ---- - -## Implementation Notes - -**State Management**: -- Always use `intent: "operate"` for simple redistributions -- Always specify FINAL allocations, never deltas -- The clearnode computes deltas internally by comparing with previous state -- Version numbers must be strictly sequential -- The session_data field can be updated in any intent - -**Performance**: -- Updates are instant (< 1 second) and off-chain -- Zero gas fees for all operations -- All updates are logged for audit trail - -**Notifications**: -- Participants are notified on all active connections of state changes -- Closed sessions remain queryable for history - -**Irreversibility**: -- Closure is instant and atomic -- All funds released simultaneously -- Once closed, cannot be reopened -- To continue, create a new session - ---- - -## Next Steps - -Explore other protocol features: - -- **[Queries & Notifications](./queries)** - Query session history and receive real-time updates -- **[Transfers](./transfers)** - Move funds between unified balances -- **[Channel Methods](./channel-methods)** - Manage underlying payment channels - -For foundational concepts: -- **[Message Format](./message-format)** - Understand request/response structure -- **[Authentication](./authentication)** - Manage session keys and security diff --git a/docs/protocol/app-layer/off-chain/authentication.mdx b/docs/protocol/app-layer/off-chain/authentication.mdx deleted file mode 100644 index 09da4d4..0000000 --- a/docs/protocol/app-layer/off-chain/authentication.mdx +++ /dev/null @@ -1,467 +0,0 @@ ---- -sidebar_position: 3 -title: Authentication ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Authentication - -Authentication with Clearnode can be done in two ways: using your **main wallet as a root signer** for all requests, or delegating to session keys via a secure 3-step challenge-response protocol. - ---- - -## Overview - -There are two authentication approaches: - -1. **Main Wallet (Root Signer)**: Sign every request with your main wallet. Simple but requires user interaction for each operation. - -2. **Session Keys (Delegated)**: Establish an authenticated session once, then use a session key for subsequent operations without repeatedly prompting the main wallet. - -:::info Main Wallet as Root Signer -You can **skip the session key flow entirely** and use your main wallet to sign all requests. This provides maximum security but requires wallet interaction for every operation. Simply sign each request with your main wallet's private key instead of creating a session key. -::: - -### Why Session Keys? - -Session keys provide **flexible security management**: - -- **Granular Permissions**: Specify which operations the session key can perform -- **Spending Allowances**: Set maximum spending limits per asset -- **Time-Bounded**: Automatic expiration reduces risk of key compromise -- **Application-Scoped**: Different keys for different apps -- **User Experience**: No repeated wallet prompts during active session - -:::success Flexible Security Management -Session keys give users a flexible way to manage security of their funds by providing specific permissions and allowances for specific apps, balancing convenience with security. -::: - -### Choosing Your Approach - -| Aspect | Main Wallet (Root Signer) | Session Keys (Delegated) | -|--------|---------------------------|--------------------------| -| **Setup** | None - use immediately | One-time 3-step flow | -| **UX** | Wallet prompt for every operation | Sign once, use for duration | -| **Security** | Maximum - full control always | Balanced - limited by allowances | -| **Use Case** | Single operations, high-value transactions | Interactive apps, frequent operations | -| **Revocation** | Not needed | Can be revoked anytime | -| **Best For** | One-time actions, security-critical operations | Gaming, trading bots, dApps with frequent interactions | - -:::tip When to Use Each -- **Use Main Wallet**: For single channel creation, large transfers, or when maximum security is required -- **Use Session Keys**: For interactive applications, gaming, automated operations, or when user experience matters -::: - -### Session Key Authentication Flow - -The 3-step process ensures both security and usability: - -```mermaid -sequenceDiagram - box rgb(255,225,225) User Wallet - participant UW as User Wallet - end - box rgb(225,245,255) Client - participant Client - end - box rgb(255,225,245) Clearnode - participant Clearnode - end - - Note over Client: Step 1: Register Session Key - Client->>Client: Generate session keypair (locally) - Client->>Client: Prepare auth parameters (address, session_key, application, allowances, expires_at) - Client->>Clearnode: auth_request (public endpoint, no signature) - - Note over Clearnode: Step 2: Challenge - Clearnode->>Clearnode: Validate parameters - Clearnode->>Clearnode: Generate challenge UUID - Clearnode->>Client: auth_challenge (challenge_message) - - Note over Client: Step 3: Verify Session Key - Client->>Client: Create EIP-712 typed data with challenge - Client->>UW: Request EIP-712 signature - UW-->>Client: Sign with main wallet - Client->>Clearnode: auth_verify (EIP-712 signature by main wallet) - - Note over Clearnode: Complete - Clearnode->>Clearnode: Recover address from EIP-712 signature - Clearnode->>Clearnode: Validate signature matches main wallet - Clearnode->>Clearnode: Create session (with allowances) - Clearnode->>Clearnode: Generate JWT token - Clearnode->>Client: Session established (address, session_key, jwt_token, success) - - Note over Client,Clearnode: All subsequent requests signed with session key - -``` - -:::info Challenge-Response Pattern -This pattern ensures that: -1. User owns the main wallet (EIP-712 signature in Step 3) -2. Challenge is unique and cannot be replayed -3. No private keys are ever transmitted -4. Session key is authorized by the main wallet -::: - ---- - -## Step 1: auth_request - -### Name - -`auth_request` - -### Usage - -Initiates authentication with Clearnode by registering a session key. The client sends authentication parameters to register a session key that can act on their behalf. The session key can have restricted permissions including spending limits (allowances), operation scope, and expiration time. - -**Important**: `auth_request` is a **public endpoint** and does not require a signature. The client simply needs to prepare and send the authentication parameters. - -### When to Use - -**Optional**: Use this when you want to delegate signing to a session key instead of using your main wallet for every request. This is the first step in establishing an authenticated session with Clearnode. - -If you prefer to use your main wallet as a root signer for all operations, you can skip this entire authentication flow. - -### Prerequisites - -- User has a wallet with funds -- Client can generate a keypair (e.g., secp256k1) -- Client can prepare authentication parameters locally - -### Request - -| Parameter | Type | Required | Description | Default | Example | Notes | -|-----------|------|----------|-------------|---------|---------|-------| -| `address` | string (wallet address) | Yes | User's main wallet address that owns the funds | - | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | - | -| `session_key` | string (wallet address) | Yes | Wallet address of the locally-generated session keypair | - | `"0x9876543210fedcba9876543210fedcba98765432"` | The private key never leaves the client | -| `application` | string | No | Application identifier for analytics and session management | `"clearnode"` | `"chess-game-app"` | Helps track which app is using which session | -| `allowances` | Array\ | No | Spending limits for this session key

**Structure (per allowance)**:
• `asset` (string) - Asset identifier (e.g., "usdc", "eth")
• `amount` (string) - Maximum amount this session can spend | Unrestricted if omitted/empty | `[{"asset": "usdc", "amount": "100.0"}]` | If empty/omitted, no spending cap is enforced | -| `scope` | string | No | Comma-separated list of permitted operations | All operations permitted | `"app.create,app.submit,transfer"` | Future feature, not fully enforced yet | -| `expires_at` | number | Yes | Unix timestamp (milliseconds) when the session key expires | — | `1762417328000` | Provide a 13-digit Unix ms timestamp; no server default is applied | - -:::tip Spending Allowances -If you omit `allowances` the session key is unrestricted. Specify explicit allowances to bound risk if a session key is compromised. -::: - -Allowances are validated against the broker’s supported assets. Unsupported symbols will cause authentication to fail. - -### Response - -| Parameter | Type | Description | Format | Example | Purpose | -|-----------|------|-------------|--------|---------|---------| -| `challenge_message` | string | UUID that client must sign with session key to prove ownership | UUID v4 | `"550e8400-e29b-41d4-a716-446655440000"` | Proves client controls session key without exposing private key | - -### Signature - -Request **does NOT require a signature** as `auth_request` is a public endpoint. - -**Process**: -1. Client prepares authentication parameters (address, session_key, application, allowances, expires_at) -2. Client stores these parameters locally for use in Step 3 (auth_verify) -3. Client sends request to Clearnode -4. Clearnode validates all parameters before generating a challenge - -:::tip Parameter Storage -Keep the authentication parameters (especially `address`, `session_key`, `application`, `allowances`, `scope`, and `expires_at`) stored locally until Step 3, as you'll need them to create the EIP-712 signature. -::: - -### Next Step - -Upon receiving the `challenge_message`, client must prepare an EIP-712 signature (or reuse a previously issued `jwt`) and call `auth_verify`. - -### Error Cases - -:::note Error Codes -Currently, the protocol does not use standardized error codes. Errors are returned as descriptive messages. -::: - -Common error scenarios: - -| Error | Description | Recovery | -|-------|-------------|----------| -| **Invalid address format** | Main wallet address is malformed | Verify address format (0x + 40 hex chars) | -| **Invalid session key format** | Session key address is malformed | Verify session key format | -| **Invalid parameters** | One or more parameters are invalid or missing | Check all required parameters | -| **Session key already registered** | This session key is already in use | Generate a new session keypair | - - ---- - -## Step 2: auth_challenge - -### Name - -`auth_challenge` - -### Usage - -Server-generated response to `auth_request` containing a challenge that the client must sign to prove control of the session key. This implements a challenge-response authentication pattern to prevent replay attacks and verify the client controls the private key of the session key they registered. - -### When to Use - -Automatically sent by Clearnode in response to valid `auth_request`. Client does not explicitly call this; it's part of the authentication flow. - -### Request - -N/A (server-initiated response to `auth_request`) - -### Response - -| Parameter | Type | Description | Format | Purpose | Example | Generation | Lifetime | -|-----------|------|-------------|--------|---------|---------|------------|----------| -| `challenge_message` | string | Randomly generated UUID that client must sign | UUID v4 | Prevents replay attacks, proves session key ownership | `"550e8400-e29b-41d4-a716-446655440000"` | Cryptographically secure random UUID | Single use, expires after 5 minutes if not verified | - -### Signature - -The challenge is returned as a normal RPC response (server signs the envelope like any other RPC response). - -### Next Step - -Client signs the challenge with session key private key and calls `auth_verify`. - -:::info Challenge Uniqueness -Each challenge is unique and single-use. It expires after 5 minutes if not verified. This prevents replay attacks where an attacker might try to reuse a captured challenge signature. -::: - ---- - -## Step 3: auth_verify - -### Name - -`auth_verify` - -### Usage - -Completes the authentication flow by submitting the signed challenge from `auth_challenge`. If the signature is valid and matches the registered session key, the authentication is complete and the session key can be used to sign subsequent requests. This proves the client controls the private key without ever transmitting it. - -### When to Use - -Immediately after receiving `auth_challenge` response. This is the final step in authentication. - -### Prerequisites - -- Completed `auth_request` and received `auth_challenge` -- Have the challenge_message -- Have the session key private key (client-side only) - -### Request - -| Parameter | Type | Required | Description | Example | Notes | -|-----------|------|----------|-------------|---------|-------| -| `challenge` | string | Yes | The challenge_message received from auth_challenge | `"550e8400-e29b-41d4-a716-446655440000"` | Must be the exact challenge from Step 2 | -| `jwt` | string | No | Existing JWT for re-login without signature | `"eyJhbGciOi..."` | If provided, signature is not required | - -### Response - -| Parameter | Type | Description | Example | Notes | -|-----------|------|-------------|---------|-------| -| `address` | string (wallet address) | Authenticated user's main wallet address | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | Confirms which account is authenticated | -| `session_key` | string (wallet address) | Confirmed session key wallet address | `"0x9876543210fedcba9876543210fedcba98765432"` | The authorized session key | -| `jwt_token` | string | JWT token for authenticated API calls | `"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."` | Store securely; validity follows the provided `expires_at` | -| `success` | boolean | Authentication success indicator | `true` | Indicates if authentication completed successfully | - -### Signature - -If `jwt` is omitted, the request **MUST** include an EIP-712 signature signed by the **main wallet** (NOT the session key). If `jwt` is present, no signature is required. - -**EIP-712 Typed Data Structure**: - -```typescript -{ - types: { - EIP712Domain: [ - { name: "name", type: "string" } - ], - Policy: [ - { name: "challenge", type: "string" }, - { name: "scope", type: "string" }, - { name: "wallet", type: "address" }, - { name: "session_key", type: "address" }, - { name: "expires_at", type: "uint64" }, - { name: "allowances", type: "Allowance[]" } - ], - Allowance: [ - { name: "asset", type: "string" }, - { name: "amount", type: "string" } - ] - }, - primaryType: "Policy", - domain: { - name: // From auth_request - }, - message: { - challenge: , // From auth_challenge - scope: , // From auth_request - wallet:
, // From auth_request - session_key: , // From auth_request - expires_at: , // From auth_request (13-digit Unix ms) - allowances: // From auth_request - } -} -``` - -**Signing Process**: -1. Client creates EIP-712 typed data with challenge and all parameters from Step 1 -2. User's wallet signs the typed data: `signature = signTypedData(typedData, mainWalletPrivateKey)` -3. Client sends request with EIP-712 signature in `sig` array - -:::danger Critical Security Requirement -The `auth_verify` signature MUST be an **EIP-712 signature signed by the main wallet**, not the session key. This proves the main wallet owner authorizes the session key to act on their behalf. The signature binds the challenge to the session key authorization. -::: - -### Next Step - -Session is authenticated. All subsequent private method calls should be signed with the session key. You may also re-authenticate later by sending `auth_verify` with the previously issued `jwt` (no signature required). - -### Error Cases - -:::note Error Codes -Currently, the protocol does not use standardized error codes. Errors are returned as descriptive messages. -::: - -Common error scenarios: - -| Error | Description | Recovery | -|-------|-------------|----------| -| **Invalid signature** | EIP-712 signature doesn't match main wallet or is malformed | Verify main wallet private key used for signing, check EIP-712 structure | -| **Challenge expired** | Challenge older than 5 minutes | Restart auth flow from `auth_request` | -| **Challenge already used** | Challenge has been verified already | Generate new session or use existing if still valid | -| **Invalid challenge** | Challenge not found in pending auths | Ensure `auth_request` succeeded first | -| **Challenge mismatch** | Challenge doesn't match pending auth | Use exact challenge from `auth_challenge` | - - ---- - -## Complete Authentication Flow Example - -Putting it all together: - -```mermaid -stateDiagram-v2 - [*] --> Unauthenticated - - Unauthenticated --> PreparingAuth: Generate session keypair - PreparingAuth --> WaitingForChallenge: auth_request
(public, no signature) - - WaitingForChallenge --> CreatingEIP712: Receive challenge_message - CreatingEIP712 --> SigningWithWallet: Create EIP-712 typed data - SigningWithWallet --> WaitingForConfirmation: auth_verify
(EIP-712 sig by main wallet) - - WaitingForConfirmation --> Authenticated: Session + JWT established - - Authenticated --> Authenticated: Use session key for requests - Authenticated --> SessionExpired: Timeout (expires_at reached) - Authenticated --> SessionInvalidated: Spending limit exceeded - Authenticated --> SessionRevoked: Manual revocation - - SessionExpired --> Unauthenticated: Must re-authenticate - SessionInvalidated --> Unauthenticated: Must re-authenticate - SessionRevoked --> Unauthenticated: Must re-authenticate - - PreparingAuth --> Unauthenticated: Error (retry) - WaitingForChallenge --> Unauthenticated: Timeout (5 min) - CreatingEIP712 --> Unauthenticated: Error (retry) - SigningWithWallet --> Unauthenticated: Error (retry) -``` - ---- - -## Session Management - -### Session Lifecycle - -1. **Creation**: After successful `auth_verify` -2. **Active**: Can perform operations until expiration or allowance exceeded -3. **Expiration**: Automatic after specified duration -4. **Invalidation**: When spending allowances exhausted -5. **Revocation**: User or the clearnode can revoke manually - -### Checking Session Status - -Use `get_session_keys` to view active sessions and their remaining allowances. The response includes session details with current allowance usage and respects the `expires_at` provided during `auth_request`. - -### Session Expiration Handling - -When a session expires according to the `expires_at` you provided, the clearnode will return an error response: - -```json -{ - "res": [ - , - "error", - { - "error": "session expired, please re-authenticate" - }, - - ], - "sig": [] -} -``` - -:::note Error Format -The protocol does not use numeric error codes. Errors are returned as method `"error"` with a descriptive message in the params. -::: - -**Recovery**: Re-authenticate by running the 3-step flow again. - -### Spending Allowance Tracking - -The clearnode tracks spending by monitoring all ledger debit operations: - -``` -Initial state: - allowance = specified_limit - used = 0 - remaining = specified_limit - -After operations: - allowance = specified_limit (unchanged) - used = sum_of_all_debits - remaining = allowance - used - -When operation exceeds remaining (for assets with an allowance): - Error: "Session key allowance exceeded: amount_required, remaining_available" -``` - -:::warning Allowance Enforcement -When a session key reaches its spending cap, all further operations are rejected. The user must create a new session with fresh allowances or use their main wallet directly. -::: - ---- - -## Security Best Practices - -### For Users - -1. **Set Spending Limits**: Always specify `allowances` when creating sessions -2. **Short Expirations**: Use shorter expiration times for sensitive operations -3. **Application Scoping**: Use different session keys for different applications -4. **Monitor Usage**: Regularly check session key spending via `get_session_keys` -5. **Revoke When Done**: Revoke sessions when application use is complete - -### For Developers - -1. **Secure Storage**: Store session key private keys securely (encrypted storage, secure enclaves) -2. **Never Transmit**: Never send session key private keys over network -3. **Handle Expiration**: Implement automatic re-authentication on session expiry -4. **Clear on Logout**: Delete session keys when user logs out -5. **Verify Signatures**: Always verify the clearnode's signatures on responses - ---- - -## Next Steps - -Now that you're authenticated, you can: - -- **[Create Channels](./channel-methods)** - Open payment channels and deposit funds -- **[Transfer Funds](./transfers)** - Send instant off-chain payments -- **[Manage App Sessions](./app-sessions)** - Create multi-party application channels -- **[Query Data](./queries)** - Check balances, transactions, and channel status - -For protocol fundamentals, see: -- **[Message Format](./message-format)** - Understand request/response structure -- **[Off-Chain RPC Overview](./overview)** - High-level protocol overview diff --git a/docs/protocol/app-layer/off-chain/channel-methods.mdx b/docs/protocol/app-layer/off-chain/channel-methods.mdx deleted file mode 100644 index 744730d..0000000 --- a/docs/protocol/app-layer/off-chain/channel-methods.mdx +++ /dev/null @@ -1,594 +0,0 @@ ---- -sidebar_position: 4 -title: Channel Management Methods ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# 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 VirtualApp protocol. They lock funds on-chain while enabling instant off-chain operations within a unified balance. - -### Channel Lifecycle Summary - -```mermaid -stateDiagram-v2 - [*] --> Requesting: create_channel (off-chain) - Requesting --> OnChain: User submits create() transaction - OnChain --> ACTIVE: Contract locks user funds (status = open) - - ACTIVE --> Resizing: resize_channel (optional) - Resizing --> ACTIVE: User submits resize() transaction - - ACTIVE --> Closing: close_channel (cooperative) - Closing --> [*]: Funds distributed - - ACTIVE --> Disputing: challenge() (non-cooperative) - Disputing --> [*]: Challenge period then close() - - -``` - ---- - -## 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. - -:::info 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](./authentication) -- Target blockchain and token must be supported by the clearnode -- User must have native currency for gas fees - -### Request - -| Parameter | Type | Required | Description | Default | Example | Notes | -|-----------|------|----------|-------------|---------|---------|-------| -| `chain_id` | uint32 | Yes | Blockchain network identifier

**Examples**:
• 1: Ethereum Mainnet
• 137: Polygon
• 8453: Base
• 42161: Arbitrum One
• 10: Optimism | — | `137` | Use `get_config` to see supported chains | -| `token` | string (wallet address) | Yes | ERC-20 token contract address on the specified chain

Format: 0x-prefixed hex (20 bytes) | — | `"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"` | Must be supported; see `get_assets` | - -:::info 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 - -:::tip Quick Reference -Structures: [Channel](#channel-structure) • [State](#state-structure) • [StateAllocation](#stateallocation) -::: - -| Parameter | Type | Description | See Also | -|-----------|------|-------------|----------| -| `channel_id` | string | Computed channel identifier (0x-prefixed hex, 32 bytes) | — | -| `channel` | Channel | On-chain channel params | [↓ Structure](#channel-structure) | -| `state` | State | Initial state (intent INITIALIZE, version 0, empty data, zero allocations) | [↓ Structure](#state-structure) | -| `server_signature` | string | Clearnode signature over packed state (hex string) | — | - -#### Channel Structure - -| Field | Type | Description | Notes | -|-------|------|-------------|-------| -| `participants` | wallet address[] | Array of two wallet addresses: [User, Clearnode] | Order: Index 0 = User, Index 1 = Clearnode
Order is critical for signature verification | -| `adjudicator` | wallet address | Adjudicator contract address for this channel | Typically SimpleConsensus for payment channels
Validates state transitions during disputes | -| `challenge` | uint64 | Challenge period in seconds | Default: 3600 seconds (1 hour) | -| `nonce` | uint64 | Unique identifier for this channel | Ensures channelId uniqueness even with same participants
Server-generated timestamp or counter | - -**Example**: -```json -{ - "participants": ["0x742d35Cc...", "0x123456Cc..."], - "adjudicator": "0xAdjudicator123...", - "challenge": 86400, - "nonce": 1699123456 -} -``` - -#### State Structure - -| Field | Type | Description | Notes | -|-------|------|-------------|-------| -| `intent` | StateIntent | State purpose indicator | For creation: `INITIALIZE` (1) | -| `version` | uint64 | State sequence number | For creation: `0` | -| `state_data` | string | State data (hex) | For creation: `"0x"` | -| `allocations` | StateAllocation[] | Fund allocations (raw units) | Order matches participants array; both `0` on creation | - -**Example**: -```json -{ - "intent": 1, - "version": 0, - "state_data": "0x", - "allocations": [ - {"participant": "0x742d35Cc...", "token": "0x2791Bca1...", "amount": "0"}, - {"participant": "0x123456Cc...", "token": "0x2791Bca1...", "amount": "0"} - ] -} -``` - -#### StateAllocation Structure - -| Field | Type | Description | -|-------|------|-------------| -| `participant` | string (wallet address) | Participant's wallet address | -| `token` | string (wallet address) | Token contract address | -| `amount` | string | Amount in smallest unit (e.g., `"100000000"` for 100 USDC with 6 decimals) | - -:::tip 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 - -:::note Error Format -The protocol does not use numeric error codes. Errors are returned as method `"error"` with descriptive messages. -::: - -Common error scenarios: - -| Error | Description | Recovery | -|-------|-------------|----------| -| **Authentication required** | Not authenticated | Complete [authentication flow](./authentication) | -| **Unsupported chain** | `chain_id` not supported | Use `get_config` | -| **Token not supported** | Token not in asset config for chain | Use `get_assets` | -| **Invalid signature** | Caller did not sign request | Sign with channel participant wallet | -| **Channel already exists** | Open channel with broker already exists | Use existing channel or close it first | -| **Failed to prepare state** | Internal packing/signing issue | Retry 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 - - -```mermaid -sequenceDiagram - participant User - participant Clearnode - participant Blockchain - - Note over User: 1. Request Channel Creation - User->>Clearnode: create_channel(chain_id, token) - - Note over Clearnode: 2. Prepare Channel - Clearnode->>Clearnode: Generate unique nonce - Clearnode->>Clearnode: Create channel config - Clearnode->>Clearnode: Create initial state (intent: INITIALIZE, version: 0) - Clearnode->>Clearnode: Sign state - - Clearnode->>User: {channel, state, server_signature, channel_id} - - Note over User: 3. Verify & Sign - User->>User: Verify Clearnode signature - User->>User: Sign state with participant key - - Note over User: 4. Submit On-Chain - User->>Blockchain: Custody.create(channel, state, signatures) - - Note over Blockchain: 5. Create Channel (Status: ACTIVE) - Blockchain->>Blockchain: Verify signatures - Blockchain->>Blockchain: Create channel with status ACTIVE - Blockchain->>Blockchain: Emit Opened event - - Blockchain-->>User: Channel Active (zero balance) - Blockchain-->>Clearnode: Channel Active (zero balance) - - Note over User,Clearnode: Use resize_channel to add funds - -``` - ---- - -## 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. - -:::success 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 - -| Parameter | Type | Required | Description | Default | Example | Notes | -|-----------|------|----------|-------------|---------|---------|-------| -| `channel_id` | string | Yes | Identifier of the channel to close | - | `"0xabcdef1234567890..."` | From get_channels or stored after creation | -| `funds_destination` | string (wallet address) | Yes | Address where your share of channel funds should be sent | - | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | Typically your wallet address | - -### Response - -| Parameter | Type | Description | Example | Notes | -|-----------|------|-------------|---------|-------| -| `channel_id` | string | Channel identifier | `"0xabcdef1234..."` | — | -| `state` | State | Final state with intent FINALIZE and version = current+1
`state_data`: `"0x"`
`allocations`: final fund distribution (raw units) | See [State structure](#state-structure) | `channel` field is omitted in close responses | -| `server_signature` | string | Clearnode 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 - -:::note Error Format -The protocol does not use numeric error codes. Errors are returned as method `"error"` with descriptive messages. -::: - -Common error scenarios: - -| Error | Description | Recovery | -|-------|-------------|----------| -| **Authentication required** | Not authenticated | Re-authenticate | -| **Channel not found** | Invalid `channel_id` | Verify with `get_channels` | -| **Channel challenged** | Participant has challenged channels | Resolve challenges first | -| **Channel not open/resizing** | Status not `open` or `resizing` | Only open/resizing channels can close | -| **Invalid signature** | Caller did not sign request | Sign with channel participant wallet | -| **Token/asset not found** | Asset config missing | Ensure channel token is supported | -| **Insufficient/negative balance** | Ledger balance retrieval or negative balance | Ensure balances are non-negative; retry | -| **Failed to pack/sign state** | Internal packing/signing issue | Retry or contact support | - -### Comparison: Cooperative vs Challenge Closure - -| Aspect | Cooperative (this method) | Challenge | -|--------|---------------------------|-----------| -| **Speed** | Fast (1 transaction) | Slow (challenge period + 1 transaction) | -| **Gas Cost** | Low (~100k gas) | High (~200k+ gas, 2+ transactions) | -| **Requirements** | Both parties online & agree | Works if other party unresponsive | -| **Waiting Period** | None (immediate) | 24+ hours (challenge duration) | -| **Use When** | Normal operations | Disputes or unresponsiveness | - -:::caution 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. - -:::tip 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 - -| Parameter | Type | Required | Description | Default | Example | Notes | -|-----------|------|----------|-------------|---------|---------|-------| -| `channel_id` | string | Yes | Identifier of the channel to resize (stays the same) | - | `"0xabcdef1234567890..."` | 0x-prefixed hex string (32 bytes)
This channel_id will NOT change after resize | -| `allocate_amount` | string (decimal) | No | Amount to add/remove between unified balance and the channel before resize | `0` | `"50.0"` | Decimal string; can be used together with `resize_amount`; at least one of the two must be non-zero | -| `resize_amount` | string (decimal) | No | Delta to apply to the channel: positive to deposit, negative to withdraw | `0` | `"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_destination` | string (wallet address) | Yes | Destination for the user's allocation in the resize state | - | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | 0x-prefixed hex string (20 bytes) | - -### Response - -| Parameter | Type | Description | Example | Notes | -|-----------|------|-------------|---------|-------| -| `channel_id` | string | Same channel identifier (unchanged) | `"0xabcdef1234567890..."` | This does NOT change (in-place update) | -| `state` | State | Resize 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 structure](#state-structure) | `channel` field is omitted in resize responses | -| `server_signature` | string | Clearnode 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** - ```javascript - 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 - -| Error | Cause | Resolution | -|-------|-------|------------| -| Authentication required | Not authenticated | Complete authentication flow | -| Channel not found | Invalid channel_id | Verify with `get_channels` | -| Channel challenged | Participant has challenged channels | Resolve challenged channels first | -| Operation denied: resize already ongoing | Channel status is `resizing` | Wait for existing resize to complete | -| Operation denied: channel is not open | Status not `open` | Only open channels can resize | -| Invalid signature | Caller not among channel signers | Sign request with channel participant | -| Token/asset not found for channel | Asset config missing for channel token/chain | Ensure channel token is supported | -| Resize operation requires non-zero amounts | Both `resize_amount` and `allocate_amount` are zero | Provide a non-zero value | -| Insufficient unified balance | New channel amount would exceed available balance | Reduce amounts or add funds | -| New channel amount must be positive | Resize would make channel balance negative | Reduce withdrawal | -| Failed to pack resize amounts/state | Internal packing/signing error | Retry; 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**: -```javascript -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**: -```javascript -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**: -```javascript -// 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 -``` - -:::caution 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](./transfers)** - Send instant off-chain payments using unified balance -- **[App Sessions](./app-sessions)** - Create multi-party application channels -- **[Queries](./queries)** - Check channel status, balances, and history - -For protocol fundamentals: -- **[Authentication](./authentication)** - Understand authorization and session management -- **[Message Format](./message-format)** - Learn request/response structure -- **[On-Chain Protocol](/docs/protocol/app-layer/on-chain/overview)** - Deep dive into smart contracts diff --git a/docs/protocol/app-layer/off-chain/message-format.mdx b/docs/protocol/app-layer/off-chain/message-format.mdx deleted file mode 100644 index 6f5155f..0000000 --- a/docs/protocol/app-layer/off-chain/message-format.mdx +++ /dev/null @@ -1,406 +0,0 @@ ---- -sidebar_position: 2 -title: Message Format ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Message Format - -The Nitro RPC protocol uses a compact, efficient message format for all communication between clients and a clearnode. - ---- - -## General Structure - -Every Nitro RPC message consists of a compact JSON array format: - -```javascript -[requestId, method, params, timestamp] -``` - -:::tip Compact Format -This array-based format reduces message overhead by approximately 30% compared to traditional JSON-RPC, making it ideal for high-frequency state channel operations. -::: - -### Components - -| Component | Type | Description | -|-----------|------|-------------| -| **requestId** | uint64 | Unique identifier for the request, used to correlate responses | -| **method** | string | Remote method name to be invoked | -| **params** | object | Method-specific parameters as a JSON object | -| **timestamp** | uint64 | Server-provided timestamp in milliseconds | - -#### requestId - -- **Purpose**: Correlate requests with their responses -- **Type**: Unsigned 64-bit integer -- **Generation**: Client-generated, must be unique per connection -- **Range**: 0 to 2^64-1 -- **Example**: `1`, `42`, `9876543210` - -#### method - -- **Purpose**: Specify which RPC method to invoke -- **Type**: String -- **Format**: snake_case (e.g., `create_channel`, not `createChannel`) -- **Examples**: `auth_request`, `transfer`, `create_app_session` - -#### params - -- **Purpose**: Provide method-specific parameters -- **Type**: JSON object -- **Content**: Varies by method -- **Example**: `{"chain_id": 137, "token": "0x...", "amount": "100000000"}` -- **Reference**: See [Authentication](./authentication), [Channel Methods](./channel-methods), [Transfers](./transfers), [App Sessions](./app-sessions), and [Queries](./queries) for parameter specifications - -#### timestamp - -- **Purpose**: Request ordering and replay attack prevention -- **Type**: Unsigned 64-bit integer (Unix milliseconds) -- **Generation**: Client-provided on requests; server-provided on responses -- **Example**: `1699123456789` (November 5, 2023, 01:57:36 UTC) - ---- - -## Request Message - -A complete request message wraps the payload array and includes signatures. - -### Structure - -```json -{ - "req": [requestId, method, params, timestamp], - "sig": [signature1, signature2, ...] -} -``` - -### Fields - -#### req - -The request payload as a 4-element array containing: -- Request ID -- Method name -- Parameters object -- Timestamp - -#### sig - -Array of ECDSA signatures, one or more depending on the operation: -- **Single signature**: Most operations (signed by client's session key) -- **Multiple signatures**: Multi-party operations (e.g., app session creation) - -### Signature Format - -Each signature is: -- **Format**: 0x-prefixed hex string -- **Length**: 65 bytes (130 hex characters + "0x" prefix) -- **Components**: r (32 bytes) + s (32 bytes) + v (1 byte) -- **Algorithm**: ECDSA over secp256k1 curve -- **Hash**: keccak256 of the exact `req` array bytes - -**Example Signature**: -``` -0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef01 -``` - -:::info EVM-Specific Format -This signature format (ECDSA over secp256k1 with keccak256 hashing) is specific to EVM-compatible chains. If the protocol extends to support non-EVM chains in the future, signature formats may need to be adapted to match those chains' native cryptographic primitives. -::: - -:::caution Signature Security -Signatures are computed over the keccak256 hash of the JSON-encoded `req` array. The JSON encoding MUST be consistent (same key ordering, no extra whitespace) to ensure signature validity. -::: - -### Complete Example - -```json -{ - "req": [ - 1, - "auth_request", - { - "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", - "session_key": "0x9876543210fedcba9876543210fedcba98765432", - "application": "trading-dex", - "allowances": [ - {"asset": "usdc", "amount": "1000.0"}, - {"asset": "eth", "amount": "0.5"} - ], - "scope": "transfer,app.create", - "expires_at": 1762417328123 - }, - 1699123456789 - ], - "sig": [ - "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef01" - ] -} -``` - ---- - -## Response Message - -The clearnode sends response messages with the same structure, replacing `params` with `result`. - -### Structure - -```json -{ - "res": [requestId, method, result, timestamp], - "sig": [signature1, ...] -} -``` - -### Fields - -#### res - -The response payload as a 4-element array: -- Same **requestId** (to correlate with request) -- **method** (response method name) - - Usually matches the request method - - **Exception**: `auth_request` → response has `auth_challenge` method - - **Exception**: Errors → response has `error` method -- **result** (method-specific response data, replaces params) -- **timestamp** (server response time) - -#### sig - -The clearnode's signature(s) over the response: -- Proves response authenticity -- Verifies response hasn't been tampered with -- Enables non-repudiation - -### Complete Example - -```json -{ - "res": [ - 1, - "auth_challenge", - { - "challenge_message": "550e8400-e29b-41d4-a716-446655440000" - }, - 1699123457000 - ], - "sig": [ - "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab" - ] -} -``` - ---- - -## Error Response - -When an error occurs, the clearnode sends an error response with method set to `"error"`. - -### Structure - -```json -{ - "res": [ - requestId, - "error", - { - "error": "Error description message" - }, - timestamp - ], - "sig": ["0xServerSignature..."] -} -``` - -The result object at position 2 contains a single `"error"` field with a descriptive error message string. - -### Error Examples - -**Authentication Required**: -```json -{ - "res": [ - 5, - "error", - { - "error": "Authentication required: session not established" - }, - 1699123456789 - ], - "sig": ["0xServerSignature..."] -} -``` - -**Insufficient Balance**: -```json -{ - "res": [ - 12, - "error", - { - "error": "Insufficient balance: required 100 USDC, available 75 USDC" - }, - 1699123456790 - ], - "sig": ["0xServerSignature..."] -} -``` - -**Method Not Found**: -```json -{ - "res": [ - 8, - "error", - { - "error": "Method not found: 'invalid_method'" - }, - 1699123456791 - ], - "sig": ["0xServerSignature..."] -} -``` - -:::tip Error Handling -Check the response method field (position 1 in `res` array). If it equals `"error"`, extract the error message from the result object's `error` field. The error message provides human-readable context about what went wrong. -::: - ---- - -## Payload Hash Computation - -Every RPC message (request or response) is signed over the exact serialized `req` or `res` array bytes. - -### What is Signed - -- **Requests**: The `req` array `[requestId, method, params, timestamp]` exactly as sent -- **Responses**: The `res` array `[requestId, method, result, timestamp]` exactly as received - -### Hash Formula - -```javascript -payloadHash = keccak256() -``` - -Use the same bytes you transmit (or receive) when computing/verifying the hash; do not re-serialize with different spacing or key ordering. - -### Example - -**Request Payload**: -```json -[42,"create_app_session",{"definition":{...},"allocations":[...]},1699123456789] -``` - -Hash that exact byte string, then sign it (client for requests, clearnode for responses). - ---- - -## Message Flow Diagram - -The following diagram illustrates the complete request-response cycle: - -```mermaid -sequenceDiagram - box rgb(135,206,235) Client - participant Client - end - box rgb(255,182,193) Clearnode - participant Clearnode - end - - Note over Client: Generate Request - Client->>Client: Create payload: [request_id, method, params, 0] - Client->>Client: Sign payload with session key - - Client->>Clearnode: Send Request {req, sig} - - Note over Clearnode: Process Request - Clearnode->>Clearnode: Verify signature - Clearnode->>Clearnode: Validate parameters - Clearnode->>Clearnode: Execute method logic - Clearnode->>Clearnode: Generate result - - Note over Clearnode: Generate Response - Clearnode->>Clearnode: Create response: [request_id, method, result, timestamp] - Clearnode->>Clearnode: Sign response - - Clearnode->>Client: Send Response {res, sig} - - Note over Client: Process Response - Client->>Client: Verify Clearnode signature - Client->>Client: Correlate by request_id - Client->>Client: Handle result - -``` - ---- - -## Signature Verification Process - -Both clients and a clearnode MUST verify signatures on all messages. - -### Client Verifying a Clearnode Response - -1. **Extract Response**: Get `res` array from response -2. **Compute Hash**: `hash = keccak256()` -3. **Recover Address**: Use `sig` to recover signer address -4. **Verify**: Confirm recovered address matches the clearnode's known address - -### A Clearnode Verifying Client Request - -1. **Extract Request**: Get `req` array from request -2. **Compute Hash**: `hash = keccak256()` -3. **Recover Address**: Use `sig` to recover signer address -4. **Verify**: Confirm recovered address matches authenticated user or registered session key - -:::warning Signature Verification Requirements -**Most** messages MUST be cryptographically signed and verified. All state-changing operations (channel creation/closure, transfers, app sessions) and authenticated methods require valid signatures. However, **some query methods** (such as `get_config`) may be accessed without signatures. Refer to individual method specifications for signature requirements. -::: - ---- - -## JSON Encoding Consistency - -To ensure signature validity, JSON encoding MUST be consistent across all implementations. - -### Requirements - -1. **Key Ordering**: Object keys MUST be in a deterministic order -2. **No Whitespace**: Remove all unnecessary whitespace -3. **No Trailing Commas**: Standard JSON (no trailing commas) -4. **UTF-8 Encoding**: Use UTF-8 character encoding -5. **Number Format**: Numbers as strings for large integers (avoid precision loss) - -### Canonical Example - -**Consistent** (valid for signing): -```json -[1,"transfer",{"amount":"100","asset":"usdc","destination":"0x..."},1699123456] -``` - -**Inconsistent** (would produce different hash): -```json -[ 1, "transfer", { "destination": "0x...", "amount": "100", "asset": "usdc" }, 1699123456 ] -``` - -:::tip Implementation Note -Use a JSON library that supports canonical JSON serialization, or implement strict key ordering and whitespace removal before computing hashes. -::: - ---- - -## Next Steps - -Now that you understand the message format, explore how it's used in practice: - -- **[Authentication](./authentication)** - Learn the 3-step authentication flow -- **[Channel Methods](./channel-methods)** - See request/response examples for channel operations -- **[Transfers](./transfers)** - Understand transfer message structure -- **[App Sessions](./app-sessions)** - Explore multi-signature app session messages - -For a high-level overview, return to **[Off-Chain RPC Overview](./overview)**. diff --git a/docs/protocol/app-layer/off-chain/overview.mdx b/docs/protocol/app-layer/off-chain/overview.mdx deleted file mode 100644 index fe7f310..0000000 --- a/docs/protocol/app-layer/off-chain/overview.mdx +++ /dev/null @@ -1,242 +0,0 @@ ---- -sidebar_position: 1 -title: Off-Chain RPC Overview ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Off-Chain RPC Protocol Overview - -The Off-Chain RPC Protocol defines how clients communicate with a clearnode to perform state channel operations without touching the blockchain. - ---- - -## What is Nitro RPC? - -**Nitro RPC** is a lightweight RPC protocol designed for state channel communication. It uses a compact JSON array format for efficiency and includes signature-based authentication. - -:::info Protocol Purpose -Nitro RPC enables clients to interact with a clearnode for channel management, fund transfers, and application-specific operations—all happening off-chain with instant finality and zero gas costs. -::: - ---- - -## Key Features - -### 1. Compact Message Format - -Nitro RPC uses a streamlined JSON array format instead of verbose JSON objects, reducing message size and improving network efficiency. - -```javascript -// Compact format: [requestId, method, params, timestamp] -[1, "create_channel", {"chain_id": 137, "token": "0x...", "amount": "1000000"}, 1699123456789] -``` - -:::tip Efficiency Benefit -The compact array format reduces bandwidth usage by approximately 30% compared to traditional JSON-RPC, crucial for high-frequency state channel updates. -::: - -### 2. Signature-Based Authentication - -Every request and response is cryptographically signed, ensuring: -- **Message authenticity**: Verify sender identity -- **Message integrity**: Detect tampering -- **Non-repudiation**: Proof of communication - -### 3. Multi-Signature Support - -Supports operations requiring multiple participants' signatures: -- Channel creation (user + a clearnode) -- App session state updates (multiple participants based on quorum) -- Cooperative channel closure - -### 4. Timestamp-Based Request Ordering - -All messages include timestamps (client-provided on requests, server-provided on responses) enabling: -- Request ordering -- Replay attack prevention -- Audit trail for debugging - -### 5. Channel-Aware Message Structure - -The protocol understands channel concepts natively: -- Packed states -- Multi-party signatures -- State versioning - ---- - -## Protocol Versions - -Nitro RPC has evolved to support advanced features while maintaining backward compatibility. - -### Version Comparison - -| Feature | NitroRPC/0.2 | NitroRPC/0.4 | -|---------|-------------|-------------| -| **Status** | Legacy | **Current** | -| **Basic State Updates** | ✅ | ✅ | -| **Intent System** | ❌ | ✅ | -| **DEPOSIT Intent** | ❌ | ✅ (add funds to app sessions) | -| **WITHDRAW Intent** | ❌ | ✅ (remove funds from app sessions) | -| **OPERATE Intent** | Implicit only | ✅ Explicit | -| **Recommended** | No | **Yes** | - -:::caution Version Recommendation -**Always use NitroRPC/0.4** for new implementations. Version 0.2 is maintained for backward compatibility only and lacks the intent system required for flexible app session management. -::: - -### NitroRPC/0.2 (Legacy) - -**Features**: -- Basic state updates for app sessions -- All updates redistribute existing funds -- Cannot add or remove funds from active sessions -- Must close and recreate sessions to change total funds - -**Use Case**: Maintained for existing applications, not recommended for new development. - -### NitroRPC/0.4 (Current) - -**Features**: -- Intent-based state updates: **OPERATE**, **DEPOSIT**, **WITHDRAW** -- Add funds to active app sessions (DEPOSIT) -- Remove funds from active sessions (WITHDRAW) -- Better error handling and validation -- Enhanced security checks - -**Use Case**: All new implementations should use this version. - ---- - -## Communication Architecture - -Nitro RPC enables bidirectional real-time communication between clients and a clearnode. - -```mermaid -graph TB - A[Client Application] <-->|RPC Connection| B[Clearnode] - B <-->|Event Monitoring| C[Blockchain] - B <-->|State Management| D[Database] - - A -->|"1. RPC Request
(signed)"| B - B -->|"2. Process & Validate"| D - B -->|"3. RPC Response
(signed)"| A - - B -.->|"Async Notifications"| A - C -.->|"Blockchain Events"| B - - style A fill:#e1f5ff - style B fill:#ffe1f5 - style C fill:#f5ffe1 - style D fill:#fff5e1 -``` - -### Connection Flow - -1. **Client Establishes Connection**: Open persistent connection to a clearnode -2. **Authentication**: Complete 3-step auth flow (auth_request → auth_challenge → auth_verify) -3. **RPC Communication**: Send requests, receive responses -4. **Notifications**: Receive real-time updates (balance changes, channel events) -5. **Keep-Alive**: Periodic ping/pong to maintain connection (optional, depends upon the implementation chosen) - ---- - -## Message Categories - -Nitro RPC methods are organized into functional categories: - -### 1. Authentication Methods - -Establish and manage authenticated sessions: -- `auth_request` - Initiate authentication (response: `auth_challenge`) -- `auth_verify` - Complete authentication with challenge response - -### 2. Channel Management Methods - -Create and manage payment channels: -- `create_channel` - Open new channel -- `close_channel` - Cooperatively close channel -- `resize_channel` - Adjust channel allocations - -### 3. Transfer Methods - -Move funds between users: -- `transfer` - Send funds off-chain with instant settlement - -### 4. App Session Methods - -Manage multi-party application channels: -- `create_app_session` - Create new app session -- `submit_app_state` - Update session state (with intents) -- `close_app_session` - Finalize and distribute funds - -### 5. Query Methods - -Read state and configuration: -- Public: `get_config`, `get_assets`, `get_app_definition`, `get_channels`, `get_app_sessions`, `get_ledger_entries`, `get_ledger_transactions`, `ping` -- Private (auth required): `get_ledger_balances`, `get_rpc_history`, `get_user_tag`, `get_session_keys` - -### 6. Notifications (Server-to-Client) - -Real-time updates: -- `bu` (balance update) - Balance changed -- `cu` (channel update) - Channel status changed -- `tr` (transfer) - Incoming/outgoing transfer -- `asu` (app session update) - App session state changed - ---- - -## Security Model - -The Off-Chain RPC Protocol provides multiple layers of security: - -### Cryptographic Security - -- **ECDSA Signatures**: Every message signed with secp256k1 -- **Keccak256 Hashing**: Message integrity verification -- **Challenge-Response Auth**: Prove key ownership without exposing private keys - -### Protocol-Level Security - -- **Request Ordering**: Timestamps prevent replay attacks -- **Session Expiration**: Session keys have time limits -- **Spending Allowances**: Limit session key spending power -- **Signature Verification**: All operations require valid signatures - -### Network Security - -- **TLS Encrypted Communication**: Encrypted communication channel -- **Origin Validation**: Prevent unauthorized connections - -:::success Strong Security Model -The combination of cryptographic signatures, challenge-response authentication, and spending allowances ensures that even if a session key is compromised, damage is limited by spending caps and expiration times. -::: - ---- - -## Next Steps - -Explore the detailed specifications for each part of the protocol: - -- **[Message Format](./message-format)** - Learn the request/response structure -- **[Authentication](./authentication)** - Implement secure session management -- **[Channel Methods](./channel-methods)** - Create and manage payment channels -- **[Transfers](./transfers)** - Enable instant off-chain payments -- **[App Sessions](./app-sessions)** - Build multi-party applications -- **[Queries & Notifications](./queries)** - Read state and receive updates - ---- - -## Key Concepts - -Before diving into specific methods, ensure you understand these core concepts from the protocol foundation: - -- **Channel** - Payment channel locking funds on-chain -- **State** - Snapshot of channel at a point in time -- **Participant** - Entity in a channel (user, a clearnode) -- **Unified Balance** - Aggregated balance across chains -- **Session Key** - Temporary key with spending limits - -Refer to the **[Terminology](/docs/protocol/terminology)** page for complete definitions. diff --git a/docs/protocol/app-layer/off-chain/queries.mdx b/docs/protocol/app-layer/off-chain/queries.mdx deleted file mode 100644 index 18ec0bd..0000000 --- a/docs/protocol/app-layer/off-chain/queries.mdx +++ /dev/null @@ -1,877 +0,0 @@ ---- -sidebar_position: 7 -title: Query Methods & Notifications ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Query Methods & Notifications - -Query methods retrieve information from a clearnode, while notifications provide real-time updates about state changes. - ---- - -## Overview - -The Nitro RPC protocol provides two types of information retrieval: - -**Query Methods**: Client-initiated requests to retrieve current state information (balances, channels, sessions, transactions). - -**Notifications**: Server-initiated messages sent to all relevant active connections when events occur (balance changes, channel updates, incoming transfers). - -:::tip Real-Time Updates -Combine query methods for initial state retrieval with notifications for ongoing monitoring. This pattern ensures your application always reflects the latest state without constant polling. -::: - ---- - -## Query Methods Summary - -| Method | Authentication | Purpose | Pagination | -|--------|---------------|---------|------------| -| `get_config` | Public | Retrieve clearnode configuration | No | -| `get_assets` | Public | List supported assets | No | -| `get_app_definition` | Public | Fetch the definition for a specific app session | No | -| `get_channels` | Public | List payment channels | Yes | -| `get_app_sessions` | Public | List app sessions | Yes | -| `get_ledger_balances` | Private | Query current balances | No | -| `get_ledger_entries` | Public | Detailed accounting entries | Yes | -| `get_ledger_transactions` | Public | User-facing transaction history | Yes | -| `get_rpc_history` | Private | Fetch recent RPC invocations | Yes | -| `get_user_tag` | Private | Retrieve user's alphanumeric tag | No | -| `get_session_keys` | Private | List active session keys | Yes | -| `ping` | Public | Connection health check | No | - -:::info Authentication -**Public methods** can be called without authentication. **Private methods** require completing the [authentication flow](./authentication) first. -::: - -:::note Pagination defaults -Unless explicitly provided, paginated methods default to `limit = 10` (maximum 100) and `offset = 0`, matching the broker’s `ListOptions`. -::: - ---- - -## get_config - -### Name - -`get_config` - -### Usage - -Retrieves the clearnode's configuration: broker address plus supported blockchains and their custody/adjudicator contracts. - -### Request - -No parameters. - -### Response - -| Parameter | Type | Description | Example | -|-----------|------|-------------|---------| -| `broker_address` | string | Clearnode's wallet address | `"0xbbbb567890abcdef..."` | -| `networks` | array\ | List of supported blockchain networks | See structure below | - -#### BlockchainInfo Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `chain_id` | uint32 | Network identifier | `137` (Polygon) | -| `name` | string | Human-readable blockchain name | `"Polygon"` | -| `custody_address` | string | Custody contract address on this chain | `"0xCustodyContractAddress..."` | -| `adjudicator_address` | string | Adjudicator contract address on this chain | `"0xAdjudicatorAddress..."` | - -**Use Cases**: -- Discover supported chains and contract addresses -- Verify clearnode wallet address - ---- - -## get_assets - -### Name - -`get_assets` - -### Usage - -Retrieves all supported assets and their configurations across supported blockchains. - -### Request - -| Parameter | Type | Required | Description | Example | Notes | -|-----------|------|----------|-------------|---------|-------| -| `chain_id` | uint32 | No | Filter by specific chain | `137` | If omitted, returns assets for all chains | - -### Response - -| Parameter | Type | Description | Example | -|-----------|------|-------------|---------| -| `assets` | array\ | List of supported assets | See structure below | - -#### Asset Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `token` | string | Token contract address | `"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"` | -| `chain_id` | uint32 | Blockchain network identifier | `137` | -| `symbol` | string | Token symbol | `"usdc"` | -| `decimals` | uint8 | Number of decimal places | `6` | - -**Use Cases**: -- Display supported assets in UI -- Validate asset identifiers before transfers -- Get contract addresses for specific chains - ---- - -## get_app_definition - -### Name - -`get_app_definition` - -### Usage - -Retrieves the immutable definition for a given app session so clients can verify governance parameters and participants. - -### Request - -| Parameter | Type | Required | Description | Example | -|-----------|------|----------|-------------|---------| -| `app_session_id` | string | Yes | Target app session identifier | `"0x9876543210fedcba..."` | - -### Response - -Returns the [AppDefinition](../off-chain/app-sessions#appdefinition) structure: - -| Field | Type | Description | -|-------|------|-------------| -| `protocol` | string | Protocol version (`"NitroRPC/0.2"` or `"NitroRPC/0.4"`) | -| `participants` | array\ | Wallet addresses authorized for this session | -| `weights` | array\ | Voting weight per participant (aligned with `participants` order) | -| `quorum` | uint64 | Minimum combined weight required for updates | -| `challenge` | uint64 | Dispute timeout (seconds) | -| `nonce` | uint64 | Unique instance identifier | - -**Use Cases**: -- Validate session metadata before signing states -- Display governance rules in UI -- Confirm protocol version compatibility - ---- - -## get_channels - -### Name - -`get_channels` - -### Usage - -Lists all channels for a specific participant address across all supported chains. - -### Request - -| Parameter | Type | Required | Description | Default | Example | -|-----------|------|----------|-------------|---------|---------| -| `participant` | string | No | Participant wallet address to query | (empty = all channels) | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | -| `status` | string | No | Filter by status | - | `"open"` | -| `offset` | number | No | Pagination offset | `0` | `42` | -| `limit` | number | No | Number of channels to return | `10` (max 100) | `10` | -| `sort` | string | No | Sort order by created_at | `"desc"` | `"desc"` | - -**Allowed status values**: `"open"` \| `"closed"` \| `"challenged"` \| `"resizing"` - -### Response - -| Parameter | Type | Description | Example | -|-----------|------|-------------|---------| -| `channels` | array\ | List of channels | See structure below | - -#### Channel Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `channel_id` | string | Unique channel identifier | `"0xabcdef..."` | -| `participant` | string | User's wallet address | `"0x742d35Cc..."` | -| `status` | string | Channel status | `"open"` | -| `token` | string | Asset contract address | `"0x2791Bca1..."` | -| `wallet` | string | Participant's wallet address | `"0x742d35Cc..."` | -| `amount` | string | Total channel capacity (human-readable) | `"100.0"` | -| `chain_id` | uint32 | Blockchain network identifier | `137` | -| `adjudicator` | string | Dispute resolution contract address | `"0xAdjudicator..."` | -| `challenge` | uint64 | Dispute timeout period (seconds) | `3600` | -| `nonce` | uint64 | Unique nonce ensuring channel uniqueness | `1699123456789` | -| `version` | uint64 | Current state version | `5` | -| `created_at` | string | Channel creation timestamp (ISO 8601) | `"2023-05-01T12:00:00Z"` | -| `updated_at` | string | Last modification timestamp (ISO 8601) | `"2023-05-01T14:30:00Z"` | - -**Use Cases**: -- Display user's open channels -- Check channel status before operations -- Monitor multi-chain channel distribution - ---- - -## get_app_sessions - -### Name - -`get_app_sessions` - -### Usage - -Lists all app sessions for a participant, sorted by creation date (newest first by default). Optionally filter by status (open/closed). Returns complete session information including participants, voting weights, quorum, protocol version, and current state. Supports pagination for large result sets. - -### Request - -| Parameter | Type | Required | Description | Default | Allowed Values | Example | -|-----------|------|----------|-------------|---------|----------------|---------| -| `participant` | string (address) | No | Filter by participant wallet address | (empty = all sessions) | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | -| `status` | string | No | Filter by status | - | `"open"` | -| `offset` | number | No | Pagination offset | 0 | - | `42` | -| `limit` | number | No | Number of sessions to return | 10 (max 100) | - | `10` | -| `sort` | string | No | Sort order by created_at | "desc" | `"desc"` | - -**Allowed status values**: `"open"` \| `"closed"` - -### Response - -| Parameter | Type | Description | See Also | -|-----------|------|-------------|----------| -| `app_sessions` | array\ | List of app sessions | See structure below | - -#### AppSessionInfo - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `app_session_id` | string | Unique identifier | `"0x9876543210fedcba..."` | -| `application` | string | Application identifier | `"VirtualAppChess"` | -| `status` | string | Current status | `"open"` \| `"closed"` | -| `participants` | array\ | All participant wallet addresses | `["0x742d35Cc...", "0x8B3192f2..."]` | -| `weights` | array\ | Voting weights per participant | `[50, 50, 100]` | -| `quorum` | uint64 | Required weight for state updates | `100` | -| `protocol` | string | Protocol version | `"NitroRPC/0.4"` | -| `challenge` | uint64 | Challenge period in seconds | `86400` | -| `version` | number | Current state version | `5` | -| `nonce` | uint64 | Unique session identifier | `1699123456789` | -| `session_data` | string | Current application state | `"{\"gameType\":\"chess\",\"turn\":\"white\"}"` | -| `created_at` | string (timestamp) | Creation timestamp | `"2023-05-01T12:00:00Z"` | -| `updated_at` | string (timestamp) | Last update timestamp | `"2023-05-01T14:30:00Z"` | - -**Use Cases**: -- Display user's active games or escrows -- Monitor session history -- Paginate through large session lists - -:::tip Pagination Best Practice -When dealing with users who have many app sessions, use pagination with reasonable `limit` values (10-50) to improve performance and user experience. -::: - ---- - -## get_ledger_balances - -### Name - -`get_ledger_balances` - -### Usage - -Retrieves the ledger balances for an account. If no parameters are provided, returns the authenticated user's unified balance across all assets. Can also query balance within a specific app session by providing the app_session_id. Returns all tracked assets (including those that currently evaluate to zero). - -### Request - -| Parameter | Type | Required | Description | Format | Example | -|-----------|------|----------|-------------|--------|---------| -| `account_id` | string | No | Account or app session identifier | 0x-prefixed hex string or wallet address | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | - -:::info App Session Balances -To query balance within a specific app session, provide the `app_session_id` as the `account_id`. -::: - -### Response - -| Parameter | Type | Description | Example | -|-----------|------|-------------|---------| -| `ledger_balances` | array\ | Balance per asset | See structure below | - -#### Balance Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `asset` | string | Asset identifier | `"usdc"` | -| `amount` | string | Balance in human-readable format | `"100.0"` | - -**Use Cases**: -- Display user's current balances -- Check available funds before operations -- Monitor balance changes in real-time - ---- - -## get_ledger_entries - -### Name - -`get_ledger_entries` - -### Usage - -Retrieves detailed ledger entries for an account, providing a complete audit trail of all debits and credits. Each entry represents one side of a double-entry bookkeeping transaction. Used for detailed financial reconciliation and accounting. Supports filtering by account, asset, and pagination. Sorted by creation date (newest first by default). - -### Request - -| Parameter | Type | Required | Description | Default | Allowed Values | Example | -|-----------|------|----------|-------------|---------|----------------|---------| -| `account_id` | string | No | Filter by account identifier | - | - | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | -| `wallet` | string (address) | No | Filter by wallet address | - | - | `"0x742d35Cc..."` | -| `asset` | string | No | Filter by asset | - | - | `"usdc"` | -| `offset` | number | No | Pagination offset | 0 | - | - | -| `limit` | number | No | Number of entries to return | 10 (max 100) | - | - | -| `sort` | string | No | Sort order by created_at | "desc" | "asc" \| "desc" | - | - -### Response - -| Parameter | Type | Description | Structure | Example | -|-----------|------|-------------|-----------|---------| -| `ledger_entries` | array\ | List of ledger entries | See structure below | - -#### LedgerEntry Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `id` | number | Unique entry identifier | `123` | -| `account_id` | string | Account this entry belongs to | `"0x742d35Cc..."` | -| `account_type` | number | Ledger account classification (`1000`=asset, `2000`=liability, etc.) | `1000` | -| `asset` | string | Asset symbol | `"usdc"` | -| `participant` | string | Participant wallet address | `"0x742d35Cc..."` | -| `credit` | string | Credit amount (incoming funds, "0.0" if debit) | `"100.0"` | -| `debit` | string | Debit amount (outgoing funds, "0.0" if credit) | `"25.0"` | -| `created_at` | string | Entry creation timestamp (ISO 8601) | `"2023-05-01T12:00:00Z"` | - -Account types follow the broker’s GAAP-style codes: `1000` series for assets, `2000` liabilities, `3000` equity, `4000` revenue, and `5000` expenses. - -### Double-Entry Bookkeeping - -Every transaction creates two entries: - -``` -Transfer: Alice sends 50 USDC to Bob - -Entry 1 (Alice's ledger): - account_id: Alice's address - asset: usdc - credit: 0.0 - debit: 50.0 - -Entry 2 (Bob's ledger): - account_id: Bob's address - asset: usdc - credit: 50.0 - debit: 0.0 -``` - -:::info Accounting Principle -The double-entry system ensures that the total of all debits always equals the total of all credits, providing mathematical proof of accounting accuracy. This is the same principle used by traditional financial institutions. -::: - -**Use Cases**: -- Detailed financial reconciliation -- Audit trail generation -- Accounting system integration -- Verify balance calculations - ---- - -## get_ledger_transactions - -### Name - -`get_ledger_transactions` - -### Usage - -Retrieves user-facing transaction history showing transfers, deposits, withdrawals, and app session operations. Unlike ledger entries (which show accounting details), this provides a simplified view of financial activity with sender, receiver, amount, and transaction type. Supports filtering by asset and transaction type. Sorted by creation date (newest first by default). - -### Request - -| Parameter | Type | Required | Description | Default | Allowed Values | Example | -|-----------|------|----------|-------------|---------|----------------|---------| -| `account_id` | string | No | Filter by account identifier | - | - | `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"` | -| `asset` | string | No | Filter by asset | - | - | `"usdc"` | -| `tx_type` | string | No | Filter by transaction type | - | "transfer" \| "deposit" \| "withdrawal" \| "app_deposit" \| "app_withdrawal" \| "escrow_lock" \| "escrow_unlock" | `"transfer"` | -| `offset` | number | No | Pagination offset | 0 | - | - | -| `limit` | number | No | Number of transactions to return | 10 (max 100) | - | - | -| `sort` | string | No | Sort order by created_at | "desc" | "asc" \| "desc" | - | - -### Response - -| Parameter | Type | Description | Example | -|-----------|------|-------------|---------| -| `ledger_transactions` | array\ | List of transactions | See structure below | - -#### LedgerTransaction Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `id` | number | Unique transaction identifier | `1` | -| `tx_type` | string | Transaction type | `"transfer"` | -| `from_account` | string | Sender account identifier (wallet, channel, or app session) | `"0x742d35Cc..."` | -| `from_account_tag` | string | Sender's user tag (empty if none) | `"NQKO7C"` | -| `to_account` | string | Receiver account identifier (wallet, channel, or app session) | `"0x8B3192f2..."` | -| `to_account_tag` | string | Receiver's user tag (empty if none) | `"UX123D"` | -| `asset` | string | Asset symbol | `"usdc"` | -| `amount` | string | Transaction amount | `"50.0"` | -| `created_at` | string | Transaction timestamp (ISO 8601) | `"2023-05-01T12:00:00Z"` | - -`from_account` and `to_account` mirror the broker’s internal `AccountID` values, so they can reference wallets, app session escrow accounts, or channel escrows. - -### Transaction Types - -| Type | Description | Direction | -|------|-------------|-----------| -| **transfer** | Direct transfer between unified balances | Off-chain ↔ Off-chain | -| **deposit** | Funds deposited from channel to unified balance | On-chain → Off-chain | -| **withdrawal** | Funds withdrawn from unified balance to channel | Off-chain → On-chain | -| **app_deposit** | Funds moved from unified balance into app session | Unified → App Session | -| **app_withdrawal** | Funds released from app session to unified balance | App Session → Unified | -| **escrow_lock** | Funds temporarily locked for blockchain operations | Unified → Escrow | -| **escrow_unlock** | Funds released from escrow after blockchain confirmation | Escrow → Unified | - -**Use Cases**: -- Display transaction history in UI -- Export transaction records -- Monitor specific transaction types -- Track payment flows - ---- - -## get_rpc_history - -### Name - -`get_rpc_history` - -### Usage - -Returns the authenticated user's recent RPC invocations, including signed request and response payloads. Useful for audit trails and debugging client integrations. - -### Request - -| Parameter | Type | Required | Description | Default | Example | -|-----------|------|----------|-------------|---------|---------| -| `offset` | number | No | Pagination offset | `0` | `20` | -| `limit` | number | No | Maximum entries to return | `10` (max 100) | `25` | -| `sort` | string | No | Sort order by timestamp | `"desc"` | `"asc"` | - -### Response - -| Parameter | Type | Description | See Also | -|-----------|------|-------------|----------| -| `rpc_entries` | array\ | Recorded invocations | See structure below | - -#### RPCEntry Structure - -| Field | Type | Description | -|-------|------|-------------| -| `id` | number | Internal history identifier | -| `sender` | string | Wallet that issued the call | -| `req_id` | number | Request sequence number | -| `method` | string | RPC method name | -| `params` | string | JSON-encoded request parameters | -| `timestamp` | number | Unix timestamp (seconds) | -| `req_sig` | array\ | Signatures attached to the request | -| `response` | string | JSON-encoded response payload | -| `res_sig` | array\ | Response signatures | - -**Use Cases**: -- Debug client/server mismatches -- Provide user-facing audit logs -- Verify signed payloads during dispute resolution - ---- - -## get_user_tag - -### Name - -`get_user_tag` - -### Usage - -Retrieves the authenticated user's unique alphanumeric tag. User tags provide a human-readable alternative to addresses for [transfer](./transfers) operations, similar to username systems. Tags are automatically generated upon first interaction with a clearnode and remain constant. This is a convenience feature for improving user experience. - -### Request - -No parameters. - -### Response - -| Parameter | Type | Description | Format | Example | Notes | -|-----------|------|-------------|--------|---------|-------| -| `tag` | string | User's unique alphanumeric tag | 6 uppercase alphanumeric characters | `"UX123D"` | Can be used in transfer operations as destination_user_tag | - -### Usage in Transfers - -Instead of using full address: - -```javascript -transfer({destination: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", ...}) -``` - -Users can use the tag: - -```javascript -transfer({destination_user_tag: "UX123D", ...}) -``` - -:::note Human-Readable Addresses -User tags make it easier for users to share their "address" verbally or in non-technical contexts, similar to payment apps like Venmo or Cash App usernames. -::: - ---- - -## get_session_keys - -### Name - -`get_session_keys` - -### Usage - -Retrieves all active (non-expired) session keys for the authenticated user. Shows each session key's address, application name, spending allowances, current usage, expiration, and permissions. Used for managing delegated keys and monitoring spending caps. Only returns session keys (not custody signers). - -### Authentication - -Required (private method) - -### Request - -| Parameter | Type | Required | Description | Default | Example | -|-----------|------|----------|-------------|---------|---------| -| `offset` | number | No | Pagination offset | `0` | `20` | -| `limit` | number | No | Results per page | `10` (max 100) | `25` | -| `sort` | string | No | Sort order by created_at | `"desc"` | `"asc"` | - -### Response - -| Parameter | Type | Description | See Also | -|-----------|------|-------------|----------| -| `session_keys` | array\ | List of active session keys | See structure below | - -#### SessionKeyInfo Structure - -| Field | Type | Description | Default | Notes | -|-------|------|-------------|---------|-------| -| `id` | number | Internal identifier | — | — | -| `session_key` | string (address) | Session key address | — | — | -| `application` | string | Application name for this session | `"clearnode"` | — | -| `allowances` | array\ | Spending limits and usage | — | See structure below | -| `scope` | string | Permission scope | — | Future feature, not fully enforced yet | -| `expires_at` | string (timestamp) | Session expiration time (ISO 8601 format) | — | — | -| `created_at` | string (timestamp) | Session creation time (ISO 8601 format) | — | — | - -**Example**: -```json -{ - "id": 1, - "session_key": "0x9876543210fedcba...", - "application": "Chess Game", - "allowances": [ - {"asset": "usdc", "allowance": "100.0", "used": "45.0"} - ], - "scope": "app.create,transfer", - "expires_at": "2023-05-02T12:00:00Z", - "created_at": "2023-05-01T12:00:00Z" -} -``` - -#### AllowanceUsage - -| Field | Type | Description | -|-------|------|-------------| -| `asset` | string | Asset identifier (e.g., `"usdc"`) | -| `allowance` | string | Total spending limit | -| `used` | string | Amount already spent | - -### Spending Tracking - -The clearnode tracks session key spending by monitoring all ledger debit operations: - -``` -Initial: allowance = 100 USDC, used = 0 USDC -After transfer of 45 USDC: allowance = 100 USDC, used = 45 USDC -Remaining = 55 USDC available for future operations -``` - -When a session key reaches its spending cap, further operations are rejected: - -``` -Error: "operation denied: insufficient session key allowance: 60 required, 55 available" -``` - -:::tip Spending Caps -Session key allowances provide important security: even if a session key is compromised, the maximum loss is limited to the allowance amount. -::: - -**Use Cases**: -- Display active sessions in UI -- Monitor spending against caps -- Manage session lifecycles -- Security auditing - ---- - -## ping - -### Name - -`ping` - -### Usage - -Simple connectivity check to verify the clearnode is responsive and the RPC connection is alive. Returns immediately with success. Used for heartbeat, connection testing, and latency measurement. - -### Authentication - -Not required (public method) - -### Request - -No parameters required (empty object `{}`). - -### Response - -The response method should be `"pong"`. - -| Parameter | Type | Description | Value/Example | Notes | -|-----------|------|-------------|---------------|-------| -| (empty) | object | Empty object or confirmation data | `{}` | Response indicates successful connection | - -### Use Cases - -**Heartbeat**: Periodic ping to keep RPC connection alive -```javascript -setInterval(() => clearnode.call("ping"), 30000) // Every 30 seconds -``` - -**Latency Measurement**: Measure round-trip time -```javascript -const start = Date.now() -await clearnode.call("ping") -const latency = Date.now() - start -console.log(`Latency: ${latency}ms`) -``` - -**Health Check**: Verify connection before critical operations -```javascript -try { - await clearnode.call("ping") - // Connection healthy, proceed with operation -} catch (error) { - // Connection lost, reconnect -} -``` - -**Authentication Status**: Test if session is still valid -```javascript -const response = await clearnode.call("ping") -// If no auth error, session is active -``` - ---- - -## Notifications (Server-to-Client) - -The clearnode sends unsolicited notifications to clients via RPC when certain events occur. These are not responses to requests, but asynchronous messages initiated by the server. - -```mermaid -sequenceDiagram - box rgb(225,245,255) Client - participant Client - end - box rgb(255,225,245) Clearnode - participant Clearnode - end - box rgb(255,245,225) Events - participant Event as Event Source - end - - Note over Client,Clearnode: RPC Connection Established - - Event->>Clearnode: Transfer (incoming/outgoing) - Clearnode->>Client: tr (transfer) notification - - Event->>Clearnode: Balance changed - Clearnode->>Client: bu (balance update) notification - - Event->>Clearnode: Channel opened - Clearnode->>Client: cu (channel update) notification - - Event->>Clearnode: App session updated - Clearnode->>Client: asu (app session update) notification - -``` - -### Notification Types - -| Method | Description | Data Structure | -|--------|-------------|----------------| -| `bu` | Balance update | `balance_updates` array with updated balances | -| `cu` | Channel update | Full `Channel` object | -| `tr` | Transfer (incoming/outgoing) | `transactions` array with transfer details | -| `asu` | App session update | `app_session` object and `participant_allocations` | - ---- - -## bu (Balance Update) - -### Method - -`bu` - -### When Sent - -Whenever account balances change due to transfers, app session operations, or channel operations. - -### Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `balance_updates` | array\ | Updated balances for affected accounts | See structure below | - -#### LedgerBalance Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `asset` | string | Asset symbol | `"usdc"` | -| `amount` | string | New balance amount | `"150.0"` | - -**Use Cases**: -- Update balance display in real-time -- Trigger UI animations for balance changes -- Log balance history for analytics - ---- - -## cu (Channel Update) - -### Method - -`cu` - -### When Sent - -When a channel's state changes (opened, resized, challenged, closed). - -### Structure - -The notification contains the complete updated `Channel` object. See [Channel Structure](#channel-structure) in the `get_channels` section for the full field list. - -**Use Cases**: -- Update channel status in UI -- Alert user when channel becomes active -- Monitor for unexpected channel closures - ---- - -## tr (Transfer) - -### Method - -`tr` - -### When Sent - -When a transfer affects the user's account (both incoming and outgoing transfers). - -### Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `transactions` | array\ | Array of transaction objects for the transfer | See structure below | - -The `LedgerTransaction` structure is identical to the one returned by `get_ledger_transactions`. See [LedgerTransaction Structure](#ledgertransaction-structure) for the full field list. - -**Use Cases**: -- Display incoming/outgoing payment notifications -- Play sound/show toast for transfers -- Update transaction history in real-time - -:::success Real-Time Payments -Combine `tr` notifications with `bu` (balance update) to provide immediate feedback when users send or receive funds. -::: - ---- - -## asu (App Session Update) - -### Method - -`asu` - -### When Sent - -When an app session state changes (new state submitted, session closed, deposits/withdrawals). - -### Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `app_session` | AppSession | Complete app session object | See `get_app_sessions` for structure | -| `participant_allocations` | array\ | Current allocations for each participant | See structure below | - -#### AppAllocation Structure - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `participant` | string | Participant wallet address | `"0x742d35Cc..."` | -| `asset` | string | Asset symbol | `"usdc"` | -| `amount` | string | Allocated amount | `"50.0"` | - -**Use Cases**: -- Update game UI when opponent makes a move -- Refresh session state in real-time -- Alert when session is closed -- Sync multi-participant applications - ---- - -## Implementation Notes - -**Connection Management**: -- Maintain persistent connection for notifications -- Implement automatic reconnection on disconnect -- Re-fetch current state after reconnection - -**Notification Handling**: -- All notifications are asynchronous -- No response required from client -- Multiple notifications may arrive rapidly (batch if needed) - -**Best Practices**: -- Use query methods for initial state retrieval -- Use notifications for ongoing monitoring -- Don't rely solely on notifications (could be missed during disconnect) -- Implement periodic state refresh as backup - -**Pagination**: -- For methods with pagination, use reasonable `limit` values - ---- - -## Next Steps - -Explore other protocol features: - -- **[App Sessions](./app-sessions)** - Create and manage multi-party applications -- **[Transfers](./transfers)** - Send funds between users -- **[Channel Methods](./channel-methods)** - Manage payment channels - -For protocol fundamentals: -- **[Authentication](./authentication)** - Manage session keys -- **[Message Format](./message-format)** - Understand request/response structure diff --git a/docs/protocol/app-layer/off-chain/transfers.mdx b/docs/protocol/app-layer/off-chain/transfers.mdx deleted file mode 100644 index ae9f66f..0000000 --- a/docs/protocol/app-layer/off-chain/transfers.mdx +++ /dev/null @@ -1,377 +0,0 @@ ---- -sidebar_position: 5 -title: Transfer Method ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Transfer Method - -Transfer method enable instant, off-chain fund movement between users. - ---- - -## Overview - -The transfer system allows users to send funds to each other instantly using their unified balance, without any on-chain transactions. Transfers are backed by the security of underlying payment channels and use double-entry bookkeeping for accounting accuracy. - -### Why Use Transfer? - -**Instant Settlement**: Transfers complete immediately with instant finality. - -**No Blockchain Fees**: No blockchain transactions means no gas costs for both sender and recipient. - -**Cross-Chain Unified**: Send from your unified balance across multiple chains. - -**Auditable**: Complete transaction history with double-entry ledger tracking. - -:::success Instant Off-Chain Payments -Transfers provide the speed and convenience of traditional payment networks while maintaining the security guarantees of blockchain-backed channels. -::: - ---- - -## transfer - -### Name - -`transfer` - -### Usage - -Transfer funds from the authenticated user's unified balance to another user's unified balance within the Yellow Network. This is a purely off-chain operation, which results in instant settlement. The transfer updates internal ledger entries using double-entry bookkeeping principles and creates a transaction record for both parties. The security guarantee comes from the underlying on-chain channels that back the unified balance. - -### When to Use - -When sending funds to another Yellow Network user. Common use cases include peer-to-peer payments, merchant payments, tipping. - -### Prerequisites - -- Sender must be [authenticated](./authentication) -- Sender must have sufficient available balance in unified account -- Recipient must be identified by valid wallet address or user tag - -:::info Recipient Requirements -The recipient does not need to have an existing balance or account on the clearnode. Transfers can be sent to any valid wallet address, and the recipient's account will be created automatically on the first login if it doesn't exist. -::: - -### Request - -| Parameter | Type | Required | Description | Format | Example | Notes | -|-----------|------|----------|-------------|--------|---------|-------| -| `destination` | string (wallet address) | Yes (if `destination_user_tag` not provided) | Recipient's wallet address | 0x-prefixed hex string (20 bytes) | `"0x8B3192f2F7b1b34f2e4e7B8C9D1E0F2A3B4C5D6E"` | - | -| `destination_user_tag` | string | Yes (if destination not provided) | Recipient's randomly generated user identifier | Alphanumeric string | `"UX123D"` | Alternative to address; internal feature, may change | -| `allocations` | TransferAllocation[] | Yes (minimum: 1) | Assets and amounts to transfer | Array of allocation objects | `[{"asset": "usdc", "amount": "50.0"}]` | See structure below | - -#### TransferAllocation Structure - -Each allocation in the `allocations` array specifies an asset and amount to transfer: - -| Field | Type | Required | Description | Format | Example | -|-------|------|----------|-------------|--------|---------| -| `asset` | string | Yes | Asset symbol identifier | Lowercase string | `"usdc"`, `"eth"`, `"weth"`, `"btc"` | -| `amount` | string | Yes | Amount to transfer in human-readable format | Decimal string | `"50.0"`, `"0.01"` | - -**Notes**: -- Asset symbols must be lowercase -- Use `get_assets` method to see all supported assets -- Amounts are in human-readable format (e.g., "50.0" for 50 USDC) -- Clearnode handles conversion to smallest unit internally -- Multiple assets can be transferred in a single operation - -**Example**: -```json -{ - "allocations": [ - { - "asset": "usdc", - "amount": "50.0" - }, - { - "asset": "eth", - "amount": "0.01" - } - ] -} -``` - -### Response - -The response contains an array of transactions, with one transaction for each asset being transferred: - -| Parameter | Type | Description | Example | Notes | -|-----------|------|-------------|---------|-------| -| `transactions` | LedgerTransaction[] | Array of transaction objects for each asset | See below | One transaction per asset transferred | - -**LedgerTransaction Structure** (per transaction): - -| Field | Type | Description | Example | -|-------|------|-------------|---------| -| `id` | number | Numeric transaction identifier | `1` | -| `tx_type` | string | Transaction type | `"transfer"` | -| `from_account` | string | Sender account identifier (wallet/app session/channel) | `"0x1234567890abcdef..."` | -| `from_account_tag` | string | Sender's user tag (if exists) | `"NQKO7C"` | -| `to_account` | string | Recipient account identifier | `"0x9876543210abcdef..."` | -| `to_account_tag` | string | Recipient's user tag (if exists) | `"UX123D"` | -| `asset` | string | Asset symbol that was transferred | `"usdc"` | -| `amount` | string | Amount transferred for this asset (decimal string) | `"50.0"` | -| `created_at` | string | ISO 8601 timestamp | `"2023-05-01T12:00:00Z"` | - -**Example Response**: - -```json -{ - "transactions": [ - { - "id": 1, - "tx_type": "transfer", - "from_account": "0x1234567890abcdef...", - "from_account_tag": "NQKO7C", - "to_account": "0x9876543210abcdef...", - "to_account_tag": "UX123D", - "asset": "usdc", - "amount": "50.0", - "created_at": "2023-05-01T12:00:00Z" - }, - { - "id": 2, - "tx_type": "transfer", - "from_account": "0x1234567890abcdef...", - "from_account_tag": "NQKO7C", - "to_account": "0x9876543210abcdef...", - "to_account_tag": "UX123D", - "asset": "eth", - "amount": "0.1", - "created_at": "2023-05-01T12:00:00Z" - } - ] -} -``` - ---- - -## Off-Chain Processing - -When a transfer is executed, the clearnode performs the following operations: - -```mermaid -sequenceDiagram - box rgb(225,245,255) Client A - participant Alice as Client A (Sender) - end - box rgb(255,225,245) Service - participant Clearnode - end - box rgb(225,255,225) Client B - participant Bob as Client B (Recipient) - end - - Note over Alice: 1. Send Transfer Request - Alice->>Clearnode: transfer({ destination, allocations }) - - Note over Clearnode: 2. Validate - Clearnode->>Clearnode: Verify authentication - Clearnode->>Clearnode: Check available balance - Clearnode->>Clearnode: Validate allocations - - Note over Clearnode: 3. Update Ledger - Clearnode->>Clearnode: Create debit entry (Alice -50 USDC) - Clearnode->>Clearnode: Create credit entry (Bob +50 USDC) - Clearnode->>Clearnode: Record transaction - - Note over Clearnode: 4. Send Responses & Notifications - Note over Bob: 5. Balance Updated - Bob->>Bob: Balance +50 USDC - - Clearnode->>Alice: tr (transfer) notification - Clearnode->>Alice: bu (balance update) notification - Clearnode->>Bob: tr (transfer) notification - Clearnode->>Bob: bu (balance update) notification - Clearnode->>Alice: response - -``` - -### Step-by-Step Process - -#### 1. Validates Request - -The clearnode performs comprehensive validation: -- Verifies authentication and signature -- Checks sender has sufficient available balance in unified account -- Validates allocations format and asset support - -#### 2. Updates Ledger (Double-Entry Bookkeeping) - -Every transfer creates two ledger entries - one for the sender and one for the recipient. The ledger uses double-entry bookkeeping principles where each entry has both `credit` and `debit` fields, with amounts always recorded as positive values. - -:::info Double-Entry Bookkeeping -The double-entry system ensures that the total of all debits always equals the total of all credits, providing mathematical proof of accounting accuracy. Every transfer is recorded twice - once as a debit to the sender's account and once as a credit to the recipient's account. -::: - -#### 3. Records Transaction - -A user-facing transaction record is created for each asset being transferred, containing information about the sender, recipient, asset, and amount. - -#### 4. Sends Notifications - -- **Both parties** receive `tr` (transfer) notification with transaction details -- **Both parties** receive `bu` (balance update) notification with updated balances - -#### 5. Response - -- **Sender** receives response with transaction details - ---- - -## Unified Balance Mechanics - -The unified balance aggregates funds from all chains. - -### Example: Multi-Chain Aggregation - -``` -User deposited: - $10 USDC on Ethereum - $5 USDC on Polygon - $3 USDC on Base - -Unified Balance: $18 USDC total - -User can transfer: Any amount up to $18 USDC -``` - -### Account Types - -The ledger system maintains three types of accounts: - -1. **Unified Account**: Main account identified by wallet address. This is where user funds are stored and can be transferred or withdrawn. - -2. **App Session Account**: Identified by app session ID. Participant wallets are beneficiaries of this account. Funds in app sessions are locked for the duration of the session. - -3. **Channel Escrow Account**: Temporary account that locks funds when user requests blockchain operations like resize. Funds remain in this account until the transaction is confirmed on-chain. - - - -{/* TODO: Document actual error codes from implementation. Currently removed as placeholder errors were inaccurate. */} - - ---- - -## Transaction History Query Methods - -Users can query their transfer history using two methods for different levels of detail. - ---- - -### get_ledger_transactions - -Retrieves user-facing transaction log with sender, recipient, amount, and type. This endpoint provides a view of transactions where the specified account appears as either the sender or receiver. - -:::info Public Endpoint -This is a public endpoint - authentication is not required. -::: - -#### Request - -| Parameter | Type | Required | Description | Format | Example | Notes | -|-----------|------|----------|-------------|--------|---------|-------| -| `account_id` | string | No | Filter by account ID (wallet, app session, or channel) | Hex string or ID | `"0x1234567890abcdef..."` | Returns transactions for this account | -| `asset` | string | No | Filter by asset symbol | Lowercase string | `"usdc"` | Returns transactions for this asset only | -| `tx_type` | string | No | Filter by transaction type | `transfer`, `deposit`, `withdrawal`, `app_deposit`, `app_withdrawal`, `escrow_lock`, `escrow_unlock` | `"transfer"` | Returns only this type of transaction | -| `offset` | number | No | Pagination offset | `0` | `42` | Defaults to `0` | -| `limit` | number | No | Number of transactions to return | `10` (max 100) | `10` | Defaults to 10 if omitted | -| `sort` | string | No | Sort order by created_at | `"asc"` or `"desc"` | `"desc"` | Default: `"desc"` | - -#### Response - -| Parameter | Type | Description | -|-----------|------|-------------| -| `ledger_transactions` | LedgerTransaction[] | Array of transaction objects | - -**LedgerTransaction Structure**: - -| Field | Type | Description | -|-------|------|-------------| -| `id` | number | Unique transaction reference | -| `tx_type` | string | Transaction type | -| `from_account` | string | Sender account identifier | -| `from_account_tag` | string | Sender's user tag (empty if none) | -| `to_account` | string | Recipient account identifier | -| `to_account_tag` | string | Recipient's user tag (empty if none) | -| `asset` | string | Asset symbol | -| `amount` | string | Transaction amount (decimal string) | -| `created_at` | string | ISO 8601 timestamp | - ---- - -### get_ledger_entries - -Retrieves detailed accounting entries showing all debits and credits. This endpoint provides double-entry bookkeeping records for detailed reconciliation and audit trails. - -:::info Public Endpoint -This is a public endpoint - authentication is not required. -::: - -#### Request - -| Parameter | Type | Required | Description | Format | Example | Notes | -|-----------|------|----------|-------------|--------|---------|-------| -| `account_id` | string | No | Filter by account ID (wallet/app session/channel) | Hex string or ID | `"0x1234567890abcdef..."` | Returns entries for this account | -| `wallet` | string | No | Filter by participant wallet | 0x-prefixed hex string (20 bytes) | `"0x1234567890abcdef..."` | Returns entries for this participant | -| `asset` | string | No | Filter by asset symbol | Lowercase string | `"usdc"` | Returns entries for this asset only | -| `offset` | number | No | Pagination offset | `0` | `42` | Defaults to `0` | -| `limit` | number | No | Number of entries to return | `10` (max 100) | `10` | Defaults to 10 if omitted | -| `sort` | string | No | Sort order by created_at | `"asc"` or `"desc"` | `"desc"` | Default: `"desc"` | - -#### Response - -| Parameter | Type | Description | -|-----------|------|-------------| -| `ledger_entries` | LedgerEntry[] | Array of ledger entry objects | - -**LedgerEntry Structure**: - -| Field | Type | Description | -|-------|------|-------------| -| `id` | number | Unique entry ID | -| `account_id` | string | Account identifier | -| `account_type` | number | Account type (`1000`=asset, `2000`=liability, etc.) | -| `asset` | string | Asset symbol | -| `participant` | string | Participant wallet address | -| `credit` | string | Credit amount (positive value or "0.0") | -| `debit` | string | Debit amount (positive value or "0.0") | -| `created_at` | string | ISO 8601 timestamp | - ---- - -## Implementation Notes - -**Performance**: -- Transfers are instant (< 1 second) and atomic -- No blockchain transaction required -- No blockchain fees - -**Features**: -- Unified balance is updated immediately -- Transfer can include multiple assets in one operation -- Transaction IDs can be used to track and query transfer status via `get_ledger_transactions` - -**Audit Trail**: -- Clearnode maintains complete audit trail of all transfers -- Double-entry bookkeeping ensures mathematical accuracy -- All records queryable via `get_ledger_*` methods - ---- - -## Next Steps - -Explore other off-chain operations: - -- **[App Sessions](./app-sessions)** - Create multi-party application channels -- **[Queries & Notifications](./queries)** - Check balances, transactions, and receive updates -- **[Channel Methods](./channel-methods)** - Manage payment channels - -For protocol fundamentals: -- **[Authentication](./authentication)** - Understand authorization and session management -- **[Message Format](./message-format)** - Learn request/response structure diff --git a/docs/protocol/app-layer/on-chain/_category_.json b/docs/protocol/app-layer/on-chain/_category_.json deleted file mode 100644 index a75feed..0000000 --- a/docs/protocol/app-layer/on-chain/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "On-Chain Contracts", - "position": 1, - "collapsible": false, - "collapsed": false -} diff --git a/docs/protocol/app-layer/on-chain/channel-lifecycle.mdx b/docs/protocol/app-layer/on-chain/channel-lifecycle.mdx deleted file mode 100644 index df747dc..0000000 --- a/docs/protocol/app-layer/on-chain/channel-lifecycle.mdx +++ /dev/null @@ -1,425 +0,0 @@ ---- -sidebar_position: 3 -title: "Channel Lifecycle" ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Channel Lifecycle - -## State Transitions Overview - -The lifecycle of a channel moves through well-defined states depending on how participants interact with the custody contract. - -```mermaid -stateDiagram-v2 - [*] --> VOID - VOID --> INITIAL: create() - VOID --> ACTIVE: create() (sigs from all participants) - INITIAL --> ACTIVE: join() (all participants) - ACTIVE --> ACTIVE: resize() - ACTIVE --> ACTIVE: checkpoint() - ACTIVE --> DISPUTE: challenge() - ACTIVE --> FINAL: close() (cooperative) - DISPUTE --> ACTIVE: checkpoint() (newer state) - DISPUTE --> FINAL: challenge period expires - FINAL --> [*] - - note right of ACTIVE: Operational state
Off-chain updates occur here - note right of DISPUTE: Challenge period active
Parties can submit newer states -``` - -Use the sections below for details on each phase. - -## Creation Phase - -**Purpose**: Initiate a new channel with specified participants and initial funding. - -**Process**: - -1. The Creator: - - Constructs a Channel configuration with participants, adjudicator, challenge period, and nonce - - Prepares an initial State with application-specific app data - - Defines expected token deposits for all participants in `state.allocations` - - Signs the computed packedState of this initial state - - Includes Creator's signature in `state.sigs` at position 0 - - Calls either `create(...)` or `depositAndCreate(...)` function with the channel configuration and initial signed state - -:::tip Implicit Join (Immediate Activation) -If the Creator obtains the second participant's signature on the initial state **before** calling `create()`, they can supply both signatures in `state.sigs` (positions 0 and 1). When the contract detects `sigs.length == 2`: -- It verifies both signatures -- Locks funds from both participants -- Transitions directly to `ACTIVE` status (skipping `INITIAL`) -- Emits both `Joined` and `Opened` events - -This "implicit join" is the **recommended approach** for faster channel activation and reduced gas costs (single transaction instead of two). -::: - -2. The contract: - - Verifies the Creator's signature on the funding packedState - - Verifies Creator has sufficient balance to fund their allocation - - Locks the Creator's funds according to the allocation - - Sets the channel status to `INITIAL` - - Emits a `Created` event with channelId, channel configuration, and expected deposits - -```mermaid -sequenceDiagram - participant Creator - participant Contract - - Note over Contract: Status = VOID - Creator->>Creator: Construct Channel config - Creator->>Creator: Create initial State - Creator->>Creator: Sign packedState - Creator->>Contract: create(channel, state) - Contract->>Contract: Verify signature - Contract->>Contract: Lock Creator funds - Contract->>Contract: Set status to INITIAL - Note over Contract: Status = INITIAL - Contract->>Creator: Emit Created event -``` - -:::info Participant versus Caller address -The first participant address is usually different from the caller (EOA or contract), thus enabling channel operation delegation. This can be fruitful as users can fund channels for other ones. -::: - -## Joining Phase - -:::info Two Channel Opening Flows -There are two ways to open a channel: -1. **Modern/Recommended**: Provide ALL signatures in `create()` → channel immediately ACTIVE (see [Architecture](../../architecture#app-layer-architecture)) -2. **Legacy/Manual**: Provide only creator's signature in `create()` → status INITIAL → separate `join()` calls → ACTIVE - -This section documents flow #2. Most implementations use flow #1. -::: - -**Purpose**: Allow other participants to join and fund the channel (when using separate join flow). - -**Process**: - -1. Each non-Creator participant: - - Verifies the channelId and expected allocations - - Signs the same funding packedState - - Calls the `join` function with channelId, their participant index, and signature - -2. The contract: - - Verifies the participant's signature against the funding packedState - - Confirms the signer matches the expected participant at the given index - - Locks the participant's funds according to the allocation - - Tracks the actual deposit in the channel metadata - - Emits a `Joined` event with channelId and participant index - -3. When all participants have joined, the contract: - - Verifies that all expected deposits are fulfilled - - Sets the channel status to `ACTIVE` - - Emits an `Opened` event with channelId - -```mermaid -sequenceDiagram - participant P as Participant - participant C as Contract - participant S as System - - rect rgb(200, 220, 250) - Note right of C: Status INITIAL - P->>P: Sign funding packedState - P->>C: join(channelId, index, signature) - C->>C: Verify signature - C->>C: Lock participant funds - C->>P: Emit Joined event - end - - alt All participants joined - Note right of C: Status ACTIVE - C->>C: Set status to ACTIVE - C->>S: Emit Opened event - end - -``` - -:::success Channel Activation -The channel becomes operational only when ALL participants have successfully joined and funded their allocations. -::: - -## Active Phase - -**Purpose**: Enable off-chain state updates while channel is operational. - -### Off-Chain Updates - -Participants: -- Exchange and sign state updates off-chain via the Nitro RPC protocol -- Maintain a record of the latest valid state -- Use application-specific data in the `state.data` field - -Each new state: -- May update allocations when assets are transferred (though allocations can remain unchanged between states, e.g., game moves without fund transfers) -- MUST be signed by the necessary participants according to adjudicator rules -- MUST comply with the validation rules of the channel's adjudicator - -The on-chain contract remains unchanged during the active phase unless participants choose to checkpoint a state. - -:::tip Off-Chain Efficiency -During the active phase, state updates occur entirely off-chain with zero gas costs and sub-second latency. -::: - -## Checkpointing - -**Purpose**: Record a state on-chain without entering dispute mode. - -**Process**: - -1. Any participant: - - Calls the `checkpoint` function with a valid state and required proofs - -2. The contract: - - Verifies the submitted state via the adjudicator - - If valid and more recent than the previously checkpointed state, stores it - - Emits a `Checkpointed` event with channelId - -```mermaid -graph LR - A[Active Channel
Status: ACTIVE] -->|checkpoint| B[Verify State] - B -->|Valid| C[Store State] - C --> D[Emit Event] - D --> E[Remain Active
Status: ACTIVE] - - style A fill:#90EE90 - style E fill:#90EE90 -``` - -:::note Optional Operation -Checkpointing is optional but recommended for long-lived channels or after significant value transfers. -::: - -## Closure - Cooperative - -**Purpose**: Close channel to distribute locked funds, after all participants have agreed on the final state. - -**Process**: - -1. Any participant: - - Prepare a final State with `intent` equal to `FINALIZE`. - - Collects signatures from all participants on this final state - - Calls the `close` function with channelId, final state, and any required proofs - -2. The contract: - - Verifies all participant signatures on the closing packedState - - Verifies the state has `intent` equal to `FINALIZE`. - - Distributes funds according to the final state's allocations - - Sets the channel status to `FINAL` - - Deletes the channel metadata - - Emits a `Closed` event - -```mermaid -sequenceDiagram - participant User - participant Contract - - Note over Contract: Status = ACTIVE - User->>User: Create final State (intent=FINALIZE) - User->>User: Collect all signatures - User->>Contract: close(channelId, state, proofs) - Contract->>Contract: Verify all signatures - Contract->>Contract: Verify intent = FINALIZE - Contract->>Contract: Distribute funds - Contract->>Contract: Set status to FINAL - Note over Contract: Status = FINAL - Contract->>Contract: Delete metadata - Contract->>User: Emit Closed event -``` - -:::success Preferred Method -**This is the preferred closure method as it is fast and gas-efficient.** It requires only one transaction and completes immediately without a challenge period. -::: - -## Closure - Challenge-Response - -**Purpose**: Handle closure when participants disagree or one party is unresponsive. - -### Challenge Process - -1. To initiate a challenge, a participant: - - Calls the `challenge` function with their latest valid state and required proofs - -:::note Latest State Location -The participant's latest state may only exist off-chain and not be known on-chain yet. The challenge process brings this off-chain state on-chain for validation. -::: - -2. The contract: - - Verifies the submitted state via the adjudicator - - If valid, stores the state and starts the challenge period - - Sets a challenge expiration timestamp (current time + challenge duration) - - Sets the channel status to `DISPUTE` - - Emits a `Challenged` event with channelId and expiration time - -```mermaid -sequenceDiagram - participant User - participant Contract - participant Timer - - Note over Contract: Status = ACTIVE - User->>Contract: challenge(channelId, state, proofs) - Contract->>Contract: Verify state - Contract->>Contract: Store state - Contract->>Contract: Set status to DISPUTE - Note over Contract: Status = DISPUTE - Contract->>Timer: Start challenge period - Contract->>User: Emit Challenged event -``` - -### Resolving Challenge with Checkpoint - -During the challenge period, any participant: -- Submits a more recent valid state by calling `checkpoint()` -- If the new state is valid and more recent (as determined by the adjudicator or IComparable interface), the contract updates the stored state, resets the challenge period, and returns the channel to `ACTIVE` status - -### Challenge Period Elapse - -After the challenge period expires, any participant: -- Call `close` with an empty candidate and proof to distribute funds according to the last valid challenged state - -The contract: -- Verifies the challenge period has elapsed -- Distributes funds according to the challenged state's allocations -- Sets channel status to `FINAL` -- Deletes the channel metadata -- Emits a `Closed` event - -:::warning Key Principle -The challenge mechanism gives parties time to prove they have a newer state. If no one responds with a newer state, the challenged state is assumed correct. -::: - -**Complete Challenge-Response Flow**: - -```mermaid -stateDiagram-v2 - [*] --> Active - Active --> Dispute: challenge() - Dispute --> Active: checkpoint() with newer state - Dispute --> Final: close() after timeout - Final --> [*] - - note right of Dispute: Challenge period active
Parties can submit
newer states -``` - -## Resize Protocol - -**Purpose**: Adjust funds locked in the channel by locking or unlocking funds **without closing the channel**. - -**Process**: - -1. Any participant: - - Calls the `resize` function with: - - The channelId (remains unchanged) - - A candidate State with: - - `intent` = `StateIntent.RESIZE` - - `version` = precedingState.version + 1 - - `data` = ABI-encoded `int256[]` containing delta amounts (positive for deposit, negative for withdrawal) respectively for participants - - `allocations` = Allocation[] after resize (absolute amounts) - - Signatures from **ALL participants** (consensus required) - - An array of proof states containing the previous state (`version-1`) first and its proof later in the array - -:::note Deposit Requirement -The participant depositing must have at least the corresponding amount in their Custody ledger account (available balance) to lock additional funds to the channel. -::: - -2. The contract: - - Verifies the channel is in ACTIVE status - - Verifies all participants have signed the resize state - - Decodes delta amounts from `candidate.data` - - Validates adjudicator approves the preceding state - - For positive deltas: Locks additional funds from custody account - - For negative deltas: Unlocks funds back to custody account - - Updates expected deposits to match new allocations - - Emits `Resized(channelId, deltaAllocations)` event - -3. The channel: - - **channelId remains UNCHANGED** (same channel persists) - - Status remains **ACTIVE** throughout - - Version increments by 1 - - No new channel is created - -```mermaid -sequenceDiagram - participant User - participant Contract - - Note over Contract: Status = ACTIVE - User->>Contract: resize(channelId, resizeState, proofs) - Note right of Contract: Same channelId
State version + 1
Intent = RESIZE - Contract->>Contract: Verify signatures (all participants) - Contract->>Contract: Decode delta amounts from state.data - Contract->>Contract: Lock funds (positive deltas) - Contract->>Contract: Unlock funds (negative deltas) - Contract->>Contract: Update expected deposits - Note over Contract: Status = ACTIVE - Contract->>User: Resized(channelId, deltas) - Note right of User: Same channelId
Channel still ACTIVE -``` - -**Use Cases**: -- Increasing funds locked in the channel (positive delta: adding funds) -- Decreasing funds locked in the channel (negative delta: removing funds) -- Adjusting fund distribution while maintaining channel continuity - -:::tip In-Place Update -The resize operation updates the channel **in place**. The channelId stays the same, and the channel remains ACTIVE throughout. This differs from closing and reopening, which would create a new channel. -::: - -:::note Implicit Transfer with Resize -It is possible to combine a transfer (change of allocations among participants) with a resize operation. For example: -- Previous state allocations: `[5, 10]` -- Desired transfer: 2 tokens from second to first participant → `[7, 8]` -- Additional changes: first participant withdraws all 7, second participant deposits 6 -- Delta amounts: `[-7, 6]` -- Resize state allocations: `[0, 14]` - -**Rule**: `sum(allocations_resize_state) = sum(allocations_prev_state) + sum(delta_amounts)` -For this example: `14 = 15 + (-1)` ✓ -::: - -## State Transition Summary - -The complete channel lifecycle state machine: - -```mermaid -stateDiagram-v2 - [*] --> VOID: Initial - VOID --> INITIAL: create() - VOID --> ACTIVE: create() with all sigs - INITIAL --> ACTIVE: join() all - ACTIVE --> ACTIVE: checkpoint() - ACTIVE --> ACTIVE: resize() - ACTIVE --> DISPUTE: challenge() - ACTIVE --> FINAL: close() cooperative - DISPUTE --> ACTIVE: checkpoint() newer - DISPUTE --> FINAL: close() after timeout - FINAL --> [*]: Deleted - - note right of VOID: Channel does not exist - note right of INITIAL: Awaiting participants - note right of ACTIVE: Operational
Off-chain updates - note right of DISPUTE: Challenge active
Response period - note right of FINAL: Funds distributed
Ready for deletion -``` - -**Valid Transitions**: - -| From | To | Trigger | Requirements | -|------|----|---------|--------------| -| VOID | INITIAL | `create()` | Creator signature, sufficient balance, INITIALIZE intent || VOID | ACTIVE | `create()` with all sigs | All participants' signatures, sufficient balances | -| INITIAL | ACTIVE | `join()` | All participants joined and funded | -| ACTIVE | ACTIVE | `checkpoint()` | Valid newer state | -| ACTIVE | ACTIVE | `resize()` | All signatures, valid deltas, sufficient balance | -| ACTIVE | DISPUTE | `challenge()` | Valid state newer than latest known on-chain | -| ACTIVE | FINAL | `close()` | All signatures, FINALIZE intent | -| DISPUTE | ACTIVE | `checkpoint()` | Valid newer state | -| DISPUTE | FINAL | `close()` | Challenge period expired | -| FINAL | VOID | Automatic | Metadata deleted | - -:::note Channel Deletion -When a channel reaches FINAL status, the channel metadata is deleted from the chain and funds are distributed according to the final state allocations. -::: diff --git a/docs/protocol/app-layer/on-chain/data-structures.mdx b/docs/protocol/app-layer/on-chain/data-structures.mdx deleted file mode 100644 index f236dfe..0000000 --- a/docs/protocol/app-layer/on-chain/data-structures.mdx +++ /dev/null @@ -1,212 +0,0 @@ ---- -sidebar_position: 2 -title: "Data Structures" ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Data Structures - -## Channel - -Represents the configuration of a state channel. - -```solidity -struct Channel { - address[] participants; // List of participants in the channel - address adjudicator; // Contract that validates state transitions - uint64 challenge; // Duration in seconds for dispute resolution - uint64 nonce; // Unique identifier for the channel -} -``` - -**Fields**: - -- `participants`: An ordered array of participant addresses. Index 0 is typically the Creator, index 1 is the clearnode. -- `adjudicator`: Address of the adjudicator contract responsible for validating state transitions. -- `challenge`: Challenge period duration in seconds. Determines a time window when a challenge can be resolved by a counterparty. Otherwise, a channel is considered closed and funds can be withdrawn. -- `nonce`: A unique number that, combined with other fields, creates a unique channel identifier. - -:::info Participant versus Caller Address -The first participant address is usually different from the caller (EOA or contract), thus enabling channel operation delegation. This can be fruitful as users can fund channels for other ones. -::: - -## State - -Represents a snapshot of channel state at a point in time. - -```solidity -struct State { - StateIntent intent; // Intent of the state (INITIALIZE, OPERATE, RESIZE, FINALIZE) - uint256 version; // State version incremental number to compare most recent - bytes data; // Application-specific data - Allocation[] allocations; // Asset allocation for each participant - bytes[] sigs; // Participant signatures authorizing the packed state payload -} -``` - -**Fields**: - -- `intent`: The intent of this state, indicating its purpose (see StateIntent enum). -- `version`: Incremental version number used to compare and validate state freshness. Higher versions supersede lower versions. -- `data`: Application-specific data which adjudicators can operate on. For a `resize(...)` state must contain `allocationDeltas`. For more information, please check the [resize operation docs](./channel-lifecycle#resize-protocol). -- `allocations`: Array of allocations defining how funds are distributed. -- `sigs`: Array of participant signatures over the canonical packed state payload. Order corresponds to the Channel's participants array. - -## Allocation - -Specifies how a particular amount of a token should be allocated. - -```solidity -struct Allocation { - address destination; // Recipient of funds - address token; // ERC-20 token address - uint256 amount; // Token amount in smallest unit -} -``` - -**Fields**: - -- `destination`: Address that will receive the funds when channel closes. -- `token`: Contract address of the ERC-20 token (or zero address for native currency). -- `amount`: Amount in the token's smallest unit (wei for ETH, considering decimals for ERC-20). - -## Signatures - -Signatures in VirtualApp are stored as raw `bytes` so the protocol can validate multiple scheme formats. - -```solidity -struct Signature { - uint8 v; // Recovery identifier - bytes32 r; // First 32 bytes of signature - bytes32 s; // Second 32 bytes of signature -} -``` - -At a minimum VirtualApp currently recognizes the following signature families (see the [Signature Formats](./signature-formats) reference for the full specification): - -- **Raw/Pre-EIP-191 ECDSA** – Signs `keccak256(packedState)` without any prefix. -- **EIP-191 (version `0x45`)** – Signs a structured message that prefixes the packed state with the Ethereum signed message header and length. -- **EIP-712 Typed Data** – Signs `keccak256(abi.encode(domainSeparator, hashStruct(state)))`. -- **EIP-1271 Smart-Contract Signatures** – Arbitrary bytes validated via `isValidSignature` on the signer contract. -- **EIP-6492 Counterfactual Signatures** – Wraps deployment data to prove a not-yet-deployed ERC-4337 wallet authorized the state. - -Refer to the dedicated page for verification order, payload layouts, and implementation guidance. - -## Amount - -Represents a quantity of a specific token. - -```solidity -struct Amount { - address token; // ERC-20 token address - uint256 amount; // Token amount -} -``` - -## Channel Status - -Enum representing the lifecycle stage of a channel. - -```solidity -enum Status { - VOID, // Channel does not exist - INITIAL, // Creation in progress, awaiting all participants - ACTIVE, // Fully funded and operational - DISPUTE, // Challenge period active - FINAL // Ready to be closed and deleted -} -``` - -## Protocol Constants - -### Participant Indices - -```solidity -constant uint256 CLIENT_IDX = 0; // Client/Creator participant index -constant uint256 SERVER_IDX = 1; // Server/Clearnode participant index -constant uint256 PART_NUM = 2; // Number of participants (always 2) -``` - -### Challenge Period - -```solidity -uint256 public constant MIN_CHALLENGE_PERIOD = 1 hours; -``` - -The minimum challenge period enforced by the Custody Contract. Channel configurations must specify a challenge period of at least 1 hour. - -### EIP-712 Type Hashes - -The protocol uses EIP-712 structured data signing with the following domain parameters: - -```solidity -// EIP-712 Domain -name: "VirtualApp:Custody" -version: "0.3.0" -``` - -Type hashes for state validation: - -```solidity -// State hash computation for signatures -bytes32 constant STATE_TYPEHASH = keccak256( - "AllowStateHash(bytes32 channelId,uint8 intent,uint256 version,bytes data,Allocation[] allocations)Allocation(address destination,address token,uint256 amount)" -); - -// Challenge state hash computation -bytes32 public constant CHALLENGE_STATE_TYPEHASH = keccak256( - "AllowChallengeStateHash(bytes32 channelId,uint8 intent,uint256 version,bytes data,Allocation[] allocations)Allocation(address destination,address token,uint256 amount)" -); -``` - -These type hashes enable human-readable signature prompts in wallets and improve security by preventing signature replay attacks across different contexts. - -## Identifier Computation - -### Channel Identifier - -The channelId MUST be computed as: - -```javascript -channelId = keccak256( - abi.encode( - channel.participants, - channel.adjudicator, - channel.challenge, - channel.nonce, - chainId - ) -) -``` - -This creates a deterministic, unique identifier for each channel. - -:::info App Session Identifiers -App sessions use a different computation: `keccak256(JSON.stringify(definition))` where definition includes the app configuration but **not** `chainId`, since sessions are entirely off-chain. See [Off-chain › App Sessions › Session Identifier](../off-chain/app-sessions#session-identifier) for details. -::: - -:::note Deterministic IDs -Channel IDs are deterministically computed from the channel configuration, ensuring the same configuration always produces the same identifier. -::: - -### Packed State - -The legacy state hash concept was removed in v0.3.0 when non-ECDSA signatures were introduced. Instead, participants use the **packed state** payload for signing: - -```javascript -packedState = abi.encode( - channelId, - state.intent, - state.version, - state.data, - state.allocations -) -``` - -The packed state is simply `abi.encode(channelId, state.intent, state.version, state.data, state.allocations)`. This byte array is fed into the selected signing scheme (EIP-712 hashing, ERC-1271 contract checks, NO_EIP712 fallback, etc.). Each scheme may wrap or hash `packedState` as needed, but the canonical payload MUST be the input. - -:::warning Signature Verification -All state updates MUST be verified by checking signatures against the canonical `packedState` payload (after the signing method applies its required hashing/wrapping) before accepting them on-chain. -::: diff --git a/docs/protocol/app-layer/on-chain/overview.mdx b/docs/protocol/app-layer/on-chain/overview.mdx deleted file mode 100644 index f1dd387..0000000 --- a/docs/protocol/app-layer/on-chain/overview.mdx +++ /dev/null @@ -1,50 +0,0 @@ ---- -sidebar_position: 1 -title: "Overview" ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# On-Chain Protocol Overview - -The on-chain protocol defines the smart contract interfaces and data structures that form the foundation of VirtualApp's security guarantees. This layer operates on the blockchain and handles: - -- **Fund Custody**: Secure locking and unlocking of participant assets -- **Dispute Resolution**: Challenge-response mechanism for disagreements -- **Final Settlement**: Distribution of funds according to validated states -- **Channel Lifecycle**: State transitions from creation to closure - -## Key Responsibilities - -The on-chain layer MUST provide: - -1. **Deterministic channel identifiers** computed from channel configuration -2. **Signature verification** to authenticate state updates -3. **State validation** through adjudicator contracts -4. **Challenge periods** to ensure fair dispute resolution -5. **Fund safety** guaranteeing users can always recover their assets - -:::info EVM Compatibility -The initial version of VirtualApp is designed for EVM-compatible blockchains including Ethereum, Polygon, Base, and other EVM chains. Support for additional networks is continuously expanding. -::: - -## Contract Interfaces - -The protocol defines three primary contract interfaces: - -- **IChannel**: Core channel lifecycle operations (create, join, challenge, close) -- **IDeposit**: Token deposit and withdrawal management -- **IChannelReader**: Read-only queries for channel state and status - -These interfaces are implemented by the **Custody Contract**, which serves as the main entry point for on-chain operations. - - -## Next Steps - -The following sections detail: - -- [Data Structures](./data-structures): Core types and identifier computation -- [Channel Lifecycle](./channel-lifecycle): Complete state machine and operations -- [Security Considerations](./security): Threat model and best practices - diff --git a/docs/protocol/app-layer/on-chain/security.mdx b/docs/protocol/app-layer/on-chain/security.mdx deleted file mode 100644 index 1298fc2..0000000 --- a/docs/protocol/app-layer/on-chain/security.mdx +++ /dev/null @@ -1,331 +0,0 @@ ---- -sidebar_position: 4 -title: "Security Considerations" ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Security Considerations - -## Current Limitations - -The current Custody contract implementation has the following limitations: - -- **Two-participant channels only**: Channels support exactly 2 participants -- **Participant role constraint**: First participant must always be a client, while second must be a Clearnode -- **Single allocation per participant**: Each participant can have only 1 allocation -- **Same-token allocations**: Both allocations must be for the same token -- **Minimum challenge duration**: Challenge duration is set to be no less than 1 hour -- **No re-challenge**: It is not possible to challenge an already challenged channel -- **No direct EOA resize**: It is not possible to resize directly from or to your EOA; you must deposit to or withdraw funds from the Custody contract first -- **Channel required for withdrawal**: It is not possible to withdraw your funds from the Unified Balance on a chain with no open channel without opening a channel first. In a future major release, we plan to merge these steps in one operation -- **Separate resize and balance operations**: It is not possible to top-up a Unified Balance from or withdraw to your EOA balance in the same `resize(...)` operation. You must deposit your funds prior to or withdraw after the `resize(...)` operation. In a future major release, we plan to merge these steps in one operation - -:::note Future Improvements -Many of these limitations are implementation-specific and are planned to be addressed in future major releases. They do not represent fundamental protocol constraints. -::: - -## Threat Model - -### Assumptions - -The protocol operates under the following security assumptions: - -- **At least one honest party per channel** willing to enforce their rights -- **Blockchain is secure and censorship-resistant** within reasonable bounds -- **Cryptographic primitives are secure** (ECDSA, keccak256) -- **Participants have access to the blockchain** to submit challenges within the challenge period - -:::info Trust Model -VirtualApp is designed as a **trustless protocol** - no single party can steal funds or prevent others from recovering their legitimate share. -::: - -### Protected Against - -The protocol provides protection against: - -- **Replay attacks** via version number checking in Custody contract -- **State withholding** via challenge mechanism -- **Unauthorized state transitions** via signature verification -- **Funds theft** - all transitions require valid signatures from appropriate parties - -### Not Protected Against - -The protocol cannot protect against: - -- **All participants colluding** to violate application rules -- **Blockchain-level attacks** (51% attacks, MEV exploitation, etc.) -- **Denial of service by blockchain congestion** - may affect ability to respond to challenges - -:::warning Blockchain Dependency -The security of VirtualApp channels depends on the underlying blockchain's liveness and security. Extended blockchain downtime during a challenge period could prevent parties from responding. -::: - -## Security Properties - -### Funds Safety - -**Property**: Participants can always recover their funds according to the latest valid signed state, even if other participants become unresponsive. - -**Mechanism**: The challenge-response system ensures that: -1. Any party can initiate closure unilaterally -2. Challenge period allows time for others to respond with newer states -3. Newest valid state always wins -4. Funds are distributed according to the final accepted state - -```mermaid -graph TB - A["User has latest
signed state"] --> B{"Other participant responsive?"} - B -->|Yes| C["Cooperative close
Fast & cheap"] - B -->|No| D["Challenge with
latest state"] - D --> E["Wait challenge
period"] - E --> F{"Communication continuation suggested?"} - F -->|No| G["Close & recover funds"] - F -->|Yes| H["Create and submit a
newer state via checkpoint(...)"] - H --> E - - style C fill:#90EE90 - style G fill:#90EE90 - -``` - -### State Validity - -**Property**: Only states signed by the required participants (as determined by the adjudicator) can be accepted. - -**Mechanism**: -- Every state update requires cryptographic signatures -- Signatures are verified against the packedState -- Adjudicator validates state transitions according to application rules -- Invalid states are rejected on-chain - -:::success Cryptographic Security -State validity is enforced through [supported signatures](./signature-formats), all of which are supported by Ethereum itself. -::: - -#### EIP-712 Signature Support - -VirtualApp supports **EIP-712 (Typed Structured Data)** signatures in addition to raw ECDSA and EIP-191. This provides significant security and user experience advantages: - -**Security Benefits**: -- **Domain Separation**: Signatures are bound to a specific contract and chain, preventing replay attacks across different applications or networks -- **Type Safety**: Structured data hashing ensures only valid state structures can be signed, preventing malformed data injection -- **Semantic Clarity**: Each field's type and purpose is cryptographically enforced, reducing ambiguity attacks - -**User Experience Benefits**: -- **Human-Readable**: Modern wallets (MetaMask, Ledger, etc.) display EIP-712 signatures as structured fields instead of opaque hex strings -- **Transparency**: Users see exactly what `channelId`, `intent`, `version`, `allocations`, and `data` they're signing -- **Trust**: Clear presentation reduces phishing risks and increases user confidence - -**Example Wallet Display**: -``` -Sign Typed Data: - channelId: 0xabcd1234... - intent: OPERATE (1) - version: 5 - allocations: - [0] destination: 0x742d35Cc..., token: USDC, amount: 100.00 - [1] destination: 0x123456Cc..., token: USDC, amount: 0.00 -``` - -Compared to EIP-191 which would show: -``` -Sign Message: -0x1ec5000000000000000000000000000000000000000000000000000000001234abcd... -[500+ more hex characters] -``` - -**Implementation Note**: The protocol accepts **all three formats** (raw ECDSA, EIP-191, EIP-712) for maximum compatibility, but EIP-712 is **strongly recommended** for production applications due to its superior security and UX properties. - -Supporting EIP-712 signatures also differentiates VirtualApp by keeping state channel operations wallet-friendly and lowering integration friction compared to protocols limited to raw message signing. - -### Liveness - -**Property**: As long as the blockchain is live and accepts transactions within the challenge period, honest participants can enforce their rights. - -**Requirements**: -- Blockchain must be operational -- Participant must be able to submit transactions -- Challenge period must be sufficient for transaction confirmation - -**Recommended Challenge Periods**: -- **High-value channels**: 24-48 hours (default: 24 hours / 86400 seconds) -- **Medium-value channels**: 12-24 hours -- **Low-value rapid channels**: 6-12 hours - -:::caution Challenge Period Trade-offs -Longer challenge periods provide more security but slower dispute resolution. Shorter periods enable faster closure but require more vigilant monitoring. -::: - -### Censorship Resistance - -**Property**: Since anyone can submit challenges and responses, censorship of a single participant does not prevent channel closure. - -**Mechanism**: -- Any participant can initiate challenge -- Any participant can respond to challenge -- Multiple participants can attempt the same operation -- As long as one honest party can transact, the channel can be resolved - -## Attack Vectors and Mitigations - -### Replay Attacks - -**Attack**: Resubmitting old signed states to revert channel to a previous favorable allocation. - -**Mitigation**: -- Adjudicators MUST implement version checking to verify that a supplied "candidate" is indeed supported by a supplied "proof". -- Higher version numbers supersede lower versions -- On-chain contract tracks the highest version seen -- Old states are automatically rejected - -```mermaid -graph LR - A[State v10 submitted] --> B{Compare versions} - B -->|v10 > v5| C[Accept new state] - B -->|v10 < v20| D[Reject old state] - - style C fill:#90EE90 - style D fill:#FFB6C1 -``` - -:::tip Version Monotonicity -Always ensure state versions increase monotonically. Never sign two different states with the same version number. -::: - -### State Withholding - -**Attack**: Refusing to cooperate in closing channel, holding funds hostage. - -**Mitigation**: -- Challenge mechanism allows unilateral closure -- Challenge period ensures fair dispute resolution -- Latest signed state always prevails - -**Example Scenario**: -``` -1. Alice and Bob have channel with $1000 each -2. After trading, valid state shows Alice: $1500, Bob: $500 -3. Bob refuses to cooperate in cooperative close -4. Alice initiates challenge with latest signed state -5. Bob has access only to an older state, meaning he is unable to resolve the challenge -6. After challenge period elapses, Alice's state becomes the final one -7. Alice recovers her $1500 -``` - -### Challenge Griefing - -**Attack**: Repeatedly challenging with old states to delay closure and grief the counterparty. - -**Mitigation**: -- Each valid newer state resets the challenge period -- Attacker must pay gas for each challenge attempt -- Eventually attacker runs out of old states -- Newest state always wins regardless of challenge count -- The party being griefed can checkpoint with the latest valid state, impeding the griefer from challenging with any intermediate state - -:::note Economic Disincentive -Challenge griefing is economically costly for the attacker (gas fees) while only causing time delay, not fund loss, for the victim. -::: - -### Front-Running - -**Attack**: Observing pending challenge transaction and front-running with a newer state. - -**Mitigation**: -- **This is actually desired behavior** in VirtualApp -- The newest state should always win -- Front-running helps ensure the most recent state is used -- Both parties benefit from accurate state resolution - -## Best Practices - -### For Users - -**Essential Practices**: - -1. **Never sign duplicate versions**: Never sign two different states with the same version number -2. **Keep records**: Maintain a record of the latest state you've signed -3. **Monitor events**: Watch the blockchain for channel events (Challenged, Closed) -4. **Respond promptly**: React to challenges within the challenge period -5. **Verify adjudicators**: Only use adjudicator contracts from trusted sources - -:::danger Critical Rule -**NEVER sign two different states with the same version number.** This creates ambiguity about the true latest state and can lead to disputes. -::: - -### For Implementers - -**Implementation Requirements**: - -1. **Validate thoroughly**: Check all inputs before submitting transactions -2. **Use adjudicators wisely**: Leverage adjudicators to enforce application rules -3. **Set appropriate challenge periods**: Balance security needs with user experience -4. **Implement proper key management**: Secure storage for participant private keys -5. **Log state transitions**: Maintain audit trail of all state updates - -**Sample Validation Checklist**: - -```markdown -Before submitting state on-chain: -☐ Verify all required signatures present -☐ Verify signatures are valid for expected participants -☐ Verify state version is sequential -☐ Verify allocations sum correctly -☐ Verify magic numbers (CHANOPEN/CHANCLOSE) if applicable -☐ Verify channelId matches expected value -☐ Test with small amounts first -``` - -### For Adjudicator Developers - -**Critical Requirements**: - -1. **Implement strict version comparison**: Ensure newer states always supersede older ones -2. **Validate state transitions**: Enforce application-specific rules correctly -3. **Optimize for gas efficiency**: Validation happens on-chain during disputes -4. **Consider edge cases**: Handle all possible state transition scenarios -5. **Audit thoroughly**: Security review before deployment is essential - -:::warning Adjudicator Responsibility -Adjudicators are critical to channel security. A flawed adjudicator can undermine the entire channel's safety guarantees. -::: - -:::caution Before Implementing Your Own Adjudicator -The Adjudicator is an incredibly important part of the VirtualApp protocol. Yellow Network is built on top of a specific adjudicator, which if changed, will render interoperability and security guarantees impossible. Before starting to implement your own Adjudicator, please be sure to advise the VirtualApp developer team, so that your work is not left out. -::: - -## Security Guarantees Summary - -| Property | Guarantee | Mechanism | -|----------|-----------|-----------| -| **Funds Safety** | Cannot lose funds with valid signed state | Challenge-response + signatures | -| **State Validity** | Only properly signed states accepted | Signature verification | -| **Liveness** | Can always close if blockchain is live | Unilateral challenge mechanism | -| **Censorship Resistance** | Any party can enforce closure | Multiple submission paths | -| **No Replay** | Old states cannot be reused | Version number validation | - -:::success Strong Security Model -VirtualApp provides **strong security guarantees** built on top of Layer 1 blockchain security, while enabling Layer 2 scalability and efficiency. -::: - -## Emergency Procedures - -### If a Clearnode Becomes Unresponsive - -1. **Retrieve latest signed state** from local storage -2. **Initiate challenge** on-chain with latest state -3. **Close the channel** after challenge period expires -4. **Funds are recovered** according to latest valid state - -### If You Have Been Challenged - -1. **Check for the latest state** - make sure the channel was challenged with the latest state. If not, you should checkpoint it with one to avoid funds loss -2. **Ensure blockchain access** - check network connectivity -3. **Use appropriate gas prices** - ensure timely confirmation -4. **Have backup RPC endpoints** - don't rely on single provider - -:::tip Monitoring Best Practice -Set up automated monitoring with alerts for channel events. This ensures you can respond quickly to challenges even if you're not actively watching. -::: diff --git a/docs/protocol/app-layer/on-chain/signature-formats.mdx b/docs/protocol/app-layer/on-chain/signature-formats.mdx deleted file mode 100644 index 69c6a10..0000000 --- a/docs/protocol/app-layer/on-chain/signature-formats.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -sidebar_position: 3 -title: "Signature Formats" ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Signature Formats - -VirtualApp treats each signature inside `State.sigs` as an opaque `bytes` value. At verification time the Custody contract inspects that payload to detect which validation flow to run. This page captures the current formats the protocol accepts and how they are evaluated. - -## Supported Formats - -### Raw / Pre-EIP-191 ECDSA - -- Signs the raw packedState with no prefix. -- Produces the canonical `(v, r, s)` tuple encoded as 65 bytes. -- Recommended for chain-agnostic clients or when hardware-wallet compatibility is required. - -### EIP-191 (`0x45`) Ethereum Signed Message - -- Payload: `keccak256(\"\\x19Ethereum Signed Message:\\n\" + len(packedState) + packedState)`. -- Matches the UX most wallets expose when calling `eth_sign`. -- VirtualApp stores the resulting `(v, r, s)` so adjudicators can re-create the prefixed hash for verification. - -### EIP-712 Typed Data - -- Payload: `keccak256(\"\\x19\\x01\" ++ domainSeparator ++ hashStruct(state))`. -- Domain separator includes chain ID, verifying contract, and an application-specific salt to prevent replay. -- Provides the strongest replay protection when both parties agree on the domain definition. - -### EIP-1271 Smart-Contract Signatures - -- Supports smart contract wallets (multi-sigs, modules, account abstraction). -- The `bytes` payload is passed to the signer's `isValidSignature(hash, bytes signature)` function. -- Implementations can encode arbitrary metadata (e.g., batched approvals, guardians). - -### EIP-6492 Counterfactual Signatures - -- Wraps an EIP-1271 signature with deployment bytecode and a detection suffix `0x6492649264926492649264926492649264926492649264926492649264926492`. -- Allows a not-yet-deployed ERC-4337 smart wallet to attest to a state. -- During verification VirtualApp simulates or deploys the wallet, then forwards the inner signature to the regular EIP-1271 flow. - -## Verification Order - -The Custody contract attempts the following strategies in order: - -1. **EIP-6492** – If the detection suffix is present, unwrap and validate as counterfactual. -2. **EIP-1271** – If the signer currently has contract code, call `isValidSignature`. -3. **ECDSA / EIP-191 / EIP-712** – Otherwise treat it as an externally owned account signature and recover the signer using the appropriate hash for the advertised format. - -Implementations should persist metadata about which scheme was used so that adjudicators and monitoring services can reproduce the expected hash locally. - -## Implementation Notes - -- `bytes[] sigs` preserves the ordering of channel participants, but each entry may come from a different signature family. -- Wallets should expose the format they used when signing to aid debugging. -- Future versions may extend this list; storing opaque bytes ensures backward compatibility. - - diff --git a/docs/protocol/communication-flows.mdx b/docs/protocol/communication-flows.mdx deleted file mode 100644 index 4cdcaa3..0000000 --- a/docs/protocol/communication-flows.mdx +++ /dev/null @@ -1,753 +0,0 @@ ---- -sidebar_position: 6 -title: Cross-Layer Communication Flows ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Cross-Layer Communication Flows - -This section illustrates how the on-chain and off-chain layers interact during typical operations. Each flow shows the sequence of method calls and data exchange between Client, Clearnode, and Smart Contracts. - -:::info Flow Navigation -Jump to a specific flow: -- [Authentication Flow](#authentication-flow) - Establish session with session key delegation -- [Channel Creation Flow](#channel-creation-flow) - Open payment channel on blockchain -- [Off-Chain Transfer Flow](#off-chain-transfer-flow) - Instant transfers without gas -- [App Session Lifecycle](#app-session-lifecycle-flow) - Multi-party application flow -- [Cooperative Closure](#cooperative-closure-flow) - Fast channel closure -- [Challenge-Response Closure](#challenge-response-closure-flow) - Dispute resolution -::: - ---- - -## Authentication Flow - -### Purpose - -Establish authenticated session with session key delegation. - -### Actors - -- **Client**: User application or SDK -- **Clearnode**: Off-chain service provider - -### Sequence Diagram - -```mermaid -sequenceDiagram - participant Client - participant Wallet as Main Wallet - participant Clearnode - - Note over Client: 1. Generate Session Keypair - Client->>Client: session_private_key = random() - Client->>Client: session_address = address(session_public_key) - - Note over Client,Clearnode: 2. auth_request (public, no signature) - Client->>Clearnode: auth_request(address, session_key, allowances, scope, expires_at) - - Note over Clearnode: 3. Generate Challenge - Clearnode->>Clearnode: Validate params, generate UUID - Clearnode->>Client: auth_challenge(challenge_message) - - Note over Client: 4. Sign Challenge with MAIN wallet (EIP-712) - Client->>Wallet: Sign Policy typed data (challenge, scope, wallet, session_key, expires_at, allowances) - Wallet-->>Client: EIP-712 signature - Client->>Clearnode: auth_verify(challenge, sig) // or auth_verify(challenge, jwt) - - Note over Clearnode: 5. Validate & Issue Session - Clearnode->>Clearnode: Recover main wallet from sig (or validate jwt) - Clearnode->>Clearnode: Create session + JWT - Clearnode->>Client: {address, session_key, jwt_token, success} - - Note over Client,Clearnode: Subsequent requests signed with session_key -``` - -### Steps - -#### Step 1: Client Generates Session Keypair - -The session key is generated entirely off-chain and the private key never leaves the client: - -```javascript -session_private_key = random() -session_public_key = derive(session_private_key) -session_address = address(session_public_key) -``` - -#### Step 2: Client → Clearnode: `auth_request` (public, no signature) - -The client sends a public registration request (no signature required): - -```javascript -Request: -{ - address: user_wallet_address - session_key: session_address - allowances: [{"asset": "usdc", "amount": "100.0"}] - scope: "transfer,app.create" - expires_at: 1762417328123 // Unix ms -} -``` - -#### Step 3: Clearnode Validates and Generates Challenge - -The clearnode performs validation: -- Validate address/session_key format, optional allowances/scope, expires_at -- Generate challenge UUID - -#### Step 4: Clearnode → Client: `auth_challenge` - -The clearnode responds with a challenge: - -```javascript -Response: -{ - challenge_message: "550e8400-e29b-41d4-a716-446655440000" -} -Signature: signed by Clearnode -``` - -#### Step 5: Client Signs Challenge (MAIN wallet, EIP-712) - -The client signs the challenge using the main wallet over the Policy typed data (includes challenge, wallet, session_key, expires_at, scope, allowances): - -```javascript -challenge_signature = signTypedData(policyTypedData, main_wallet_private_key) -``` - -#### Step 6: Client → Clearnode: `auth_verify` - -The client submits the signed challenge (or a previously issued JWT): - -```javascript -Request: -{ - challenge: "550e8400-e29b-41d4-a716-446655440000", - // alternatively: - // jwt: "" -} -Signature: EIP-712 signature by main wallet (required if jwt is absent) -``` - -#### Step 7: Clearnode Validates Challenge - -The clearnode validates: -- Signature recovers the wallet used in `auth_request` -- Challenge matches pending authentication -- Challenge not expired or reused - -#### Step 8: Clearnode → Client: `auth_verify` Response - -The clearnode confirms authentication: - -```javascript -Response: -{ - address: user_wallet_address - session_key: session_address - jwt_token: "" - success: true -} -``` - -#### Step 9: Session Established - -- All subsequent requests signed with `session_private_key` -- The clearnode enforces allowances and expiration -- No main wallet interaction required until session expires - -### Key Points - -:::success Session Security -- Session private key **NEVER** leaves the client -- Main wallet only signs once (`auth_request`) -- All subsequent operations use session key -- Allowances prevent unlimited spending -- Challenge-response prevents replay attacks -::: - -**Related Methods**: [`auth_request`](./app-layer/off-chain/authentication#step-1-auth_request), [`auth_challenge`](./app-layer/off-chain/authentication#step-2-auth_challenge), [`auth_verify`](./app-layer/off-chain/authentication#step-3-auth_verify) - ---- - -## Channel Creation Flow - -### Purpose - -Open a payment channel with zero initial balance; fund it later via `resize_channel`. - -### Actors - -- **Client**: User application or SDK -- **Clearnode**: Off-chain service provider -- **Smart Contract**: Custody Contract -- **Blockchain**: Ethereum-compatible network - -### Sequence Diagram - -```mermaid -sequenceDiagram - participant Client - participant Clearnode - participant Blockchain - - Note over Client,Clearnode: Off-Chain Preparation - Client->>Clearnode: create_channel(chain_id, token) - - Note over Clearnode: 2. Prepare Channel - Clearnode->>Clearnode: Generate unique nonce - Clearnode->>Clearnode: Create channel config - Clearnode->>Clearnode: Create initial state (intent: INITIALIZE, version: 0, zero allocations) - Clearnode->>Clearnode: Pack & sign state - - Clearnode->>Client: {channel_id, channel, state, server_signature} - - Note over Client: 3. Validate & Sign - Client->>Client: Verify Clearnode signature - Client->>Client: Sign packed state with user key - - Note over Client,Blockchain: On-Chain Execution - Client->>Blockchain: Custody.create(channel, state, sig_user, sig_clearnode) - Blockchain->>Blockchain: Verify signatures - Blockchain->>Blockchain: Create channel (zero balance) - Blockchain->>Blockchain: Set status to OPEN/ACTIVE - Blockchain->>Blockchain: Emit Opened event - - Blockchain-->>Clearnode: Opened event (monitored) - Blockchain-->>Client: Opened event - - Note over Client,Clearnode: Channel is now ACTIVE -``` - -### Steps - -#### Step 1: Client → Clearnode: `create_channel` - -Client requests channel creation: - -```javascript -Request: -{ - chain_id: 137 // Polygon - token: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174" // USDC -} -Signature: session key signature -``` - -#### Step 2: Clearnode Processes Request - -The clearnode: -- Validates token is supported on chain -- Generates unique nonce -- Selects adjudicator (SimpleConsensus for payment channels) -- Creates Channel struct -- Computes `channelId` = `keccak256(abi.encode(Channel))` -- Creates initial State with `intent: INITIALIZE`, `version: 0`, `state_data: "0x"`, zero allocations -- Packs state (`abi.encode(channelId, intent, version, data, allocations)` in Solidity terms) -- Signs packed state with clearnode's participant key - -#### Step 3: Clearnode → Client: Response - -```javascript -Response: -{ - channel: { - participants: [user_address, clearnode_address] - adjudicator: 0xSimpleConsensusAddress - challenge: 86400 - nonce: 1699123456789 - } - state: { - intent: INITIALIZE - version: 0 - data: "0x" - allocations: [ - {destination: user_address, token: usdc, amount: 0}, - {destination: clearnode_address, token: usdc, amount: 0} - ] - } - server_signature: "0xClearnodeSig..." - channel_id: "0xChannelId..." -} -``` - -:::tip Clearnode Signs First -The clearnode provides its signature **BEFORE** the user commits funds on-chain. This ensures both parties have committed before any on-chain transaction occurs. -::: - -#### Steps 4-5: Client Validates and Signs - -Client: -- Recomputes `channelId` and verifies it matches -- Recomputes packed state and verifies clearnode signature -- Signs packed state with user's participant key - -#### Step 6: Client → Blockchain: `Custody.create()` - -Client submits transaction: -```javascript -Custody.create(channel, state, userSig, serverSig) -``` - -#### Step 7: Blockchain Validates and Creates Channel - -Contract: -- Verifies user's signature is valid -- Verifies clearnode's signature is valid -- Stores channel parameters and funding state (zero balances) -- Sets channel status to `OPEN` -- Emits `Opened` event - -#### Step 8: Event Listener Detects Creation - -The clearnode's event listener: -- Detects `Opened` event -- Validates channel parameters - -#### Steps 9-10: Notifications - -The clearnode: -- Updates internal database: channel status = open (zero balance) -- Sends `channel_update` notification to client - -#### Step 11: Channel Active - -- Channel active with zero balance -- Use `resize_channel` to fund the channel - -### Key Points - -:::success Two-Phase Process -- **Off-chain preparation**: Clearnode prepares and signs channel configuration -- **On-chain execution**: User submits transaction to lock funds -- This ensures clearnode is ready to join before user risks funds -::: - -**Related Methods**: [`create_channel`](./app-layer/off-chain/channel-methods#create_channel) - ---- - -## Off-Chain Transfer Flow - -### Purpose - -Transfer funds between users instantly without blockchain transaction. - -### Actors - -- **Sender (Client A)**: Initiating user -- **Clearnode**: Off-chain service provider -- **Receiver (Client B)**: Receiving user - -### Sequence Diagram - -```mermaid -sequenceDiagram - participant Sender as Client A - participant Clearnode - participant Receiver as Client B - - Sender->>Clearnode: transfer(destination, amount, asset) - - Note over Clearnode: 2. Validate Request - Clearnode->>Clearnode: Check A authenticated - Clearnode->>Clearnode: Check A has sufficient balance - Clearnode->>Clearnode: Check B exists (has channel) - - Note over Clearnode: 3. Update Ledger - Clearnode->>Clearnode: Debit entry (A: -50 USDC) - Clearnode->>Clearnode: Credit entry (B: +50 USDC) - Clearnode->>Clearnode: Create transaction record - - Clearnode->>Sender: Transfer confirmed ✓ - - Note over Clearnode,Receiver: 4. Notify Receiver - Clearnode->>Receiver: transfer_received event - Clearnode->>Receiver: balance_update event - - Note over Sender,Receiver: Complete: < 1 second, zero gas -``` - -### Steps - -#### Step 1: Client A → Clearnode: `transfer` - -Sender initiates transfer: - -```javascript -Request: -{ - destination: "0xClientB_Address", // or destination_user_tag: "UX123D" - allocations: [{"asset": "usdc", "amount": "50.0"}] -} -Signature: Client A's session key -``` - -#### Step 2: Clearnode Validates - -The clearnode validates: -- Client A is authenticated -- Client A has >= 50 USDC available balance -- Destination address/tag is valid (account is created if new) -- Asset "usdc" is supported - -#### Step 3: Clearnode Creates Ledger Entries - -Double-entry bookkeeping: - -**Entry 1 (Debit from Client A unified account)**: -```javascript -{ - account_id: Client A address - asset: "usdc" - credit: "0.0" - debit: "50.0" -} -``` - -**Entry 2 (Credit to Client B unified account)**: -```javascript -{ - account_id: Client B address - asset: "usdc" - credit: "50.0" - debit: "0.0" -} -``` - -#### Step 4: Clearnode Creates Transaction Record - -```javascript -{ - id: 1, - tx_type: "transfer", - from_account: Client A address, - from_account_tag: "NQKO7C", - to_account: Client B address, - to_account_tag: "UX123D", - asset: "usdc", - amount: "50.0", - created_at: "2023-05-01T12:00:00Z" -} -``` - -#### Step 5: Clearnode → Client A: Response - -```javascript -Response: -{ - transactions: [ - { - id: 1, - tx_type: "transfer", - from_account: "0xA...", - from_account_tag: "NQKO7C", - to_account: "0xB...", - to_account_tag: "UX123D", - asset: "usdc", - amount: "50.0", - created_at: "2023-05-01T12:00:00Z" - } - ] -} -``` - -#### Step 6-7: Clearnode → Clients: Notifications - -- `tr` (transfer) notification to sender/receiver with `transactions` array -- `bu` (balance update) notification reflecting new balances - -#### Step 9: Transfer Complete - -- Instant (< 1 second) -- No blockchain transaction -- Zero gas fees -- Both parties notified - -### Key Points - -:::success Instant Settlement -- **Purely off-chain**: Database transaction, no blockchain -- **Instant settlement**: < 1 second typical -- **Zero gas fees**: No on-chain transaction required -- **Double-entry bookkeeping**: Accounting accuracy guaranteed -- **Receiver account auto-created**: Destination tag/address need not have a prior balance -::: - -**Related Methods**: [`transfer`](./app-layer/off-chain/transfers#transfer) - ---- - -## App Session Lifecycle Flow - -### Purpose - -Create, update, and close a collaborative app session with multiple participants. - -### Actors - -- **Client A**: Participant 1 -- **Client B**: Participant 2 -- **Clearnode**: Off-chain service provider - -### Scenario - -Two-player chess game with 100 USDC stake each. - -### Sequence Diagram - -```mermaid -sequenceDiagram - participant A as Client A - participant B as Client B - participant Clearnode - - Note over A,B: Create (lock funds) - A->>Clearnode: create_app_session(definition, allocations, session_data?) - B-->>Clearnode: co-sign (if non-zero allocation) - Clearnode->>Clearnode: Validate quorum, balances, allowances - Clearnode->>Clearnode: Lock allocations from unified balances - Clearnode-->>A: {app_session_id, status:"open", version:1} - Clearnode-->>B: asu/bu notifications - - Note over A,B: Update (submit_app_state) - A->>Clearnode: submit_app_state(app_session_id, intent, version, allocations, session_data?) - B-->>Clearnode: co-signs to meet quorum - Clearnode->>Clearnode: Validate intent rules, version, quorum, allowances - Clearnode->>Clearnode: Apply operate/deposit/withdraw - Clearnode-->>A: {app_session_id, status:"open", version:n} - Clearnode-->>B: asu/bu notifications - - Note over A,B: Close - A->>Clearnode: close_app_session(app_session_id, allocations, session_data?) - B-->>Clearnode: co-signs to meet quorum - Clearnode->>Clearnode: Validate sums and quorum, release to unified balances - Clearnode-->>A: {app_session_id, status:"closed", version:n+1} - Clearnode-->>B: asu/bu notifications - -``` - -### Sequence (Create → Update → Close) - -1. **Create (off-chain): `create_app_session`** - - Client signs request (all participants with non-zero allocations must sign). - - Clearnode validates protocol version (0.2/0.4), quorum, balances, allowances/session keys. - - Funds are locked from each signer’s unified balance into the app session account. - - **Response (minimal)**: `app_session_id`, `status: "open"`, `version: 1`. Full metadata is not echoed; use `get_app_sessions` to read it. - - **Example Request**: - ```json - { - "req": [1,"create_app_session",{ - "definition": { - "protocol": "NitroRPC/0.4", - "participants": ["0xA","0xB"], - "weights": [100,100], - "quorum": 200, - "challenge": 86400, - "nonce": 1699123 - }, - "allocations": [ - {"participant": "0xA","asset": "usdc","amount": "100.0"}, - {"participant": "0xB","asset": "usdc","amount": "100.0"} - ], - "session_data": "{\"game\":\"chess\"}" - },1699123456789], - "sig": ["0xUserSig","0xCoSig"] - } - ``` - -2. **State Updates (off-chain): `submit_app_state`** - - v0.4 requires `version = current+1`; v0.2 rejects `intent`/`version` and only allows a single update. - - Intents: - - `operate`: redistribute, sum must stay equal. - - `deposit`: sum must increase; depositor must sign and have available unified balance. - - `withdraw`: sum must decrease; session must have funds. - - Quorum required; session-key allowances enforced. - - **Response (minimal)**: `app_session_id`, `status: "open"`, `version` (new). No metadata echoed. - - Notifications: `asu` (app session update) + `bu` (balance update for deposit/withdraw). - - **Example Request (deposit v0.4)**: - ```json - { - "req": [2,"submit_app_state",{ - "app_session_id": "0xSession", - "intent": "deposit", - "version": 2, - "allocations": [ - {"participant": "0xA","asset": "usdc","amount": "150.0"}, - {"participant": "0xB","asset": "usdc","amount": "100.0"} - ] - },1699123456790], - "sig": ["0xUserSig","0xCoSig"] - } - ``` - -3. **Close (off-chain): `close_app_session`** - - Requires quorum signatures; final allocations must match total balances. - - **Response (minimal)**: `app_session_id`, `status: "closed"`, `version` (incremented). No metadata echoed. - - Funds are released to participants’ unified balances; notifications `asu` and `bu` are sent. - - **Example Request**: - ```json - { - "req": [3,"close_app_session",{ - "app_session_id": "0xSession", - "allocations": [ - {"participant": "0xA","asset": "usdc","amount": "180.0"}, - {"participant": "0xB","asset": "usdc","amount": "20.0"} - ] - },1699123456795], - "sig": ["0xUserSig","0xCoSig"] - } - ``` - -### Key Points - -:::info App Sessions -App sessions enable multi-party applications with custom governance rules. Funds are locked from unified balance for the duration of the session. -::: - -**Related Methods**: [`create_app_session`](./app-layer/off-chain/app-sessions#create_app_session), [`submit_app_state`](./app-layer/off-chain/app-sessions#submit_app_state), [`close_app_session`](./app-layer/off-chain/app-sessions#close_app_session) - ---- - -## Cooperative Closure Flow - -### Purpose - -Close channel when all parties agree on final state. - -### Actors - -- **Client**: User application -- **Clearnode**: Off-chain service provider -- **Smart Contract**: Custody Contract -- **Blockchain**: Ethereum-compatible network - -### Key Points - -:::success Preferred Method -Cooperative closure is **fast (1 transaction)**, **cheap (low gas)**, and **immediate (no waiting period)**. Always use this when possible. -::: - -### Sequence Diagram - -```mermaid -sequenceDiagram - participant Client - participant Clearnode - participant Blockchain - - Client->>Clearnode: close_channel(channel_id, funds_destination) - Clearnode->>Clearnode: Validate channel open/resizing and not challenged - Clearnode->>Clearnode: Build FINALIZE state (version = current+1, data = "0x", allocations) - Clearnode-->>Client: {channel_id, state, server_signature} - Client->>Client: Verify server_signature and sign packed state - Client->>Blockchain: Custody.close(channel_id, state, userSig, serverSig) - Blockchain->>Blockchain: Verify signatures, close channel, emit event - Blockchain-->>Clearnode: Event observed - Clearnode->>Clearnode: Update DB and balances - Clearnode-->>Client: cu + bu notifications - -``` - -### Sequence - -1. **Client → Clearnode**: `close_channel(channel_id, funds_destination)` - - Authenticated request signed by the user (session key or wallet). - - **Example Request**: - ```json - { - "req": [10,"close_channel",{ - "channel_id": "0xChannel", - "funds_destination": "0xUser" - },1699123457000], - "sig": ["0xUserSig"] - } - ``` -2. **Clearnode**: validates channel exists and is `open`/`resizing`, checks challenged-channel guard, builds FINALIZE state: - - `intent: FINALIZE`, `version = current+1`, `state_data: "0x"`, allocations split between user and broker based on channel balance. - - Signs packed state (`keccak256(abi.encode(channelId, intent, version, data, allocations))`). -3. **Clearnode → Client**: response with `channel_id`, `state`, `server_signature`. -4. **Client**: verifies server signature, signs the same packed state. -5. **Client → Blockchain**: `Custody.close(channel_id, state, userSig, serverSig)` (one tx). -6. **Blockchain**: verifies both signatures, closes channel, emits `Closed/Opened`-equivalent event (implementation-specific), releases funds. -7. **Clearnode**: observes event, updates DB, sends `cu` (channel update) and `bu` (balance update) notifications. - -**Related Methods**: [`close_channel`](./app-layer/off-chain/channel-methods#close_channel) - ---- - -## Challenge-Response Closure Flow - -### Purpose - -Close channel when other party is unresponsive or disputes final state. - -### Actors - -- **Client**: User application -- **Clearnode**: Off-chain service provider (may be unresponsive) -- **Smart Contract**: Custody Contract -- **Blockchain**: Ethereum-compatible network - -### Key Points - -:::warning Challenge Period -This method requires waiting for the challenge period (typically 24 hours) to elapse. Use only when cooperative closure fails. -::: - -### Sequence Diagram - -```mermaid -sequenceDiagram - participant Client - participant Blockchain - participant Clearnode - - Note over Client: Hold latest signed state - Client->>Blockchain: Custody.challenge(channelId, state, sigs) - Blockchain->>Blockchain: Start challenge timer (challenge period) - alt Newer state posted - OtherParty->>Blockchain: Custody.checkpoint(channelId, newerState, sigs) - Blockchain->>Blockchain: Replace pending state - end - Note over Client,Blockchain: Wait for challenge period expiry - Client->>Blockchain: Custody.close(channelId, state, sigs) // after timeout if uncontested - Blockchain->>Blockchain: Finalize channel, emit event - Blockchain-->>Clearnode: Event observed when back online - Clearnode->>Clearnode: Update DB, balances - Clearnode-->>Client: cu + bu notifications -``` - -### Sequence (User-initiated, clearnode unresponsive) - -1. **Prerequisite**: User holds the latest mutually signed state (or clearnode-signed latest) for the channel. -2. **Client → Blockchain**: `Custody.challenge(channelId, state, sigs...)` - - Submits the latest signed state to start the challenge. -3. **Challenge Window**: Other party can respond with a newer valid state before timeout. -4. **If no newer state is posted**: After the challenge period, user calls `Custody.close(channelId, state, sigs...)` to finalize. -5. **Blockchain**: finalizes channel, releases funds per challenged state, emits closure event. -6. **Clearnode** (when responsive again): observes event, updates DB, sends `cu`/`bu` notifications to participants. - -**Related Methods**: On-chain `Custody.challenge()` and `Custody.close()` - ---- - -## Next Steps - -Now that you understand how all protocol layers work together: - -1. **Review Method Details**: Visit Part 2 (Off-Chain RPC Protocol) for complete method specifications -2. **Explore Reference**: See [Protocol Reference](./protocol-reference) for constants and standards -3. **Implementation Guide**: Check [Implementation Checklist](./implementation-checklist) for best practices -4. **Quick Start**: Follow the [Quick Start Guide](/docs/build/quick-start) to begin building - -:::tip Complete Flows -These flows represent the most common operations. For edge cases and error handling, consult the specific method documentation in Part 2. -::: diff --git a/docs/protocol/glossary.mdx b/docs/protocol/glossary.mdx deleted file mode 100644 index 063a3d0..0000000 --- a/docs/protocol/glossary.mdx +++ /dev/null @@ -1,415 +0,0 @@ ---- -sidebar_position: 9 -title: Glossary ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Glossary - -Complete alphabetical reference of all protocol terms and concepts. - -:::tip Quick Reference -This glossary provides concise definitions of all VirtualApp protocol terms. For detailed explanations with examples and diagrams, see the respective sections in the documentation. -::: - ---- - -## A - -### Adjudicator - -Smart contract that validates state transitions according to application-specific rules. Each channel specifies an adjudicator that determines which state updates are valid. - -**Examples**: SimpleConsensus, RemittanceAdjudicator - -**Related**: [Data Structures](./app-layer/on-chain/data-structures), [Security Considerations](./app-layer/on-chain/security) - ---- - -### Allocation - -Specification of how funds are distributed, containing destination, token, and amount. Allocations define where funds should be sent when a channel closes or how they're distributed within an app session. - -**Structure**: -```javascript -{ - destination: address, // Recipient wallet address - token: address, // ERC-20 token contract - amount: uint256 // Amount in smallest unit -} -``` - -**Related**: [Data Structures](./app-layer/on-chain/data-structures#allocation) - ---- - -### App Session - -Off-chain channels built on top of payment channels, intended to be used by app developers to enable application-specific interactions and transactions without touching the blockchain. Previously known as Virtual Ledger Channels (VLC). - -**Features**: -- Multi-party participation -- Custom governance rules (quorum, weights) -- Fund locking from unified balance -- Application-specific state management - -**Related**: [App Sessions Methods](./app-layer/off-chain/app-sessions), [Communication Flows](./communication-flows#app-session-lifecycle-flow) - ---- - -## C - -### Challenge Period - -Duration (in seconds) that parties have to respond to a dispute. When a channel is challenged, all participants have this amount of time to submit a newer state before the challenged state becomes final. - -**Typical Values**: -- Default: 86400 seconds (24 hours) -- Minimum recommended: 3600 seconds (1 hour) -- Maximum recommended: 604800 seconds (7 days) - -**Related**: [Channel Lifecycle](./app-layer/on-chain/channel-lifecycle#closure---challenge-response) - ---- - -### Channel - -A secure communication pathway between participants that locks funds in an on-chain smart contract while enabling off-chain state updates. Channels are the foundation of the VirtualApp protocol. - -**Lifecycle**: VOID → INITIAL → ACTIVE → (DISPUTE) → FINAL - -**Related**: [On-Chain Protocol](./app-layer/on-chain/overview), [Data Structures](./app-layer/on-chain/data-structures#channel) - ---- - -### channelId - -A unique identifier for a channel, formatted as a 0x-prefixed hex string (32 bytes). Computed deterministically from the channel configuration. - -**Computation**: -```javascript -channelId = keccak256(abi.encode( - channel.participants, - channel.adjudicator, - channel.challenge, - channel.nonce -)) -``` - -**Related**: [Data Structures](./app-layer/on-chain/data-structures#channel-identifier) - ---- - -### Checkpoint - -Recording a state on-chain without entering dispute mode. Checkpointing creates an on-chain record of the current state but keeps the channel in ACTIVE status. - -**Benefits**: -- Shortens effective challenge history -- Provides on-chain proof of state -- Doesn't start challenge period - -**Related**: [Channel Lifecycle](./app-layer/on-chain/channel-lifecycle#checkpointing) - ---- - -### Clearnode - -A virtual ledger layer operated by independent node operators using the issuer's open-source software. It provides a unified ledger (through Nitro RPC) and coordinates state channels (through VirtualApp), providing chain abstraction for developers and users. - -**Responsibilities**: -- Manage off-chain RPC protocol -- Provide unified balance -- Join payment channels -- Coordinate app sessions - -**Related**: [Architecture](./architecture), [Off-Chain RPC Protocol](./app-layer/off-chain/overview) - ---- - -### Creator - -The participant at index 0 in a channel who initiates channel creation. The Creator constructs the channel configuration, prepares the initial funding state, signs it (signature at position 0), and calls the on-chain `create()` function to lock their funds and establish the channel. - -**Typically**: A user or light client opening a payment channel with a clearnode. - -**Related**: [Channel Lifecycle](./app-layer/on-chain/channel-lifecycle#creation-phase), [Protocol Reference](./protocol-reference#participant-indices) - ---- - -### Custody Contract - -The main on-chain contract implementing channel creation, joining, closure, and resizing. It is an implementation of the VirtualApp protocol. - -**Interfaces Implemented**: -- `IChannel` - Core channel operations -- `IDeposit` - Fund management -- `IChannelReader` - State queries - -**Related**: [On-Chain Overview](./app-layer/on-chain/overview), [Data Structures](./app-layer/on-chain/data-structures) - ---- - -## I - -### Intent - -In NitroRPC/0.4, specifies the type of app session state update. The intent system enables dynamic fund management within active sessions. - -**Types**: -- **OPERATE**: Redistribute existing funds (sum unchanged) -- **DEPOSIT**: Add funds to session from unified balance -- **WITHDRAW**: Remove funds from session to unified balance - -**Related**: [App Sessions](./app-layer/off-chain/app-sessions#submit_app_state) - ---- - -## L - -### Ledger Entry - -Double-entry bookkeeping record of a debit or credit. Every financial operation in a clearnode creates two ledger entries to maintain accounting accuracy. - -**Fields**: -- account_id -- asset -- credit (incoming) -- debit (outgoing) -- created_at - -**Related**: [Transfers](./app-layer/off-chain/transfers#off-chain-processing), [Query Methods](./app-layer/off-chain/queries#get_ledger_entries) - ---- - -### Ledger Transaction - -User-facing transaction record showing transfers, deposits, withdrawals, and app session operations. Provides a simplified view of financial activity. - -**Types**: -- `transfer` - Direct transfer between users -- `deposit` - Funds deposited to unified balance -- `withdrawal` - Funds withdrawn from unified balance -- `app_deposit` - Funds locked in app session -- `app_withdrawal` - Funds released from app session - -**Related**: [Query Methods](./app-layer/off-chain/queries#get_ledger_transactions) - ---- - -## M - -### Magic Number - -Constant in `state.data` signaling special states. Magic numbers enable smart contracts to identify the type of state without complex parsing. - -**Values**: -- **CHANOPEN** = 7877 (0x1EC5) - Initial funding state -- **CHANCLOSE** = 7879 (0x1EC7) - Final closing state - -**Related**: [Protocol Reference](./protocol-reference#magic-numbers), [Data Structures](./app-layer/on-chain/data-structures) - ---- - -## N - -### Nonce - -Unique number ensuring channel identifier uniqueness. Even with identical participants and configuration, different nonces create different channel IDs. - -**Typical Value**: Timestamp in milliseconds - -**Related**: [Data Structures](./app-layer/on-chain/data-structures#channel), [Channel Creation](./app-layer/off-chain/channel-methods#create_channel) - ---- - -### Nitro RPC - -The off-chain communication protocol for state channel operations. A lightweight, RPC-based RPC protocol with compact message format and signature-based authentication. - -**Versions**: -- **NitroRPC/0.2**: Legacy (basic state updates) -- **NitroRPC/0.4**: Current (intent system) - -**Related**: [Off-Chain RPC Protocol](./app-layer/off-chain/overview), [Message Format](./app-layer/off-chain/message-format) - ---- - -### VirtualApp (App Layer / YApps) - -The App Layer protocol for state channels and application hosting. Includes on-chain contracts (Custody, Adjudicator) and the off-chain Nitro RPC protocol. Also known as YApps. - -**Version**: 0.5.0 (Mainnet deployments live; not production yet) - -**Related**: [On-Chain Protocol](./app-layer/on-chain/overview), [Protocol Reference](./protocol-reference#protocol-versions) - ---- - -## P - -### Participant - -An entity (identified by a wallet address) that is part of a channel. Typically includes a Creator (user) and a Clearnode or Clearnode Operator. - -**In Payment Channels**: -- Index 0: Creator (user) -- Index 1: Clearnode - -**Related**: [Data Structures](./app-layer/on-chain/data-structures#channel), [Protocol Reference](./protocol-reference#participant-indices) - ---- - -## Q - -### Quorum - -Minimum total weight of signatures required to approve app session state updates. The quorum defines the governance threshold for multi-party applications. - -**Example**: -```javascript -participants: [Alice, Bob, Judge] -weights: [40, 40, 50] -quorum: 80 - -// Valid signature combinations: -// - Alice + Bob (40 + 40 = 80) ✓ -// - Alice + Judge (40 + 50 = 90) ✓ -// - Bob + Judge (40 + 50 = 90) ✓ -// - Alice alone (40 < 80) ✗ -``` - -**Related**: [App Sessions](./app-layer/off-chain/app-sessions#create_app_session), [Governance Examples](./app-layer/off-chain/app-sessions#governance-examples) - ---- - -## R - -### requestId - -A unique identifier for an RPC request, used for correlating requests and responses. Generated by the client for each request. - -**Type**: uint64 - -**Related**: [Message Format](./app-layer/off-chain/message-format#general-structure) - ---- - -## S - -### Session Key - -A temporary cryptographic key delegated by a user's main wallet that provides a flexible way for the user to manage security of their funds by giving specific permissions and allowances for specific apps. - -**Features**: -- Spending limits (allowances) -- Operation scope restrictions -- Expiration time -- Gasless signing (no wallet prompts) - -**Related**: [Authentication](./app-layer/off-chain/authentication), [Communication Flows](./communication-flows#authentication-flow) - ---- - -### State - -A snapshot of the channel at a point in time, including fund allocations and application-specific data. States are signed by participants to authorize transitions. - -**Components**: -- `intent` - Purpose of the state (INITIALIZE, OPERATE, RESIZE, FINALIZE) -- `version` - Incremental version number for comparison -- `data` - Application-specific data or magic number -- `allocations` - Fund distribution -- `sigs` - Participant signatures - -**Related**: [Data Structures](./app-layer/on-chain/data-structures#state) - ---- - -### packedState - -A specific encoding of a channelId, state.intent, state.version, state.data, state.allocations, used for signing and signature verification. - -**Computation**: -```javascript -packedState = abi.encode( - channelId, - state.intent, - state.version, - state.data, - state.allocations -) -``` - -**Related**: [Data Structures](./app-layer/on-chain/data-structures#packed-state) - ---- - -## U - -### Unified Balance - -An abstraction that aggregates a user's funds across multiple blockchain networks, managed by a clearnode. The unified balance provides a single view of all assets regardless of which chain they're locked on. - -**Benefits**: -- Chain abstraction -- Simplified fund management -- Cross-chain transfers without bridges -- Single balance for all operations - -**Related**: [Architecture](./architecture#fund-flow), [Transfers](./app-layer/off-chain/transfers#unified-balance-mechanics) - ---- - -## W - -### Weight - -Voting power assigned to a participant in an app session. Weights determine how much influence each participant has in governance decisions. - -**Usage**: Sum of signer weights must meet quorum for state updates to be valid. - -**Related**: [App Sessions](./app-layer/off-chain/app-sessions#create_app_session) - ---- - -## Additional Terms - -### appSessionId - -A unique identifier for an app session, formatted as a 0x-prefixed hex string (32 bytes). Used for all subsequent operations on that specific app session. - ---- - -### chainId - -A blockchain network identifier (uint64). Examples: 1 (Ethereum Mainnet), 137 (Polygon), 8453 (Base), 42161 (Arbitrum One), 10 (Optimism). - ---- - -### assetSymbol - -A lowercase string identifier for a supported asset (e.g., "usdc", "eth", "weth", "usdt", "dai", "wbtc"). Asset symbols are consistent across chains. - ---- - -### walletAddress - -A user's blockchain address (0x-prefixed hex string, 20 bytes) that identifies their account and owns funds. Used to identify participants in channels and app sessions. - ---- - -## Cross-References - -For detailed explanations of these terms with examples, diagrams, and use cases, refer to: - -- **Core Concepts**: [Terminology](./terminology) -- **On-Chain Details**: [On-Chain Protocol](./app-layer/on-chain/overview) -- **Off-Chain Details**: [Off-Chain RPC Protocol](./app-layer/off-chain/overview) -- **Implementation**: [Quick Start Guide](/docs/build/quick-start), [Implementation Checklist](./implementation-checklist) - -:::tip Using This Glossary -Press `Ctrl+F` (or `Cmd+F` on Mac) to search for specific terms. Most terms also appear as tooltips throughout the documentation for quick reference. -::: - diff --git a/docs/protocol/implementation-checklist.mdx b/docs/protocol/implementation-checklist.mdx deleted file mode 100644 index c1eda5e..0000000 --- a/docs/protocol/implementation-checklist.mdx +++ /dev/null @@ -1,566 +0,0 @@ ---- -sidebar_position: 10 -title: Implementation Checklist ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Implementation Checklist - -Comprehensive checklist for building a compliant VirtualApp client with security best practices. - -:::tip Progressive Implementation -You don't need to implement everything at once. Start with Core Protocol and On-Chain Integration, then add Off-Chain RPC and advanced features progressively. -::: - ---- - -## Core Protocol Support - -Foundation requirements for any VirtualApp implementation. - -### Identifier Computation - -- [ ] **Compute channelId from Channel struct** - - Hash participants, adjudicator, challenge, nonce using keccak256 - - Verify deterministic computation (same inputs = same output) - - Reference: [Data Structures](./app-layer/on-chain/data-structures#channel-identifier) - -- [ ] **Compute payload hash (packedState) from channel state** - - Compute `packedState = keccak256(abi.encode(channelId, state.intent, state.version, state.data, state.allocations))` - - Ensure proper ABI encoding - - Reference: [Data Structures](./app-layer/on-chain/data-structures#packed-state) - -### Signature Handling - -- [ ] **Generate signatures** - - Support ECDSA signatures (standard for EOA wallets) - - Encode as `bytes` format (65 bytes: r + s + v) - - For on-chain: sign raw `packedState` hash - - For off-chain RPC: sign EIP-712 typed data structures - - Reference: [Signature Standards](./protocol-reference#signature-standards) - -- [ ] **Verify signatures** - - Recover signer address from signature - - Validate signer matches expected participant - - Support EIP-1271 for smart contract wallets - - Support EIP-6492 for counterfactual contracts - - Handle EIP-191 for personal signatures where applicable - -:::caution Signature Standards -On-chain signatures use raw `packedState` hash for chain-agnostic compatibility. Off-chain RPC messages use EIP-712 typed data for user-facing signatures (e.g., authentication). Refer to [Signature Standards](./protocol-reference#signature-standards) for details. -::: - ---- - -## On-Chain Integration - -Smart contract interactions for channel lifecycle management. - -### Blockchain Connection - -- [ ] **Connect to Ethereum-compatible blockchain** - - Support multiple chains (Ethereum, Polygon, Arbitrum, Optimism, Base) - - Use Web3 provider (e.g., Infura, Alchemy) - - Handle network switching - - Implement retry logic for failed connections - -- [ ] **Load contract ABIs** - - Custody Contract ABI - - Adjudicator contract ABI (application-specific) - - ERC-20 token ABI - -### Channel Operations - -- [ ] **Create channel (`Custody.create`)** - - Verify state has `intent = INITIALIZE` (1) and `version = 0` - - Preferred: include both participant signatures to start in `ACTIVE` - - Legacy: single sig → `INITIAL`; wait for `join()` to reach `ACTIVE` - - Handle ERC-20 approvals only if depositing at creation (legacy flow) - - Reference: [Channel Lifecycle](./app-layer/on-chain/channel-lifecycle#creation-phase) - -- [ ] **Monitor activation / join** - - Subscribe to `Opened` and `Joined` events - - In legacy flow, ensure `join()` transitions `INITIAL` → `ACTIVE` - -- [ ] **Cooperative closure (`Custody.close`)** - - Build state with `intent = FINALIZE` (3), `version = current+1`, `data = "0x"` - - Require both participant signatures; submit single tx - - Confirm funds destination and allocations match expectations - - Reference: [Channel Lifecycle](./app-layer/on-chain/channel-lifecycle#cooperative-closure) - -- [ ] **Dispute / challenge** - - Persist latest fully signed state for `challenge()` - - During `DISPUTE`, accept newer state via `checkpoint()` (on-chain) if available - - After challenge timeout, finalize with `close()` using challenged state - - Reference: [Channel Lifecycle](./app-layer/on-chain/channel-lifecycle#challenge-phase) - -### Event Listening - -- [ ] **Listen to contract events** - - `Opened(channelId, channel, deposits)` - Channel created and active - - `Challenged(channelId, state, expiration)` - Dispute started (expiration = challenge period end) - - `Closed(channelId, allocations)` - Channel finalized - - `Resized(channelId)` - Channel capacity changed - -- [ ] **Process events in order** - - Maintain event log cursor/checkpoint - - Handle blockchain reorganizations - - Implement event replay for recovery - -- [ ] **Update internal state based on events** - - Sync channel status (INITIAL → ACTIVE → DISPUTE → FINAL) - - Update unified balance when channels open/close - - Notify users of status changes - -:::tip Event Recovery -Implement event recovery for when your application restarts or loses connection. Replay events from last checkpoint to current block. -::: - ---- - -## Off-Chain RPC - -RPC communication with clearnode. - -### Connection Management - -- [ ] **Establish RPC connection** - - Connect to clearnode RPC endpoint - - Handle connection timeouts - - Implement exponential backoff for reconnection - - Reference: [Off-Chain Overview](./app-layer/off-chain/overview) - -- [ ] **Implement message format** - - Compact JSON array format: `[requestId, method, params, timestamp]` - - Request wrapper: `{req: [...], sig: [...]}` - - Response wrapper: `{res: [...], sig: [...]}` - - Error format: `{res: [requestId, "error", {error: "message"}, timestamp], sig: [...]}` - - Reference: [Message Format](./app-layer/off-chain/message-format) - -- [ ] **Handle network outages gracefully** - - Detect connection loss - - Queue pending requests - - Reconnect automatically - - Resubmit queued requests after reconnection - -### Authentication - -- [ ] **Implement 3-step flow (auth_request → auth_challenge → auth_verify)** - - Generate session keypair locally; never transmit the private key - - `auth_request`: public, unsigned; send address, session_key, allowances (optional), scope (optional, not enforced), **expires_at (required, ms)** - - Store the exact parameters; response method is `auth_challenge` with `challenge_message` - - `auth_verify`: sign EIP-712 Policy typed data with **main wallet** (not session key) including challenge, wallet, session_key, expires_at, scope, allowances; or pass `jwt` to reuse without signature - - Response returns `{address, session_key, jwt_token, success}`; use session key for all subsequent private calls - - Reference: [Authentication](./app-layer/off-chain/authentication) - -- [ ] **Session key management** - - Specify allowances per asset (unrestricted if omitted); enforce spending caps on every debit - - Set `expires_at`; re-authenticate before expiry; handle “session expired” errors - - Rotate/revoke session keys as needed; avoid reusing keys across applications - -- [ ] **Request signing & verification** - - Client signs all private RPC requests with session key; validate clearnode signatures on responses - - Ensure canonical JSON serialization of `req`/`res` arrays before hashing/signing - -### Method Implementation - -- [ ] **Implement all required methods** - - **Authentication**: auth_request, auth_verify - - **Channel Management**: create_channel, close_channel, resize_channel - - **Transfers**: transfer - - **App Sessions**: create_app_session, submit_app_state, close_app_session - - **Queries**: get_config, get_assets, get_app_definition, get_channels, get_app_sessions, get_ledger_balances, get_ledger_entries, get_ledger_transactions, get_rpc_history, get_user_tag, get_session_keys, ping - - Reference: [Queries](./app-layer/off-chain/queries) - -- [ ] **Handle server notifications** - - `bu` (Balance Update) - Balance changed - - `cu` (Channel Update) - Channel status changed - - `tr` (Transfer) - Incoming/outgoing transfer - - `asu` (App Session Update) - App session state changed - - Reference: [Notifications](./app-layer/off-chain/queries#notifications) - -:::tip Method Prioritization -Start with: authentication → create_channel → transfer → get_ledger_balances. Add other methods as needed for your use case. -::: - ---- - -## State Management - -Off-chain state tracking and synchronization. - -### State Storage - -- [ ] **Store latest signed states securely** - - Save complete state struct (data, allocations, sigs) - - Include channelId and version - - Persist to durable storage (database, filesystem) - - Implement atomic updates - -- [ ] **Track state versions** - - Maintain version counter per channel and app session - - Reject states with version ≤ current version - - Increment version for each new state - -- [ ] **Implement unified balance tracking** - - Aggregate funds across all chains - - Track funds in unified account vs channel escrow vs app session accounts - - Update on channel open/close and transfers - - Reference: [Transfers](./app-layer/off-chain/transfers) - -- [ ] **Handle app session state updates** - - Verify quorum met (sum of weights ≥ quorum) - - Track locked funds per session - - Release funds on session close - - Reference: [App Sessions](./app-layer/off-chain/app-sessions) - -### State Validation - -- [ ] **Verify signatures before accepting states** - - Check all required signatures present - - Validate each signature against expected signer - - Ensure quorum met for app sessions - -- [ ] **Validate state transitions** - - For channels: verify StateIntent (INITIALIZE, RESIZE, FINALIZE) - - For app sessions: verify quorum and allocation rules - - Verify version increments correctly - - For closure: allocations valid and complete - -- [ ] **Maintain state history** - - Keep N most recent states per channel - - Useful for dispute resolution - - Implement pruning strategy for old states - ---- - -## Security - -Critical security practices for production deployments. - -### Key Management - -- [ ] **Secure key storage** - - Never log private keys - - Use secure key storage (keychain, HSM, encrypted database) - - Implement key rotation - - Separate signing keys from storage keys - -- [ ] **Implement signature verification** - - Verify all incoming signatures - - Validate signer matches expected participant - - Check signature freshness (timestamp) - -- [ ] **Never share private keys or session key private keys** - - Session keys stay on client - - Never transmit private keys over network - - Use separate keys for different purposes - -### Challenge Monitoring - -- [ ] **Monitor blockchain for channel events** - - Subscribe to all channels you participate in - - Alert on `Challenged` events - - Automated response to challenges - -- [ ] **Respond to challenges within challenge period** - - Maintain latest valid state - - Submit newer state if challenged with old state - - Set alerts for challenge expiration - -- [ ] **Implement automated challenge response** - - Detect challenges automatically - - Submit newer state without manual intervention - - Fallback to manual response if needed - -### Session Key Management - -- [ ] **Session key allowance enforcement** - - Track spending per session key - - Reject operations exceeding allowance - - Alert user when approaching limit - -- [ ] **Validate spending limits client-side** - - Check allowance before submitting operations - - Provide clear error messages - - Offer to re-authenticate with higher allowance - -### Best Practices - -- [ ] **Never sign two states with same version number** - - Maintain version counter - - Reject duplicate versions - - Use atomic version increment - -- [ ] **Keep track of latest state you've signed** - - Store all signed states - - Never sign older version - - Use for dispute resolution - -- [ ] **Set appropriate challenge periods** - - Balance security (longer) vs UX (shorter) - - Consider block time and congestion - - Minimum: 1 hour (enforced by Custody Contract `MIN_CHALLENGE_PERIOD`) - -- [ ] **Validate all inputs thoroughly** - - Check address formats - - Verify amounts are positive - - Validate asset symbols - - Sanitize user input - -- [ ] **Log all state transitions for auditing** - - Timestamp all operations - - Record signatures and signers - - Maintain audit trail - - Implement log rotation - ---- - -## Error Handling - -Robust error handling for production reliability. - -### RPC Errors - -- [ ] **Handle error responses** - - Error response format: `{res: [requestId, "error", {error: "descriptive message"}, timestamp], sig: [...]}` - - No numeric error codes; errors have descriptive messages only - - Common errors: "authentication required", "insufficient balance", "channel not found", "session expired, please re-authenticate" - - Always check if response method is `"error"` - - Reference: [Error Handling](./protocol-reference#error-handling) - -### Transaction Errors - -- [ ] **Implement retry logic for critical operations** - - Exponential backoff - - Maximum retry attempts - - Idempotent operations - -- [ ] **Handle gas estimation failures** - - Provide manual gas limit option - - Retry with higher gas limit - - Alert user to potential issues - -- [ ] **Handle transaction reverts** - - Parse revert reason - - Provide helpful error messages - - Suggest corrective actions - ---- - -## Testing - -Comprehensive testing strategy for confidence in production. - -### Unit Testing - -- [ ] **Test signature generation and verification** - - Known test vectors - - Round-trip signing - - Invalid signature rejection - -- [ ] **Test identifier computation** - - channelId determinism - - packedState (payload hash) consistency - - Known test vectors - -- [ ] **Test state validation logic** - - Version ordering - - Allocation sum validation - - StateIntent validation (INITIALIZE, RESIZE, FINALIZE for channels) - -### Integration Testing - -- [ ] **Test both cooperative and challenge closure paths** - - Cooperative close (happy path) - - Challenge initiation - - Challenge response - - Challenge timeout - -- [ ] **Test multi-chain operations** - - Open channels on different chains - - Cross-chain transfers (via unified balance) - - Chain-specific edge cases - -- [ ] **Test network reconnection** - - Simulate network interruption - - Verify automatic reconnection - - Check state synchronization - -### End-to-End Testing - -- [ ] **Test complete user journeys** - - Authentication → Channel Open → Transfer → Channel Close - - App session creation → State updates → Closure - - Error scenarios and recovery - -- [ ] **Test with real clearnodes** - - Testnet deployment - - Mainnet staging environment - - Monitor performance and errors - ---- - -## Performance Optimization - -Optimize for production workloads. - -### Efficiency - -- [ ] **Minimize blockchain queries** - - Cache contract addresses - - Batch event queries - - Use multicall for multiple reads - -- [ ] **Implement connection pooling** - - Reuse RPC connections - - Pool blockchain RPC connections - - Implement connection limits - -- [ ] **Optimize state storage** - - Index by channelId and app_session_id - - Prune old states - - Compress stored states - -### Monitoring - -- [ ] **Implement health checks** - - RPC connection status - - Blockchain connection status - - Event listener status - - Use `ping` method for clearnode health - -- [ ] **Monitor latency** - - RPC request/response time - - Transaction confirmation time - - Event processing delay - -- [ ] **Track error rates** - - Failed transactions - - RPC errors - - Signature verification failures - ---- - -## Documentation - -Documentation for maintainability. - -### Code Documentation - -- [ ] **Document adjudicator-specific requirements clearly** - - State validation rules - - Version comparison logic - - Gas cost estimates - -- [ ] **Document custom state formats** - - Application-specific data structures - - Serialization format - - Version compatibility - -### User Documentation - -- [ ] **Provide integration guide** - - Setup instructions - - Code examples - - Common patterns - -- [ ] **Document error messages** - - User-friendly descriptions - - Suggested actions - - Support contact information - ---- - -## Deployment Checklist - -Pre-production validation. - -### Pre-Production - -- [ ] **Audit smart contracts thoroughly before deployment** - - Use established auditors - - Test on testnets first - - Gradual mainnet rollout - -- [ ] **Test on testnet extensively** - - All user flows - - Error scenarios - - Performance under load - -- [ ] **Implement monitoring and alerting** - - Error rate alerts - - Performance degradation alerts - - Challenge event alerts - -### Production - -- [ ] **Use appropriate challenge periods** - - Longer for high-value channels - - Consider network congestion - - Balance security vs UX - -- [ ] **Implement proper key management** - - Hardware security modules (HSM) - - Key rotation policy - - Backup and recovery procedures - -- [ ] **Set up incident response procedures** - - On-call rotation - - Escalation procedures - - Communication plan - ---- - -## Compliance Levels - -### Minimal (User Client) - -Essential for basic client functionality: -- Core Protocol Support ✓ -- On-Chain Integration (create, close) ✓ -- Off-Chain RPC (auth, transfer, basic queries) ✓ -- Basic Security ✓ - -### Standard (Production Application) - -Add: -- Complete method implementation ✓ -- State Management ✓ -- Comprehensive Error Handling ✓ -- Testing ✓ - -### Advanced (Clearnode Implementation) - -Add: -- Server-side RPC routing and authentication ✓ -- Event-driven architecture ✓ -- Unified balance management (double-entry ledger) ✓ -- App session coordination ✓ -- High availability and fault tolerance ✓ - ---- - -## Next Steps - -1. **Start Simple**: Implement Core Protocol + Basic On-Chain integration -2. **Add RPC**: Connect to clearnode, implement authentication and basic methods -3. **Enhance Security**: Implement all security best practices -4. **Test Thoroughly**: Unit, integration, and end-to-end tests -5. **Deploy Gradually**: Testnet → Staging → Production - -:::success Ready to Build -Use this checklist as a guide throughout your implementation. Check off items as you complete them and refer back to detailed documentation for each section. -::: - ---- - -## Resources - -- **Communication Flows**: [Communication Flows](./communication-flows) -- **Reference**: [Protocol Reference](./protocol-reference) -- **Channel Lifecycle**: [Channel Lifecycle](./app-layer/on-chain/channel-lifecycle) -- **RPC Methods**: [Queries](./app-layer/off-chain/queries) -- **Example Code**: [Integration Tests](https://github.com/layer-3/nitrolite/tree/main/integration) diff --git a/docs/protocol/protocol-reference.mdx b/docs/protocol/protocol-reference.mdx deleted file mode 100644 index 7f02434..0000000 --- a/docs/protocol/protocol-reference.mdx +++ /dev/null @@ -1,343 +0,0 @@ ---- -sidebar_position: 8 -title: Protocol Reference ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Protocol Reference - -Quick reference guide for protocol versions, constants, standards, and specifications. - -:::info Quick Navigation -Jump to a section: -- [Protocol Versions](#protocol-versions) - VirtualApp & Nitro RPC versions -- [State Intent System](#state-intent-system) - Channel state classification -- [Participant Indices](#participant-indices) - Creator & Clearnode positions -- [Channel Status](#channel-status-state-machine) - Status transitions -- [Signature Standards](#signature-standards) - On-chain & off-chain formats -- [EIP References](#eip-references) - Ethereum standards used -- [Protocol Constants](#protocol-constants) - Core constants -::: - ---- - -## Protocol Versions - -### VirtualApp Protocol - -| Property | Value | -|----------|-------| -| **Version** | 0.5.0 | -| **Status** | Mainnet deployments live; not production yet | -| **Compatibility** | EVM-compatible chains | - -**Supported Chains**: Ethereum, Polygon, Arbitrum One, Optimism, Base, and other EVM-compatible networks. - -### Nitro RPC Protocol - -| Version | Status | Features | -|---------|--------|----------| -| **0.2** | Legacy | Basic state updates only | -| **0.4** | Current | Intent system (OPERATE, DEPOSIT, WITHDRAW) | - -:::tip Version Recommendation -**Always use NitroRPC/0.4** for new implementations. Version 0.4 adds the intent system for app sessions, enabling dynamic fund management (deposits and withdrawals) within active sessions. -::: - -**Breaking Changes**: -- NitroRPC/0.4 introduces the `intent` parameter in `submit_app_state` -- NitroRPC/0.2 sessions cannot use DEPOSIT or WITHDRAW intents -- Protocol version is set during app session creation and cannot be changed - ---- - -## State Intent System - -Channel states are classified by `state.intent` (uint8) to signal their purpose. The Solidity enum defines: - -### StateIntent Enumeration - -```solidity -enum StateIntent { - OPERATE, // 0: Normal updates (challenge/checkpoint) - INITIALIZE, // 1: Channel funding/creation - RESIZE, // 2: In-place capacity change - FINALIZE // 3: Cooperative closure -} -``` - -### Intent Usage - -| Intent | Value | When Used | Method | -|--------|-------|-----------|---------| -| `INITIALIZE` | 1 | Channel creation | `Custody.create()` | -| `RESIZE` | 2 | Channel resize | `Custody.resize()` | -| `FINALIZE` | 3 | Cooperative closure | `Custody.close()` | -| `OPERATE` | 0 | Challenge/checkpoint | `Custody.challenge()`, `Custody.checkpoint()` | - -**Example**: -```javascript -// Creation state -state.intent = 1 // INITIALIZE -state.version = 0 -state.data = "0x" // Empty for basic channels - -// Closing state -state.intent = 3 // FINALIZE -state.version = currentVersion + 1 -state.data = "0x" -``` - -:::caution Intent Validation -Smart contracts validate the `intent` field to ensure proper channel lifecycle. Incorrect intent values will cause transactions to revert. -::: - ---- - -## Participant Indices - -In a standard payment channel, participants are identified by their array index. - -### Index 0: Creator (User) - -**Role**: Creator - -**Responsibilities**: -- Initiates channel creation -- Typically the one depositing funds -- First to sign states (`state.sigs[0]`) -- Calls `Custody.create()` on-chain - -**Example**: -```javascript -channel.participants[0] = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" // User -``` - -### Index 1: Clearnode - -**Role**: Service provider - -**Responsibilities**: -- Co-signs the initial state before on-chain `create()`; there is no separate `join()` call -- Provides off-chain services (Nitro RPC, unified balance management) -- Second to sign states (`state.sigs[1]`) - -**Example**: -```javascript -channel.participants[1] = "0x123456789abcdef0123456789abcdef012345678" // Clearnode -``` - -:::warning Signature Order Critical -Signatures array order **MUST** match participants array order. Mismatched signatures will cause transaction failures. - -```javascript -state.sigs[0] = creator_signature // Must be from participants[0] -state.sigs[1] = clearnode_signature // Must be from participants[1] -``` -::: - ---- - -## Channel Status State Machine - -Channel lifecycle is governed by status transitions. - -### Status Enumeration - -```solidity -enum Status { - VOID, // 0: Channel does not exist - INITIAL, // 1: Creation in progress, awaiting all participants - ACTIVE, // 2: Fully funded and operational - DISPUTE, // 3: Challenge period active - FINAL // 4: Ready to be closed and deleted -} -``` - -### State Transition Diagram - -```mermaid -stateDiagram-v2 - [*] --> VOID - VOID --> INITIAL : create() (creator only) - VOID --> ACTIVE : create() (all sigs present) - INITIAL --> ACTIVE : join() (remaining participants) - ACTIVE --> DISPUTE : challenge() - ACTIVE --> FINAL : close() (cooperative) - DISPUTE --> ACTIVE : checkpoint() (newer state) - DISPUTE --> FINAL : close() (after timeout) - FINAL --> [*] - - note right of VOID - Channel does not exist - on blockchain - end note - - note right of INITIAL - Creator has joined, - awaiting other participants - end note - - note right of ACTIVE - Operational state, - can perform off-chain updates - end note - - note right of DISPUTE - Challenge active, - parties can submit newer states - end note - - note right of FINAL - Ready for deletion, - funds distributed - end note -``` - -### Valid Transitions - -| From | To | Trigger | Notes | -|------|----|---------|---------| -| `VOID` | `INITIAL` | `create()` (creator only) | Legacy flow; awaiting other participants | -| `VOID` | `ACTIVE` | `create()` (all sigs present) | Current flow; both participants co-sign initial state | -| `INITIAL` | `ACTIVE` | `join()` | Remaining participants join | -| `ACTIVE` | `DISPUTE` | `challenge()` | Dispute initiated | -| `ACTIVE` | `FINAL` | `close()` | Cooperative closure | -| `DISPUTE` | `ACTIVE` | `checkpoint()` | Newer state accepted | -| `DISPUTE` | `FINAL` | `close()` | Challenge timeout | - -:::tip Quick Closure -The fastest way to close a channel is **ACTIVE → FINAL** via cooperative `close()`. This skips the challenge period entirely. -::: - ---- - -## Signature Standards - -### On-Chain Signatures (Solidity) - -Used in smart contract transactions (`create`, `join`, `close`, `challenge`, `resize`). - -**Format**: Variable-length byte arrays supporting multiple signature types (since v0.3.0) - -**Structure**: -```solidity -struct State { - // ... other fields ... - bytes[] sigs; // Array of signatures from participants -} -``` - -**Supported Types**: -- **ECDSA** (65 bytes): Standard signatures from EOA wallets -- **ERC-1271**: Smart contract wallet signatures -- **ERC-6492**: Counterfactual contract signatures (not yet deployed) - -**Hash**: Raw `packedState` (no EIP-191 prefix for chain-agnostic compatibility) - -**Example**: -```javascript -packedState = keccak256(abi.encode(channelId, state.intent, state.version, state.data, state.allocations)) -signature = sign(packedState, participantPrivateKey) // Raw hash, no prefix -``` - -### Off-Chain Signatures (Nitro RPC) - -Used in RPC requests and responses over RPC. - -**Format**: 0x-prefixed hex string (typically ECDSA from session keys) - -**Typical Length**: 65 bytes for ECDSA -- `r`: 32 bytes -- `s`: 32 bytes -- `v`: 1 byte - -**Representation**: 130 hex characters + `0x` prefix - -**Example**: -```javascript -signature = "0x1234567890abcdef...xyz" // 132 characters total (ECDSA) -``` - -**Computed Over**: -```javascript -rpcHash = keccak256(JSON.stringify(req)) -signature = sign(rpcHash, sessionPrivateKey) -``` - -:::info Session Key Signatures -Off-chain RPC signatures are typically ECDSA from session keys (EOA wallets), but the protocol supports other signature types for future flexibility. -::: - -:::caution Chain-Agnostic Signatures -On-chain signatures do NOT use EIP-191 or EIP-712 prefixes to maintain chain-agnostic compatibility. This differs from typical Ethereum signing patterns. Off-chain RPC signatures (e.g., authentication) DO use EIP-712 for better wallet UX. -::: - ---- - -## EIP References - -Ethereum Improvement Proposals referenced or used by the protocol. - -### EIP-191: Signed Data Standard - -**Status**: Not used in on-chain signatures (chain-agnostic design) -**Link**: https://eips.ethereum.org/EIPS/eip-191 - -**Why not used for on-chain**: On-chain signatures are computed over raw `packedState` hash without EIP-191 prefix to maintain compatibility across different EVM chains and potential non-EVM implementations. - -### EIP-712: Typed Structured Data Hashing - -**Status**: Used for off-chain RPC authentication -**Link**: https://eips.ethereum.org/EIPS/eip-712 - -**Usage**: Authentication flow uses EIP-712 typed data for signing the Policy structure (challenge, wallet, session_key, expires_at, scope, allowances) with the main wallet. This provides better wallet UX by displaying human-readable signing data. - -### EIP-1271: Contract Signature Validation - -**Status**: Supported by adjudicators -**Link**: https://eips.ethereum.org/EIPS/eip-1271 - -**Usage**: Enables smart contract wallets to sign state updates as participants. - -### EIP-20 (ERC-20): Token Standard - -**Status**: Required for all assets -**Link**: https://eips.ethereum.org/EIPS/eip-20 - -**Usage**: All assets must be ERC-20 compliant tokens. The Custody Contract uses `transferFrom` and `transfer` methods. - -:::note Standards Compliance -While the protocol references these EIPs, implementation details may vary. Always consult the specific smart contract code for authoritative behavior. -::: - ---- - -## Protocol Constants - -The only protocol-wide constants defined in code are: - -```solidity -uint256 constant PART_NUM = 2; // Channels are always 2-party -uint256 constant CLIENT_IDX = 0; // Client/creator participant index -uint256 constant SERVER_IDX = 1; // Server/clearnode participant index -``` - -All channel arrays (participants, allocations, sigs) and state validation logic rely on these indices and fixed participant count. - ---- - -## Next Steps - -Now that you have the complete protocol reference: - -1. **Terminology**: Review [Terminology](./terminology) for all term definitions -2. **Communication Flows**: See [Communication Flows](./communication-flows) for sequence diagrams -3. **Implementation Guide**: Follow [Implementation Checklist](./implementation-checklist) to build compliant clients -4. **Channel Lifecycle**: See [Channel Lifecycle](./app-layer/on-chain/channel-lifecycle) for detailed state transitions - -:::tip Reference Updates -This reference reflects protocol version 0.5.0. For the latest updates, check the [Nitrolite repository](https://github.com/layer-3/nitrolite) or use `get_config` to query clearnode capabilities dynamically. -::: diff --git a/docs/protocol/terminology.mdx b/docs/protocol/terminology.mdx deleted file mode 100644 index c493f02..0000000 --- a/docs/protocol/terminology.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -sidebar_position: 2 -title: "Terminology" ---- - -import Tooltip from '@site/src/components/Tooltip'; -import { tooltipDefinitions } from '@site/src/constants/tooltipDefinitions'; - -# Terminology - -## Core Concepts - -**Channel**: {tooltipDefinitions.channel} - -**State**: {tooltipDefinitions.channelState} - -**Participant**: {tooltipDefinitions.participant} - -**Clearnode**: {tooltipDefinitions.clearnode} - -**Creator**: {tooltipDefinitions.creatorParticipant} - -**App Sessions**: {tooltipDefinitions.appChannel} - -**Unified Balance**: {tooltipDefinitions.unifiedBalance} - -**Session Key**: {tooltipDefinitions.sessionKey} - -## Identifiers - -**channelId**: {tooltipDefinitions.channelId} - -**packedState**: {tooltipDefinitions.packedState} - -**requestId**: A unique identifier for an RPC request, used for correlating requests and responses formatted as a 0x-prefixed hex string (32 bytes). - -**appSessionId**: {tooltipDefinitions.appSessionId} Used for all subsequent operations on that specific app session. - -**accountId**: An identifier for an account or app session within the unified ledger. Can be either a 0x-prefixed hex string or a wallet address. - -**chainId**: {tooltipDefinitions.chainId} Examples: 1 (Ethereum Mainnet), 137 (Polygon), 8453 (Base), 42161 (Arbitrum One), 10 (Optimism). - -**assetSymbol**: {tooltipDefinitions.assetSymbol} Asset symbols are consistent across chains. - -**walletAddress**: {tooltipDefinitions.walletAddress} Used to identify participants in channels and app sessions. - -**userId**: Identifies a user after authentication to the Clearnode. Currently, this is always equivalent to the user's walletAddress. - -## On-Chain Contracts - -**Custody Contract**: The main on-chain contract implementing the VirtualApp protocol. It provides the functionality to lock and unlock funds; create, close and challenge a channel; track channel state, and coordinate with adjudicators to validate state transitions on state updates. - -**Adjudicator**: A smart contract that defines the rules for validating state transitions during all channel lifecycle operations. The adjudicator's `adjudicate(...)` function is called by the Custody contract to verify whether a new state is valid based on previous states and application-specific logic. Examples include SimpleConsensus (requires both signatures) and Remittance (only sender must sign). - -## Protocol Layers - -**Decentralized Layer (YNP)**: The peer-to-peer overlay network — Kademlia DHT, BLS threshold clusters, elastic security, and the liquidity layer. Manages account state, cross-shard transfers, and settlement. - -**App Layer (VirtualApp)**: The state channel protocol (also known as YApps) — on-chain custody and adjudicator contracts, plus the off-chain Nitro RPC protocol for fast state updates. - -**Nitro RPC**: The off-chain communication protocol used by the App Layer for channel operations, transfers, and app sessions. - -## Decentralized Layer Terms - -**NodeID**: A 256-bit identity derived from on-chain randomness at registration. Each NodeID requires collateral. - -**Signing Cluster (C_sign)**: The set of *k* nodes closest to an account's key in the DHT, responsible for BLS threshold signing. - -**Replication Set (C_watch)**: A larger ring of *r* nodes that independently verify signing cluster state. Always a superset of the signing cluster. - -**Certificate**: The protocol's universal primitive for state change — a cluster-attested operation carrying a BLS threshold signature. - -**Escrow Certificate**: A certificate authorizing a withdrawal from the network to L1. - -**Dispute Certificate**: A certificate produced by the replication set to override a fraudulent withdrawal. - -:::tip Quick Reference -These terms are used throughout the protocol specification. Bookmark this page for easy reference while reading other sections. -::: diff --git a/docs/tutorials/_category_.json b/docs/tutorials/_category_.json deleted file mode 100644 index 422afbb..0000000 --- a/docs/tutorials/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "Tutorials", - "position": 5, - "link": { - "type": "doc", - "id": "tutorials/index" - } -} \ No newline at end of file diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md deleted file mode 100644 index f6d7e76..0000000 --- a/docs/tutorials/index.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Tutorials -description: Step-by-step tutorials and guides -displayed_sidebar: tutorialsSidebar ---- - -# Tutorials - -:::info Work in Progress -This section is currently under development. Step-by-step tutorials and comprehensive guides will be available soon. -::: - -Coming soon: Interactive tutorials covering various development scenarios and use cases. \ No newline at end of file diff --git a/docusaurus.config.ts b/docusaurus.config.ts index eb6211d..c0f73b2 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -32,8 +32,7 @@ const config: Config = { organizationName: 'layer-3', // Usually your GitHub org/user name. projectName: 'docs', // Usually your repo name. - onBrokenLinks: 'warn', - onBrokenMarkdownLinks: 'warn', + onBrokenLinks: 'throw', // SEO metadata - injected into HTML head headTags: [ @@ -59,10 +58,11 @@ const config: Config = { 'classic', { docs: { - sidebarPath: './sidebars.ts', - routeBasePath: '/docs', + path: './docs/nitrolite', + sidebarPath: './sidebars-nitrolite.ts', + routeBasePath: '/nitrolite', editUrl: - 'https://github.com/layer-3/docs/tree/master/', + 'https://github.com/layer-3/docs/tree/main/', sidebarCollapsed: false, sidebarCollapsible: false, breadcrumbs: true, @@ -90,9 +90,26 @@ const config: Config = { markdown: { mermaid: true, + hooks: { + onBrokenMarkdownLinks: 'throw', + }, }, themes: ['@docusaurus/theme-mermaid'], plugins: [ + [ + '@docusaurus/plugin-content-docs', + { + id: 'clearnet', + path: './docs/clearnet', + routeBasePath: '/clearnet', + sidebarPath: './sidebars-clearnet.ts', + editUrl: 'https://github.com/layer-3/docs/tree/main/', + sidebarCollapsed: false, + sidebarCollapsible: false, + breadcrumbs: true, + // No versions block — Clearnet starts unversioned + }, + ], [ 'docusaurus-lunr-search', { @@ -118,45 +135,85 @@ const config: Config = { srcDark: 'img/themes/dark/logo.svg', }, items: [ + // Portal-only product entry links + { + to: '/nitrolite', + label: 'Nitrolite', + position: 'left', + customProps: { showOn: 'portal' }, + }, + { + to: '/clearnet/learn/introduction', + label: 'Clearnet', + position: 'left', + customProps: { showOn: 'portal' }, + }, + // Nitrolite navbar items (shown on /nitrolite/*) { type: 'doc', docId: 'learn/index', label: 'Learn', position: 'left', + customProps: { showOn: 'nitrolite' }, }, { - type: 'doc', - docId: 'build/quick-start/index', + to: '/nitrolite/build/getting-started/quickstart', + activeBasePath: '/nitrolite/build/', label: 'Build', position: 'left', + customProps: { showOn: 'nitrolite' }, }, { type: 'doc', docId: 'protocol/introduction', label: 'Protocol', position: 'left', + customProps: { showOn: 'nitrolite' }, + }, + { + to: '/nitrolite/builder-toolkit', + activeBasePath: '/nitrolite/builder-toolkit', + label: 'Builder Toolkit', + position: 'left', + customProps: { showOn: 'nitrolite', hideIfPathStartsWith: '/nitrolite/0.5.x' }, }, { type: 'doc', docId: 'guides/index', label: 'Guides', position: 'left', + customProps: { showOn: 'nitrolite', onlyIfPathStartsWith: '/nitrolite/0.5.x' }, }, + // Clearnet navbar items (shown on /clearnet/*) — single "Learn" entry + // is a plain link to Introduction, not a doc reference, so the navbar + // item itself doesn't represent a page. + { + to: '/clearnet/learn/introduction', + label: 'Learn', + position: 'left', + customProps: { showOn: 'clearnet' }, + }, + // Whitepaper only on portal { to: '/whitepaper', label: 'Whitepaper', position: 'left', + customProps: { showOn: 'portal' }, }, + // GitHub link visible on all sub-sites { - href: 'https://github.com/layer-3', + href: 'https://github.com/layer-3/docs', position: 'right', className: 'header-github-link', 'aria-label': 'GitHub repository', + customProps: { showOn: 'all' }, }, + // Version dropdown only on Nitrolite (Clearnet is unversioned). { type: 'docsVersionDropdown', position: 'right', className: 'navbar-version-dropdown', + customProps: { showOn: 'nitrolite' }, }, ], }, @@ -175,20 +232,42 @@ const config: Config = { ], }, { - title: 'Docs', + title: 'Nitrolite', items: [ { label: 'Learn', - to: '/docs/learn', + to: '/nitrolite/learn', }, { label: 'Build', - to: '/docs/build/quick-start', + to: '/nitrolite/build/getting-started/quickstart', + }, + { + label: 'Protocol', + to: '/nitrolite/protocol/introduction', + }, + ], + }, + { + title: 'Clearnet', + items: [ + { + label: 'Introduction', + to: '/clearnet/learn/introduction', }, { - label: 'Guides', - to: '/docs/guides', + label: 'Architecture', + to: '/clearnet/learn/architecture', }, + { + label: 'Contracts', + to: '/clearnet/learn/contracts', + }, + ], + }, + { + title: 'More', + items: [ { label: 'Whitepaper', to: '/whitepaper', @@ -198,6 +277,10 @@ const config: Config = { { title: 'Community', items: [ + { + label: 'Telegram', + href: 'https://t.me/YellowSDKCommunity', + }, { label: 'Discord', href: 'https://discord.com/invite/yellownetwork', @@ -224,7 +307,7 @@ const config: Config = { theme: prismThemes.github, darkTheme: prismThemes.dracula, additionalLanguages: ['bash', 'diff', 'json', 'go', 'typescript'], - defaultLanguage: 'javascript', + defaultLanguage: 'typescript', magicComments: [ { className: 'git-diff-remove', diff --git a/examples/nitrolite-v1-lifecycle/.env.example b/examples/nitrolite-v1-lifecycle/.env.example new file mode 100644 index 0000000..d6cbc70 --- /dev/null +++ b/examples/nitrolite-v1-lifecycle/.env.example @@ -0,0 +1,22 @@ +# Disposable test wallets only. Do not reuse production keys. +USER_PRIVATE_KEY=0x... +APP_PRIVATE_KEY=0x... + +# Native v1 Nitronode and matching chain RPC. +# TODO: Replace with the canonical sandbox URL once it is live. +NITRONODE_WS_URL= +RPC_URL=https://ethereum-sepolia-rpc.publicnode.com +CHAIN_ID=11155111 +ASSET=yellow + +# Keep these tiny for docs verification. CHANNEL_DEPOSIT_AMOUNT funds the +# user's home channel; APP_DEPOSIT_AMOUNT is committed from that balance into +# the app session. Adjust both for the faucet asset and chain. +CHANNEL_DEPOSIT_AMOUNT=0.01 +APP_DEPOSIT_AMOUNT=0.005 +OPERATE_AMOUNT=0.001 +WITHDRAW_AMOUNT=0.002 + +# Optional: uncomment to checkpoint and close the user's home channel after the +# app-session lifecycle. This is destructive to the example channel balance. +# CLOSE_HOME_CHANNEL=true diff --git a/examples/nitrolite-v1-lifecycle/.gitignore b/examples/nitrolite-v1-lifecycle/.gitignore new file mode 100644 index 0000000..ee62cd4 --- /dev/null +++ b/examples/nitrolite-v1-lifecycle/.gitignore @@ -0,0 +1,6 @@ +.env +.env.* +!.env.example +dist/ +node_modules/ +npm-debug.log* diff --git a/examples/nitrolite-v1-lifecycle/README.md b/examples/nitrolite-v1-lifecycle/README.md new file mode 100644 index 0000000..1595ec5 --- /dev/null +++ b/examples/nitrolite-v1-lifecycle/README.md @@ -0,0 +1,29 @@ +# Nitrolite v1 Lifecycle Example + +Runnable TypeScript example for the native `@yellow-org/sdk` channel and app-session lifecycle. + +The script: + +1. Connects a user wallet and a separate app signer. +2. Checks Nitronode config and asset support. +3. Prepares the user's home channel with `approveToken`, `deposit`, and `checkpoint` when needed. +4. Creates a two-party app session. +5. Deposits channel funds into the app session. +6. Runs `operate`, `withdraw`, and `close` app-session updates. + +## Setup + +```bash +cp .env.example .env +npm install +npm run build +npm run lifecycle +``` + +Use disposable test wallets only. The app signer can be an unfunded test wallet; the user wallet needs Sepolia gas and the selected test asset. + +Set `NITRONODE_WS_URL` to the current v1 sandbox or test endpoint provided for your environment before running `npm run lifecycle`. + +The script calls `enableNodeLocalAccountTransactions()` after creating each SDK client. This keeps viem's local account attached to `writeContract()` requests in Node.js so `approveToken()` and `checkpoint()` can send transactions through public RPC endpoints. Browser wallet apps do not need this shim. + +Set `CLOSE_HOME_CHANNEL=true` only when you intentionally want to close the example home channel after the app-session lifecycle. diff --git a/examples/nitrolite-v1-lifecycle/package-lock.json b/examples/nitrolite-v1-lifecycle/package-lock.json new file mode 100644 index 0000000..42ea228 --- /dev/null +++ b/examples/nitrolite-v1-lifecycle/package-lock.json @@ -0,0 +1,539 @@ +{ + "name": "@yellow-org/docs-nitrolite-v1-lifecycle", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@yellow-org/docs-nitrolite-v1-lifecycle", + "version": "1.0.0", + "dependencies": { + "@yellow-org/sdk": "1.2.1", + "decimal.js": "^10.6.0", + "dotenv": "^17.2.3", + "viem": "2.46.1" + }, + "devDependencies": { + "@types/node": "^24.12.2", + "typescript": "~6.0.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", + "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/@yellow-org/sdk": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@yellow-org/sdk/-/sdk-1.2.1.tgz", + "integrity": "sha512-ZRV1OjHLkCP3BOKaSRqsqfw8LOKo5TK5XU7LOS8JEiv7kRu3xApjCSjdof8EWkoMdwMYlU0fvRxxD5glTkuWBA==", + "license": "MIT", + "dependencies": { + "abitype": "^1.2.3", + "decimal.js": "^10.4.3", + "jest-util": "^30.3.0", + "viem": "^2.46.1", + "zod": "^4.3.6" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/abitype": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.4.tgz", + "integrity": "sha512-dpKH+N27vRjarMVTFFkeY445VTKftzGWpL0FiT7xmVmzQRKazZexzC5uHG0f6XKsVLAuUlndnbGau6lRejClxg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", + "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/ox": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.12.1.tgz", + "integrity": "sha512-uU0llpthaaw4UJoXlseCyBHmQ3bLrQmz9rRLIAUHqv46uHuae9SE+ukYBRIPVCnlEnHKuWjDUcDFHWx9gbGNoA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.2.3", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/viem": { + "version": "2.46.1", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.46.1.tgz", + "integrity": "sha512-c5YPQR/VueqoPG09Tp1JBw2iItKVRGVI0YkWekquRDZw0ciNBhO3muu2QjO9xFelOXh18q3d/kLbW83B2Oxf0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.2.3", + "isows": "1.0.7", + "ox": "0.12.1", + "ws": "8.18.3" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/examples/nitrolite-v1-lifecycle/package.json b/examples/nitrolite-v1-lifecycle/package.json new file mode 100644 index 0000000..74f32b7 --- /dev/null +++ b/examples/nitrolite-v1-lifecycle/package.json @@ -0,0 +1,25 @@ +{ + "name": "@yellow-org/docs-nitrolite-v1-lifecycle", + "version": "1.0.0", + "private": true, + "description": "Runnable Nitrolite v1 TypeScript SDK channel and app-session lifecycle example.", + "type": "module", + "engines": { + "node": ">=20.0.0" + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "lifecycle": "node dist/index.js", + "start": "npm run build && npm run lifecycle" + }, + "dependencies": { + "@yellow-org/sdk": "1.2.1", + "decimal.js": "^10.6.0", + "dotenv": "^17.2.3", + "viem": "2.46.1" + }, + "devDependencies": { + "@types/node": "^24.12.2", + "typescript": "~6.0.2" + } +} diff --git a/examples/nitrolite-v1-lifecycle/src/index.ts b/examples/nitrolite-v1-lifecycle/src/index.ts new file mode 100644 index 0000000..6af116e --- /dev/null +++ b/examples/nitrolite-v1-lifecycle/src/index.ts @@ -0,0 +1,548 @@ +import 'dotenv/config'; + +import { Decimal } from 'decimal.js'; +import { isAddress, type Address, type Hex } from 'viem'; +import { + AppSessionWalletSignerV1, + AppStateUpdateIntent, + Client, + EthereumMsgSigner, + createSigners, + packAppStateUpdateV1, + packCreateAppSessionRequestV1, + withBlockchainRPC, + type AppDefinitionV1, + type AppSessionInfoV1, + type AppStateUpdateV1, + type State, +} from '@yellow-org/sdk'; + +type LifecycleConfig = { + userPrivateKey: Hex; + appPrivateKey: Hex; + wsURL: string; + rpcURL: string; + chainId: bigint; + asset: string; + channelDepositAmount: Decimal; + appDepositAmount: Decimal; + operateAmount: Decimal; + withdrawAmount: Decimal; + closeHomeChannel: boolean; +}; + +type Clients = { + user: Client; + app: Client; +}; + +type WalletClientWithWriteContract = { + account?: unknown; + writeContract?: (request: Record) => Promise; +}; + +type EVMClientFactoryResult = { + walletClient?: WalletClientWithWriteContract | null; +}; + +type ClientWithEVMFactory = { + createEVMClients?: (chainId: bigint, rpcURL: string) => EVMClientFactoryResult; +}; + +function requireEnv(name: string): string { + const value = process.env[name]?.trim(); + if (!value) { + throw new Error(`Missing required environment variable: ${name}`); + } + return value; +} + +function optionalEnv(name: string, fallback: string): string { + const value = process.env[name]?.trim(); + return value || fallback; +} + +function privateKeyEnv(name: string): Hex { + const value = requireEnv(name); + if (!/^0x[0-9a-fA-F]{64}$/.test(value)) { + throw new Error(`${name} must be a 32-byte hex private key with 0x prefix`); + } + return value as Hex; +} + +function decimalEnv(name: string, fallback: string): Decimal { + const value = optionalEnv(name, fallback); + const amount = new Decimal(value); + if (!amount.isFinite() || amount.isNegative()) { + throw new Error(`${name} must be a non-negative decimal amount`); + } + return amount; +} + +function loadConfig(): LifecycleConfig { + const userPrivateKey = privateKeyEnv('USER_PRIVATE_KEY'); + const appPrivateKey = privateKeyEnv('APP_PRIVATE_KEY'); + + if (userPrivateKey.toLowerCase() === appPrivateKey.toLowerCase()) { + throw new Error('USER_PRIVATE_KEY and APP_PRIVATE_KEY must be different wallets'); + } + + const chainId = BigInt(requireEnv('CHAIN_ID')); + const asset = requireEnv('ASSET').toLowerCase(); + const appDepositAmount = decimalEnv('APP_DEPOSIT_AMOUNT', '0.005'); + const operateAmount = decimalEnv('OPERATE_AMOUNT', '0.001'); + const withdrawAmount = decimalEnv('WITHDRAW_AMOUNT', '0.002'); + + if (!appDepositAmount.isPositive()) { + throw new Error('APP_DEPOSIT_AMOUNT must be greater than zero'); + } + if (operateAmount.plus(withdrawAmount).greaterThan(appDepositAmount)) { + throw new Error('OPERATE_AMOUNT + WITHDRAW_AMOUNT must be <= APP_DEPOSIT_AMOUNT'); + } + + return { + userPrivateKey, + appPrivateKey, + wsURL: requireEnv('NITRONODE_WS_URL'), + rpcURL: requireEnv('RPC_URL'), + chainId, + asset, + channelDepositAmount: decimalEnv('CHANNEL_DEPOSIT_AMOUNT', '0.01'), + appDepositAmount, + operateAmount, + withdrawAmount, + closeHomeChannel: optionalEnv('CLOSE_HOME_CHANNEL', 'false').toLowerCase() === 'true', + }; +} + +function stage(title: string): void { + console.log(`\n== ${title} ==`); +} + +function logState(label: string, state: State): void { + console.log( + `${label}: version=${state.version.toString()} transition=${state.transition.type} ` + + `homeBalance=${state.homeLedger.userBalance.toFixed()} homeChannel=${state.homeChannelId ?? 'none'}` + ); +} + +function logSession(label: string, session: AppSessionInfoV1, userAddress: Address, appAddress: Address, asset: string): void { + const userAllocation = allocationFor(session, userAddress, asset); + const appAllocation = allocationFor(session, appAddress, asset); + console.log( + `${label}: version=${session.version.toString()} closed=${session.isClosed} ` + + `user=${userAllocation.toFixed()} app=${appAllocation.toFixed()}` + ); +} + +function maxDecimal(left: Decimal, right: Decimal): Decimal { + return left.greaterThan(right) ? left : right; +} + +function isAllowanceError(error: unknown): boolean { + const message = error instanceof Error ? error.message : String(error); + return /allowance|approval|approve|insufficient.*allow/i.test(message); +} + +async function getLatestStateOrNull(client: Client, wallet: Address, asset: string, onlySigned: boolean): Promise { + try { + return await client.getLatestState(wallet, asset, onlySigned); + } catch { + return null; + } +} + +async function checkpointWithApproval(client: Client, config: LifecycleConfig): Promise { + try { + return await client.checkpoint(config.asset); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (message.toLowerCase().includes('does not require a blockchain operation')) { + return null; + } + if (!isAllowanceError(error)) { + throw error; + } + + console.log(`checkpoint needs token approval; approving ${config.channelDepositAmount.toFixed()} ${config.asset}`); + await client.approveToken(config.chainId, config.asset, config.channelDepositAmount); + return await client.checkpoint(config.asset); + } +} + +async function connectClients(config: LifecycleConfig): Promise { + const userSigners = createSigners(config.userPrivateKey); + const appSigners = createSigners(config.appPrivateKey); + + const user = await Client.create( + config.wsURL, + userSigners.stateSigner, + userSigners.txSigner, + withBlockchainRPC(config.chainId, config.rpcURL) + ); + + const app = await Client.create( + config.wsURL, + appSigners.stateSigner, + appSigners.txSigner, + withBlockchainRPC(config.chainId, config.rpcURL) + ); + + enableNodeLocalAccountTransactions(user); + enableNodeLocalAccountTransactions(app); + + return { user, app }; +} + +function enableNodeLocalAccountTransactions(client: Client): void { + const sdkClient = client as unknown as ClientWithEVMFactory; + const originalFactory = sdkClient.createEVMClients?.bind(client); + if (!originalFactory) { + console.warn( + 'Warning: enableNodeLocalAccountTransactions could not patch createEVMClients. ' + + 'The SDK shape may have changed. LocalAccount preservation may not work on Node.js public RPCs.' + ); + return; + } + + sdkClient.createEVMClients = (chainId: bigint, rpcURL: string): EVMClientFactoryResult => { + const result = originalFactory(chainId, rpcURL); + const walletClient = result.walletClient; + if (!walletClient?.account || !walletClient.writeContract) { + return result; + } + + // Node.js public RPCs need the LocalAccount object when viem sends writes. + // Keep using SDK methods, but preserve the local account after simulation. + const localAccount = walletClient.account; + const originalWriteContract = walletClient.writeContract.bind(walletClient); + walletClient.writeContract = (request: Record): Promise => { + return originalWriteContract({ ...request, account: localAccount }); + }; + + return result; + }; +} + +async function inspectNode(client: Client, config: LifecycleConfig): Promise { + const nodeConfig = await client.getConfig(); + console.log(`node=${nodeConfig.nodeAddress} version=${nodeConfig.nodeVersion}`); + + const blockchains = await client.getBlockchains(); + const selectedChain = blockchains.find((chain) => chain.id === config.chainId); + if (!selectedChain) { + throw new Error(`Nitronode does not list configured CHAIN_ID ${config.chainId.toString()}`); + } + console.log(`chain=${selectedChain.name} id=${selectedChain.id.toString()}`); + + const assets = await client.getAssets(config.chainId); + const selectedAsset = assets.find((asset) => asset.symbol.toLowerCase() === config.asset); + if (!selectedAsset) { + throw new Error(`Nitronode does not list asset ${config.asset} on chain ${config.chainId.toString()}`); + } + console.log(`asset=${selectedAsset.symbol} decimals=${selectedAsset.decimals}`); +} + +async function setHomeBlockchain(client: Client, asset: string, chainId: bigint): Promise { + try { + await client.setHomeBlockchain(asset, chainId); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (!message.includes('home blockchain is already set')) { + throw error; + } + } +} + +async function prepareHomeChannel(client: Client, config: LifecycleConfig): Promise { + const wallet = client.getUserAddress(); + await setHomeBlockchain(client, config.asset, config.chainId); + + const signedBefore = await getLatestStateOrNull(client, wallet, config.asset, true); + const latestBefore = await getLatestStateOrNull(client, wallet, config.asset, false); + + if (signedBefore) { + logState('signed state before prepare', signedBefore); + } + if (latestBefore && (!signedBefore || latestBefore.id !== signedBefore.id || latestBefore.version !== signedBefore.version)) { + logState('pending state before prepare', latestBefore); + } + + if (latestBefore && latestBefore.homeLedger.userBalance.isPositive() && !latestBefore.userSig) { + console.log(`acknowledging pending ${config.asset} channel state`); + try { + await client.acknowledge(config.asset); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (!message.toLowerCase().includes('already acknowledged')) { + throw error; + } + } + const txHash = await checkpointWithApproval(client, config); + console.log(txHash ? `ack checkpoint tx=${txHash}` : 'acknowledged without on-chain checkpoint'); + } + + const signedAfterAck = await getLatestStateOrNull(client, wallet, config.asset, true); + const existingBalance = signedAfterAck?.homeLedger.userBalance ?? new Decimal(0); + if (signedAfterAck && existingBalance.greaterThanOrEqualTo(config.appDepositAmount)) { + logState('home channel ready', signedAfterAck); + return signedAfterAck; + } + + const deficit = config.appDepositAmount.minus(existingBalance); + const depositAmount = maxDecimal(config.channelDepositAmount, deficit); + const onChainBalance = await client.getOnChainBalance(config.chainId, config.asset, wallet); + console.log(`on-chain ${config.asset} balance=${onChainBalance.toFixed()}`); + + if (onChainBalance.lessThan(depositAmount)) { + throw new Error( + `Wallet ${wallet} needs at least ${depositAmount.toFixed()} ${config.asset} on chain ${config.chainId.toString()}` + ); + } + + console.log(`approving ${depositAmount.toFixed()} ${config.asset}`); + await client.approveToken(config.chainId, config.asset, depositAmount); + + console.log(`depositing ${depositAmount.toFixed()} ${config.asset} into the home channel`); + const depositState = await client.deposit(config.chainId, config.asset, depositAmount); + logState('deposit state', depositState); + + const txHash = await checkpointWithApproval(client, config); + console.log(txHash ? `deposit checkpoint tx=${txHash}` : 'deposit did not require an on-chain checkpoint'); + + const ready = await getLatestStateOrNull(client, wallet, config.asset, true); + if (!ready || ready.homeLedger.userBalance.lessThan(config.appDepositAmount)) { + throw new Error(`Home channel did not reach ${config.appDepositAmount.toFixed()} ${config.asset}`); + } + + logState('home channel ready', ready); + return ready; +} + +function makeAppID(): string { + const suffix = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; + return `docs-v1-lifecycle-${suffix}`; +} + +function appSessionSigner(privateKey: Hex): AppSessionWalletSignerV1 { + return new AppSessionWalletSignerV1(new EthereumMsgSigner(privateKey)); +} + +async function getSession(client: Client, appSessionId: string): Promise { + const { sessions } = await client.getAppSessions({ appSessionId }); + const session = sessions[0]; + if (!session) { + throw new Error(`App session ${appSessionId} was not returned by Nitronode`); + } + return session; +} + +function allocationFor(session: AppSessionInfoV1, participant: Address, asset: string): Decimal { + const allocation = session.allocations.find( + (entry) => entry.participant.toLowerCase() === participant.toLowerCase() && entry.asset.toLowerCase() === asset + ); + return allocation?.amount ?? new Decimal(0); +} + +function nextVersion(session: AppSessionInfoV1): bigint { + return session.version + 1n; +} + +async function signUpdate( + update: AppStateUpdateV1, + userSessionSigner: AppSessionWalletSignerV1, + appSessionSignerValue: AppSessionWalletSignerV1 +): Promise<[Hex, Hex]> { + const payload = packAppStateUpdateV1(update); + const userSig = await userSessionSigner.signMessage(payload); + const appSig = await appSessionSignerValue.signMessage(payload); + return [userSig, appSig]; +} + +async function runAppSessionLifecycle(clients: Clients, config: LifecycleConfig): Promise { + const userAddress = clients.user.getUserAddress(); + const appAddress = clients.app.getUserAddress(); + const userSessionSigner = appSessionSigner(config.userPrivateKey); + const appSessionSignerValue = appSessionSigner(config.appPrivateKey); + + if (!isAddress(userAddress) || !isAddress(appAddress)) { + throw new Error('SDK returned an invalid signer address'); + } + + const appID = makeAppID(); + console.log(`registering app=${appID} owner=${appAddress}`); + try { + await clients.app.registerApp(appID, JSON.stringify({ name: 'Docs v1 lifecycle example' }), true); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (!message.includes('apps.v1 group is disabled')) { + throw error; + } + console.log('app registry is disabled on this node; continuing with app-session creation'); + } + + const sessionData = JSON.stringify({ intent: 'init', source: 'yellow-docs' }); + const definition: AppDefinitionV1 = { + applicationId: appID, + participants: [ + { walletAddress: userAddress, signatureWeight: 1 }, + { walletAddress: appAddress, signatureWeight: 1 }, + ], + quorum: 2, + nonce: BigInt(Date.now()) * 1_000_000n + BigInt(Math.floor(Math.random() * 1_000_000)), + }; + + const createPayload = packCreateAppSessionRequestV1(definition, sessionData); + const createUserSig = await userSessionSigner.signMessage(createPayload); + const createAppSig = await appSessionSignerValue.signMessage(createPayload); + const created = await clients.user.createAppSession(definition, sessionData, [createUserSig, createAppSig]); + console.log(`created app session=${created.appSessionId} version=${created.version} status=${created.status}`); + + let session = await getSession(clients.user, created.appSessionId); + logSession('after create', session, userAddress, appAddress, config.asset); + + const depositUpdate: AppStateUpdateV1 = { + appSessionId: created.appSessionId, + intent: AppStateUpdateIntent.Deposit, + version: nextVersion(session), + allocations: [ + { participant: userAddress, asset: config.asset, amount: config.appDepositAmount }, + { participant: appAddress, asset: config.asset, amount: new Decimal(0) }, + ], + sessionData: JSON.stringify({ intent: 'user_deposit', amount: config.appDepositAmount.toFixed() }), + }; + const depositSigs = await signUpdate(depositUpdate, userSessionSigner, appSessionSignerValue); + const nodeSig = await clients.user.submitAppSessionDeposit( + depositUpdate, + depositSigs, + config.asset, + config.appDepositAmount + ); + console.log(`app-session deposit nodeSig=${nodeSig}`); + + session = await getSession(clients.user, created.appSessionId); + logSession('after app deposit', session, userAddress, appAddress, config.asset); + + const userAfterDeposit = allocationFor(session, userAddress, config.asset); + const appAfterDeposit = allocationFor(session, appAddress, config.asset); + if (userAfterDeposit.lessThan(config.operateAmount)) { + throw new Error('OPERATE_AMOUNT exceeds the user app-session allocation'); + } + + const operateUpdate: AppStateUpdateV1 = { + appSessionId: created.appSessionId, + intent: AppStateUpdateIntent.Operate, + version: nextVersion(session), + allocations: [ + { participant: userAddress, asset: config.asset, amount: userAfterDeposit.minus(config.operateAmount) }, + { participant: appAddress, asset: config.asset, amount: appAfterDeposit.plus(config.operateAmount) }, + ], + sessionData: JSON.stringify({ intent: 'purchase', amount: config.operateAmount.toFixed() }), + }; + const operateSigs = await signUpdate(operateUpdate, userSessionSigner, appSessionSignerValue); + await clients.user.submitAppState(operateUpdate, operateSigs); + + session = await getSession(clients.user, created.appSessionId); + logSession('after operate', session, userAddress, appAddress, config.asset); + + const userAfterOperate = allocationFor(session, userAddress, config.asset); + const appAfterOperate = allocationFor(session, appAddress, config.asset); + if (userAfterOperate.lessThan(config.withdrawAmount)) { + throw new Error('WITHDRAW_AMOUNT exceeds the user app-session allocation'); + } + + const withdrawUpdate: AppStateUpdateV1 = { + appSessionId: created.appSessionId, + intent: AppStateUpdateIntent.Withdraw, + version: nextVersion(session), + allocations: [ + { participant: userAddress, asset: config.asset, amount: userAfterOperate.minus(config.withdrawAmount) }, + { participant: appAddress, asset: config.asset, amount: appAfterOperate }, + ], + sessionData: JSON.stringify({ intent: 'user_withdraw', amount: config.withdrawAmount.toFixed() }), + }; + const withdrawSigs = await signUpdate(withdrawUpdate, userSessionSigner, appSessionSignerValue); + await clients.user.submitAppState(withdrawUpdate, withdrawSigs); + + session = await getSession(clients.user, created.appSessionId); + logSession('after withdraw', session, userAddress, appAddress, config.asset); + + const closeUpdate: AppStateUpdateV1 = { + appSessionId: created.appSessionId, + intent: AppStateUpdateIntent.Close, + version: nextVersion(session), + allocations: [ + { participant: userAddress, asset: config.asset, amount: allocationFor(session, userAddress, config.asset) }, + { participant: appAddress, asset: config.asset, amount: allocationFor(session, appAddress, config.asset) }, + ], + sessionData: JSON.stringify({ intent: 'close' }), + }; + const closeSigs = await signUpdate(closeUpdate, userSessionSigner, appSessionSignerValue); + await clients.user.submitAppState(closeUpdate, closeSigs); + + session = await getSession(clients.user, created.appSessionId); + logSession('after close', session, userAddress, appAddress, config.asset); + + return created.appSessionId; +} + +async function maybeCloseHomeChannel(client: Client, config: LifecycleConfig): Promise { + if (!config.closeHomeChannel) { + console.log('home-channel close skipped; set CLOSE_HOME_CHANNEL=true to run cleanup'); + return; + } + + const closeState = await client.closeHomeChannel(config.asset); + logState('home-channel close state', closeState); + const txHash = await checkpointWithApproval(client, config); + console.log(txHash ? `home-channel close checkpoint tx=${txHash}` : 'home-channel close did not require checkpoint'); +} + +async function main(): Promise { + const config = loadConfig(); + let clients: Clients | null = null; + + try { + stage('Connect clients'); + clients = await connectClients(config); + console.log(`user=${clients.user.getUserAddress()}`); + console.log(`app=${clients.app.getUserAddress()}`); + + stage('Inspect Nitronode config and set home blockchain'); + await inspectNode(clients.user, config); + await setHomeBlockchain(clients.app, config.asset, config.chainId); + + stage('Prepare user home channel'); + await prepareHomeChannel(clients.user, config); + + stage('Run app-session lifecycle'); + const appSessionId = await runAppSessionLifecycle(clients, config); + + stage('Final queries'); + const finalState = await getLatestStateOrNull(clients.user, clients.user.getUserAddress(), config.asset, true); + if (finalState) { + logState('final signed channel state', finalState); + } + const finalSession = await getSession(clients.user, appSessionId); + logSession('final app session', finalSession, clients.user.getUserAddress(), clients.app.getUserAddress(), config.asset); + + stage('Optional cleanup'); + await maybeCloseHomeChannel(clients.user, config); + + console.log('\nLifecycle complete.'); + } finally { + if (clients) { + await Promise.allSettled([clients.user.close(), clients.app.close()]); + } + } +} + +main() + .then(() => { + process.exit(0); + }) + .catch((error) => { + console.error('\nLifecycle failed.'); + console.error(error instanceof Error ? error.message : error); + process.exit(1); + }); diff --git a/examples/nitrolite-v1-lifecycle/tsconfig.json b/examples/nitrolite-v1-lifecycle/tsconfig.json new file mode 100644 index 0000000..deb0ef6 --- /dev/null +++ b/examples/nitrolite-v1-lifecycle/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "rootDir": "src", + "outDir": "dist", + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} diff --git a/i18n/en/docusaurus-theme-classic/footer.json b/i18n/en/docusaurus-theme-classic/footer.json index d6cc919..72c76bc 100644 --- a/i18n/en/docusaurus-theme-classic/footer.json +++ b/i18n/en/docusaurus-theme-classic/footer.json @@ -21,7 +21,7 @@ }, "link.item.label.Build": { "message": "Build", - "description": "The label of footer link with label=Build linking to /docs/build/quick-start" + "description": "The label of footer link with label=Build linking to /nitrolite/build/getting-started/quickstart" }, "link.item.label.Guides": { "message": "Guides", diff --git a/package-lock.json b/package-lock.json index aa03b5f..180b8c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "docs", "version": "1.x", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/faster": "3.10.0", - "@docusaurus/preset-classic": "3.10.0", - "@docusaurus/theme-mermaid": "3.10.0", - "@docusaurus/theme-search-algolia": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/faster": "3.10.1", + "@docusaurus/preset-classic": "3.10.1", + "@docusaurus/theme-mermaid": "3.10.1", + "@docusaurus/theme-search-algolia": "3.10.1", "@mdx-js/react": "^3.1.1", "@tippyjs/react": "^4.2.6", "clsx": "^2.0.0", @@ -23,9 +23,9 @@ "tippy.js": "^6.3.7" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.10.0", - "@docusaurus/tsconfig": "3.10.0", - "@docusaurus/types": "3.10.0", + "@docusaurus/module-type-aliases": "3.10.1", + "@docusaurus/tsconfig": "3.10.1", + "@docusaurus/types": "3.10.1", "@tailwindcss/postcss": "^4.2.4", "@yellow-org/contracts": "^1.0.4", "autoprefixer": "^10.5.0", @@ -38,46 +38,46 @@ } }, "node_modules/@algolia/abtesting": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.15.1.tgz", - "integrity": "sha512-2yuIC48rUuHGhU1U5qJ9kJHaxYpJ0jpDHJVI5ekOxSMYXlH4+HP+pA31G820lsAznfmu2nzDV7n5RO44zIY1zw==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.18.1.tgz", + "integrity": "sha512-aehCadlWOGvrT91KUIZpC0MbB8KBW9yUuvTJFd2xesR7le/IsT4nJUnjCCZ4ZqZCeTcPHPV5mo//fZ5oxcSVYw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.2.tgz", - "integrity": "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw==", + "version": "1.19.8", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.8.tgz", + "integrity": "sha512-3YEorYg44niXcm7gkft3nXYItHd44e8tmh4D33CTszPgP0QWkaLEaFywiNyJBo7UL/mqObA/G9RYuU7R8tN1IA==", "license": "MIT", "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.19.2", - "@algolia/autocomplete-shared": "1.19.2" + "@algolia/autocomplete-plugin-algolia-insights": "1.19.8", + "@algolia/autocomplete-shared": "1.19.8" } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.2.tgz", - "integrity": "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg==", + "version": "1.19.8", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.8.tgz", + "integrity": "sha512-ZvJWO8ZZJDpc1LNM2TTBdmQsZBLMR4rU5iNR2OYvEeFBiaf/0ESnRSSLQbryarJY4SVxtoz6A2ZtDMNM+iQEAA==", "license": "MIT", "dependencies": { - "@algolia/autocomplete-shared": "1.19.2" + "@algolia/autocomplete-shared": "1.19.8" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.2.tgz", - "integrity": "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w==", + "version": "1.19.8", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.8.tgz", + "integrity": "sha512-h5hf2t8ejF6vlOgvLaZzQbWs5SyH2z4PAWygNAvvD/2RI29hdQ54ldUGwqVuj9Srs+n8XUKTPUqb7fvhBhQrnQ==", "license": "MIT", "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -85,99 +85,99 @@ } }, "node_modules/@algolia/client-abtesting": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.49.1.tgz", - "integrity": "sha512-h6M7HzPin+45/l09q0r2dYmocSSt2MMGOOk5c4O5K/bBBlEwf1BKfN6z+iX4b8WXcQQhf7rgQwC52kBZJt/ZZw==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.52.1.tgz", + "integrity": "sha512-HmXOGBOAOJPounpBzBpuY0zDYeiCpxgHnQmuA7JO6ScukcBdGp3/XM9zJk5pJx/xNGD68mbPGXWpDxGtl6BwDQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.49.1.tgz", - "integrity": "sha512-048T9/Z8OeLmTk8h76QUqaNFp7Rq2VgS2Zm6Y2tNMYGQ1uNuzePY/udB5l5krlXll7ZGflyCjFvRiOtlPZpE9g==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.52.1.tgz", + "integrity": "sha512-5oo4+I8iixie9vXhCyNFCzeIr8pqA3FQ//VsLHTDvZAV4ttYOPGvYHGQq5NSalrLx5Jc3dRro/5uDOlnUMcBJg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.49.1.tgz", - "integrity": "sha512-vp5/a9ikqvf3mn9QvHN8PRekn8hW34aV9eX+O0J5mKPZXeA6Pd5OQEh2ZWf7gJY6yyfTlLp5LMFzQUAU+Fpqpg==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.52.1.tgz", + "integrity": "sha512-qCDoZfx5MpX7XQzvQ3bC4tSEMkQWQMaF/ABtLuoze03Y/flR563CCSws02qIJ23oX7lxl92LsilZjINVyTdtLw==", "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.49.1.tgz", - "integrity": "sha512-B6N7PgkvYrul3bntTz/l6uXnhQ2bvP+M7NqTcayh681tSqPaA5cJCUBp/vrP7vpPRpej4Eeyx2qz5p0tE/2N2g==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.52.1.tgz", + "integrity": "sha512-hnGs0/lsFJ2PWDxNBz7pxreXo/Xz7gxYRcfePBUjsH26ad0kU/sgnVZd9LwWBpsQv65z2jlb5dkyaB9WE9M9FQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.49.1.tgz", - "integrity": "sha512-v+4DN+lkYfBd01Hbnb9ZrCHe7l+mvihyx218INRX/kaCXROIWUDIT1cs3urQxfE7kXBFnLsqYeOflQALv/gA5w==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.52.1.tgz", + "integrity": "sha512-2VxxNc/uBysyKvGeBdSM5n9eIDKH8kWD7wd9/yqbJAiVwU4Yv6tU1LSJusHKrXV/aCu1KW7t9Gug9QyeEmtn/Q==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.49.1.tgz", - "integrity": "sha512-Un11cab6ZCv0W+Jiak8UktGIqoa4+gSNgEZNfG8m8eTsXGqwIEr370H3Rqwj87zeNSlFpH2BslMXJ/cLNS1qtg==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.52.1.tgz", + "integrity": "sha512-O6mPtsw3xEfNOe6gWFpYLeAZAIljNa4Hgna3bq15PwyN7nbjTY0wXJFRbzs/0YVf75Br+SbOQUmjKxXYjDiSiQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.49.1.tgz", - "integrity": "sha512-Nt9hri7nbOo0RipAsGjIssHkpLMHHN/P7QqENywAq5TLsoYDzUyJGny8FEiD/9KJUxtGH8blGpMedilI6kK3rA==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.52.1.tgz", + "integrity": "sha512-gA8oJOV1LnQQkDf91iebNnFInHuW0gRPEgLSOQ7EfipCEjYTHm5swm1DlH9H5RaRw4RrHuzHBegnlzc0MAstcg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" @@ -190,81 +190,81 @@ "license": "MIT" }, "node_modules/@algolia/ingestion": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.49.1.tgz", - "integrity": "sha512-b5hUXwDqje0Y4CpU6VL481DXgPgxpTD5sYMnfQTHKgUispGnaCLCm2/T9WbJo1YNUbX3iHtYDArp804eD6CmRQ==", + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.52.1.tgz", + "integrity": "sha512-U9zZfc5xIu9wRxZkt+HceJUAD4VKHKbAyLSloJdEyMRmphXeibfrY9cxqIXBcmPeZzGhn3Imb35Dq8l19PkJhw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.49.1.tgz", - "integrity": "sha512-bvrXwZ0WsL3rN6Q4m4QqxsXFCo6WAew7sAdrpMQMK4Efn4/W920r9ptOuckejOSSvyLr9pAWgC5rsHhR2FYuYw==", + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.52.1.tgz", + "integrity": "sha512-a3SGNceHmkQfq77iG8Ka+w1pvwfZa/0lzEIgse30fL0kD+yKnd/dg0dQvSfFPAEt2f21DMcGkDSSeJlO3KdQjQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.49.1.tgz", - "integrity": "sha512-h2yz3AGeGkQwNgbLmoe3bxYs8fac4An1CprKTypYyTU/k3Q+9FbIvJ8aS1DoBKaTjSRZVoyQS7SZQio6GaHbZw==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.52.1.tgz", + "integrity": "sha512-z98QEguCFDpxb4S/PyrUK1igqF8tPsdbqOUUO6ON91vJ58w+Gwa6ncrI0oNXSFcrkxA5EqPKPQ2A1PBCn08TYQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.49.1.tgz", - "integrity": "sha512-2UPyRuUR/qpqSqH8mxFV5uBZWEpxhGPHLlx9Xf6OVxr79XO2ctzZQAhsmTZ6X22x+N8MBWpB9UEky7YU2HGFgA==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.52.1.tgz", + "integrity": "sha512-CI7+/0I11QeZM59Uc8whd2or0kqzFVjpaPn9Qpwll/krHcBAxk24WkAQ6WX+IwDVMfpont4YGbKwAmCre3vE8Q==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1" + "@algolia/client-common": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.49.1.tgz", - "integrity": "sha512-N+xlE4lN+wpuT+4vhNEwPVlrfN+DWAZmSX9SYhbz986Oq8AMsqdntOqUyiOXVxYsQtfLwmiej24vbvJGYv1Qtw==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.52.1.tgz", + "integrity": "sha512-S6bDuw9byfOvm3T71cgdoZgrgnZq6hpdMLkx52Louh57nUAmvGQESz2aojOynQHjbTiV55smvAFbgn0qT4tJrg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1" + "@algolia/client-common": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.49.1.tgz", - "integrity": "sha512-zA5bkUOB5PPtTr182DJmajCiizHp0rCJQ0Chf96zNFvkdESKYlDeYA3tQ7r2oyHbu/8DiohAQ5PZ85edctzbXA==", + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.52.1.tgz", + "integrity": "sha512-tqZXM+54rWo4mk5jL5Z/flE11nPmNEdXwFBM5py9DkOmbjeCNemfVd45FyM97XdzfZ0dl9uOJC6PYn1FpkeyQg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.49.1" + "@algolia/client-common": "5.52.1" }, "engines": { "node": ">= 14.0.0" @@ -311,9 +311,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -412,9 +412,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", + "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -422,7 +422,7 @@ "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", + "@babel/traverse": "^7.29.0", "semver": "^6.3.1" }, "engines": { @@ -468,9 +468,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz", - "integrity": "sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", @@ -645,22 +645,22 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -718,6 +718,22 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", + "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", @@ -1248,9 +1264,9 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", - "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", @@ -1785,18 +1801,19 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.0.tgz", - "integrity": "sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==", + "version": "7.29.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", + "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.29.0", + "@babel/compat-data": "^7.29.3", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", @@ -1828,7 +1845,7 @@ "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.28.6", - "@babel/plugin-transform-modules-systemjs": "^7.29.0", + "@babel/plugin-transform-modules-systemjs": "^7.29.4", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-new-target": "^7.27.1", @@ -1869,12 +1886,12 @@ } }, "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz", - "integrity": "sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", "core-js-compat": "^3.48.0" }, "peerDependencies": { @@ -3378,9 +3395,9 @@ } }, "node_modules/@docsearch/core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.6.0.tgz", - "integrity": "sha512-IqG3oSd529jVRQ4dWZQKwZwQLVd//bWJTz2HiL0LkiHrI4U/vLrBasKB7lwQB/69nBAcCgs3TmudxTZSLH/ZQg==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.6.3.tgz", + "integrity": "sha512-rUOujwIpxJRgD7+kicVsI3D5sqBvdiRTquzWBpTEXZs8ZXfGbfzpus5HqumaNYTppN2HvH8E2yNuRwYdHJeOlA==", "license": "MIT", "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", @@ -3400,20 +3417,20 @@ } }, "node_modules/@docsearch/css": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.0.tgz", - "integrity": "sha512-YlcAimkXclvqta47g47efzCM5CFxDwv2ClkDfEs/fC/Ak0OxPH2b3czwa4o8O1TRBf+ujFF2RiUwszz2fPVNJQ==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.3.tgz", + "integrity": "sha512-nlOwcXcsNAptQl4vlL4MA78qNJKO0Qlds5GuBjCoePgkebTXLSf8Qt1oyZ3YBshYupKXG9VRGEsk1zr23d+bzQ==", "license": "MIT" }, "node_modules/@docsearch/react": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.6.0.tgz", - "integrity": "sha512-j8H5B4ArGxBPBWvw3X0J0Rm/Pjv2JDa2rV5OE0DLTp5oiBCptIJ/YlNOhZxuzbO2nwge+o3Z52nJRi3hryK9cA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.6.3.tgz", + "integrity": "sha512-Bg2wdDsoQVlNCcEKuEJAU04tvHCqgx8rIu+uIoM4pRtcx3TBKJuXutJik3LTA8LRc9YEyHkrYUrmcC0D7BYf+g==", "license": "MIT", "dependencies": { "@algolia/autocomplete-core": "1.19.2", - "@docsearch/core": "4.6.0", - "@docsearch/css": "4.6.0" + "@docsearch/core": "4.6.3", + "@docsearch/css": "4.6.3" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", @@ -3436,10 +3453,42 @@ } } }, + "node_modules/@docsearch/react/node_modules/@algolia/autocomplete-core": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.2.tgz", + "integrity": "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw==", + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.19.2", + "@algolia/autocomplete-shared": "1.19.2" + } + }, + "node_modules/@docsearch/react/node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.2.tgz", + "integrity": "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg==", + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.19.2" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@docsearch/react/node_modules/@algolia/autocomplete-shared": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.2.tgz", + "integrity": "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w==", + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, "node_modules/@docusaurus/babel": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.10.0.tgz", - "integrity": "sha512-mqCJhCZNZUDg0zgDEaPTM4DnRsisa24HdqTy/qn/MQlbwhTb4WVaZg6ZyX6yIVKqTz8fS1hBMgM+98z+BeJJDg==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.10.1.tgz", + "integrity": "sha512-DZzFO1K3v/GoEt1fx1DiYHF4en+PuhtQf1AkQJa5zu3CoeKSpr5cpQRUlz3jr0m44wyzmSXu9bVpfir+N4+8bg==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", @@ -3451,8 +3500,8 @@ "@babel/preset-typescript": "^7.25.9", "@babel/runtime": "^7.25.9", "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.10.0", - "@docusaurus/utils": "3.10.0", + "@docusaurus/logger": "3.10.1", + "@docusaurus/utils": "3.10.1", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3462,17 +3511,17 @@ } }, "node_modules/@docusaurus/bundler": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.10.0.tgz", - "integrity": "sha512-iONUGZGgp+lAkw/cJZH6irONcF4p8+278IsdRlq8lYhxGjkoNUs0w7F4gVXBYSNChq5KG5/JleTSsdJySShxow==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.10.1.tgz", + "integrity": "sha512-HIqQPvbqnnQRe4NsBd1774KRarjXqS6wHsWELtyuSs1gCfvixJO2jUGH/OEBtr1Gvzpw+ze5CjGMvSJ8UE1KUw==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.10.0", - "@docusaurus/cssnano-preset": "3.10.0", - "@docusaurus/logger": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", + "@docusaurus/babel": "3.10.1", + "@docusaurus/cssnano-preset": "3.10.1", + "@docusaurus/logger": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", "babel-loader": "^9.2.1", "clean-css": "^5.3.3", "copy-webpack-plugin": "^11.0.0", @@ -3490,7 +3539,7 @@ "tslib": "^2.6.0", "url-loader": "^4.1.1", "webpack": "^5.95.0", - "webpackbar": "^6.0.1" + "webpackbar": "^7.0.0" }, "engines": { "node": ">=20.0" @@ -3505,18 +3554,18 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.10.0.tgz", - "integrity": "sha512-mgLdQsO8xppnQZc3LPi+Mf+PkPeyxJeIx11AXAq/14fsaMefInQiMEZUUmrc7J+956G/f7MwE7tn8KZgi3iRcA==", - "license": "MIT", - "dependencies": { - "@docusaurus/babel": "3.10.0", - "@docusaurus/bundler": "3.10.0", - "@docusaurus/logger": "3.10.0", - "@docusaurus/mdx-loader": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-common": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.10.1.tgz", + "integrity": "sha512-3pf2fXXw0eVk8WnC3T4LIigRDupcpvngpKo9Vy7mYyBhuddc0klDUuZAIfzMoK6z05pdlk6EFC/vBSX43+1O5w==", + "license": "MIT", + "dependencies": { + "@docusaurus/babel": "3.10.1", + "@docusaurus/bundler": "3.10.1", + "@docusaurus/logger": "3.10.1", + "@docusaurus/mdx-loader": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-common": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -3572,9 +3621,9 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.10.0.tgz", - "integrity": "sha512-qzSshTO1DB3TYW+dPUal5KHM7XPc5YQfzF3Kdb2NDACJUyGbNcFtw3tGkCJlYwhNCRKbZcmwraKUS1i5dcHdGg==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.10.1.tgz", + "integrity": "sha512-eNfHGcTKCSq6xmcavAkX3RRclHaE2xRCMParlDXLdXVP01/a2e/jKXMj/0ULnLFQSNwwuI62L0Ge8J+nZsR7UQ==", "license": "MIT", "dependencies": { "cssnano-preset-advanced": "^6.1.2", @@ -3587,12 +3636,12 @@ } }, "node_modules/@docusaurus/faster": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/faster/-/faster-3.10.0.tgz", - "integrity": "sha512-GNPtVH14ISjHfSwnHu3KiFGf86ICmJSQDeSv/QaanpBgiZGOtgZaslnC5q8WiguxM1EVkwcGxPuD8BXF4eggKw==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/faster/-/faster-3.10.1.tgz", + "integrity": "sha512-XTZhE5C1gZ/DaYYMlSk02dwP5vhpQON5QHVz1s3892mSESAywgWanURpXEDAvt4GvGuq7s+XP8rTWHZvfaJmdQ==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.10.0", + "@docusaurus/types": "3.10.1", "@rspack/core": "^1.7.10", "@swc/core": "^1.7.39", "@swc/html": "^1.13.5", @@ -3611,9 +3660,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.10.0.tgz", - "integrity": "sha512-9jrZzFuBH1LDRlZ7cznAhCLmAZ3HSDqgwdrSSZdGHq9SPUOQgXXu8mnxe2ZRB9NS1PCpMTIOVUqDtZPIhMafZg==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.10.1.tgz", + "integrity": "sha512-oPjNFnfJsRCkePVjkGrxWGq4MvJKRQT0r9jOP0eRBTZ7Wr9FAbzdP/Gjs0I2Ss6YRkPoEgygKG112OkE6skvJw==", "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -3624,14 +3673,14 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.10.0.tgz", - "integrity": "sha512-mQQV97080AH4PYNs087l202NMDqRopZA4mg5W76ZZyTFrmWhJ3mHg+8A+drJVENxw5/Q+wHMHLgsx+9z1nEs0A==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.10.1.tgz", + "integrity": "sha512-GRmeb/wQ+iXRrFwcHBfgQhrJxGElgCsoTWZYDhccjsZVne1p8MK/EpQVIloXttz76TCe78kKD5AEG9n1xc1oxQ==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/logger": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -3663,12 +3712,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.10.0.tgz", - "integrity": "sha512-/1O0Zg8w3DFrYX/I6Fbss7OJrtZw1QoyjDhegiFNHVi9A9Y0gQ3jUAytVxF6ywpAWpLyLxch8nN8H/V3XfzdJQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.10.1.tgz", + "integrity": "sha512-YoOZKUdGlp8xSYhuAkGdSo5Ydkbq4V4eK3sD8v0a2hloxCWdQbNBhkc+Ko9QyjpESc0BYcIGM5iHVAy5hdFV6w==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.10.0", + "@docusaurus/types": "3.10.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3682,19 +3731,19 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.10.0.tgz", - "integrity": "sha512-RuTz68DhB7CL96QO5UsFbciD7GPYq6QV+YMfF9V0+N4ZgLhJIBgpVAr8GobrKF6NRe5cyWWETU5z5T834piG9g==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/logger": "3.10.0", - "@docusaurus/mdx-loader": "3.10.0", - "@docusaurus/theme-common": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-common": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.10.1.tgz", + "integrity": "sha512-mmkgE6Q2+K74tnkou7tXlpDLvoCU/qkSa2GSQ3XUiHWvcebCoDQzS670RR3tO8PmaWlIyWWISYWzZLuMfxunRA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.10.1", + "@docusaurus/logger": "3.10.1", + "@docusaurus/mdx-loader": "3.10.1", + "@docusaurus/theme-common": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-common": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "cheerio": "1.0.0-rc.12", "combine-promises": "^1.1.0", "feed": "^4.2.2", @@ -3717,20 +3766,20 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.10.0.tgz", - "integrity": "sha512-9BjHhf15ct8Z7TThTC0xRndKDVvMKmVsAGAN7W9FpNRzfMdScOGcXtLmcCWtJGvAezjOJIm6CxOYCy3Io5+RnQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/logger": "3.10.0", - "@docusaurus/mdx-loader": "3.10.0", - "@docusaurus/module-type-aliases": "3.10.0", - "@docusaurus/theme-common": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-common": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.10.1.tgz", + "integrity": "sha512-2jRVrtzjf8LClGTHQlwlwuD3wQXRx3WEoF7XUarJ8Ou+0onV+SLtejsyfY9JLpfUh9hPhXM4pbBGkyAY4Bi3HQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.10.1", + "@docusaurus/logger": "3.10.1", + "@docusaurus/mdx-loader": "3.10.1", + "@docusaurus/module-type-aliases": "3.10.1", + "@docusaurus/theme-common": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-common": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -3750,16 +3799,16 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.10.0.tgz", - "integrity": "sha512-5amX8kEJI+nIGtuLVjYk59Y5utEJ3CHETFOPEE4cooIRLA4xM4iBsA6zFgu4ljcopeYwvBzFEWf5g2I6Yb9SkA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.10.1.tgz", + "integrity": "sha512-huJpaRPMl42nsFwuCXvV8bVDj2MazuwRJIUylI/RSlmZeJssVoZXeCjVf1y+1Drtpa9SKcdGn8yoJ76IRJijtw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/mdx-loader": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/mdx-loader": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -3773,15 +3822,15 @@ } }, "node_modules/@docusaurus/plugin-css-cascade-layers": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.10.0.tgz", - "integrity": "sha512-6q1vtt5FJcg5osgkHeM1euErECNqEZ5Z1j69yiNx2luEBIso+nxCkS9nqj8w+MK5X7rvKEToGhFfOFWncs51pQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.10.1.tgz", + "integrity": "sha512-r//fn+MNHkE1wCof8T29VAQezt1enGCpsFxoziBbvLgBM4JfXN2P3rxrBaavHmvLvm7lYkpJeitcDthwnmWCTw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "tslib": "^2.6.0" }, "engines": { @@ -3789,14 +3838,14 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.10.0.tgz", - "integrity": "sha512-XcljKN+G+nmmK69uQA1d9BlYU3ZftG3T3zpK8/7Hf/wrOlV7TA4Ampdrdwkg0jElKdKAoSnPhCO0/U3bQGsVQQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.10.1.tgz", + "integrity": "sha512-9KqOpKNfAyqGZykRb9LhIT/vyRF6sm/ykhjj/39JvaJahDS+jZJE0Z1Wfz9q3DUNDTMNN0Q7u/kk4rKKU+IJuA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", "fs-extra": "^11.1.1", "react-json-view-lite": "^2.3.0", "tslib": "^2.6.0" @@ -3810,14 +3859,14 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.10.0.tgz", - "integrity": "sha512-hTEoodatpBZnUat5nFExbuTGA1lhWGy7vZGuTew5Q3QDtGKFpSJLYmZJhdTjvCFwv1+qQ67hgAVlKdJOB8TXow==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.10.1.tgz", + "integrity": "sha512-8o0P1KtmgdYQHH+oInitPpRWI0Of5XednAX4+DMhQNSmGSRNrsEEHg1ebv35m9AgRClfAytCJ5jA9KvcASTyuA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "tslib": "^2.6.0" }, "engines": { @@ -3829,14 +3878,14 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.10.0.tgz", - "integrity": "sha512-iB/Zzjv/eelJRbdULZqzWCbgMgJ7ht4ONVjXtN3+BI/muil6S87gQ1OJyPwlXD+ELdKkitC7bWv5eJdYOZLhrQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.10.1.tgz", + "integrity": "sha512-pu3xIUo5o/zCMLfUY9BO5KOwSH0zIsAGyFRPvXHayFSA5XIhCU/SFuB0g0ZNjFn9niZLCaNvoeAuOGFJZq0fdw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "@types/gtag.js": "^0.0.20", "tslib": "^2.6.0" }, @@ -3849,14 +3898,14 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.10.0.tgz", - "integrity": "sha512-FEjZxqKgLHa+Wez/EgKxRwvArNCWIScfyEQD95rot7jkxp6nonjI5XIbGfO/iYhM5Qinwe8aIEQHP2KZtpqVuA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.10.1.tgz", + "integrity": "sha512-f6fyGHiCm7kJHBtAisGQS5oNBnpnMTYQZxDXeVrnw/3zWU+LMA22pr6UHGYkBKDbN+qPC5QHG3NuOfzQLq3+Lw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "tslib": "^2.6.0" }, "engines": { @@ -3868,17 +3917,17 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.10.0.tgz", - "integrity": "sha512-DVTSLjB97hIjmayGnGcBfognCeI7ZuUKgEnU7Oz81JYqXtVg94mVTthDjq3QHTylYNeCUbkaW8VF0FDLcc8pPw==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.10.1.tgz", + "integrity": "sha512-C26MbmmqgdjkDq1htaZ3aD7LzEDKFWXfpyQpt0EOUThuq5nV77zDaedV20yHcVo9p+3ey9aZ4pbHA0D3QcZTzg==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/logger": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-common": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/logger": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-common": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -3892,15 +3941,15 @@ } }, "node_modules/@docusaurus/plugin-svgr": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.10.0.tgz", - "integrity": "sha512-lNljBESaETZqVBMPqkrGchr+UPT1eZzEPLmJhz8I76BxbjqgsUnRvrq6lQJ9sYjgmgX52KB7kkgczqd2yzoswQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.10.1.tgz", + "integrity": "sha512-6SFxsmjWFkVLDmBUvFK6i72QjUwqyQFe4Ovz+SUJophJjOyVG3ZZG5IQpBC/kX/Gfv1yWeU9nWauH6F6Q7QX/Q==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "@svgr/core": "8.1.0", "@svgr/webpack": "^8.1.0", "tslib": "^2.6.0", @@ -3915,26 +3964,26 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.10.0.tgz", - "integrity": "sha512-kw/Ye02Hc6xP1OdTswy8yxQEHg0fdPpyWAQRxr5b2x3h7LlG2Zgbb5BDFROnXDDMpUxB7YejlocJIE5HIEfpNA==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/plugin-content-blog": "3.10.0", - "@docusaurus/plugin-content-docs": "3.10.0", - "@docusaurus/plugin-content-pages": "3.10.0", - "@docusaurus/plugin-css-cascade-layers": "3.10.0", - "@docusaurus/plugin-debug": "3.10.0", - "@docusaurus/plugin-google-analytics": "3.10.0", - "@docusaurus/plugin-google-gtag": "3.10.0", - "@docusaurus/plugin-google-tag-manager": "3.10.0", - "@docusaurus/plugin-sitemap": "3.10.0", - "@docusaurus/plugin-svgr": "3.10.0", - "@docusaurus/theme-classic": "3.10.0", - "@docusaurus/theme-common": "3.10.0", - "@docusaurus/theme-search-algolia": "3.10.0", - "@docusaurus/types": "3.10.0" + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.10.1.tgz", + "integrity": "sha512-YO/FL8v1zmbxoTso6mjMz/RDjhaTJxb1UpFFTDdY5847LLDCeyYiYlrhyTbgN1RIN3xnkLKZ9Lj1x8hUzI4JOg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.10.1", + "@docusaurus/plugin-content-blog": "3.10.1", + "@docusaurus/plugin-content-docs": "3.10.1", + "@docusaurus/plugin-content-pages": "3.10.1", + "@docusaurus/plugin-css-cascade-layers": "3.10.1", + "@docusaurus/plugin-debug": "3.10.1", + "@docusaurus/plugin-google-analytics": "3.10.1", + "@docusaurus/plugin-google-gtag": "3.10.1", + "@docusaurus/plugin-google-tag-manager": "3.10.1", + "@docusaurus/plugin-sitemap": "3.10.1", + "@docusaurus/plugin-svgr": "3.10.1", + "@docusaurus/theme-classic": "3.10.1", + "@docusaurus/theme-common": "3.10.1", + "@docusaurus/theme-search-algolia": "3.10.1", + "@docusaurus/types": "3.10.1" }, "engines": { "node": ">=20.0" @@ -3945,24 +3994,24 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.10.0.tgz", - "integrity": "sha512-9msCAsRdN+UG+RwPwCFb0uKy4tGoPh5YfBozXeGUtIeAgsMdn6f3G/oY861luZ3t8S2ET8S9Y/1GnpJAGWytww==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/logger": "3.10.0", - "@docusaurus/mdx-loader": "3.10.0", - "@docusaurus/module-type-aliases": "3.10.0", - "@docusaurus/plugin-content-blog": "3.10.0", - "@docusaurus/plugin-content-docs": "3.10.0", - "@docusaurus/plugin-content-pages": "3.10.0", - "@docusaurus/theme-common": "3.10.0", - "@docusaurus/theme-translations": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-common": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.10.1.tgz", + "integrity": "sha512-VU1RK0qb2pab0si4r7HFK37cYco8VzqLj3u1PspVipSr/z/GPVKHO4/HXbnePqHoWDk8urjyGSeatH0NIMBM1A==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.10.1", + "@docusaurus/logger": "3.10.1", + "@docusaurus/mdx-loader": "3.10.1", + "@docusaurus/module-type-aliases": "3.10.1", + "@docusaurus/plugin-content-blog": "3.10.1", + "@docusaurus/plugin-content-docs": "3.10.1", + "@docusaurus/plugin-content-pages": "3.10.1", + "@docusaurus/theme-common": "3.10.1", + "@docusaurus/theme-translations": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-common": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", @@ -3986,15 +4035,15 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.10.0.tgz", - "integrity": "sha512-Dkp1YXKn16ByCJAdIjbDIOpVb4Z66MsVD694/ilX1vAAHaVEMrVsf/NPd9VgreyFx08rJ9GqV1MtzsbTcU73Kg==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.10.1.tgz", + "integrity": "sha512-0YtmIeoNo1fIw65LO8+/1dPgmDV86UmhMkow37gzjytuiCSQm9xob6PJy0L4kuQEMTLfUOGvkXvZr7GPrHquMA==", "license": "MIT", "dependencies": { - "@docusaurus/mdx-loader": "3.10.0", - "@docusaurus/module-type-aliases": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-common": "3.10.0", + "@docusaurus/mdx-loader": "3.10.1", + "@docusaurus/module-type-aliases": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-common": "3.10.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -4014,16 +4063,16 @@ } }, "node_modules/@docusaurus/theme-mermaid": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.10.0.tgz", - "integrity": "sha512-Y2xrlwhIJ80oOZIO3PXL6A7J869splfcMI87E3NKpYsy3zJxOyV+BP1QMtGi59ajKgU868HPuyyn6J+6BZGOBg==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.10.1.tgz", + "integrity": "sha512-2gxpmln8Pc4EN1oWzshQEx2HTs67jk14v7MmgqGs8ZU7Nm8oihg+fTouof2u4vN8DtB3Fln4cDJu4UprSX1S3Q==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/module-type-aliases": "3.10.0", - "@docusaurus/theme-common": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/module-type-aliases": "3.10.1", + "@docusaurus/theme-common": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "mermaid": ">=11.6.0", "tslib": "^2.6.0" }, @@ -4042,20 +4091,20 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.10.0.tgz", - "integrity": "sha512-f5FPKI08e3JRG63vR/o4qeuUVHUHzFzM0nnF+AkB67soAZgNsKJRf2qmUZvlQkGwlV+QFkKe4D0ANMh1jToU3g==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.10.1.tgz", + "integrity": "sha512-OTaARARVZj2GvkJQjB+1jOIxntRaXea+G+fMsNqrZBAU1O1vJKDW22R7kECOHW27oJCLFN9HKaZeRrfAUyviug==", "license": "MIT", "dependencies": { "@algolia/autocomplete-core": "^1.19.2", "@docsearch/react": "^3.9.0 || ^4.3.2", - "@docusaurus/core": "3.10.0", - "@docusaurus/logger": "3.10.0", - "@docusaurus/plugin-content-docs": "3.10.0", - "@docusaurus/theme-common": "3.10.0", - "@docusaurus/theme-translations": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-validation": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/logger": "3.10.1", + "@docusaurus/plugin-content-docs": "3.10.1", + "@docusaurus/theme-common": "3.10.1", + "@docusaurus/theme-translations": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-validation": "3.10.1", "algoliasearch": "^5.37.0", "algoliasearch-helper": "^3.26.0", "clsx": "^2.0.0", @@ -4074,9 +4123,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.10.0.tgz", - "integrity": "sha512-L9IbFLwTc5+XdgH45iQYufLn0SVZd6BUNelDbKIFlH+E4hhjuj/XHWAFMX/w2K59rfy8wak9McOaei7BSUfRPA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.10.1.tgz", + "integrity": "sha512-cLMyaKivjBVWKMJuWqyFVVgtqe8DPJNPkog0bn8W1MDVAKcPdxRFycBfC1We1RaNp7Rdk513bmtW78RR6OBxBw==", "license": "MIT", "dependencies": { "fs-extra": "^11.1.1", @@ -4087,16 +4136,16 @@ } }, "node_modules/@docusaurus/tsconfig": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.10.0.tgz", - "integrity": "sha512-TXdC3WXuPrdQAexLvjUJfnYf3YKEgEqAs5nK0Q88pRBCW7t7oN4ILvWYb3A5Z1wlSXyXGWW/mCUmLEhdWsjnDQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.10.1.tgz", + "integrity": "sha512-rYvB7yqkdqWIpAbDzQljGfM4cDBkLTbhmagZBEcsyj6oPUsz47lmW2pYdN1j+7sGFgltbAmQH62xfbrij4Eh6Q==", "dev": true, "license": "MIT" }, "node_modules/@docusaurus/types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.10.0.tgz", - "integrity": "sha512-F0dOt3FOoO20rRaFK7whGFQZ3ggyrWEdQc/c8/UiRuzhtg4y1w9FspXH5zpCT07uMnJKBPGh+qNazbNlCQqvSw==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.10.1.tgz", + "integrity": "sha512-XYMK8k1szDCFMw2V+Xyen0g7Kee1sP3dtFnl7vkGkZOkeAJ/oPDQPL8iz4HBKOo/cwU8QeV6onVjMqtP+tFzsw==", "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.0.0", @@ -4130,14 +4179,14 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.10.0.tgz", - "integrity": "sha512-T3B0WTigsIthe0D4LQa2k+7bJY+c3WS+Wq2JhcznOSpn1lSN64yNtHQXboCj3QnUs1EuAZszQG1SHKu5w5ZrlA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.10.1.tgz", + "integrity": "sha512-3ojeJry9xBYdJO6qoyyzqeJFSJBVx2mXhyDzSdjwL2+URFQMf+h25gG38iswGImicK0ELjTd1EL2xzk8hf3QPw==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.10.0", - "@docusaurus/types": "3.10.0", - "@docusaurus/utils-common": "3.10.0", + "@docusaurus/logger": "3.10.1", + "@docusaurus/types": "3.10.1", + "@docusaurus/utils-common": "3.10.1", "escape-string-regexp": "^4.0.0", "execa": "^5.1.1", "file-loader": "^6.2.0", @@ -4162,12 +4211,12 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.10.0.tgz", - "integrity": "sha512-JyL7sb9QVDgYvudIS81Dv0lsWm7le0vGZSDwsztxWam1SPBqrnkvBy9UYL/amh6pbybkyYTd3CMTkO24oMlCSw==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.10.1.tgz", + "integrity": "sha512-5mFSgEADtnFxFH7RLw02QA5MpU5JVUCj0MPeIvi/aF4Fi45tQRIuTwXoXDqJ+1VfQJuYJGz3SI63wmGz4HvXzA==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.10.0", + "@docusaurus/types": "3.10.1", "tslib": "^2.6.0" }, "engines": { @@ -4175,14 +4224,14 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.10.0.tgz", - "integrity": "sha512-c+6n2+ZPOJtWWc8Bb/EYdpSDfjYEScdCu9fB/SNjOmSCf1IdVnGf2T53o0tsz0gDRtCL90tifTL0JE/oMuP1Mw==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.10.1.tgz", + "integrity": "sha512-cRv1X69jwaWv47waglllgZVWzeBFLhl53XT/XED/83BerVBTC5FTP8WTcVl8Z6sZOegDSwitu/wpCSPCDOT6lg==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.10.0", - "@docusaurus/utils": "3.10.0", - "@docusaurus/utils-common": "3.10.0", + "@docusaurus/logger": "3.10.1", + "@docusaurus/utils": "3.10.1", + "@docusaurus/utils-common": "3.10.1", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -5619,9 +5668,9 @@ } }, "node_modules/@swc/core": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.32.tgz", - "integrity": "sha512-/eWL0n43D64QWEUHLtTE+jDqjkJhyidjkDhv6f0uJohOUAhywxQ9wXYp845DNNds0JpCdI4Uo0a9bl+vbXf+ew==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.33.tgz", + "integrity": "sha512-jOlwnFV2xhuuZeAUILGFULeR6vDPfijEJ57evfocwznQldLU3w2cZ9bSDryY9ip+AsM3r1NJKzf47V2NXebkeQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -5636,18 +5685,18 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.32", - "@swc/core-darwin-x64": "1.15.32", - "@swc/core-linux-arm-gnueabihf": "1.15.32", - "@swc/core-linux-arm64-gnu": "1.15.32", - "@swc/core-linux-arm64-musl": "1.15.32", - "@swc/core-linux-ppc64-gnu": "1.15.32", - "@swc/core-linux-s390x-gnu": "1.15.32", - "@swc/core-linux-x64-gnu": "1.15.32", - "@swc/core-linux-x64-musl": "1.15.32", - "@swc/core-win32-arm64-msvc": "1.15.32", - "@swc/core-win32-ia32-msvc": "1.15.32", - "@swc/core-win32-x64-msvc": "1.15.32" + "@swc/core-darwin-arm64": "1.15.33", + "@swc/core-darwin-x64": "1.15.33", + "@swc/core-linux-arm-gnueabihf": "1.15.33", + "@swc/core-linux-arm64-gnu": "1.15.33", + "@swc/core-linux-arm64-musl": "1.15.33", + "@swc/core-linux-ppc64-gnu": "1.15.33", + "@swc/core-linux-s390x-gnu": "1.15.33", + "@swc/core-linux-x64-gnu": "1.15.33", + "@swc/core-linux-x64-musl": "1.15.33", + "@swc/core-win32-arm64-msvc": "1.15.33", + "@swc/core-win32-ia32-msvc": "1.15.33", + "@swc/core-win32-x64-msvc": "1.15.33" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -5659,9 +5708,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.32.tgz", - "integrity": "sha512-/YWMvJDPu+AAwuUsM2G+DNQ/7zhodURGzdQyewEqcvgklAdDHs3LwQmLLnyn6SJl8DT8UOxkbzK+D1PmPeelRg==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.33.tgz", + "integrity": "sha512-N+L0uXhuO7FIfzqwgxmzv0zIpV0qEp8wPX3QQs2p4atjMoywup2JTeDlXPw+z9pWJGCae3JjM+tZ6myclI+2gA==", "cpu": [ "arm64" ], @@ -5675,9 +5724,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.32.tgz", - "integrity": "sha512-KOTXJXdAhWL+hZ77MYP3z+4pcMFaQhQ74yqyN1uz093q0YnbxpqMtYpPISbYvMHzVRNNx5kN+9RZAXEaadhWVA==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.33.tgz", + "integrity": "sha512-/Il4QHSOhV4FekbsDtkrNmKbsX26oSysvgrRswa/RYOHXAkwXDbB4jaeKq6PsJLSPkzJ2KzQ061gtBnk0vNHfA==", "cpu": [ "x64" ], @@ -5691,9 +5740,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.32.tgz", - "integrity": "sha512-oOoxLweljlc0A4X8ybsgxV7cVaYTwBOg2iMDJcFR3Sr48C+lsv9VzSmqdK/IVIXF4W4GjLc3VqTAdSMXlfVLuQ==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.33.tgz", + "integrity": "sha512-C64hBnBxq4viOPQ8hlx+2lJ23bzZBGnjw7ryALmS+0Q3zHmwO8lw1/DArLENw4Q18/0w5wdEO1k3m1wWNtKGqQ==", "cpu": [ "arm" ], @@ -5707,12 +5756,15 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.32.tgz", - "integrity": "sha512-oDzEkdl6D6BAWdMtU5KGO7y3HR5fJcvByNLyEk9+ugj8nP5Ovb7P4kBcStBXc4MPExFGQryehiINMlmY8HlclA==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.33.tgz", + "integrity": "sha512-TRJfnJbX3jqpxRDRoieMzRiCBS5jOmXNb3iQXmcgjFEHKLnAgK1RZRU8Cq1MsPqO4jAJp/ld1G4O3fXuxv85uw==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5723,12 +5775,15 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.32.tgz", - "integrity": "sha512-omcqjoZP/b8D8PuczVoRwJieC6ibj7qIxTftNYokz4/aSmKFHvsd7nIFfPk5ZvtzncbH4AY7+Dkr/Lp2gWxYeA==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.33.tgz", + "integrity": "sha512-il7tYM+CpUNzieQbwAjFT1P8zqAhmGWNAGhQZBnxurXZ0aNn+5nqYFTEUKNZl7QibtT0uQXzTZrNGHCIj6Y1Og==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5739,12 +5794,15 @@ } }, "node_modules/@swc/core-linux-ppc64-gnu": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.32.tgz", - "integrity": "sha512-KGkTMyz/Tbn3PBNu0AVZ4GTDFKnICrYcTiNPZq8DrvK42pnFsf3GNDrIG9E5AtQlTmC0YigkWKmu0eMcfTrmgA==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.33.tgz", + "integrity": "sha512-ZtNBwN0Z7CFj9Il0FcPaKdjgP7URyKu/3RfH46vq+0paOBqLj4NYldD6Qo//Duif/7IOtAraUfDOmp0PLAufog==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5755,12 +5813,15 @@ } }, "node_modules/@swc/core-linux-s390x-gnu": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.32.tgz", - "integrity": "sha512-G3Aa4tVS/3OGZBkoNIwUF9F6RAy+Osb4GOlo62SinLmDiErz/ykmM7KH0wkz6l9kM8jJq1HyAM6atJTUEbBk7g==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.33.tgz", + "integrity": "sha512-De1IyajoOmhOYYjw/lx66bKlyDpHZTueqwpDrWgf5O7T6d1ODeJJO9/OqMBmrBQc5C+dNnlmIufHsp4QVCWufA==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5771,12 +5832,15 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.32.tgz", - "integrity": "sha512-ERsjfGcj6CBmj3vJnGDO8m8rTvw6RqMcWo1dogOtNx3/+/0+NNpJiXDobJrr1GwInI/BHAEkvSFIH6d2LqPcUQ==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.33.tgz", + "integrity": "sha512-mGTH0YxmUN+x6vRN/I6NOk5X0ogNktkwPnJ94IMvR7QjhRDwL0O8RXEDhyUM0YtwWrryBOqaJQBX4zruxEPRGw==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5787,12 +5851,15 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.32.tgz", - "integrity": "sha512-N4Ggahe/8SUbTX50P6EdhbW9YWcgbZVb52R4cq6MK+zsoMjRq7rGvV5ztA05QnbaCYqMYx8rTY7KAIA3Crdo4Q==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.33.tgz", + "integrity": "sha512-hj628ZkSEJf6zMf5VMbYrG2O6QqyTIp2qwY6VlCjvIa9lAEZ5c2lfPblCLVGYubTeLJDxadLB/CxqQYOQABeEQ==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5803,9 +5870,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.32.tgz", - "integrity": "sha512-01yN0o9jvo8xBTP12aPK2wW8b41jmOlGbDDlAnoynotc4pO6xA0zby9f1z6j++qXDpGBttLySq1omgVrlQKYcw==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.33.tgz", + "integrity": "sha512-GV2oohtN2/5+KSccl86VULu3aT+LrISC8uzgSq0FRnikpD+Zwc+sBlXmoKQ+Db6jI57ITUOIB8jRkdGMABC29g==", "cpu": [ "arm64" ], @@ -5819,9 +5886,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.32.tgz", - "integrity": "sha512-fLagI9XZYNpTcmlqAcp3KBtmj7E19WCmYD80Jxj1Kn5tGNa7yxNLd3NNdWxuZGUPl5iC0/KqZru7g08gF6Fsrw==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.33.tgz", + "integrity": "sha512-gtyvzSNR8DHKfFEA2uqb8Ld1myqi6uEg2jyeUq3ikn5ytYs7H8RpZYC8mdy4NXr8hfcdJfCLXPlYaqqfBXpoEQ==", "cpu": [ "ia32" ], @@ -5835,9 +5902,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.32.tgz", - "integrity": "sha512-gbc2bQ/T2CiR+w0OvcVKwLOFAcPZBvmWmolbwpg1E8UrpeC03DGtyMUApOHNXNYWA3SHFrYXCQtosrcMza1YFg==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.33.tgz", + "integrity": "sha512-d6fRqQSkJI+kmMEBWaDQ7TMl8+YjLYbwRUPZQ9DY0ORBJeTzOrG0twvfvlZ2xgw6jA0ScQKgfBm4vHLSLl5Hqg==", "cpu": [ "x64" ], @@ -5857,9 +5924,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/html": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html/-/html-1.15.32.tgz", - "integrity": "sha512-Mv37uFfZQt7j89U3KJPqeQt6vl5Bxk7aqOrNDKWRAmrQOJ+lYJKq4hmYWW6Rk3wdGw03SlEfK3RmnzCN9gsqAA==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html/-/html-1.15.33.tgz", + "integrity": "sha512-PZIfmj5zYpAJ2eMptf0My2q9Bl8bkraW28+FD1pRnxOiYMrKrP5vL2tB2PdxMRjS0ziLFVM5HEuGFw8PxEDOaw==", "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -5868,24 +5935,24 @@ "node": ">=14" }, "optionalDependencies": { - "@swc/html-darwin-arm64": "1.15.32", - "@swc/html-darwin-x64": "1.15.32", - "@swc/html-linux-arm-gnueabihf": "1.15.32", - "@swc/html-linux-arm64-gnu": "1.15.32", - "@swc/html-linux-arm64-musl": "1.15.32", - "@swc/html-linux-ppc64-gnu": "1.15.32", - "@swc/html-linux-s390x-gnu": "1.15.32", - "@swc/html-linux-x64-gnu": "1.15.32", - "@swc/html-linux-x64-musl": "1.15.32", - "@swc/html-win32-arm64-msvc": "1.15.32", - "@swc/html-win32-ia32-msvc": "1.15.32", - "@swc/html-win32-x64-msvc": "1.15.32" + "@swc/html-darwin-arm64": "1.15.33", + "@swc/html-darwin-x64": "1.15.33", + "@swc/html-linux-arm-gnueabihf": "1.15.33", + "@swc/html-linux-arm64-gnu": "1.15.33", + "@swc/html-linux-arm64-musl": "1.15.33", + "@swc/html-linux-ppc64-gnu": "1.15.33", + "@swc/html-linux-s390x-gnu": "1.15.33", + "@swc/html-linux-x64-gnu": "1.15.33", + "@swc/html-linux-x64-musl": "1.15.33", + "@swc/html-win32-arm64-msvc": "1.15.33", + "@swc/html-win32-ia32-msvc": "1.15.33", + "@swc/html-win32-x64-msvc": "1.15.33" } }, "node_modules/@swc/html-darwin-arm64": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-darwin-arm64/-/html-darwin-arm64-1.15.32.tgz", - "integrity": "sha512-WgY386nwyz24cTJ+Nztd4cKvfPJexLYAzurSYDmuYxS3HihWoTFZWMDomTfM8yr2UCi8SwW+zTNAWxJxUaKESg==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-darwin-arm64/-/html-darwin-arm64-1.15.33.tgz", + "integrity": "sha512-zyO6uMBfLyCh55wundAxKX+8P/f98ecuyir4VX6nTmn6y7x37ndB8f01LUrd9Tiq6eEAvDXLiqEUvuGjEc7Pmg==", "cpu": [ "arm64" ], @@ -5899,9 +5966,9 @@ } }, "node_modules/@swc/html-darwin-x64": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-darwin-x64/-/html-darwin-x64-1.15.32.tgz", - "integrity": "sha512-uge7XExmbPREWO+2dZQvAbeiAvUlR+3TxxgYETJw39f8Nlclc3rWXaievpO3iRPbg1s8BsZ9fGGhoN7yYrwUwg==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-darwin-x64/-/html-darwin-x64-1.15.33.tgz", + "integrity": "sha512-MaGunsY/J5l7Rb5OmoztEWh+ikooydT7nWkjiDovj7UfkB9HLk5sLr9O7ZdNGJ2u9dD6FX89SzMdA0Psm9NJrQ==", "cpu": [ "x64" ], @@ -5915,9 +5982,9 @@ } }, "node_modules/@swc/html-linux-arm-gnueabihf": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-linux-arm-gnueabihf/-/html-linux-arm-gnueabihf-1.15.32.tgz", - "integrity": "sha512-EuserzRHqXX6R6KScuBn+U3IX9ll3j4+sHM2Y3J/vIH7TbQ5IrvCFuu8w7El5R9j0ByCWvsDa2QdiLCQtsmFlw==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-linux-arm-gnueabihf/-/html-linux-arm-gnueabihf-1.15.33.tgz", + "integrity": "sha512-CrbUDjVl6/hQ1C5KPMiK4vxk/eOMjxkVELqwnOxsZ+aFVTv3L3YrGMaJ5H47vvIihkPhqiSOUPmMEFqxvqKmXg==", "cpu": [ "arm" ], @@ -5931,12 +5998,15 @@ } }, "node_modules/@swc/html-linux-arm64-gnu": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.15.32.tgz", - "integrity": "sha512-gvlByySjNDWX2FUIGVBWOhd00rySz0AOydQpuXCK0ldYbFVMby9oXbp2JVmE5UsB6J4YZqZh4ajmmqCGvFHi4Q==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.15.33.tgz", + "integrity": "sha512-7tZ0IgmUslI9Extu/TpxJS0GjJoDx0j9zeq2cIidPdM/njSBpyRB7n4B292Q5WFVh7PcZl7WXqqqMczibQ27aA==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5947,12 +6017,15 @@ } }, "node_modules/@swc/html-linux-arm64-musl": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-musl/-/html-linux-arm64-musl-1.15.32.tgz", - "integrity": "sha512-iTrXjSeVwhHp+w7I5srCSpG9prebj+j/lmWalljNXGagTuVmtEWdH/EDvtSq1JfJyKQ6KRKyeB3Qg6yGHhjr+Q==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-musl/-/html-linux-arm64-musl-1.15.33.tgz", + "integrity": "sha512-gYi2ainYZV2z+jwjp9UKuPVOf3c5q+NkH3QRDjqDrIPLagqDsYNjobi8p5oajGcPGFLNTcVw08VTcubJGChReA==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5963,12 +6036,15 @@ } }, "node_modules/@swc/html-linux-ppc64-gnu": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-linux-ppc64-gnu/-/html-linux-ppc64-gnu-1.15.32.tgz", - "integrity": "sha512-sh6yGlZk7YqaQ4XisqEe0tBSTy0WcwmEnMEq9EG6mIU16PCGTbROHMfTw0W81jtvUyjqtCQC9axbSJLAW3zIeA==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-linux-ppc64-gnu/-/html-linux-ppc64-gnu-1.15.33.tgz", + "integrity": "sha512-6CfzyVQSdD8ezFdxFve4J/b6qTgXIwYFWEvSdaJvXSgwTy976uUV5Ff1LOF86mt2zWMhZJX9DqmkGyIhepbyWw==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5979,12 +6055,15 @@ } }, "node_modules/@swc/html-linux-s390x-gnu": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-linux-s390x-gnu/-/html-linux-s390x-gnu-1.15.32.tgz", - "integrity": "sha512-zBavh0QnsvjM9QMPmtOqMaUGSLr5tdj2gxEx4xXzOXBFkUKKRA+qEfx6LdTzIbrqqtK5Xf6CPBPT9kzhDLuaCA==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-linux-s390x-gnu/-/html-linux-s390x-gnu-1.15.33.tgz", + "integrity": "sha512-Msx1eniw95lhMHUSe3D5FXweKHtkHtzJLsHJDj920uL4Dm7UHqzwaCuZdCmzbkHnO96YjjQvAm266djg8wupmQ==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -5995,12 +6074,15 @@ } }, "node_modules/@swc/html-linux-x64-gnu": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.15.32.tgz", - "integrity": "sha512-IveuScZfAwDZEBs6pTvdG/MwGyMPuxp74l9ngp2PbUboVBIfUS894kATBaBuSBYXajZ4v4wqv01PGM81rUhGQg==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.15.33.tgz", + "integrity": "sha512-JDNb4Uq+7g+23QuOtwWnP0/EqztWIHFFdQdeBIS5zx83YBG2dYRMdPAjnHJWh2YRZxdepd8q6S9MUIxpSrouAg==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6011,12 +6093,15 @@ } }, "node_modules/@swc/html-linux-x64-musl": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-linux-x64-musl/-/html-linux-x64-musl-1.15.32.tgz", - "integrity": "sha512-wRXdcS0eaYU1Pm15gNGmVM7fsJ/xCxy3BnfNH52MC/ObgCIzBcFl3Yjn6yr6nkqNtDZyEt8IlQFQno5zEEvIpw==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-linux-x64-musl/-/html-linux-x64-musl-1.15.33.tgz", + "integrity": "sha512-NSpZdbz4dj0pu1A0Z9l68Bll5HAzEMtBAeMe6jc4GEVfpIw6eeafQHm2/yMUEh09tgl8t9LzM9DycfdTZDjM4g==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6027,9 +6112,9 @@ } }, "node_modules/@swc/html-win32-arm64-msvc": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-win32-arm64-msvc/-/html-win32-arm64-msvc-1.15.32.tgz", - "integrity": "sha512-GzQQkdi4kC5ZjKloTQXgsI9FNQYb3z1mmF9ZbVDykdRgzKnqWGbx/gwTcWUhiurI9KsyL1t95PmOnU11Jw/mqg==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-win32-arm64-msvc/-/html-win32-arm64-msvc-1.15.33.tgz", + "integrity": "sha512-w7iho3/zS3lCDqgUZMDLMBO0ElX7j+KgvMb8BOrKqLDOSTDDj3lY/BClNJ7vBpAliI2kPQs/mUikdZyzi4MBjQ==", "cpu": [ "arm64" ], @@ -6043,9 +6128,9 @@ } }, "node_modules/@swc/html-win32-ia32-msvc": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-win32-ia32-msvc/-/html-win32-ia32-msvc-1.15.32.tgz", - "integrity": "sha512-BJqmiTbCWcd1hG9nLEktIASTv0SD91cIKHdt8Zza7AvSjH+BmDX0AW8krVDsJpXWQEtWEYzroe9rweNOMSVfjQ==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-win32-ia32-msvc/-/html-win32-ia32-msvc-1.15.33.tgz", + "integrity": "sha512-6hJ2pBweSfZ38trYHXmzTBDpRNvqJgFl2PkIWdy4IXbV/Fv0v9Dqe0t9Gi2ZVEBpgI7PD6pF42AT4HmrNTVFyQ==", "cpu": [ "ia32" ], @@ -6059,9 +6144,9 @@ } }, "node_modules/@swc/html-win32-x64-msvc": { - "version": "1.15.32", - "resolved": "https://registry.npmjs.org/@swc/html-win32-x64-msvc/-/html-win32-x64-msvc-1.15.32.tgz", - "integrity": "sha512-8uOl327V1nCISIILtpQWrY8dl4JFo8UK5TCFcpMJ8ldt4wggr7cPnvkp2bJkT/pL7djjrDJQZ2BpNfu62M2zWA==", + "version": "1.15.33", + "resolved": "https://registry.npmjs.org/@swc/html-win32-x64-msvc/-/html-win32-x64-msvc-1.15.33.tgz", + "integrity": "sha512-eaY/vNE7rkPKluJYjhOiQOA1tto5VbJOoD1C1xFTBmr9t7WsqYUfbQhYQy5A26/z83NNgtDwELM85rkMB+/vWA==", "cpu": [ "x64" ], @@ -6755,9 +6840,9 @@ } }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "license": "MIT", "dependencies": { "@types/ms": "*" @@ -7108,9 +7193,9 @@ "license": "MIT" }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", "license": "ISC" }, "node_modules/@webassemblyjs/ast": { @@ -7440,34 +7525,34 @@ } }, "node_modules/algoliasearch": { - "version": "5.49.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.49.1.tgz", - "integrity": "sha512-X3Pp2aRQhg4xUC6PQtkubn5NpRKuUPQ9FPDQlx36SmpFwwH2N0/tw4c+NXV3nw3PsgeUs+BuWGP0gjz3TvENLQ==", - "license": "MIT", - "dependencies": { - "@algolia/abtesting": "1.15.1", - "@algolia/client-abtesting": "5.49.1", - "@algolia/client-analytics": "5.49.1", - "@algolia/client-common": "5.49.1", - "@algolia/client-insights": "5.49.1", - "@algolia/client-personalization": "5.49.1", - "@algolia/client-query-suggestions": "5.49.1", - "@algolia/client-search": "5.49.1", - "@algolia/ingestion": "1.49.1", - "@algolia/monitoring": "1.49.1", - "@algolia/recommend": "5.49.1", - "@algolia/requester-browser-xhr": "5.49.1", - "@algolia/requester-fetch": "5.49.1", - "@algolia/requester-node-http": "5.49.1" + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.52.1.tgz", + "integrity": "sha512-fHA8+kXTbjagw3jkLiaS7KKrH8qe2DyOsiUhGlN4cdT77PEsfqXZl7ewDk1hsg+pJnPlnE50XtLxjR91iJOpmg==", + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.18.1", + "@algolia/client-abtesting": "5.52.1", + "@algolia/client-analytics": "5.52.1", + "@algolia/client-common": "5.52.1", + "@algolia/client-insights": "5.52.1", + "@algolia/client-personalization": "5.52.1", + "@algolia/client-query-suggestions": "5.52.1", + "@algolia/client-search": "5.52.1", + "@algolia/ingestion": "1.52.1", + "@algolia/monitoring": "1.52.1", + "@algolia/recommend": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.28.0.tgz", - "integrity": "sha512-GBN0xsxGggaCPElZq24QzMdfphrjIiV2xA+hRXE4/UMpN3nsF2WrM8q+x80OGvGpJWtB7F+4Hq5eSfWwuejXrg==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.29.1.tgz", + "integrity": "sha512-6ck2YFudF2Pje7szQoPBiRFTGfd+1I+0I/WfLPGn0bj1kvrFoOQmNyedNiDxTk3/r4IfSLDYk+RA4G7u8H6+yA==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" @@ -7505,33 +7590,6 @@ "node": ">=8" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-html-community": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", @@ -7568,6 +7626,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -7709,13 +7776,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", - "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==", + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", "semver": "^6.3.1" }, "peerDependencies": { @@ -7745,12 +7812,12 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz", - "integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.6" + "@babel/helper-define-polyfill-provider": "^0.6.8" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -8784,9 +8851,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.48.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz", - "integrity": "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", "license": "MIT", "dependencies": { "browserslist": "^4.28.1" @@ -9139,9 +9206,9 @@ } }, "node_modules/cssdb": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.8.0.tgz", - "integrity": "sha512-QbLeyz2Bgso1iRlh7IpWk6OKa3lLNGXsujVjDMPl9rOZpxKeiG69icLpbLCFxeURwmcdIfZqQyhlooKJYM4f8Q==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.9.0.tgz", + "integrity": "sha512-J8jOU/hLjaXcO1LldOLraJSQpfLXRKof0I7mtbRyOy2AAXgqst0x9rlgi2qXeD6d0ou3ZLqcPAMqYVbpCbrxEw==", "funding": [ { "type": "opencollective", @@ -10983,30 +11050,6 @@ "node": ">=0.4.0" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", @@ -11028,9 +11071,9 @@ } }, "node_modules/file-loader/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -11618,9 +11661,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -12792,12 +12835,12 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -19049,9 +19092,9 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", - "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", + "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.1.0" @@ -19380,11 +19423,12 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" @@ -19558,9 +19602,9 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", - "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" @@ -21224,9 +21268,9 @@ } }, "node_modules/url-loader/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -21769,75 +21813,30 @@ } }, "node_modules/webpackbar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-6.0.1.tgz", - "integrity": "sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-7.0.0.tgz", + "integrity": "sha512-aS9soqSO2iCHgqHoCrj4LbfGQUboDCYJPSFOAchEK+9psIjNrfSWW4Y0YEz67MKURNvMmfo0ycOg9d/+OOf9/Q==", "license": "MIT", "dependencies": { - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", + "ansis": "^3.2.0", "consola": "^3.2.3", - "figures": "^3.2.0", - "markdown-table": "^2.0.0", "pretty-time": "^1.1.0", - "std-env": "^3.7.0", - "wrap-ansi": "^7.0.0" + "std-env": "^3.7.0" }, "engines": { "node": ">=14.21.3" }, "peerDependencies": { + "@rspack/core": "*", "webpack": "3 || 4 || 5" - } - }, - "node_modules/webpackbar/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/webpackbar/node_modules/markdown-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", - "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "license": "MIT", - "dependencies": { - "repeat-string": "^1.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webpackbar/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpackbar/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/websocket-driver": { diff --git a/package.json b/package.json index 3678679..1ca59a2 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,11 @@ "version:release": "node scripts/release-version.js" }, "dependencies": { - "@docusaurus/core": "3.10.0", - "@docusaurus/faster": "3.10.0", - "@docusaurus/preset-classic": "3.10.0", - "@docusaurus/theme-mermaid": "3.10.0", - "@docusaurus/theme-search-algolia": "3.10.0", + "@docusaurus/core": "3.10.1", + "@docusaurus/faster": "3.10.1", + "@docusaurus/preset-classic": "3.10.1", + "@docusaurus/theme-mermaid": "3.10.1", + "@docusaurus/theme-search-algolia": "3.10.1", "@mdx-js/react": "^3.1.1", "@tippyjs/react": "^4.2.6", "clsx": "^2.0.0", @@ -34,9 +34,9 @@ "tippy.js": "^6.3.7" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.10.0", - "@docusaurus/tsconfig": "3.10.0", - "@docusaurus/types": "3.10.0", + "@docusaurus/module-type-aliases": "3.10.1", + "@docusaurus/tsconfig": "3.10.1", + "@docusaurus/types": "3.10.1", "@tailwindcss/postcss": "^4.2.4", "@yellow-org/contracts": "^1.0.4", "autoprefixer": "^10.5.0", diff --git a/sidebars-clearnet.ts b/sidebars-clearnet.ts new file mode 100644 index 0000000..ec394e7 --- /dev/null +++ b/sidebars-clearnet.ts @@ -0,0 +1,28 @@ +import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +const sidebars: SidebarsConfig = { + clearnetSidebar: [ + 'learn/introduction', + 'learn/architecture', + { + type: 'category', + label: 'Decentralized Layer', + items: [ + {type: 'autogenerated', dirName: 'learn/decentralized-layer'}, + ], + collapsed: false, + }, + { + type: 'category', + label: 'Contracts', + items: [ + {type: 'autogenerated', dirName: 'learn/contracts'}, + ], + collapsed: false, + }, + ], +}; + +export default sidebars; diff --git a/sidebars.ts b/sidebars-nitrolite.ts similarity index 66% rename from sidebars.ts rename to sidebars-nitrolite.ts index 2462c88..10808b3 100644 --- a/sidebars.ts +++ b/sidebars-nitrolite.ts @@ -20,6 +20,8 @@ const sidebars: SidebarsConfig = { id: 'learn/index', label: 'Learn', }, + 'learn/whats-new-from-0.5.3', + 'learn/migrating-from-0.5.3', { type: 'category', label: 'Introduction', @@ -28,18 +30,6 @@ const sidebars: SidebarsConfig = { 'learn/introduction/architecture-at-a-glance', 'learn/introduction/supported-chains', ], - collapsible: false, - collapsed: false, - }, - { - type: 'category', - label: 'Getting Started', - items: [ - 'learn/getting-started/quickstart', - 'learn/getting-started/prerequisites', - 'learn/getting-started/key-terms', - ], - collapsible: false, collapsed: false, }, { @@ -47,13 +37,10 @@ const sidebars: SidebarsConfig = { label: 'Core Concepts', items: [ 'learn/core-concepts/state-channels-vs-l1-l2', - 'learn/core-concepts/app-sessions', - 'learn/core-concepts/session-keys', 'learn/core-concepts/challenge-response', - 'learn/core-concepts/message-envelope', 'learn/core-concepts/yellow-token', + 'learn/glossary', ], - collapsible: false, collapsed: false, }, { @@ -62,16 +49,6 @@ const sidebars: SidebarsConfig = { items: [ {type: 'autogenerated', dirName: 'learn/protocol-flows'}, ], - collapsible: false, - collapsed: false, - }, - { - type: 'category', - label: 'Advanced', - items: [ - 'learn/advanced/managing-session-keys', - ], - collapsible: false, collapsed: false, }, ], @@ -84,22 +61,6 @@ const sidebars: SidebarsConfig = { }, ], - // Tutorials section sidebar - tutorialsSidebar: [ - { - type: 'autogenerated', - dirName: 'tutorials', - }, - ], - - // Guides section sidebar - guidesSidebar: [ - { - type: 'autogenerated', - dirName: 'guides', - }, - ], - // API Reference section sidebar apiSidebar: [ { @@ -108,11 +69,28 @@ const sidebars: SidebarsConfig = { }, ], - // Protocol section sidebar + // Protocol section sidebar — current Nitrolite App Layer only. protocolSidebar: [ { - type: 'autogenerated', - dirName: 'protocol', + type: 'category', + label: 'App Layer', + items: [ + { + type: 'doc', + id: 'protocol/introduction', + label: 'Overview', + }, + 'protocol/terminology', + 'protocol/cryptography-and-signing', + 'protocol/state-and-ledger-model', + 'protocol/channel-lifecycle', + 'protocol/enforcement-and-settlement', + 'protocol/cross-chain-and-assets', + 'protocol/interaction-model', + 'protocol/security-and-limitations', + ], + collapsible: false, + collapsed: false, }, ], diff --git a/src/components/HomepageFeatures/index.tsx b/src/components/HomepageFeatures/index.tsx index 7047bc4..b1bea0e 100644 --- a/src/components/HomepageFeatures/index.tsx +++ b/src/components/HomepageFeatures/index.tsx @@ -17,66 +17,40 @@ type FeatureItem = { const FeatureList: FeatureItem[] = [ { - title: 'Learn the Basics', - imageSrc: require('@site/static/img/themes/light/icons/learn.png').default, - imageSrcDark: require('@site/static/img/themes/dark/icons/learn.png').default, - description: ( - <> - Understand the fundamentals of Yellow Network, its architecture, - and how decentralized clearing and settlement works. - - ), - link: '/docs/learn', - }, - { - title: 'Build a Yellow App', + title: 'Nitrolite', imageSrc: require('@site/static/img/themes/light/icons/build.png').default, imageSrcDark: require('@site/static/img/themes/dark/icons/build.png').default, description: ( <> - Create decentralized applications using Yellow SDK with real-time - trading capabilities and instant cross-chain settlements. + State channel SDK for building decentralized apps with instant + finality, real-time trading, and cross-chain settlement. ), - link: '/docs/build/quick-start', + link: '/nitrolite', }, { - title: 'Run a Clearnode', + title: 'Clearnet', imageSrc: require('@site/static/img/themes/light/icons/clearnode.png').default, imageSrcDark: require('@site/static/img/themes/dark/icons/clearnode.png').default, description: ( <> - Set up and operate a clearnode to provide network services - and contribute to decentralized infrastructure. + Decentralized clearing and settlement protocol — peer-to-peer overlay + network, smart contracts, governance, and node operations. ), - link: '/docs/guides/manuals/running-clearnode-locally', + link: '/clearnet/learn/introduction', }, { - title: 'Join the Community', - imageSrc: require('@site/static/img/themes/light/icons/community.png').default, - imageSrcDark: require('@site/static/img/themes/dark/icons/community.png').default, - description: ( - <> - Connect with developers, traders, and node operators in our - Discord community and contribute to the ecosystem. - - ), - link: 'https://discord.com/invite/yellownetwork', - isExternal: true, - }, - { - title: 'Apply for Grants', - imageSrc: require('@site/static/img/themes/light/icons/grants.png').default, - imageSrcDark: require('@site/static/img/themes/dark/icons/grants.png').default, + title: 'Whitepaper', + imageSrc: require('@site/static/img/themes/light/icons/learn.png').default, + imageSrcDark: require('@site/static/img/themes/dark/icons/learn.png').default, description: ( <> - Get funding support for your Yellow Network project through - our grants program for innovative applications and integrations. + The Yellow Network whitepaper — protocol design, economic model, + and technical reference for the decentralized clearing layer. ), - link: 'https://forms.yellow.org/build', - isExternal: true, + link: '/whitepaper', }, ]; @@ -89,7 +63,7 @@ function Feature({title, imageSrc, imageSrcDark, description, link, isExternal}: : { to: link }; return ( -
+
-
+
{FeatureList.map((props, idx) => ( ))} diff --git a/src/css/custom.css b/src/css/custom.css index b5e9667..3ca205d 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -26,7 +26,7 @@ --ifm-font-weight-base: 300; --ifm-font-weight-semibold: 500; --ifm-font-weight-bold: 500; - --doc-sidebar-width: 315px; + --doc-sidebar-width: 270px; --ifm-navbar-search-input-icon: url('data:image/svg+xml;utf8,'); } @@ -69,8 +69,8 @@ font-family: 'Poppins', sans-serif !important; font-weight: 300 !important; font-style: normal !important; - font-size: 16px !important; - line-height: 24px !important; + font-size: 15px !important; + line-height: 17px !important; letter-spacing: 0.15px !important; vertical-align: middle !important; } @@ -82,8 +82,8 @@ font-family: 'Poppins', sans-serif !important; font-weight: 500 !important; font-style: normal !important; - font-size: 16px !important; - line-height: 24px !important; + font-size: 15px !important; + line-height: 17px !important; letter-spacing: 0.15px !important; vertical-align: middle !important; } @@ -143,9 +143,11 @@ a.menu__link--active { background-color: #EDEDED; } -/* Level 3 items border positioning */ +/* Level 3 leaf vertical line: align with the level-2 collapsible chevron, + * which sits at padding-left: 32px on its container. + */ .menu__list .menu__list .menu__list .menu__link:not(.menu__link--sublist):not(.menu__link--sublist-caret)::before { - left: 8px; + left: 12px; } /* Add proper padding for all sidebar items with borders */ @@ -153,17 +155,30 @@ a.menu__link--active { position: relative; } -/* Add extra spacing only for sub-items (level-2 items under categories) */ +/* Double the default horizontal padding of the central doc content column */ +[class*='docItemCol'] { + padding: 0 40px !important; +} + +/* Nested indentation — each level shifts further right */ .theme-doc-sidebar-item-link-level-2 .menu__link { padding-left: 32px !important; } -.menu__list .menu__list .menu__link { - padding-left: 24px !important; +.menu__list .menu__list .menu__link:not(.menu__link--sublist) { + padding-left: 20px !important; } -.menu__list .menu__list .menu__list .menu__link { - padding-left: 32px !important; +/* Level-2 category headers (e.g. "TypeScript SDK" under "SDK") — + * chevron at 28px so it aligns with sibling doc items */ +.menu__list .menu__list .menu__list-item-collapsible, +.menu__list .menu__list .menu__list-item-collapsible--active, +.menu__list .menu__list .menu__list-item-collapsible:hover { + padding-left: 28px !important; +} + +.menu__list .menu__list .menu__list .menu__link:not(.menu__link--sublist) { + padding-left: 36px !important; } /* Table of contents styling - use darker/lighter colors instead of theme colors */ @@ -203,11 +218,273 @@ a.table-of-contents__link.table-of-contents__link--active code { color: #333333 !important; } +/* Nitrolite Build examples page */ +.nitrolite-example-list { + display: flex; + flex-direction: column; + gap: 24px; +} + +.nitrolite-example-card { + border-radius: 8px; + overflow: hidden; +} + +.nitrolite-example-card__layout { + display: grid; + grid-template-columns: minmax(340px, 48%) minmax(0, 1fr); + align-items: center; +} + +.nitrolite-example-card__media { + display: flex; + align-items: center; + justify-content: center; + align-self: center; + aspect-ratio: 16 / 10; + overflow: hidden; + background: var(--ifm-color-emphasis-100); + border-right: 1px solid var(--ifm-color-emphasis-200); +} + +.nitrolite-example-card__image { + display: block; + width: 100%; + height: 100%; + object-fit: contain; + object-position: center; +} + +.nitrolite-example-card__content { + align-self: stretch; + min-width: 0; +} + +.nitrolite-example-card__content .card__header h2 { + margin: 0; + font-size: 1.55rem; + line-height: 1.2; +} + +.nitrolite-example-card__summary { + margin-bottom: 1rem; +} + +.nitrolite-example-card__details { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 20px; +} + +.nitrolite-example-card__details > div:last-child { + grid-column: 1 / -1; +} + +.nitrolite-example-card__details code { + overflow-wrap: anywhere; +} + +.nitrolite-example-card__details h3 { + margin-bottom: 0.5rem; + font-size: 1rem; +} + +.nitrolite-example-card__details ul { + margin: 0; + padding-left: 1.1rem; +} + +.nitrolite-example-card__details li { + margin-bottom: 0.35rem; +} + +@media (max-width: 996px) { + .nitrolite-example-card__layout { + grid-template-columns: 1fr; + } + + .nitrolite-example-card__media { + aspect-ratio: 16 / 10; + border-right: 0; + border-bottom: 1px solid var(--ifm-color-emphasis-200); + } +} + +@media (max-width: 640px) { + .nitrolite-example-card__details { + grid-template-columns: 1fr; + } +} + +/* Nitrolite Builder Toolkit */ +.nitrolite-toolkit-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 20px; + margin: 24px 0 32px; +} + +.nitrolite-toolkit-upcoming { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 20px; + margin: 20px 0 32px; +} + +.nitrolite-toolkit-card { + display: flex; + flex-direction: column; + min-width: 0; + border-radius: 8px; + overflow: hidden; +} + +.nitrolite-toolkit-card__media { + display: block; + aspect-ratio: 16 / 10; + overflow: hidden; + background: var(--ifm-color-emphasis-100); + border-bottom: 1px solid var(--ifm-color-emphasis-200); +} + +.nitrolite-toolkit-card__media img { + display: block; + width: 100%; + height: 100%; + object-fit: contain; + object-position: center; +} + +.nitrolite-toolkit-card__body { + flex: 1; + min-width: 0; +} + +.nitrolite-toolkit-card__body h2 { + margin: 0 0 0.65rem; + font-size: 1.35rem; + line-height: 1.2; +} + +.nitrolite-toolkit-card__body p { + margin-bottom: 1rem; +} + +.nitrolite-toolkit-card__eyebrow, +.nitrolite-toolkit-card__badge { + display: inline-flex; + width: fit-content; + align-items: center; + margin-bottom: 0.75rem; + border-radius: 999px; + padding: 0.2rem 0.55rem; + background: rgba(253, 218, 22, 0.16); + color: var(--ifm-color-emphasis-900); + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0; +} + +.nitrolite-toolkit-card__meta { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.nitrolite-toolkit-card__meta span { + display: inline-flex; + max-width: 100%; + align-items: center; + border-radius: 6px; + padding: 0.2rem 0.45rem; + background: var(--ifm-color-emphasis-100); + color: var(--ifm-color-emphasis-800); + font-size: 0.8rem; + line-height: 1.35; +} + +.nitrolite-toolkit-card__meta code { + overflow-wrap: anywhere; +} + +.nitrolite-toolkit-card__footer { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.nitrolite-toolkit-card--disabled { + opacity: 0.68; + background: var(--ifm-color-emphasis-100); + border-style: dashed; +} + +.nitrolite-toolkit-card--disabled .button.disabled { + pointer-events: none; + cursor: not-allowed; + opacity: 0.72; +} + +.nitrolite-toolkit-links { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 16px; + margin: 20px 0 8px; +} + +.nitrolite-toolkit-link { + display: flex; + flex-direction: column; + gap: 8px; + border-radius: 8px; + padding: 16px; + color: inherit; + text-decoration: none; +} + +.nitrolite-toolkit-link:hover { + color: inherit; + text-decoration: none; + border-color: #FDDA16; +} + +.nitrolite-toolkit-link span { + color: var(--ifm-color-emphasis-700); + font-size: 0.9rem; + line-height: 1.45; +} + +@media (max-width: 996px) { + .nitrolite-toolkit-grid, + .nitrolite-toolkit-upcoming, + .nitrolite-toolkit-links { + grid-template-columns: 1fr; + } +} + [data-theme='dark'] .table-of-contents__link:hover, [data-theme='dark'] .table-of-contents__link:hover code { color: #cccccc !important; } +/* Remove left padding from the right TOC column */ +.col.col--3 { + padding-left: 0 !important; +} + +/* "ON THIS PAGE:" heading above the TOC */ +.theme-doc-toc-desktop::before { + content: 'ON THIS PAGE:'; + display: block; + font-family: 'Poppins', sans-serif; + font-size: 13px; + font-weight: 500; + letter-spacing: 0.08em; + margin-bottom: 12px; + margin-left: 6px; +} + /* Pagination navigation styling */ .pagination-nav__link { border: 1px solid #e0e0e0 !important; @@ -340,11 +617,14 @@ a.table-of-contents__link.table-of-contents__link--active code { -/* Remove black border and styling for parent categories (categories with subcategories) */ +/* Remove black border and styling for parent categories (categories with subcategories). + * Padding is intentionally NOT set here — the chevron + label spacing is owned by + * .menu__list-item-collapsible (flex container with gap). Setting padding-left on + * the link would shift the label text away from the chevron when a child is active. + */ .menu__link--sublist.menu__link--active, a.menu__link.menu__link--sublist.menu__link--active { border-left: none !important; - padding-left: 16px !important; font-weight: 600; background: transparent !important; background-color: transparent !important; @@ -367,57 +647,181 @@ a.menu__link.menu__link--sublist.menu__link--active:hover { color: #cccccc !important; } -/* Move expand/collapse icon to the left side of text */ -.menu__link--sublist, -.menu__list-item-collapsible>.menu__link { +/* Sidebar collapsible category: chevron-only toggle. + * The .menu__caret