0Gkitdocs↗ HomePlaygroundGitHub

@foundryprotocol/0gkit-core

The neutral 0G foundation: network presets, a viem client factory, the Receipt envelope, the ZeroGError taxonomy, and canonical JSON.

What it does

core is the shared base every other primitive builds on. It has zero runtime dependencies beyond viem. It owns four things: the network presets (aristotle, galileo, local), the createClient viem factory, the uniform Receipt shape, and the ZeroGError error taxonomy. It also provides the deterministic JSON digest used by DA and Attestation.

When to use it

  • Always — it is a transitive dependency of every primitive. You use it directly when you need a viem client, want to read a network preset, or want to catch ZeroGError.
  • When you want raw viem access to 0G: createClient(...).public is a full viem PublicClient.

Where to use it

Node scripts, servers, edge runtimes, the browser — it is pure TypeScript + viem with no Node-only dependency. (The Node-only 0G SDK peers live in storage/compute, not here.)

Install

npm install @foundryprotocol/0gkit-core viem

API reference

networks / aristotle / galileo / local

const aristotle: NetworkPreset; // mainnet, chainId 16661
const galileo: NetworkPreset; // testnet, chainId 16602 (the default everywhere)
const local: NetworkPreset; // local Anvil, chainId 31337
const networks: Record<NetworkName, NetworkPreset>;

NetworkName is "aristotle" | "galileo" | "local". See Getting started → networks for the resolved field values.

getNetwork(name)

function getNetwork(name: NetworkName): NetworkPreset;

Returns the preset for name. Throws ConfigError if name is not a known network (the hint lists the valid names).

type NetworkPreset

interface NetworkPreset {
  readonly name: NetworkName;
  readonly chainId?: number; // undefined ⇒ createClient throws ConfigError
  readonly rpcUrl?: string; // undefined ⇒ createClient throws ConfigError
  readonly explorer?: string; // undefined ⇒ explorerUrl() throws
  readonly faucetUrl?: string; // programmatic faucet endpoint (testnet)
  readonly faucetWebUrl?: string; // human faucet page, used in faucet()'s hint
  readonly testnet: boolean;
}

createClient(options)

interface CreateClientOptions {
  network: NetworkName;
  rpcUrl?: string; // overrides the preset RPC (required if preset has none)
  chainId?: number; // overrides the preset chain id
  privateKey?: string; // leading 0x optional; when set, a wallet is returned
}
interface ZeroGClient {
  network: NetworkPreset;
  public: PublicClient; // viem
  wallet?: WalletClient; // viem, only if privateKey was passed
}
function createClient(opts: CreateClientOptions): ZeroGClient;

Throws ConfigError when the preset has no rpcUrl/chainId and none was passed, or when privateKey is not a valid 32-byte hex string.

buildChain(preset, rpcUrl?, chainId?)

function buildChain(preset: NetworkPreset, rpcUrl?: string, chainId?: number): Chain; // a viem Chain

The lower-level helper createClient uses internally. Throws ConfigError if neither the preset nor the override supplies a rpcUrl / chainId.

type Receipt

The uniform result envelope. See Concepts → Receipt for the full shape and why txHash is a 0x-hex-or-string union.

Errors: ZeroGError, ConfigError, NetworkError, ChainError, AttestationError

type ZeroGErrorCode = "CONFIG" | "NETWORK" | "CHAIN" | "ATTESTATION";

class ZeroGError extends Error {
  readonly code: ZeroGErrorCode;
  readonly hint: string;
  constructor(code: ZeroGErrorCode, message: string, hint: string);
}
class ConfigError extends ZeroGError {} // code "CONFIG"
class NetworkError extends ZeroGError {} // code "NETWORK"
class ChainError extends ZeroGError {} // code "CHAIN"
class AttestationError extends ZeroGError {} // code "ATTESTATION"

Full taxonomy and "thrown when" table: Concepts → the ZeroGError taxonomy.

canonicalJsonStringify(value) / digestJson(value)

function canonicalJsonStringify(value: unknown): string; // deterministic JSON
function digestJson(value: unknown): Hex; // keccak256 of the canonical JSON

digestJson is the cross-package, on-chain digest anchor used by DA and Attestation.

define0GConfig({ server?, client?, edge? })

import { define0GConfig } from "@foundryprotocol/0gkit-core";
import { z } from "zod";

export const config = define0GConfig({
  server: {
    ZEROG_NETWORK: z.enum(["galileo", "aristotle", "local"]).default("galileo"),
    PRIVATE_KEY: z.string().min(64).describe("Funds 0G transactions."),
  },
  client: {
    NEXT_PUBLIC_ZEROG_NETWORK: z.string().default("galileo"),
  },
});

const env = config.server(); // throws ConfigError on missing / malformed env
console.log(config.envExample()); // emits a stringified .env.example

