diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md new file mode 100644 index 0000000..9525226 --- /dev/null +++ b/.agents/AGENTS.md @@ -0,0 +1,21 @@ +# Universal Anti-Hallucination & Verification Protocol (MANDATORY FOR ALL AGENTS) + +## 1. Zero-Hallucination Policy & Strict Verification (Universal Scope) +- **Universal Applicability:** This protocol applies universally to ALL technologies, domains, and tools — including **MCP (Model Context Protocol) servers/tools**, smart contracts, blockchain SDKs (e.g., Zama, Circle, Viem), UI frameworks (e.g., shadcn, Next.js, Tailwind), cloud APIs, databases, and system architectures. +- **Never Assume or Guess:** Do NOT invent, guess, or hallucinate function signatures, MCP tool parameters, contract ABIs, addresses, API endpoints, CSS classes, or configuration headers from training data memory. +- **Read Authoritative Sources FIRST:** Before calling any tool, writing code, or generating documentation, you MUST explicitly read and verify ground-truth sources: + 1. **MCP Servers & Tools:** Before calling lazy-loaded MCP tools or using MCP resources, always read their schema definitions (`.json`), check available tools/resources (`list_resources`, `read_resource`), and review server instructions (`instructions.md`). + 2. **Local Codebase & ABIs:** Inspect existing files (e.g., ABI files, types, interfaces, utility wrappers) to verify exact names, types, and signatures in use. + 3. **Installed Dependencies & Skills:** When integrating third-party SDKs or frameworks, inspect `node_modules` or read local skill instructions (`SKILL.md`) and reference docs (`references/`) before implementing domain logic. + +## 2. Mandatory "Think -> Read -> Plan -> Execute" Workflow +Before executing any coding, configuration, or integration task across any technology stack: +1. **Think & Analyze:** Identify exact information needed (e.g., MCP tool schemas, API parameters, contract addresses, decimal scaling rules). +2. **Read & Verify:** Use read tools (`view_file`, `grep_search`, `read_resource`, `call_mcp_tool`) to verify 100% accurate data from ground-truth sources. +3. **Plan:** Outline exact changes or tool invocations, confirming that every name, parameter, and address matches verified sources without guessing. +4. **Execute & Audit:** Apply changes and double-check against ground truth to ensure zero discrepancies, syntax errors, or placeholder values remain. + +## 3. Strict Prohibitions +- **No Hallucinated MCP / Tool Calls:** Never call MCP tools or agent skills with guessed arguments; always verify parameter schemas first. +- **No Unauthorized Architectural / Security Changes:** Never add custom headers (like COOP/COEP/CORP), security policies, or infrastructure overrides without explicit user authorization or documented vendor requirements. +- **No Placeholder Leakage:** Never put fake placeholder addresses (e.g., `0x1000...`), mock ABIs, or fake API keys into production code, SDKs, or LLM reference manifests (`llms.txt`, `llms-full.txt`, `agent-tools.ts`). Always use verified ground-truth values. diff --git a/README.md b/README.md index 6c3a274..01c3c8c 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,17 @@ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6?logo=typescript)](https://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -ShadowLine is an enterprise-grade, non-custodial decentralized application (dApp) that acts as the primary gateway for Zama's FHEVM Wrappers Registry. Built entirely on Fully Homomorphic Encryption (FHE), ShadowLine enables users and institutions to seamlessly shield standard ERC-20 tokens into ERC-7984 confidential tokens (cTokens) and perform private on-chain asset transfers. +ShadowLine is a non-custodial dApp built on top of Zama's Confidential +Token Wrappers Registry, powered by Zama's FHEVM. It lets you shield +ERC-20 tokens into ERC-7984 confidential tokens (cTokens), unshield them +back, and send confidential transfers with encrypted amounts. + +Beyond wrapping, ShadowLine includes user decryption of your own balances, +a browsable token registry with custom-token support, a portfolio view, +and a testnet faucet — across Sepolia and coming soon on Ethereum mainnet. + +Because balances and transfer amounts are handled as ERC-7984 confidential tokens, they stay encrypted on-chain and are computed in their encrypted state via FHE. Wallet addresses remain public on-chain, as with any standard transaction. -With ShadowLine, transaction amounts and token balances remain completely encrypted on the blockchain, computable only in their encrypted state, while sender and receiver identities are preserved for ledger auditing. --- @@ -26,13 +34,16 @@ With ShadowLine, transaction amounts and token balances remain completely encryp - [8. How to Configure a New Token Pair](#8-how-to-configure-a-new-token-pair) - [9. Local Development & Setup](#9-local-development--setup) - [10. Repository Structure](#10-repository-structure) -- [11. License](#11-license) +- [11. Zama SDK 3.0.1 — methods used](#11-zama-sdk-301--methods-used) +- [12. FAQ: Why doesn't Decrypt ask for signature on some tokens?](#12-faq-why-doesnt-decrypt-ask-for-signature-on-some-tokens) +- [13. License](#13-license) --- + ## 1. About ShadowLine -Traditional blockchain networks expose all transaction values and account balances to public block explorers, posing significant security and privacy risks for both retail users and commercial enterprises. ShadowLine addresses this challenge by utilizing Torus Fully Homomorphic Encryption (TFHE) on-chain via Zama's FHEVM. +Traditional blockchain networks expose all transaction values and account balances to public block explorers, posing significant security and privacy risks for both retail users and commercial enterprises. ShadowLine addresses this challenge by using Fully Homomorphic Encryption (FHE) on-chain via Zama's FHEVM. It wraps public ERC-20 tokens into **ERC-7984 Confidential Wrappers** (cTokens), converting open balance data into cryptographic ciphertext handles (`euint64`). Transactions and balances are processed on-chain in their encrypted state, ensuring confidentiality while maintaining decentralized validation. @@ -51,6 +62,7 @@ ShadowLine supports the following network configurations: --- + ## 3. Core Features Deep Dive ShadowLine is divided into specialized modules tailored for retail and enterprise confidentiality management: @@ -101,6 +113,7 @@ An in-app documentation portal explaining technical architecture, decimal scalin --- + ## 4. Technical Architecture & Data Flows ShadowLine's architecture decouples public blockchain logic, local cryptographic calculations, and decentralized key management: @@ -137,6 +150,7 @@ ShadowLine's architecture decouples public blockchain logic, local cryptographic └──────────────────────┘ ``` + ### 4.1 FHE Shielding Flow (Public to Confidential) The diagram below illustrates the process of shielding public ERC-20 tokens into encrypted cTokens: @@ -162,6 +176,7 @@ sequenceDiagram Wrap-->>User: Tx Confirmed (Shield Completed) ``` + ### 4.2 FHE Decryption Flow (Confidential to Plaintext) To query and view confidential balances, ShadowLine uses EIP-712 permits. The process prevents gas consumption and ensures the plaintext is only visible to the user: @@ -187,8 +202,10 @@ sequenceDiagram SDK->>User: Display Plaintext Balance (e.g., 1,000 cUSDT) ``` + --- + ## 5. Security & Cryptographic Trust Model ShadowLine's privacy architecture relies on the following security properties: @@ -198,8 +215,10 @@ ShadowLine's privacy architecture relies on the following security properties: * **EIP-712 Permit Scoping:** Permit signatures are read-only and restricted to balance views. They cannot approve token transfers, withdraw funds, or modify contract states. * **Zero-Knowledge KMS Boundaries:** The Key Management System (KMS) re-encrypts FHE ciphertexts from the network key to the user's session key. This cryptographic handshake ensures that neither the KMS gateway nor any relayer can inspect the user's plaintext values. + --- + ## 6. Hybrid Registry Sourcing Strategy To guarantee uptime and developer flexibility, ShadowLine merges token information from three layers: @@ -225,6 +244,7 @@ To guarantee uptime and developer flexibility, ShadowLine merges token informati --- + ## 7. B2B & Enterprise Use Cases Confidential ERC-7984 wrapper standard implementations enable several corporate use cases: @@ -235,11 +255,13 @@ Confidential ERC-7984 wrapper standard implementations enable several corporate --- + ## 8. How to Configure a New Token Pair Two paths, no on-chain governance required. Both flow the pair through the exact same shield / unshield / decrypt code paths as an Official registry pair — the only difference is which section lists it (**Official — Zama Registry** vs **Custom / Dev-only Tokens**). -The on-chain Wrappers Registry is owned by the Zama Protocol DAO — calling `registerPair` from ShadowLine reverts. So ShadowLine declares custom pairs **locally**: either seeded in the repo (path A, ships with the app) or added at runtime in the browser (path B, per-user). +The on-chain Wrappers Registry is permissioned and not publicly writable — its `registerConfidentialToken(erc20, wrapper)` entrypoint cannot be called by ShadowLine. So ShadowLine declares custom pairs **locally**: either seeded in the repo (path A, ships with the app) or added at runtime in the browser (path B, per-user). + Resolution order at read time: **on-chain registry (primary) → `CUSTOM_PAIRS` config → browser localStorage → hardcoded offline snapshot**. On-chain always wins on any address conflict. @@ -270,6 +292,7 @@ export const CUSTOM_PAIRS: CustomPair[] = [ **Requirement:** `erc7984Address` must implement ERC-165 and return `true` for interface id `0x4958f2a4`. If it doesn't, ShadowLine's Add-Custom-Pair form rejects it — see path B. + ### Path B — Add a pair from the UI (persists only in this browser) **Step 1:** Open the dApp at `/app` and connect a wallet on the target network (Sepolia or Mainnet). The wallet is used for chain resolution — validation itself runs against a public RPC and doesn't require a signature. @@ -305,8 +328,10 @@ Sepolia's on-chain registry contains a second, non-mintable `tGBP` wrapper deplo To demonstrate the *success* path, deploy any ERC-7984 wrapper of your own on Sepolia, paste that wrapper address, and click **Add Pair** — the row will appear under **Custom / Dev-only Tokens** and route through the same shield/unshield/decrypt code paths as any official pair. + --- + ## 9. Local Development & Setup (0-to-100 DevOps Suite) Want to run ShadowLine locally or deploy to a cloud node / VPS in under 1 minute? We built an automated, zero-friction **0-to-100 DevOps Wizard** that handles prerequisite checking (Git, Node.js v18+), environment configuration (`.env.local`), production build verification, and server launching. @@ -370,10 +395,11 @@ npm run start ``` Open `http://localhost:3000` to interact with the application. -**Live URL:** https://shadow-line.netlify.app/ +**Live URL:** https://shadow-line.vercel.app/ --- + ## 10. Repository Structure ``` @@ -404,6 +430,7 @@ src/ --- + ## 11. Zama SDK 3.0.1 — methods used ShadowLine is pinned to `@zama-fhe/sdk` + `@zama-fhe/react-sdk` **3.0.1** (verified against installed `.d.ts`, which is treated as ground truth over the docs site). The build uses only what exists in that release: @@ -424,6 +451,17 @@ ShadowLine is pinned to `@zama-fhe/sdk` + `@zama-fhe/react-sdk` **3.0.1** (verif --- -## 12. License +## 12. FAQ: Why doesn't Decrypt ask for signature on some tokens? + +There are two legitimate reasons why the Decrypt action might not prompt you for a wallet signature: + +1. **No balance yet** — If you've never received or wrapped this token, its confidential balance handle on-chain is `bytes32(0)`. The SDK recognizes this and returns `0` instantly without needing to sign or contact the relayer. There's no ciphertext to decrypt. +2. **Cached credentials** — After you sign once (via `useAllow`), an EIP-712 permit is stored in IndexedDB for up to 30 days. Any subsequent decrypt for a contract covered by that permit reuses the cached credential and skips the signature prompt. + +Both behaviors follow Zama's official SDK guidance. + +--- + +## 13. License This project is licensed under the **MIT License**. See the `LICENSE` file for details. diff --git a/package-lock.json b/package-lock.json index 694de24..fae0483 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "@types/three": "^0.184.1", "eslint": "^9", "eslint-config-next": "16.2.9", + "jimp": "^1.6.1", "tailwindcss": "^4.3.1", "typescript": "^5", "vitest": "^4.1.9" @@ -316,6 +317,17 @@ "node": ">=6.9.0" } }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/@dimforge/rapier3d-compat": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", @@ -1117,6 +1129,606 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@jimp/core": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.1.tgz", + "integrity": "sha512-+BoKC5G6hkrSy501zcJ2EpfnllP+avPevcBfRcZe/CW+EwEfY6X1EZ8QWyT7NpDIvEEJb1fdJnMMfUnFkxmw9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/file-ops": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "await-to-js": "^3.0.0", + "exif-parser": "^0.1.12", + "file-type": "^21.3.3", + "mime": "3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/diff": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.1.tgz", + "integrity": "sha512-YkKDPdHjLgo1Api3+Bhc0GLAygldlpt97NfOKoNg1U6IUNXA6X2MgosCjPfSBiSvJvrrz1fsIR+/4cfYXBI/HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "pixelmatch": "^5.3.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/file-ops": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.1.tgz", + "integrity": "sha512-T+gX6osHjprbDRad0/B71Evyre7ZdVY1z/gFGEG9Z8KOtZPKboWvPeP2UjbZYWQLy9UKCPQX1FNAnDiOPkJL7w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-bmp": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.1.tgz", + "integrity": "sha512-xzWzNT4/u5zGrTT3Tme9sGU7YzIKxi13+BCQwLqACbt5DXf9SAfdzRkopZQnmDko+6In5nqaT89Gjs43/WdnYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "bmp-ts": "^1.0.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-gif": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.1.tgz", + "integrity": "sha512-YjY2W26rQa05XhanYhRZ7dingCiNN+T2Ymb1JiigIbABY0B28wHE3v3Cf1/HZPWGu0hOg36ylaKgV5KxF2M58w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "gifwrap": "^0.10.1", + "omggif": "^1.0.10" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-jpeg": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.1.tgz", + "integrity": "sha512-HT9H3yOmlOFzYmdI15IYdfy6ggQhSRIaHeA+OTJSEORXBqEo97sUZu/DsgHIcX5NJ7TkJBTgZ9BZXsV6UbsyMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "jpeg-js": "^0.4.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-png": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.1.tgz", + "integrity": "sha512-SZ/KVhI5UjcSzzlXsXdIi/LhJ7UShf2NkMOtVrbZQcGzsqNtynAelrOXeoTxcanfVqmNhAoVHg8yR2cYoqrYjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "pngjs": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-tiff": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.1.tgz", + "integrity": "sha512-jDG/eJquID1M4MBlKMmDRBmz2TpXMv7TUyu2nIRUxhlUc2ogC82T+VQUkca9GJH1BBJ9dx5sSE5dGkWNjIbZxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "utif2": "^4.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.1.tgz", + "integrity": "sha512-MwnI7C7K81uWddY9FLw1fCOIy6SsPIUftUz36Spt7jisCn8/40DhQMlSxpxTNelnZb/2SnloFimQfRZAmHLOqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-blur": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.1.tgz", + "integrity": "sha512-lIo7Tzp5jQu30EFFSK/phXANK3citKVEjepDjQ6ljHoIFtuMRrnybnmI2Md24ulvWlDaz+hh3n6qrMb8ydwhZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/utils": "1.6.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.1.tgz", + "integrity": "sha512-kK1PavY6cKHNNKce37vdV4Tmpc1/zDKngGoeOV3j+EMatoHFZUinV3s6F9aWryPs3A0xhCLZgdJ6Zeea1d5LCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-color": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.1.tgz", + "integrity": "sha512-LtUN1vAP+LRlZAtTNVhDRSiXx+26Kbz3zJaG6a5k59gQ95jgT5mknnF8lxkHcqJthM4MEk3/tPxkdJpEybyF/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "tinycolor2": "^1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-color/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-contain": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.1.tgz", + "integrity": "sha512-m0qhrfA8jkTqretGv4w+T/ADFR4GwBpE0sCOC2uJ0dzr44/ddOMsIdrpi89kabqYiPYIrxkgdCVCLm3zn1Vkkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-contain/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-cover": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.1.tgz", + "integrity": "sha512-hZytnsth0zoll6cPf434BrT+p/v569Wr5tyO6Dp0dH1IDPhzhB5F38sZGMLDo7bzQiN9JFVB3fxkcJ/WYCJ3Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-cover/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-crop": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.1.tgz", + "integrity": "sha512-EerRSLlclXyKDnYc/H9w/1amZW7b7v3OGi/VlerPd2M/pAu5X8TkyYWtfqYCXnNp1Ixtd8oCo9zGfY9zoXT4rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-crop/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-displace": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.1.tgz", + "integrity": "sha512-K07QVl7xQwIfD6KfxRV/c3E9e7ZBXxUXdWuvoTWcKHL2qV48MOF5Nqbz/aJW4ThnQARIsxvYlZjPFiqkCjlU+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-displace/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-dither": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.1.tgz", + "integrity": "sha512-+2V+GCV2WycMoX1/z977TkZ8Zq/4MVSKElHYatgUqtwXMi2fDK2gKYU2g9V39IqFvTJsTIsK0+58VFz/ROBVew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.1.tgz", + "integrity": "sha512-XtS5ZyoZ0vxZxJ6gkqI63SivhtI58vX95foMPM+cyzYkRsJXMOYCr8DScxF5bp4Xr003NjYm/P+7+08tibwzHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-flip": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.1.tgz", + "integrity": "sha512-ws38W/sGj7LobNRayQ83garxiktOyWxM5vO/y4a/2cy9v65SLEUzVkrj+oeAaUSSObdz4HcCEla7XtGlnAGAaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-flip/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-hash": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.1.tgz", + "integrity": "sha512-sZt6ZcMX6i8vFWb4GYnw0pR/o9++ef0dTVcboTB5B/g7nrxCODIB4wfEkJ/YqZM5wUvol77K1qeS0/rVO6z21A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/js-bmp": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/js-tiff": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "any-base": "^1.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.1.tgz", + "integrity": "sha512-SIG0/FcmEj3tkwFxc7fAGLO8o4uNzMpSOdQOhbCgxefQKq5wOVMk9BQx/sdMPBwtMLr9WLq0GzLA/rk6t2v20A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-print": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.1.tgz", + "integrity": "sha512-BYVz/X3Xzv8XYilVeDy11NOp0h7BTDjlOtu0BekIFHP1yHVd24AXNzbOy52XlzYZWQ0Dl36HOHEpl/nSNrzc6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/types": "1.6.1", + "parse-bmfont-ascii": "^1.0.6", + "parse-bmfont-binary": "^1.0.6", + "parse-bmfont-xml": "^1.1.6", + "simple-xml-to-json": "^1.2.2", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-print/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-quantize": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.1.tgz", + "integrity": "sha512-J2En9PLURfP+vwYDtuZ9T8yBW6BWYZBScydAjRiPBmJfEhTcNQqiiQODrZf7EqbbX/Sy5H6dAeRiqkgoV9N6Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "image-q": "^4.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-quantize/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-resize": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.1.tgz", + "integrity": "sha512-CLkrtJoIz2HdWnpYiN6p8KYcPc00rCH/SUu6o+lfZL05Q4uhecJlnvXuj9x+U6mDn3ldPmJj6aZqMHuUJzdVqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-resize/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-rotate": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.1.tgz", + "integrity": "sha512-nOjVjbbj705B02ksysKnh0POAwEBXZtJ9zQ5qC+X7Tavl3JNn+P3BzQovbBxLPSbUSld6XID9z5ijin4PtOAUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-rotate/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-threshold": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.1.tgz", + "integrity": "sha512-JOKv9F8s6tnVLf4sB/2fF0F339EFnHvgEdFYugO6VhowKLsap0pEZmLyE/DlRnYtIj2RddHZVxVMp/eKJ04l2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-hash": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-threshold/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/types": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.1.tgz", + "integrity": "sha512-leI7YbveTNi565m910XgIOwXyuu074H5qazAD1357HImJSv2hqxnWXpwxQbadGWZ7goZRYBDZy5lpqud0p7q5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/types/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/utils": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-veFPRd93FCnS7AgmCkPgARVGoDRrJ9cm1ujuNyA+UfQ5VKbED2002sm5XfFLFwTsKC8j04heTrwe+tU1dluXOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.1", + "tinycolor2": "^1.6.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -3737,6 +4349,31 @@ "react": "^18 || ^19" } }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, "node_modules/@tweenjs/tween.js": { "version": "23.1.3", "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", @@ -4819,6 +5456,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5051,6 +5695,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/await-to-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz", + "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/axe-core": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz", @@ -5119,6 +5773,13 @@ "require-from-string": "^2.0.2" } }, + "node_modules/bmp-ts": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz", + "integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -6392,6 +7053,12 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "license": "MIT" }, + "node_modules/exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", + "dev": true + }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -6488,6 +7155,25 @@ "node": ">=16.0.0" } }, + "node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6740,6 +7426,17 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/gifwrap": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", + "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "image-q": "^4.0.0", + "omggif": "^1.0.10" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -6956,6 +7653,23 @@ "node": ">= 4" } }, + "node_modules/image-q": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", + "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "16.9.1" + } + }, + "node_modules/image-q/node_modules/@types/node": { + "version": "16.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", + "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", + "dev": true, + "license": "MIT" + }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -7505,6 +8219,45 @@ "react": "^19.0.0" } }, + "node_modules/jimp": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.1.tgz", + "integrity": "sha512-hNQh6rZtWfSVWSNVmvq87N5BPJsNH7k7I7qyrXf9DOma9xATQk3fsyHazCQe51nCjdkoWdTmh0vD7bjVSLoxxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/diff": "1.6.1", + "@jimp/js-bmp": "1.6.1", + "@jimp/js-gif": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/js-tiff": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/plugin-blur": "1.6.1", + "@jimp/plugin-circle": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-contain": "1.6.1", + "@jimp/plugin-cover": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-displace": "1.6.1", + "@jimp/plugin-dither": "1.6.1", + "@jimp/plugin-fisheye": "1.6.1", + "@jimp/plugin-flip": "1.6.1", + "@jimp/plugin-hash": "1.6.1", + "@jimp/plugin-mask": "1.6.1", + "@jimp/plugin-print": "1.6.1", + "@jimp/plugin-quantize": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/plugin-rotate": "1.6.1", + "@jimp/plugin-threshold": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -7515,6 +8268,13 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8073,6 +8833,19 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -8463,6 +9236,13 @@ "node": ">=12.20.0" } }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "dev": true, + "license": "MIT" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -8561,6 +9341,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8574,6 +9361,31 @@ "node": ">=6" } }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8626,6 +9438,39 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pixelmatch": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", + "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^6.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pixelmatch/node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -9192,6 +10037,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -9419,6 +10274,16 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-xml-to-json": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.7.tgz", + "integrity": "sha512-mz9VXphOxQWX3eQ/uXCtm6upltoN0DLx8Zb5T4TFC4FHB7S9FDPGre8CfLWqPWQQH/GrQYd2AXhhVM5LDpYx6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.12.2" + } + }, "node_modules/simplex-noise": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-4.0.3.tgz", @@ -9641,6 +10506,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -9781,6 +10663,13 @@ "dev": true, "license": "MIT" }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", @@ -9868,6 +10757,25 @@ "node": ">=8.0" } }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/troika-three-text": { "version": "0.52.4", "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz", @@ -10109,6 +11017,19 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -10266,6 +11187,16 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utif2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", + "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.11" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -10839,6 +11770,37 @@ } } }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index cffbceb..a308bfc 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@types/three": "^0.184.1", "eslint": "^9", "eslint-config-next": "16.2.9", + "jimp": "^1.6.1", "tailwindcss": "^4.3.1", "typescript": "^5", "vitest": "^4.1.9" diff --git a/public/.well-known/ai-plugin.json b/public/.well-known/ai-plugin.json index 5dee036..392e4a2 100644 --- a/public/.well-known/ai-plugin.json +++ b/public/.well-known/ai-plugin.json @@ -9,9 +9,9 @@ }, "api": { "type": "openapi", - "url": "https://shadow-line.netlify.app/openapi.json" + "url": "https://shadow-line.vercel.app/openapi.json" }, - "logo_url": "https://shadow-line.netlify.app/file.svg", + "logo_url": "https://shadow-line.vercel.app/file.svg", "contact_email": "dev@shadowline.protocol", - "legal_info_url": "https://shadow-line.netlify.app/app/docs" + "legal_info_url": "https://shadow-line.vercel.app/app/docs" } diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..84b4c92 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icon.jpg b/public/icon.jpg new file mode 100644 index 0000000..84b4c92 Binary files /dev/null and b/public/icon.jpg differ diff --git a/public/images/abstract-glass-artwork.jpg b/public/images/abstract-glass-artwork.jpg new file mode 100644 index 0000000..0c24c44 Binary files /dev/null and b/public/images/abstract-glass-artwork.jpg differ diff --git a/public/images/abstract-gold-torus.jpg b/public/images/abstract-gold-torus.jpg new file mode 100644 index 0000000..4a1ff47 Binary files /dev/null and b/public/images/abstract-gold-torus.jpg differ diff --git a/public/images/confidential-token-crystal.jpg b/public/images/confidential-token-crystal.jpg new file mode 100644 index 0000000..da36924 Binary files /dev/null and b/public/images/confidential-token-crystal.jpg differ diff --git a/public/images/crystal-shield-decor.jpg b/public/images/crystal-shield-decor.jpg new file mode 100644 index 0000000..8cba9c1 Binary files /dev/null and b/public/images/crystal-shield-decor.jpg differ diff --git a/public/images/defi-interface-mockup.jpg b/public/images/defi-interface-mockup.jpg new file mode 100644 index 0000000..ffad97c Binary files /dev/null and b/public/images/defi-interface-mockup.jpg differ diff --git a/public/images/encrypted-lock-prism.jpg b/public/images/encrypted-lock-prism.jpg new file mode 100644 index 0000000..aa4246e Binary files /dev/null and b/public/images/encrypted-lock-prism.jpg differ diff --git a/public/images/flowing-amber-aurora.jpg b/public/images/flowing-amber-aurora.jpg new file mode 100644 index 0000000..1602e92 Binary files /dev/null and b/public/images/flowing-amber-aurora.jpg differ diff --git a/public/images/obsidian-glass-sculpture.jpg b/public/images/obsidian-glass-sculpture.jpg new file mode 100644 index 0000000..39ff550 Binary files /dev/null and b/public/images/obsidian-glass-sculpture.jpg differ diff --git a/public/images/privacy-boundary-diagram.jpg b/public/images/privacy-boundary-diagram.jpg new file mode 100644 index 0000000..fb07eca Binary files /dev/null and b/public/images/privacy-boundary-diagram.jpg differ diff --git a/public/llms-full.txt b/public/llms-full.txt index b1e89b5..69347c1 100644 --- a/public/llms-full.txt +++ b/public/llms-full.txt @@ -27,7 +27,7 @@ Queries the on-chain `WrappersRegistry` contract and returns all registered toke #### Example Request: ```bash -curl -s "https://shadow-line.netlify.app/api/registry?chain=sepolia" +curl -s "https://shadow-line.vercel.app/api/registry?chain=sepolia" ``` #### Response JSON Schema: @@ -35,8 +35,8 @@ curl -s "https://shadow-line.netlify.app/api/registry?chain=sepolia" { "pairs": [ { - "tokenAddress": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", - "confidentialTokenAddress": "0x7c5B9B79d50A6cdd398d254cc92A0a4cFE64d9D4", + "tokenAddress": "0x9b5Cd13b8eFbB58Dc25A05CF411D8056058aDFfF", + "confidentialTokenAddress": "0x7c5BF43B851c1dff1a4feE8dB225b87f2C223639", "symbol": "USDC", "confidentialSymbol": "cUSDC", "name": "USD Coin", @@ -48,7 +48,7 @@ curl -s "https://shadow-line.netlify.app/api/registry?chain=sepolia" ], "total": 1, "chain": "sepolia", - "registryAddress": "0x1000000000000000000000000000000000000005", + "registryAddress": "0x2f0750Bbb0A246059d80e94c454586a7F27a128e", "timestamp": 1751740000000, "source": "on-chain" } @@ -67,13 +67,20 @@ When building automated transactions, agents must adhere to strict decimal scali ## 4. Smart Contract ABIs for AI Agents ### WrappersRegistry Contract -- **Sepolia Address:** `0x1000000000000000000000000000000000000005` -- **Mainnet Address:** `0x1000000000000000000000000000000000000005` +- **Sepolia Address:** `0x2f0750Bbb0A246059d80e94c454586a7F27a128e` +- **Mainnet Address:** `0xeb5015fF021DB115aCe010f23F55C2591059bBA0` ```json [ { - "name": "getPairsSlice", + "name": "getTokenConfidentialTokenPairsLength", + "type": "function", + "stateMutability": "view", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "name": "getTokenConfidentialTokenPairsSlice", "type": "function", "stateMutability": "view", "inputs": [ @@ -95,77 +102,69 @@ When building automated transactions, agents must adhere to strict decimal scali ``` ### ERC-7984 Confidential Wrapper Contract (`cToken`) +> **WARNING FOR AI AGENTS:** Do NOT call `transfer` or `unwrap` using raw plaintext numbers or padded hex strings in Viem/Ethers! In FHEVM, confidential transfers and unshielding require client-side WASM encryption (`euint64`) and FHE input proof generation. Always use `ShadowlineAgentHarness` or `@zama-fhe/sdk` to execute these methods. + ```json [ { - "name": "depositFor", + "name": "wrap", "type": "function", "stateMutability": "nonpayable", "inputs": [ { "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" } ], - "outputs": [{ "name": "", "type": "bool" }] - }, - { - "name": "requestWithdraw", - "type": "function", - "stateMutability": "nonpayable", - "inputs": [ - { "name": "amount", "type": "uint64" } - ], - "outputs": [{ "name": "", "type": "uint256" }] + "outputs": [{ "name": "", "type": "bytes32" }] }, { - "name": "confidentialTransfer", + "name": "confidentialBalanceOf", "type": "function", - "stateMutability": "nonpayable", - "inputs": [ - { "name": "to", "type": "address" }, - { "name": "encryptedAmount", "type": "bytes" } - ], - "outputs": [{ "name": "", "type": "bool" }] + "stateMutability": "view", + "inputs": [{ "name": "account", "type": "address" }], + "outputs": [{ "name": "", "type": "bytes32" }] } ] ``` --- -## 5. Autonomous Agent Integration Code (Viem / Node.js) +## 5. Autonomous Agent Integration Code (Headless Node.js Harness) + +To enable true autonomous execution without a browser, ShadowLine provides a headless execution harness (`ShadowlineAgentHarness`) in `@shadowline/agent-tools` (`src/lib/agent-harness.ts`). It wraps `@zama-fhe/sdk` in Node worker pool mode (`node()`) and handles EIP-712 permit signing, KMS decryption, and FHE client-side encryption automatically. ```typescript -import { createPublicClient, createWalletClient, http, parseUnits, type Address } from 'viem'; -import { privateKeyToAccount } from 'viem/accounts'; -import { sepolia } from 'viem/chains'; - -// 1. Initialize Agent Wallet -const account = privateKeyToAccount('0xYOUR_AGENT_PRIVATE_KEY'); -const publicClient = createPublicClient({ chain: sepolia, transport: http() }); -const walletClient = createWalletClient({ account, chain: sepolia, transport: http() }); - -/** - * Shield public ERC-20 tokens into ERC-7984 confidential tokens - */ -async function shieldTokens(erc20Address: Address, wrapperAddress: Address, amountInUnderlyingDecimals: string, decimals: number) { - const rawAmount = parseUnits(amountInUnderlyingDecimals, decimals); - - // Approve Wrapper Spender - const approveHash = await walletClient.writeContract({ - address: erc20Address, - abi: [{ name: 'approve', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'spender', type: 'address' }, { name: 'amount', type: 'uint256' }], outputs: [{ name: '', type: 'bool' }] }], - functionName: 'approve', - args: [wrapperAddress, rawAmount], - }); - await publicClient.waitForTransactionReceipt({ hash: approveHash }); - - // Deposit into Wrapper - const shieldHash = await walletClient.writeContract({ - address: wrapperAddress, - abi: [{ name: 'depositFor', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }], outputs: [{ name: '', type: 'bool' }] }], - functionName: 'depositFor', - args: [account.address, rawAmount], - }); - - return shieldHash; -} +import { ShadowlineAgentHarness } from '@/lib/agent-harness'; + +// 1. Initialize Headless Agent Harness +const agent = new ShadowlineAgentHarness({ + privateKey: process.env.AGENT_PRIVATE_KEY as `0x${string}`, + relayerApiKey: process.env.ZAMA_RELAYER_API_KEY || 'YOUR_API_KEY', +}); + +await agent.init(); + +// 2. Discover Verified Pairs +const pairs = await agent.getPairs(); +const usdcPair = pairs.find(p => p.symbol === 'USDC') || pairs[0]; + +// 3. Shield (Wrap) ERC-20 -> ERC-7984 +// NOTE: Shielding amount MUST use underlying ERC-20 decimals (e.g. 6 for USDC) +const shieldHash = await agent.shield(usdcPair.confidentialTokenAddress, '10.5'); +console.log('Shielded in tx:', shieldHash); + +// 4. Check Confidential Balance +// Automatically generates EIP-712 permit, queries Zama KMS, and decrypts in WASM +const balance = await agent.getConfidentialBalance(usdcPair.confidentialTokenAddress); +console.log('Decrypted Confidential Balance:', balance); + +// 5. Confidential Transfer +// Automatically encrypts amount into an FHE euint64 handle and generates FHE input proof +// NOTE: Transfer amount MUST use fixed 6 decimals scale! +const transferHash = await agent.transfer(usdcPair.confidentialTokenAddress, '0xRecipientAddress', '5.0'); +console.log('Confidential transfer tx:', transferHash); + +// 6. Unshield (Unwrap) ERC-7984 -> ERC-20 +// NOTE: Unshield amount MUST use fixed 6 decimals scale! +const unshieldHash = await agent.unshield(usdcPair.confidentialTokenAddress, '5.0'); +console.log('Unshield requested in tx:', unshieldHash); ``` diff --git a/public/llms.txt b/public/llms.txt index 5a6ccc6..688854b 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -11,9 +11,9 @@ ShadowLine enables users, DAOs, and autonomous AI agents to wrap standard public - **REST API:** Query verified wrapper pairs without a wallet or web3 provider via `GET /api/registry`. ## Key Resources -- [Live Web App & Registry](https://shadow-line.netlify.app/app) -- [Developer Documentation](https://shadow-line.netlify.app/app/docs) -- [REST API Reference](https://shadow-line.netlify.app/app/docs/rest-api) +- [Live Web App & Registry](https://shadow-line.vercel.app/app) +- [Developer Documentation](https://shadow-line.vercel.app/app/docs) +- [REST API Reference](https://shadow-line.vercel.app/app/docs/rest-api) - [GitHub Repository](https://github.com/hosein-ul/ShadowLine) - [Zama FHEVM Documentation](https://docs.zama.org/protocol) @@ -25,7 +25,7 @@ ShadowLine enables users, DAOs, and autonomous AI agents to wrap standard public ## AI Agent Integration (Drop-in Hook & API) AI agents can fetch live verified token pairs via HTTP: ```http -GET https://shadow-line.netlify.app/api/registry?chain=sepolia +GET https://shadow-line.vercel.app/api/registry?chain=sepolia ``` -For direct smart contract interaction, agents should reference `src/lib/use-shadowline.ts` or view the full documentation in `/llms-full.txt`. +For headless Node.js or Python agent execution (without a browser), import `ShadowlineAgentHarness` from `src/lib/agent-harness.ts` (also exported in `@shadowline/agent-tools`). For React dApps, reference `src/lib/use-shadowline.ts` or view the full technical specification in `/llms-full.txt`. diff --git a/public/logo-icon.jpg b/public/logo-icon.jpg new file mode 100644 index 0000000..84b4c92 Binary files /dev/null and b/public/logo-icon.jpg differ diff --git a/public/logo-text-transparent.png b/public/logo-text-transparent.png new file mode 100644 index 0000000..9543e9f Binary files /dev/null and b/public/logo-text-transparent.png differ diff --git a/public/logo-text.jpg b/public/logo-text.jpg new file mode 100644 index 0000000..9d650d5 Binary files /dev/null and b/public/logo-text.jpg differ diff --git a/public/openapi.json b/public/openapi.json index ee6b2b8..9b13286 100644 --- a/public/openapi.json +++ b/public/openapi.json @@ -7,7 +7,7 @@ }, "servers": [ { - "url": "https://shadow-line.netlify.app" + "url": "https://shadow-line.vercel.app" } ], "paths": { diff --git a/scripts/remove-bg.js b/scripts/remove-bg.js new file mode 100644 index 0000000..419ad0e --- /dev/null +++ b/scripts/remove-bg.js @@ -0,0 +1,48 @@ +const jimpLib = require("jimp"); + +async function run() { + const JimpClass = jimpLib.Jimp || jimpLib; + console.log("Reading image..."); + const image = await JimpClass.read("public/logo-text.jpg"); + const width = image.bitmap.width; + const height = image.bitmap.height; + console.log("Image loaded:", width, "x", height); + + // Sample top-left corner as background color reference + const bgR = image.bitmap.data[0]; + const bgG = image.bitmap.data[1]; + const bgB = image.bitmap.data[2]; + console.log("Background color sample:", bgR, bgG, bgB); + + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const idx = (width * y + x) << 2; + const r = image.bitmap.data[idx + 0]; + const g = image.bitmap.data[idx + 1]; + const b = image.bitmap.data[idx + 2]; + + // Euclidean distance from background color + const dist = Math.sqrt((r - bgR)**2 + (g - bgG)**2 + (b - bgB)**2); + + if (dist < 40) { + image.bitmap.data[idx + 3] = 0; // 100% transparent + } else if (dist < 80) { + // Smooth alpha edge transition + image.bitmap.data[idx + 3] = Math.floor(((dist - 40) / 40) * 255); + } + } + } + + console.log("Saving transparent PNG..."); + if (typeof image.writeAsync === "function") { + await image.writeAsync("public/logo-text-transparent.png"); + } else if (typeof image.write === "function") { + await image.write("public/logo-text-transparent.png"); + } + console.log("Successfully created public/logo-text-transparent.png!"); +} + +run().catch(err => { + console.error("Error processing image:", err); + process.exit(1); +}); diff --git a/scripts/setup.ps1 b/scripts/setup.ps1 index 5012fbd..f06fc14 100644 --- a/scripts/setup.ps1 +++ b/scripts/setup.ps1 @@ -1,4 +1,4 @@ -# ShadowLine 0-to-100 Quick Installer & Prerequisite Auto-Installer for Windows PowerShell +# ShadowLine 0-to-100 Quick Installer and Prerequisite Auto-Installer for Windows PowerShell # # Usage (one-liner in PowerShell): # irm https://raw.githubusercontent.com/hosein-ul/ShadowLine/main/scripts/setup.ps1 | iex @@ -26,7 +26,7 @@ function Refresh-Path { $env:Path = "$machinePath;$userPath" } -# 1. Check & Auto-Install Git +# 1. Check and Auto-Install Git if (-not (Get-Command git -ErrorAction SilentlyContinue)) { Write-Host "ℹ Git not found. Attempting automatic installation..." -ForegroundColor Yellow if (Get-Command winget -ErrorAction SilentlyContinue) { @@ -44,7 +44,7 @@ if (-not (Get-Command git -ErrorAction SilentlyContinue)) { Write-Host "✔ Git is already installed ($(git --version))." -ForegroundColor Green } -# 2. Check & Auto-Install Node.js (v18+) +# 2. Check and Auto-Install Node.js (v18+) if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Write-Host "ℹ Node.js not found. Attempting automatic installation of Node.js LTS..." -ForegroundColor Yellow if (Get-Command winget -ErrorAction SilentlyContinue) { diff --git a/src/app/app/docs/_docs/content/ai-agents.tsx b/src/app/app/docs/_docs/content/ai-agents.tsx index deda46d..088ce64 100644 --- a/src/app/app/docs/_docs/content/ai-agents.tsx +++ b/src/app/app/docs/_docs/content/ai-agents.tsx @@ -79,6 +79,35 @@ const response = await generateText({ });`} /> +

