SDK · Reference

The @foundryprotocol/sdk surface, frozen at 1.0.0-rc.1.

One imported class — Foundry — with five namespaces: forge, ingot, inference, revenue, lineage. No surprises, no callback hell, no opaque proxies. Read methods need no wallet; write methods require walletClient.

Foundry client

constructionts
import { Foundry } from "@foundryprotocol/sdk";
import { createWalletClient, custom } from "viem";

// Read-only — no wallet needed.
const foundry = new Foundry({ contracts: "aristotle" });

// Read + write — pass a viem WalletClient.
const wallet = createWalletClient({
  transport: custom(window.ethereum),
});
const foundryRW = new Foundry({
  contracts: "aristotle",
  walletClient: wallet,
});

Config options:

OptionTypeDefault
contracts"aristotle""aristotle"
rpcUrlstringhttps://rpc.0g.network
walletClientviem.WalletClient
inferenceEndpointstringhttps://api.foundryprotocol.xyz/v1
inferenceApiKeystring

forge.*

forge.create()

Opens a new Forge. Requires wallet.

ts
const { txHash } = await foundry.forge.create({
  modelSpec:               "0x…",
  evalSpec:                "0x…",
  evalCoordinator:         "0x…",
  contributionWindowEnds:  BigInt(Math.floor(Date.now() / 1000) + 7 * 86400),
});

forge.contributeData() / contributeCompute() / fundForge()

ts
await foundry.forge.contributeData("forge:0x…", "0x<storageRoot>");
await foundry.forge.contributeCompute("forge:0x…", "0.05"); // 0.05 OG
await foundry.forge.fundForge("forge:0x…",        "1.0");  // 1.0 OG

forge.state() / forge.list()

ts
const state = await foundry.forge.state("forge:0x…"); // 0|1|2|3|4
const ids   = await foundry.forge.list();             // ForgeId[]

// State enum: OPEN=0, TRAINING=1, ATTESTED=2, MINTED=3, FAILED=4

ingot.*

ts
const meta  = await foundry.ingot.meta(tokenId);
//   → { weightsRoot, lineageParent, forge, mintedAt, weightsSet }

const share = await foundry.ingot.shareOf(tokenId, "0x<address>");
//   → bigint (basis points, 10000 = 100%)

inference.*

ts
const { output, receipt } = await foundry.inference.run(
  "ingot:0x…",
  {
    input: "…",
    // — or —
    messages: [{ role: "user", content: "…" }],
    temperature: 0.7,
    maxTokens: 512,
  },
);

The receipt:

ts
{
  output:  string,
  ingotId: `ingot:0x${string}`,
  receipt: {
    requestId:       string,
    inferenceTxHash: `0x${string}` | undefined,
    revenueTxHash:   `0x${string}` | undefined,
    latencyMs:       number,
  },
}

revenue.*

ts
const claimable = await foundry.revenue.claimable(tokenId, "0x<address>");
const { txHash } = await foundry.revenue.claim(tokenId);

Pull payments, intentionally

Revenue is never pushed. Smiths claim when they like — gas costs are theirs, and reverts on the recipient side can't grief the splitter. This is the OpenZeppelin PaymentSplitter pattern, adapted for ERC-721 share weights.

lineage.*

ts
const { parent } = await foundry.lineage.get(tokenId);
// parent is the weightsRoot Hex of the parent Ingot, or 0x000…0 if root.

Type reference

TypeShape
IngotId`ingot:0x${string}`
ForgeId`forge:0x${string}`
Addressviem Address
Hexviem Hex
InferenceParamssee inference
InferenceResultsee inference

Error handling

All write methods throw a viem-derived error on revert. The SDK wraps inference HTTP errors in InferenceError with the upstream status code and body fragment.

ts
import { InferenceError } from "@foundryprotocol/sdk";

try {
  await foundry.inference.run("ingot:0x…", { input: "…" });
} catch (e) {
  if (e instanceof InferenceError) {
    console.error("proxy returned", e.status, e.body);
  } else throw e;
}