Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/wallet-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Wallet release

# Builds the prebuilt extension zip and attaches it to a GitHub Release so the
# website's "Download the wallet" button always serves a current build. The
# wallet is self-contained (no SDK build step), so this is a plain npm build.
on:
push:
tags: ["wallet-v*"]
workflow_dispatch:

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Build + zip the extension
working-directory: wallet
run: |
npm install --no-audit --no-fund
npm run build
npm run zip
- name: Stable asset name
run: cp wallet/.output/*-chrome.zip lightnode-wallet-chrome.zip
- name: Publish to release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ github.ref_type == 'tag' && github.ref_name || 'wallet-latest' }}"
if gh release view "$TAG" >/dev/null 2>&1; then
gh release upload "$TAG" lightnode-wallet-chrome.zip --clobber
else
gh release create "$TAG" lightnode-wallet-chrome.zip \
--title "LightNode Wallet ($TAG)" \
--notes "Prebuilt LightChain wallet extension (Chrome MV3). Unzip and Load unpacked in chrome://extensions."
fi
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ next-env.d.ts
# SDK build output
sdk/dist

# Guard against an accidental nested clone of this repo (git clone run inside it)
/lightnode/

# Wallet build output (WXT)
wallet/.output
wallet/.wxt

# Local-only owner docs (strategy/QA/capabilities)
QA-AND-BUSINESS.md
CAPABILITIES.md
Expand Down
48 changes: 29 additions & 19 deletions app/wallet/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import Link from "next/link";
import { Server, Sparkles, Landmark, ArrowLeftRight, Fuel, ShieldCheck, Download, KeyRound, Github, Lock } from "lucide-react";

export const metadata: Metadata = {
title: "LightChain Wallet - self-custodial wallet for LightChain",
title: "LightNode Wallet - a self-custodial wallet for the LightChain ecosystem",
description:
"A self-custodial browser wallet built for LightChain: worker monitoring, encrypted AI inference, and DAO intelligence. Your keys never leave your device. No smart contract, no custody.",
"LightNode Wallet: a self-custodial home for your LCAI, your worker, encrypted AI inference, and DAO governance on LightChain. Your keys never leave your device. Independent and community-built.",
};

const REPO = "https://github.com/marinom2/lightnode/tree/main/wallet";
const DOWNLOAD_URL = "https://github.com/marinom2/lightnode/releases/latest/download/lightnode-wallet-chrome.zip";

const FEATURES: { icon: typeof Server; title: string; body: string; status: "live" | "soon" }[] = [
{ icon: ShieldCheck, title: "Self-custodial", body: "Keys are generated and encrypted on your device with AES-256-GCM + scrypt. They never leave it. No server, no custody, no smart contract.", status: "live" },
{ icon: Fuel, title: "Gas done right", body: "LightChain fees are negligible, so the wallet drops the gwei sliders and scary fee modals. One tap, fee shown as what it is: nothing.", status: "live" },
{ icon: Server, title: "Worker control", body: "See if your address is a registered worker, its stake, headroom, and claimable rewards - and stake or top up in one click. No other wallet knows LightChain workers exist.", status: "soon" },
{ icon: Server, title: "Worker control", body: "Your worker, in your wallet: registration, stake, headroom, and claimable rewards at a glance, then stake or top up in a tap.", status: "soon" },
{ icon: Sparkles, title: "Encrypted AI inference", body: "Ask an AI a question and pay per call from your own key, end-to-end encrypted and settled on-chain - right inside the wallet.", status: "soon" },
{ icon: Landmark, title: "DAO intelligence", body: "Decoded proposals, quorum distance, and your voting power, surfaced from the registries. Vote on the official DAO in a tap.", status: "soon" },
{ icon: ArrowLeftRight, title: "Built-in bridge", body: "Move LCAI between Ethereum and LightChain without leaving the wallet, over the Hyperlane warp route.", status: "soon" },
Expand Down Expand Up @@ -44,19 +45,22 @@ export default function WalletPage() {
<div className="mx-auto max-w-5xl px-5 py-12">
{/* hero */}
<section className="text-center">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary">Self-custodial · LightChain</p>
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary">Self-custodial · Independent</p>
<h1 className="mt-3 text-4xl font-bold tracking-tight text-content-primary sm:text-5xl">
The <span className="text-gradient">LightChain Wallet</span>
<span className="text-gradient">LightNode</span> Wallet
</h1>
<p className="mx-auto mt-4 max-w-2xl text-lg text-content-soft">
A browser wallet built for LightChain - worker monitoring, encrypted AI, and DAO intelligence. Like Phantom, it is a pure self-custodial wallet: <span className="text-content-primary">no smart contract, no custody, and your keys never leave your device.</span>
A self-custodial wallet for the LightChain ecosystem. Hold your LCAI, monitor your worker, run encrypted AI inference, and read the DAO - all in one place. <span className="text-content-primary">Your keys never leave your device.</span>
</p>
<div className="mt-7 flex flex-wrap items-center justify-center gap-3">
<Link href="#install" className="inline-flex items-center gap-2 rounded-xl bg-gradient-primary bg-[length:200%_auto] bg-[position:left_center] px-5 py-3 text-sm font-semibold text-white shadow-[0_2px_18px_-4px_rgba(112,100,233,0.7)] transition-all hover:bg-[position:right_center] hover:brightness-110">
<Download className="size-4" /> Install it
<a href={DOWNLOAD_URL} className="inline-flex items-center gap-2 rounded-xl bg-gradient-primary bg-[length:200%_auto] bg-[position:left_center] px-5 py-3 text-sm font-semibold text-white shadow-[0_2px_18px_-4px_rgba(112,100,233,0.7)] transition-all hover:bg-[position:right_center] hover:brightness-110">
<Download className="size-4" /> Download the wallet
</a>
<Link href="#install" className="inline-flex items-center gap-2 rounded-xl border border-bdr-soft px-5 py-3 text-sm font-semibold text-content-soft transition-colors hover:border-primary/40 hover:text-content-primary">
How to install
</Link>
<a href={REPO} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-2 rounded-xl border border-bdr-soft px-5 py-3 text-sm font-semibold text-content-soft transition-colors hover:border-primary/40 hover:text-content-primary">
<Github className="size-4" /> View source
<Github className="size-4" /> Source
</a>
</div>
<div className="mt-5 flex flex-wrap items-center justify-center gap-2 text-[11px] text-content-soft">
Expand Down Expand Up @@ -84,29 +88,35 @@ export default function WalletPage() {

{/* install */}
<section id="install" className="mt-16 scroll-mt-20 rounded-2xl border border-bdr-soft bg-card/50 p-6 backdrop-blur-sm sm:p-8">
<h2 className="text-xl font-semibold text-content-primary">Install it today</h2>
<h2 className="text-xl font-semibold text-content-primary">Install it (no build needed)</h2>
<p className="mt-1 text-sm text-content-soft">
The wallet is open source and runs from source while it goes through a security audit. A Chrome Web Store listing follows the audit. Until then, load it unpacked in ~2 minutes:
Grab the prebuilt extension and load it in under a minute. A one-click Chrome Web Store listing lands after the security audit.
</p>
<div className="mt-6 space-y-5">
<Step n={1} title="Get the code and build it">
<pre className="mt-1 overflow-x-auto rounded-lg border border-bdr-soft bg-surface-base-faint p-3 font-mono text-xs leading-relaxed text-content-default">{`git clone https://github.com/marinom2/lightnode
cd lightnode/wallet
npm install
npm run build`}</pre>
<p className="mt-1.5 text-[12px]">Produces a loadable extension at <code className="rounded bg-surface-base-faint px-1 py-0.5 font-mono">wallet/.output/chrome-mv3</code>.</p>
<Step n={1} title="Download the prebuilt wallet">
<a href={DOWNLOAD_URL} className="mt-1 inline-flex items-center gap-2 rounded-lg bg-gradient-primary px-3.5 py-2 text-xs font-semibold text-white shadow-[0_2px_10px_-2px_rgba(112,100,233,0.6)] transition-all hover:brightness-110">
<Download className="size-3.5" /> lightnode-wallet-chrome.zip
</a>
<p className="mt-1.5 text-[12px]">Then unzip it anywhere - no Node, no build.</p>
</Step>
<Step n={2} title="Load it into Chrome">
Open <code className="rounded bg-surface-base-faint px-1 py-0.5 font-mono">chrome://extensions</code>, enable <span className="text-content-primary">Developer mode</span> (top right), click <span className="text-content-primary">Load unpacked</span>, and select the <code className="rounded bg-surface-base-faint px-1 py-0.5 font-mono">wallet/.output/chrome-mv3</code> folder.
Open <code className="rounded bg-surface-base-faint px-1 py-0.5 font-mono">chrome://extensions</code>, enable <span className="text-content-primary">Developer mode</span> (top right), click <span className="text-content-primary">Load unpacked</span>, and select the unzipped folder.
</Step>
<Step n={3} title="Create or import your wallet">
Pin the extension, open it, and create a new 24-word wallet (or import one). The recovery phrase is shown once and encrypted on your device - write it down; nobody, including us, can recover it.
</Step>
</div>
<p className="mt-6 flex items-start gap-2 rounded-xl border border-primary/25 bg-primary/6 p-3 text-[12px] text-content-soft">
<KeyRound className="mt-0.5 size-4 shrink-0 text-primary" />
Once installed, the wallet appears automatically in dApp connect dialogs alongside MetaMask (via EIP-6963) - no need to overwrite anything.
Once installed, the wallet appears automatically in dApp connect dialogs via EIP-6963 - it coexists cleanly with any other wallet you have.
</p>
<details className="mt-4 text-sm text-content-soft">
<summary className="cursor-pointer font-medium text-content-primary">Prefer to build from source? (developers)</summary>
<pre className="mt-2 overflow-x-auto rounded-lg border border-bdr-soft bg-surface-base-faint p-3 font-mono text-xs leading-relaxed text-content-default">{`git clone https://github.com/marinom2/lightnode
cd lightnode/wallet
npm install
npm run build # outputs wallet/.output/chrome-mv3`}</pre>
</details>
</section>

{/* security */}
Expand Down
2 changes: 1 addition & 1 deletion wallet/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# LightChain Wallet
# LightNode Wallet

A **self-custodial** browser wallet for LightChain (EVM L1, chain 9200; testnet 8200). Like Phantom/MetaMask, it is a pure client-side **EOA wallet** - there is **no smart contract**, no relayer, and no server. Your keys are generated and encrypted **on your device and never leave it**. We are not an exchange and never custody funds.

Expand Down
10 changes: 5 additions & 5 deletions wallet/entrypoints/inpage.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { PAGE_TO_CONTENT, CONTENT_TO_PAGE, type ContentMessage } from "../src/provider/protocol";

// Injected into the page's MAIN world. Exposes a standard EIP-1193 provider and
// announces it via EIP-6963 so dapps can pick "LightChain Wallet" ALONGSIDE
// MetaMask. We do not overwrite window.ethereum (only set it if nothing else has).
// announces it via EIP-6963 so dapps can pick "LightNode Wallet" alongside any other wallet.
// We do not overwrite window.ethereum (only set it if nothing else has).
type Handler = (args: unknown) => void;

function createProvider() {
Expand All @@ -22,7 +22,7 @@ function createProvider() {
});

const provider = {
isLightChainWallet: true,
isLightNodeWallet: true,
request({ method, params }: { method: string; params?: unknown[] }): Promise<unknown> {
if (typeof method !== "string") return Promise.reject({ code: -32602, message: "Invalid params" });
const id = nextId++;
Expand All @@ -46,9 +46,9 @@ function createProvider() {
function announce(provider: ReturnType<typeof createProvider>) {
const info = {
uuid: crypto.randomUUID(),
name: "LightChain Wallet",
name: "LightNode Wallet",
icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTYiIGZpbGw9IiM3MDY0ZTkiLz48L3N2Zz4=",
rdns: "ai.lightchain.wallet",
rdns: "app.lightnode.wallet",
};
const emit = () => window.dispatchEvent(new CustomEvent("eip6963:announceProvider", { detail: Object.freeze({ info, provider }) }));
window.addEventListener("eip6963:requestProvider", emit);
Expand Down
2 changes: 1 addition & 1 deletion wallet/entrypoints/popup/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function App() {
function Shell({ children }: { children: React.ReactNode }) {
return (
<div className="wrap">
<div className="brand"><span className="dot" /> LightChain Wallet</div>
<div className="brand"><span className="dot" /> LightNode Wallet</div>
{children}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion wallet/entrypoints/popup/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LightChain Wallet</title>
<title>LightNode Wallet</title>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions wallet/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion wallet/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "lightchain-wallet",
"name": "lightnode-wallet",
"private": true,
"version": "0.1.0",
"type": "module",
Expand Down
4 changes: 2 additions & 2 deletions wallet/wxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { defineConfig } from "wxt";
import { fileURLToPath } from "node:url";

// LightChain Wallet - self-custodial EOA extension. Keys never leave the device.
// LightNode Wallet - self-custodial EOA extension. Keys never leave the device.
// The inpage provider is web-accessible (required to inject into the MAIN world);
// use_dynamic_url randomizes its URL per session to blunt wallet fingerprinting.
export default defineConfig({
modules: ["@wxt-dev/module-react"],
alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) },
manifest: {
name: "LightChain Wallet",
name: "LightNode Wallet",
description: "Self-custodial wallet for LightChain. Your keys never leave this device.",
minimum_chrome_version: "120",
permissions: ["storage", "alarms", "notifications"],
Expand Down
Loading