4. Headless Execution Harness (`ShadowlineAgentHarness`)

+

+ To enable autonomous AI agents to execute real FHE asset shielding, confidential transfers, and balance decryption in a backend or script environment (without a web browser), use the headless execution harness in src/lib/agent-harness.ts: +

+ p.symbol === 'USDC')?.confidentialTokenAddress || pairs[0].confidentialTokenAddress; + +// Shield (wrap) public USDC into confidential cUSDC +const shieldHash = await agent.shield(usdcWrapper, '10.0'); + +// Decrypt balance via EIP-712 permit & WASM decryption +const balance = await agent.getConfidentialBalance(usdcWrapper); + +// Send confidential cUSDC (client-side WASM encryption + FHE input proof) +const transferHash = await agent.transfer(usdcWrapper, '0xRecipient', '5.0');`} + /> + Critical Rule for AI Agents: When shielding (depositing), input amounts MUST use the underlying token's decimals (e.g. 6 for USDC, 18 for WETH). When unshielding (withdrawing) or transferring, amounts MUST always use the wrapper's fixed euint64 6-decimal scale. diff --git a/src/app/app/faucet/page.tsx b/src/app/app/faucet/page.tsx index 46823b6..1769e93 100644 --- a/src/app/app/faucet/page.tsx +++ b/src/app/app/faucet/page.tsx @@ -141,7 +141,7 @@ export default function FaucetPage() { setIsRequestPending(false); const classified = classifyError(err); addToast({ - variant: 'error', + variant: classified.retryable ? 'warning' : 'error', title: classified.title, message: classified.message, }); diff --git a/src/app/app/page.tsx b/src/app/app/page.tsx index 9ccb7b1..c5980ee 100644 --- a/src/app/app/page.tsx +++ b/src/app/app/page.tsx @@ -140,6 +140,7 @@ function RegistryTokenRow({ // Fire-once: on decrypt error (incl. signature rejection), disable the query // so it can't re-fire on remount/focus. The Decrypt button re-arms itself. + const { addToast } = useToast(); useEffect(() => { if (!singleError) { decryptErrorRef.current = null; @@ -149,7 +150,9 @@ function RegistryTokenRow({ if (decryptErrorRef.current === msg) return; decryptErrorRef.current = msg; setDecryptRequested(false); - }, [singleError]); + const classified = classifyError(singleError); + addToast({ variant: 'warning', title: classified.title, message: classified.message }); + }, [singleError, addToast]); const handleDecrypt = () => { setPreferSingle(true); @@ -434,7 +437,7 @@ function DetectedTokenRow({ const decryptError = preferSingle ? singleError : (batchedError ?? singleError); const cleanName = shortName(token.name.replace(/\s*\(Mock\)\s*/gi, '').trim()); - const confidentialSymbol = `c${token.symbol}`; + const confidentialSymbol = isWrapper ? (token.symbol.startsWith('c') ? token.symbol : `c${token.symbol}`) : token.symbol; // App-wide reset — re-arm the button. useEffect(() => { @@ -445,6 +448,7 @@ function DetectedTokenRow({ }, [resetToken]); // Fire-once: on decrypt error, disable the query so it can't re-fire. + const { addToast } = useToast(); const decryptErrorRef = useRef(null); useEffect(() => { if (!singleError) { @@ -455,7 +459,9 @@ function DetectedTokenRow({ if (decryptErrorRef.current === msg) return; decryptErrorRef.current = msg; setDecryptRequested(false); - }, [singleError]); + const classified = classifyError(singleError); + addToast({ variant: 'warning', title: classified.title, message: classified.message }); + }, [singleError, addToast]); const handleDecrypt = () => { setPreferSingle(true); @@ -466,8 +472,9 @@ function DetectedTokenRow({ return (
- {/* ── Public token row ──────────────────────────────────────────────── */} -
+ {/* ── Public token row (only shown if token wraps an underlying ERC-20) ── */} + {isWrapper && ( +
{isWrapper ? (
@@ -547,14 +554,22 @@ function DetectedTokenRow({ )}
+ )} {/* ── Confidential wrapper row ──────────────────────────────────────── */}
-
{confidentialSymbol}
-
Confidential {cleanName}
+
+ {confidentialSymbol} + {!isWrapper && ( + + {token.isAutoDetected ? 'Detected' : 'Custom'} + + )} +
+
{isWrapper ? `Confidential ${cleanName}` : `${cleanName} (Standalone ERC-7984)`}
diff --git a/src/app/app/portfolio/page.tsx b/src/app/app/portfolio/page.tsx index cb15330..f60d4b4 100644 --- a/src/app/app/portfolio/page.tsx +++ b/src/app/app/portfolio/page.tsx @@ -8,7 +8,6 @@ import Modal from '@/components/ui/Modal'; import TokenIcon from '@/components/ui/TokenIcon'; import WalletActivityFeed from '@/components/WalletActivityFeed'; import { type WrapperPair } from '@/config/contracts'; -import { type CustomPairRecord } from '@/lib/registry'; import { formatAmount, formatAddress } from '@/lib/utils'; import { classifyError } from '@/lib/errors'; import PendingUnshieldBanner from '@/components/PendingUnshieldBanner'; @@ -95,7 +94,7 @@ function TokenPositionCard({
) : decryptError ? (
- {decryptError.message || 'Decryption failed.'} + {classifyError(decryptError).message}
) : isDecrypted ? (
@@ -157,11 +156,11 @@ function TokenPositionCard({ // ── Confidential-only per-row decrypt (singular hook, one per card) ────────── function ConfidentialOnlyCard({ - record, + wrapper, isConnected, resetToken, }: { - record: CustomPairRecord; + wrapper: WrapperPair; isConnected: boolean; resetToken: number; }) { @@ -172,7 +171,7 @@ function ConfidentialOnlyCard({ isLoading, error, } = useConfidentialBalance( - { tokenAddress: record.erc7984Address as `0x${string}` }, + { tokenAddress: wrapper.erc7984Address as `0x${string}` }, { enabled: decryptRequested && isConnected, retry: false, @@ -185,16 +184,20 @@ function ConfidentialOnlyCard({ if (resetToken > 0) setDecryptRequested(false); }, [resetToken]); - const wrapper: WrapperPair = { - erc20Address: record.erc20Address as `0x${string}`, - erc7984Address: record.erc7984Address as `0x${string}`, - symbol: record.symbol, - name: record.name, - decimals: record.decimals, - wrapperDecimals: record.wrapperDecimals, - source: 'custom', - isWrapper: false, - }; + const { addToast } = useToast(); + const lastErrorRef = useRef(null); + useEffect(() => { + if (!error) { + lastErrorRef.current = null; + return; + } + const msg = error.message ?? ''; + if (lastErrorRef.current === msg) return; + lastErrorRef.current = msg; + setDecryptRequested(false); + const classified = classifyError(error); + addToast({ variant: 'warning', title: classified.title, message: classified.message }); + }, [error, addToast]); return ( pairs.filter((p) => p.source !== 'custom'), [pairs]); // Custom wrapper pairs (isWrapper:true — has ERC-20 underlying, can unshield) - const customWrapperPairs = useMemo(() => pairs.filter((p) => p.source === 'custom'), [pairs]); + const customWrapperPairs = useMemo(() => pairs.filter((p) => p.source === 'custom' && p.isWrapper !== false), [pairs]); // Confidential-only custom pairs (isWrapper:false — no underlying, decrypt only) - const customConfOnly = useMemo(() => localRecords.filter((r) => r.isWrapper === false), [localRecords]); + const customConfOnly = useMemo(() => pairs.filter((p) => p.source === 'custom' && p.isWrapper === false), [pairs]); // All pairs passed to the activity feed (official + custom wrappers) const allWrappers = useMemo(() => pairs, [pairs]); @@ -519,10 +522,10 @@ export default function PortfolioPage() {
- {customConfOnly.map((record) => ( + {customConfOnly.map((wrapper) => ( diff --git a/src/app/app/transfer/page.tsx b/src/app/app/transfer/page.tsx index 8739e55..c78e51d 100644 --- a/src/app/app/transfer/page.tsx +++ b/src/app/app/transfer/page.tsx @@ -20,6 +20,7 @@ import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import Badge from '@/components/ui/Badge'; import TokenIcon from '@/components/ui/TokenIcon'; +import TokenSelect, { type TokenSelectGroup } from '@/components/ui/TokenSelect'; import { useToast } from '@/components/ui/Toast'; import TransactionSuccessModal from '@/components/ui/TransactionSuccessModal'; import { useActiveNetwork } from '@/app/ClientLayout'; @@ -417,12 +418,65 @@ export default function TransferPage() { } }, [canSubmit, selectedPair, writeContractAsync, recipientTrimmed, parsedAmount, amount, addToast]); + const rawModalSym = selectedPair?.symbol ?? selectedExtra?.symbol ?? ''; const tokenSymbolForModal = mode === 'confidential' - ? `c${selectedPair?.symbol ?? selectedExtra?.symbol ?? ''}` - : (selectedPair?.symbol ?? ''); + ? (selectedPair?.isWrapper === false || rawModalSym.startsWith('c') ? rawModalSym : `c${rawModalSym}`) + : rawModalSym; const isConfMode = mode === 'confidential'; + const tokenGroups = useMemo(() => { + const official = transferablePairs.filter((p) => p.source !== 'custom' && (isConfMode || p.isWrapper !== false)); + const custom = transferablePairs.filter((p) => p.source === 'custom' && (isConfMode || p.isWrapper !== false)); + + const groups: TokenSelectGroup[] = []; + if (official.length > 0) { + groups.push({ + label: 'Official Registry', + options: official.map((p) => { + const sym = isConfMode ? (p.isWrapper === false || p.symbol.startsWith('c') ? p.symbol : `c${p.symbol}`) : p.symbol; + return { + value: p.symbol, + symbol: sym, + name: p.name, + iconSymbol: p.symbol, + address: p.erc7984Address, + }; + }), + }); + } + if (custom.length > 0) { + groups.push({ + label: 'Custom / Dev-only', + options: custom.map((p) => { + const sym = isConfMode ? (p.isWrapper === false || p.symbol.startsWith('c') ? p.symbol : `c${p.symbol}`) : p.symbol; + return { + value: p.symbol, + symbol: sym, + name: p.name, + iconSymbol: p.symbol, + badge: { text: 'Custom', variant: 'accent' }, + address: p.erc7984Address, + }; + }), + }); + } + if (extraTokens.length > 0 && isConfMode) { + groups.push({ + label: 'Detected (unverified)', + options: extraTokens.map((t) => ({ + value: t.address, + symbol: t.symbol, + name: t.name, + iconSymbol: t.symbol, + badge: { text: 'Unverified', variant: 'warning' }, + address: t.address, + })), + }); + } + return groups; + }, [transferablePairs, extraTokens, isConfMode]); + const noTokensAvailable = !isRegistryLoading && transferablePairs.length === 0 && @@ -570,69 +624,25 @@ export default function TransferPage() { {/* Token select */}
+ + {/* Visual Decor Centerpiece */} + + + ShadowLine Crystal Shield +
+ + + {/* Scroll indicator */} @@ -165,7 +222,7 @@ function Hero() { } // ─── MARQUEE ───────────────────────────────────────────────────────────────── -const TRUST = ['FHE Encryption','ERC-7984 Standard','EIP-712 Permits','OpenZeppelin Audited','Zama Coprocessor','Non-Custodial','Sepolia Testnet','WASM ZK Prover','Zero-Gas Decrypt']; +const TRUST = ['FHE Encryption','ERC-7984 Standard','EIP-712 Permits','OpenZeppelin Audited','Zama Coprocessor','Non-Custodial','Sepolia Testnet','WASM FHE Client','Zero-Gas Decrypt']; // ─── PINNED STORYTELLING ───────────────────────────────────────────────────── function PinnedStory() { @@ -444,11 +501,11 @@ function Architecture() {
{[ - { label:'Your Browser', sub:'Client-side', icon:Globe, items:['FHE-encrypt amount','Generate ZK proof','Sign EIP-712 permit'], color:'#3b82f6' }, + { label:'Your Browser', sub:'Client-side', icon:Globe, items:['FHE-encrypt amount','Generate input proof','Sign EIP-712 permit'], color:'#3b82f6' }, null, { label:'Ethereum FHEVM', sub:'On-chain', icon:Database, items:['Store euint64 handles','Emit FHE events','Manage ACL'], color:'#8b5cf6' }, null, - { label:'Zama Coprocessor',sub:'Off-chain FHE',icon:Cpu, items:['Execute FHE arithmetic','Validate ZK proofs','Publish results'], color:'#FFD208' }, + { label:'Zama Coprocessor',sub:'Off-chain FHE',icon:Cpu, items:['Execute FHE arithmetic','Verify ACL permissions','Publish results'], color:'#FFD208' }, ].map((item, i) => { if (!item) return
; return ( @@ -695,67 +752,139 @@ export const CUSTOM_PAIRS: CustomPair[] = [ } function PoweredBy() { + const { theme } = useLandingTheme(); + const isDark = theme === 'dark'; + const partners = [ { name: 'Zama', logo: '/brands/zama-logo.svg', desc: 'FHE confidential computing protocol', - cardBg: '#FFD208', + badge: 'CORE FHE COPROCESSOR', + cardBg: isDark ? '#18181f' : '#fff', + textColor: isDark ? '#fff' : '#000', + descColor: isDark ? '#a1a1aa' : '#71717a', + shadow: isDark ? '0 14px 40px rgba(255,210,8,.2)' : '0 14px 40px rgba(255,210,8,.35)', href: 'https://www.zama.org', }, { name: 'Blockscout', logo: '/brands/blockscout-logo.svg', - desc: 'Open-source block explorer', - cardBg: '#fff', - href: 'https://www.blockscout.com', + desc: 'Open-source block explorer & indexer', + badge: 'VERIFIED EXPLORER', + cardBg: isDark ? '#18181f' : '#fff', + textColor: isDark ? '#fff' : '#000', + descColor: isDark ? '#a1a1aa' : '#71717a', + shadow: isDark ? '0 10px 35px rgba(0,0,0,.4)' : '0 10px 35px rgba(0,0,0,.05)', + border: isDark ? '1px solid rgba(255,255,255,.1)' : '1px solid #e4e4e7', }, { name: 'OpenZeppelin', logo: '/brands/openzeppelin-logo.svg', desc: 'Audited smart contract standards', - cardBg: '#fff', - href: 'https://www.openzeppelin.com', + badge: 'SECURITY STANDARD', + cardBg: isDark ? '#18181f' : '#fff', + textColor: isDark ? '#fff' : '#000', + descColor: isDark ? '#a1a1aa' : '#71717a', + shadow: isDark ? '0 10px 35px rgba(0,0,0,.4)' : '0 10px 35px rgba(0,0,0,.05)', + border: isDark ? '1px solid rgba(255,255,255,.1)' : '1px solid #e4e4e7', }, ]; return ( -
-
+
+
-

- Built on Trusted Infrastructure -

+
+ + Canonical Infrastructure + +

+ Powered by Verified Web3 Standards +

+
-
- {partners.map((p, i) => ( - - - {`${p.name} - +
+ {partners.map((p, i) => { + const isZama = p.name === 'Zama'; + + const cardContent = ( + <> + + {p.badge} + + {`${p.name} + {p.desc} - - - ))} + + ); + + if (isZama) { + return ( + + + + {cardContent} + + + + ); + } + + return ( + + + {cardContent} + + + ); + })}
@@ -932,7 +1061,7 @@ function FaqAccordions() { }, { q: 'Are there gas fee differences when using cTokens?', - a: 'Yes, because FHE arithmetic and zero-knowledge proof verifications are computationally heavy. However, ShadowLine routes computationally intense operations off-chain to a Zama Coprocessor. The coprocessor processes the FHE logic and returns a verified state update, keeping gas fees comparable to standard public token transactions.' + a: 'Yes, because FHE arithmetic and ciphertext processing are computationally heavy. However, ShadowLine routes computationally intense operations off-chain to a Zama Coprocessor. The coprocessor processes the FHE logic and returns a verified state update, keeping gas fees comparable to standard public token transactions.' } ]; @@ -1011,134 +1140,232 @@ function FaqAccordions() { ); } +// ─── VISUAL DECOR BANNER 1 ────────────────────────────────────────────────── +function VisualAuroraBanner() { + return ( +
+
+ + + ShadowLine Interlocking Rings +
+
+ On-Chain Privacy +

+ Encrypted in motion.
Protected at rest. +

+

+ ShadowLine transforms public ERC-20 tokens into confidential on-chain ciphertexts, giving you institutional-grade privacy without compromising self-custody. +

+
+ + +
+
+ ); +} + +// ─── VISUAL DECOR BANNER 2 ────────────────────────────────────────────────── +function VisualSculptureBanner() { + return ( +
+
+ + + ShadowLine Sculpture +
+
+ Canonical Standard +

+ Mathematical precision.
Zero fragmentation. +

+

+ Built on top of OpenZeppelin and Zama FHEVM, ShadowLine eliminates liquidity fragmentation with a single, unified wrapper interface. +

+
+ + +
+
+ ); +} + +// ─── VISUAL DECOR BANNER 3 ────────────────────────────────────────────────── +function VisualTokenBanner() { + return ( +
+
+ + + ShadowLine Confidential Tokens +
+
+ Confidential Assets +

+ Shielded balances.
Verifiable transfers. +

+

+ Experience standard ERC-20 composability with the security of Fully Homomorphic Encryption (FHE) token wrappers. +

+
+ + +
+
+ ); +} + // ─── ROOT ───────────────────────────────────────────────────────────────────── export default function LandingPage() { + const [theme, setTheme] = useState<'light' | 'dark'>('light'); + useEffect(() => { + try { + const saved = localStorage.getItem('theme') as 'light' | 'dark' | null; + if (saved) { + setTheme(saved); + document.documentElement.setAttribute('data-theme', saved); + } + } catch (e) {} + }, []); + const toggleTheme = () => { + const next = theme === 'dark' ? 'light' : 'dark'; + setTheme(next); + try { + localStorage.setItem('theme', next); + document.documentElement.setAttribute('data-theme', next); + } catch (e) {} + }; + + const isDark = theme === 'dark'; + return ( -
- - - -
- - {TRUST.map(item =>
{item}
)} -
-
- - - - - - - {/* 1. Enterprise Use Cases */} - - - - - - - {/* 2. Security & Compliance */} - - - {/* 3. FAQs */} - - - - - - -
-
- {/* Column 1: About ShadowLine */} -
-
-
- + +
+ + + +
+ + {TRUST.map(item =>
{item}
)} +
+
+ + + + + + + + + {/* 1. Enterprise Use Cases */} + + + + + + + + + {/* 2. Security & Compliance */} + + + {/* 3. FAQs */} + + + + +
+
+ {/* Column 1: About ShadowLine */} +
+
+ ShadowLine Logo + ShadowLine
- ShadowLine +

+ ShadowLine is a privacy-first asset shielding protocol built on Zama's FHEVM. We empower users and enterprises to shield, transfer, and interact with ERC-20 tokens confidentially, keeping financial data protected and on-chain. +

-

- ShadowLine is a privacy-first asset shielding protocol built on Zama's FHEVM. We empower users and enterprises to shield, transfer, and interact with ERC-20 tokens confidentially, keeping financial data protected and on-chain. -

-
- {/* Column 2: Product */} -
-

Product

-
- {[ - { l: 'Dashboard', h: '/app' }, - { l: 'Shield & Unshield', h: '/app/wrapper' }, - { l: 'Portfolio Manager', h: '/app/portfolio' }, - { l: 'Token Faucet', h: '/app/faucet' } - ].map(link => ( - - {link.l} - - ))} + {/* Column 2: Product */} +
+

Product

+
+ {[ + { l: 'Dashboard', h: '/app' }, + { l: 'Shield & Unshield', h: '/app/wrapper' }, + { l: 'Portfolio Manager', h: '/app/portfolio' }, + { l: 'Token Faucet', h: '/app/faucet' } + ].map(link => ( + + {link.l} + + ))} +
-
- {/* Column 3: Resources */} -
-

Resources

-
- {[ - { l: 'Developer Docs', h: '/app/docs' }, - { l: 'Zama Protocol', h: 'https://docs.zama.org/protocol' }, - { l: 'Security Model', h: 'https://docs.zama.org/protocol/sdk/concepts/security-model' }, - { l: 'GitHub Repository', h: 'https://github.com/hosein-ul/ShadowLine' } - ].map(link => { - if (link.h.startsWith('http')) { - return ( - - {link.l} - - ); - } else { - return ( - - {link.l} - - ); - } - })} + {/* Column 3: Resources */} +
+

Resources

+
+ {[ + { l: 'Developer Docs', h: '/app/docs' }, + { l: 'Zama Protocol', h: 'https://docs.zama.org/protocol' }, + { l: 'Security Model', h: 'https://docs.zama.org/protocol/sdk/concepts/security-model' }, + { l: 'GitHub Repository', h: 'https://github.com/hosein-ul/ShadowLine' } + ].map(link => { + if (link.h.startsWith('http')) { + return ( + + {link.l} + + ); + } else { + return ( + + {link.l} + + ); + } + })} +
-
- {/* Column 4: Technology */} -
-

Technology

-
- {[ - { l: 'Zama FHEVM', h: 'https://docs.zama.org/protocol' }, - { l: 'ERC-7984 Standard', h: '/app/docs/fhe' }, - { l: 'FHE Coprocessors', h: '/app/docs/architecture' }, - { l: 'EIP-712 Permits', h: '/app/docs/permits' } - ].map(link => { - if (link.h.startsWith('http')) { - return ( - - {link.l} - - ); - } else { - return ( - - {link.l} - - ); - } - })} + {/* Column 4: Technology */} +
+

Technology

+
+ {[ + { l: 'Zama FHEVM', h: 'https://docs.zama.org/protocol' }, + { l: 'ERC-7984 Standard', h: '/app/docs/fhe' }, + { l: 'FHE Coprocessors', h: '/app/docs/architecture' }, + { l: 'EIP-712 Permits', h: '/app/docs/permits' } + ].map(link => { + if (link.h.startsWith('http')) { + return ( + + {link.l} + + ); + } else { + return ( + + {link.l} + + ); + } + })} +
-
- {/* Footer bottom bar */} -
- © {new Date().getFullYear()} ShadowLine. All rights reserved. Built on Zama FHEVM. - Released under the MIT License. -
-
-
+ {/* Footer bottom bar */} +
+ © {new Date().getFullYear()} ShadowLine. All rights reserved. Built on Zama FHEVM. + Released under the MIT License. +
+
+
+ ); } diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 0473a86..fc9a860 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -97,13 +97,9 @@ export default function Header() {
{/* Logo — links to the marketing landing page, not the dApp */} - - - - - - Shadow - Line + + ShadowLine Logo + ShadowLine {/* Navigation — desktop only; mobile uses the hamburger drawer below */} @@ -322,14 +318,10 @@ export default function Header() { setIsMobileMenuOpen(false)} > - - - - - Shadow - Line + ShadowLine Logo + + {/* Dropdown Menu (Always positioned downwards with maxHeight!) */} + {isOpen && ( +
+ {/* Search bar (only if > 4 total items) */} + {totalOptions > 4 && ( +
+
+ + setSearchQuery(e.target.value)} + style={{ + width: '100%', + background: 'transparent', + border: 'none', + color: 'var(--text-primary)', + fontSize: '13px', + outline: 'none', + padding: 0, + }} + /> +
+
+ )} + + {/* Options list */} +
+ {filteredGroups.length === 0 ? ( +
+ No tokens match “{searchQuery}” +
+ ) : ( + filteredGroups.map((group, gIdx) => ( +
+
+ {group.label} +
+
+ {group.options.map((opt) => { + const isSelected = opt.value === value; + return ( +
{ + onChange(opt.value); + setIsOpen(false); + }} + style={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + gap: 10, + padding: '8px 10px', + borderRadius: 'var(--radius-md)', + cursor: 'pointer', + background: isSelected ? 'rgba(56, 189, 248, 0.12)' : 'transparent', + transition: 'background 150ms ease', + }} + onMouseEnter={(e) => { + if (!isSelected) e.currentTarget.style.background = 'rgba(255, 255, 255, 0.05)'; + }} + onMouseLeave={(e) => { + if (!isSelected) e.currentTarget.style.background = 'transparent'; + }} + > +
+ +
+
+ + {opt.symbol} + + {opt.badge && ( + + {opt.badge.text} + + )} +
+ + {opt.name} + +
+
+ {isSelected && } +
+ ); + })} +
+
+ )) + )} +
+
+ )} +
+ ); +} diff --git a/src/components/ui/TypingAnimation.tsx b/src/components/ui/TypingAnimation.tsx index a2d1137..573021d 100644 --- a/src/components/ui/TypingAnimation.tsx +++ b/src/components/ui/TypingAnimation.tsx @@ -19,6 +19,11 @@ export default function TypingAnimation({ const [currentText, setCurrentText] = useState(''); const [isDeleting, setIsDeleting] = useState(false); + const longestWord = React.useMemo(() => { + if (!words || words.length === 0) return ''; + return words.reduce((a, b) => (a.length > b.length ? a : b), words[0] || ''); + }, [words]); + useEffect(() => { let timer: NodeJS.Timeout; const currentWord = words[currentWordIndex]; @@ -54,17 +59,50 @@ export default function TypingAnimation({ }, [currentText, isDeleting, currentWordIndex, words, typingSpeed, deletingSpeed, delayBetweenWords]); return ( - - {currentText} + + {/* Ghost text to reserve width dynamically for the longest word */} + + {longestWord} + + + + {/* Absolutely positioned actual animated text */} + > + {currentText} + +