Integration

TypeScript SDK

A typed surface for every protocol primitive. ABIs and helpers you import from src/lib/contracts.ts and call through wagmi. No bespoke client to learn — your existing wagmi/viem app already knows how to use it.

Imports

import {
  ABIS,           // all 10 contract ABIs
  DEPLOYMENTS,    // per-chain addresses
  addressOf,      // resolver: addressOf("RepoVault", chainId) → Address | null
  isDeployed,     // convenience: any contract on this chain?
  repoId,         // keccak256("owner/name") → bytes32
  didId,          // keccak256(did) → bytes32
  type ContractName,
} from "@/lib/contracts";

The ABIs are auto-exported from the Hardhat artifacts by contracts/scripts/export-abis.ts after every recompile.

Read a repo balance

Use wagmi's useReadContract with the exported ABI:

"use client";
import { useReadContract, useChainId } from "wagmi";
import { ABIS, addressOf, repoId } from "@/lib/contracts";

export function RepoTreasury({
  owner,
  name,
  token,
}: { owner: string; name: string; token: `0x${string}` }) {
  const chainId = useChainId();
  const vault = addressOf("RepoVault", chainId);
  const id = repoId(owner, name);

  const { data: balance } = useReadContract({
    address: vault ?? undefined,
    abi: ABIS.RepoVault,
    functionName: "balanceOf",
    args: [id, token],
    query: { enabled: vault !== null },
  });

  return <span>{balance?.toString() ?? "—"}</span>;
}
"use client";
import { useWriteContract, useChainId } from "wagmi";
import { ABIS, addressOf, repoId } from "@/lib/contracts";
import { keccak256, toBytes } from "viem";

export function LinkRepoButton({ owner, name }: { owner: string; name: string }) {
  const chainId = useChainId();
  const vault = addressOf("RepoVault", chainId);
  const { writeContract, isPending } = useWriteContract();

  function link() {
    if (!vault) return;
    writeContract({
      address: vault,
      abi: ABIS.RepoVault,
      functionName: "linkRepo",
      args: [
        repoId(owner, name),
        keccak256(toBytes(`splits:${owner}/${name}:v1`)),
      ],
    });
  }

  return (
    <button onClick={link} disabled={!vault || isPending}>
      {isPending ? "Linking…" : "Link repo"}
    </button>
  );
}

This is the exact pattern the /app/connect wizard uses.

Identifier helpers

import { repoId, didId } from "@/lib/contracts";

repoId("acme", "date-fp");
// → "0x4f7a…"   (keccak256("acme/date-fp"))

didId("did:gitsea:alice.eth");
// → "0x83b1…"

These match the off-chain bot/indexer conventions — every place that derives an id uses the same keccak256(utf8 string) recipe.

High-level client

For ergonomic reads, the @gitsea/sdk package wraps the ABI + address book into a GitSea client:

import { GitSea } from "@gitsea/sdk";
import { base } from "viem/chains";

const ga = new GitSea({ chain: base, transport: http() });

// Reads
const sheet = await ga.repo("acme/date-fp").balanceSheet();
sheet.equityUsd;             // 4287.12
sheet.income.mrrUsd;         // 87.40
sheet.creditLine.available;  // 50_000n

const me = await ga.did("alice.eth").score();
me.score;   // 812
me.grade;   // "A+"

// Writes (with a wallet client)
await ga.repo("acme/date-fp").linkRepo({ splitsRoot });
await ga.creditLine(lineId).draw({ amount: 5_000n * 10n ** 18n });
await ga.market(prId, "merge").takePosition({ side: "yes", amount: 25n });

The client supports everything you can do with useReadContract / useWriteContract directly — it just gives you a typed, chainable surface.

Types

A few canonical types you'll see:

type RepoId = `0x${string}`;
type Did = `did:${string}`;

interface BalanceSheet {
  equityUsd: number;
  treasuryUsdc: bigint;
  treasuryAsset: bigint;
  income: { mrrUsd: number; perEpochUsd: number; sources: StreamSource[] };
  creditLine: { available: bigint; drawn: bigint; dueBy: Date | null };
  liabilities: Liability[];
  lastEpoch: number;
}

interface Score {
  score: number;
  grade: "A+" | "A" | "B" | "C" | "D";
  history: ScoreEvent[];
  creditLine: { available: bigint; drawn: bigint };
}

Errors

All SDK methods throw typed GitSeaError instances:

try {
  await ga.repo("acme/date-fp").credit.draw({ amount: huge, termDays: 30 });
} catch (e) {
  if (e instanceof GitSeaError && e.code === "EXCEEDS_LINE") {
    // max draw exceeded
  }
}

Codes you'll see most:

  • NOT_LINKED, UNAUTHORIZED, INSUFFICIENT_BALANCE,
  • EXCEEDS_LINE, OUT_OF_SCOPE (for agent UCANs),
  • MARKET_CLOSED, BOUNTY_CLOSED, ALREADY_BID,
  • ORACLE_PENDING.