@foundryprotocol/0gkit-core
The neutral 0G foundation: network presets, a viem client factory, the
Receiptenvelope, theZeroGErrortaxonomy, 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(...).publicis a full viemPublicClient.
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
| Symptom | Cause | Fix |
|---|---|---|
ConfigError: Unknown network '…' | Bad NetworkName passed to getNetwork. | Use aristotle, galileo, or local. |
ConfigError: Network '…' has no rpcUrl/chainId | An 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
buildDefectReportdefine0GConfigdetectLocalDevnetERROR_CODESERROR_HELP_BASEerrorNamespaceFIRST_SUCCESS_MARKERformatEstimateformatNativehelpUrlForisErrorCodeprintFirstSuccesssuggestOwnershipsuggestSeveritytype CreateClientOptionstype DefectOwnershiptype DefectReportEnvtype DefectReportErrortype DefectReportInputtype DefectSeveritytype DefineConfigOptionstype DefinedConfigtype DetectLocalDevnetOptionstype DryRunResulttype ErrorCodetype Estimatetype FirstSuccessArgstype NetworkNametype SignTypedDataArgstype SignableTxtype Signertype ZeroGClient