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
23 changes: 5 additions & 18 deletions wallet/entrypoints/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import { createPublicClient, http, parseEther, formatEther, type TypedDataDefinition } from "viem";
import { Keyring } from "../src/keyring/keyring";
import { parseTypedData } from "../src/provider/typed-data";
import { WorkerOperator, NETWORKS, type MinimalPublicClient } from "lightnode-sdk";
import { readWorkerStatus } from "../src/rpc/worker";
import { encryptVault, decryptVault, type EncryptedVault } from "../src/keyring/vault";
import { chainById, lightchainMainnet } from "../src/rpc/chains";
import { type BgMessage, type WalletOp, type JsonRpcRequest, RpcError } from "../src/provider/protocol";
Expand Down Expand Up @@ -98,23 +98,10 @@ async function handleWalletOp(op: WalletOp): Promise<unknown> {
const wei = await publicClient().getBalance({ address: op.address as `0x${string}` });
return { wei: wei.toString(), lcai: formatEther(wei) };
}
case "workerStatus": {
// Read-only worker registry/stake lookup via the SDK. No key needed; we
// return only number/bool fields (chrome.runtime can't structured-clone bigint).
const wo = new WorkerOperator(NETWORKS.mainnet, {
publicClient: publicClient() as unknown as MinimalPublicClient,
workerAddress: op.address as `0x${string}`,
});
const s = await wo.status();
return {
registered: s.registered,
belowFloor: s.belowFloor,
stakeLcai: s.stakeLcai,
minStakeLcai: Number(s.minStakeWei) / 1e18,
headroomLcai: s.headroomLcai,
claimableLcai: s.claimableLcai,
};
}
case "workerStatus":
// Read-only registry/stake lookup (no key). Already returns number/bool
// fields only, so it survives chrome.runtime's structured clone (no bigint).
return readWorkerStatus(publicClient(), op.address as `0x${string}`);
case "send": {
const kr = await restore();
const acct = kr?.accountFor(op.from);
Expand Down
9 changes: 1 addition & 8 deletions wallet/entrypoints/popup/wallet-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,4 @@ export interface PendingRequest {
origin: string;
params?: unknown[];
}
export interface WorkerStatusView {
registered: boolean;
belowFloor: boolean;
stakeLcai: number;
minStakeLcai: number;
headroomLcai: number;
claimableLcai: number;
}
export type { WorkerStatusView } from "../../src/rpc/worker";
6 changes: 1 addition & 5 deletions wallet/package-lock.json

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

4 changes: 0 additions & 4 deletions wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
"type": "module",
"description": "Self-custodial EOA browser wallet for LightChain. Non-custodial: keys never leave the device. Not an official LightChain package.",
"scripts": {
"build:sdk": "npm --prefix ../sdk install --no-audit --no-fund && npm --prefix ../sdk run build",
"predev": "npm run build:sdk",
"dev": "wxt",
"prebuild": "npm run build:sdk",
"build": "wxt build",
"zip": "wxt zip",
"compile": "tsc --noEmit",
Expand All @@ -19,7 +16,6 @@
"@noble/hashes": "^1.6.1",
"@scure/bip32": "^1.6.2",
"@scure/bip39": "^1.5.4",
"lightnode-sdk": "file:../sdk",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"viem": "^2.21.0"
Expand Down
48 changes: 48 additions & 0 deletions wallet/src/rpc/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Worker-status reads, inlined with viem so the wallet has ZERO build-time
* dependency on the lightnode-sdk monorepo package (which needs the repo's
* hoisted @types/node to compile). Same 4 calls the SDK's WorkerOperator.status()
* makes, against the LightChain mainnet worker contracts.
*/
import { type PublicClient, parseAbi } from "viem";

// LightChain mainnet: WorkerRegistry is a genesis predeploy; AIConfig + JobRegistry are proxies.
const WORKER_REGISTRY = "0x0000000000000000000000000000000000001002" as const;
const AI_CONFIG = "0x24D11533C354092ed6E18b964257819cE78Ce77D" as const;
const JOB_REGISTRY = "0xfB15F90298e4CcD7106E76fFB5e520315cC42B0b" as const;

const REGISTRY_ABI = parseAbi([
"function isWorkerRegistered(address) view returns (bool)",
"function getWorkerStake(address) view returns (uint256)",
]);
const AI_CONFIG_ABI = parseAbi(["function getMinWorkerStake() view returns (uint256)"]);
const JOB_REGISTRY_ABI = parseAbi(["function workerBalance(address) view returns (uint256)"]);

export interface WorkerStatusView {
registered: boolean;
belowFloor: boolean;
stakeLcai: number;
minStakeLcai: number;
headroomLcai: number;
claimableLcai: number;
}

const toLcai = (wei: bigint) => Number(wei) / 1e18;

export async function readWorkerStatus(client: PublicClient, address: `0x${string}`): Promise<WorkerStatusView> {
// minStake comes from AIConfig (canonical on both networks), not the registry getter.
const [registered, stakeWei, minStakeWei, claimableWei] = await Promise.all([
client.readContract({ address: WORKER_REGISTRY, abi: REGISTRY_ABI, functionName: "isWorkerRegistered", args: [address] }),
client.readContract({ address: WORKER_REGISTRY, abi: REGISTRY_ABI, functionName: "getWorkerStake", args: [address] }),
client.readContract({ address: AI_CONFIG, abi: AI_CONFIG_ABI, functionName: "getMinWorkerStake" }),
client.readContract({ address: JOB_REGISTRY, abi: JOB_REGISTRY_ABI, functionName: "workerBalance", args: [address] }),
]);
return {
registered,
belowFloor: registered && stakeWei < minStakeWei,
stakeLcai: toLcai(stakeWei),
minStakeLcai: toLcai(minStakeWei),
headroomLcai: toLcai(stakeWei - minStakeWei),
claimableLcai: toLcai(claimableWei),
};
}
Loading