Typed env reader with three slots — server (Node-only, full process.env), client (browser-safe, prefix-enforced NEXT_PUBLIC_*), edge (no process.env fallback). Validation failures throw ConfigError. Every 0gkit template ships a 0g.config.ts that calls this.

detectLocalDevnet({ rpcUrl?, timeoutMs? })

import { detectLocalDevnet } from "@foundryprotocol/0gkit-core";

if (await detectLocalDevnet()) {
  // local Anvil/0G devnet is reachable — switch to network="local"
}

Pure viem getChainId probe; default rpcUrl="http://127.0.0.1:8545", default timeoutMs=1000. Returns true only when the chainId matches the local preset (31337). No shell-out to 0g doctor (D72).

printFirstSuccess({ op, id, note? }) / FIRST_SUCCESS_MARKER

import { printFirstSuccess, FIRST_SUCCESS_MARKER } from "@foundryprotocol/0gkit-core";

printFirstSuccess({
  op: "storage.upload",
  id: result.root,
  note: "network=galileo",
});
// → boxed banner that includes "[0gkit:first-success]"

Emits a unicode-boxed success banner. FIRST_SUCCESS_MARKER is the public contract token ("[0gkit:first-success]") that log scrapers / CI gates can pin to. Callers gate idempotency themselves (the helper does not).

buildDefectReport(input) / suggestOwnership(code) / suggestSeverity(code)

import { buildDefectReport, ZeroGError } from "@foundryprotocol/0gkit-core";

try {
  await storage.upload(bytes);
} catch (err) {
  if (err instanceof ZeroGError) {
    console.error(
      buildDefectReport({
        error: err,
        product: "Foundry Protocol",
        env: { wallet: "MetaMask", chainId: 16602, network: "galileo" },
      })
    );
  }
}

Renders a ready-to-file QA defect report in the bilingual template used by the 0G ecosystem app-test program. Every ZeroGError already carries a code + helpUrl, so the report auto-fills ownership (infra-class codes → 0G Infra, integration/config codes → Hackathon项目), a suggested severity, the environment line, the actual result, and a root-cause hint — leaving only repro steps, expected result, and screenshot for the tester. suggestOwnership / suggestSeverity expose the routing + severity heuristics on their own. Framework-agnostic: call it from a browser dApp's error boundary or a CLI catch block. The 0g CLI wires it behind --defect-report.

Examples

Minimal — resolve a network and read the chain id

import { createClient, getNetwork } from "@foundryprotocol/0gkit-core";

const preset = getNetwork("aristotle");
console.log(preset.chainId); // 16661

const client = createClient({ network: "aristotle" });
console.log(client.public.chain?.id); // 16661

Realistic — a signing client + actionable error handling

import { createClient, ZeroGError, ConfigError } from "@foundryprotocol/0gkit-core";

try {
  const client = createClient({
    network: "galileo",
    privateKey: process.env.ZEROG_PRIVATE_KEY, // adds client.wallet
  });

  const address = client.wallet?.account?.address;
  const block = await client.public.getBlockNumber(); // raw viem escape hatch
  console.log(`signer ${address} — head block ${block}`);
} catch (err) {
  if (err instanceof ConfigError) {
    // e.g. malformed private key, or galileo preset edge case
    console.error(`config problem: ${err.message}\n→ ${err.hint}`);
  } else if (err instanceof ZeroGError) {
    console.error(`[${err.code}] ${err.message}\n→ ${err.hint}`);
  } else {
    throw err;
  }
}

Common errors

SymptomCauseFix
ConfigError: Unknown network '…'Bad NetworkName passed to getNetwork.Use aristotle, galileo, or local.
ConfigError: Network '…' has no rpcUrl/chainIdAn unresolved preset (or a custom network).Pass { rpcUrl, chainId } to createClient.
ConfigError: Invalid privateKey: must be a 32-byte hex…privateKey is not 64 hex chars.Pass a 64-char hex key (with or without 0x), e.g. the output of cast wallet new.

Related

Built on by every package. Start with Getting started, then explore chain / storage / compute / da / attestation. Template: npx degit rajkaria/0gkit/templates/storage-app.

Exports

  • buildDefectReport
  • define0GConfig
  • detectLocalDevnet
  • ERROR_CODES
  • ERROR_HELP_BASE
  • errorNamespace
  • FIRST_SUCCESS_MARKER
  • formatEstimate
  • formatNative
  • helpUrlFor
  • isErrorCode
  • printFirstSuccess
  • suggestOwnership
  • suggestSeverity
  • type CreateClientOptions
  • type DefectOwnership
  • type DefectReportEnv
  • type DefectReportError
  • type DefectReportInput
  • type DefectSeverity
  • type DefineConfigOptions
  • type DefinedConfig
  • type DetectLocalDevnetOptions
  • type DryRunResult
  • type ErrorCode
  • type Estimate
  • type FirstSuccessArgs
  • type NetworkName
  • type SignTypedDataArgs
  • type SignableTx
  • type Signer
  • type ZeroGClient