@overcast-xyz/evm-contracts
v0.0.0
Published
TypeScript ABIs and types for the Overcast protocol contracts.
Readme
@overcast-xyz/evm-contracts
TypeScript ABIs and types for the Overcast protocol contracts.
The package has no runtime dependencies. The ABIs are as const arrays that
viem and abitype infer from directly, and the struct types are fully expanded, so
nothing is imported into a consumer's dependency tree.
Usage
import { createPublicClient, http } from "viem";
import { coreAbi, OptionSide, type Offer } from "@overcast-xyz/evm-contracts";
const client = createPublicClient({ transport: http() });
// Return type is inferred from the ABI — no generics, no casts.
const option = await client.readContract({
address: "0x…",
abi: coreAbi,
functionName: "getOption",
args: ["0x…"],
});
const offer: Offer = {
salt: "0x…",
creator: "0x…",
optionDetails: { /* … */ },
expiry: 0n,
side: OptionSide.Maker,
counterparty: "0x…",
};coreAbi is the composed Core contract, so it carries the full surface of
every module under src/core/ — functions, events, and all protocol errors, which
means decodeErrorResult against it resolves any revert the protocol can produce.
The extension points are exported too: iSettlementLayerAbi,
iOptionCreationHookAbi, physicalSettlementAbi,
physicalSettlementFactoryAbi, chainlinkSettlementAbi.
Each struct also ships its ABI fragment — offerParameter,
optionDetailsParameter, … — for use with encodeAbiParameters, which is handy
when reproducing the content-addressed offer and option ids that
libraries/Serialization.sol computes on-chain.
Addresses
Deployed addresses come from the deployments.toml that script/Deploy.s.sol
writes, fed to the wagmi foundry plugin's deployments option. Each chain with a
committed table contributes an entry:
import { coreConfig, physicalSettlementFactoryAddress } from "@overcast-xyz/evm-contracts";
// coreConfig = { address: { 11155111: "0x…" }, abi: coreAbi }
const domain = await client.readContract({ ...coreConfig, functionName: "domain" });coreAddress is the proxy — the address to call the protocol at. The
implementation behind it is not exported, because calling it directly does
nothing useful.
Chains appear as they are deployed and their table is committed, so on a checkout with no deployments these exports simply do not exist yet.
ChainlinkSettlementFactory is exported the same way, for the chains whose
deployment table names one — a chain that has not been redeployed since it was
added simply does not appear.
Two things are deliberately not here:
Settlement layers themselves. A chain holds one
PhysicalSettlementperExerciseModeand oneChainlinkSettlementper configuration, while the plugin's model is a single address per contract. Derive them from their factory'spredictAddress(...)— the factories exist to be that source of truth.Note that this is a call rather than a local derivation, and deliberately so: the
CREATE2salt is pure ABI encoding and can be computed off-chain, but the init-code hash covers the layer's creation code, which this package does not export. Doing so would only look cheaper — a copy compiled from a different commit than the deployed factory derives a confidently wrong address, so the only bytes that answer the question correctly are the ones the deployed factory holds.Local development. An
anvildeployment's addresses belong to a chain that gets recreated on demand, so they cannot be published. Read the[31337]table ofdeployments.tomldirectly, or pass the address in from config.
Regenerating
Everything under src/generated/ is derived from the Foundry artifacts and is
committed, so consumers and downstream CI never need Foundry installed.
pnpm generate # forge build → wagmi generate (ABIs) → generate-types.ts (structs, enums)
pnpm typecheck
pnpm buildpnpm generate must be re-run and its output committed whenever a change to the
Solidity alters an external signature, struct, or enum. CI regenerates and fails
on any difference, so a stale commit does not get far.
Building
pnpm build emits the package twice — ESM to dist/esm, CommonJS to
dist/cjs — and the exports map routes import and require to the matching
half. Dual-publishing costs nothing here (the package is as const data and
types, with no runtime imports) and it is what lets the two kinds of consumer
coexist:
- ESM / bundlers take
dist/esm, sosideEffects: falsecan drop the ABI arrays a build never references. These are large, so that matters. - CommonJS — every
@overcast-xyz/*SDK package ismodule: commonjs— takesdist/cjsthrough therequirecondition, and through the top-levelmain/typesfor the oldernode10resolution those packages still use. Without that half they cannot see this package at all.
Two details are load-bearing. scripts/emit-cjs-marker.mjs writes a nested
dist/cjs/package.json marking that directory "type": "commonjs", since the
root manifest is "type": "module" and would otherwise have Node read the
CommonJS emit as ESM; tsc only picks the .cjs extension for .cts inputs, so
the marker is the way to say it. And tsconfig.build.cjs.json pairs module:
CommonJS with moduleResolution: bundler — TypeScript 7 removed node10, and
bundler is the only mode left that pairs with a CommonJS emit. Nothing under
src/ imports a package, only its own ./generated/*.js siblings, so the
resolution mode has nothing to decide.
Layout
| Path | Generated by | Contents |
| ------------------------- | --------------------------- | ----------------------------------------------- |
| src/generated/abis.ts | @wagmi/cli foundry plugin | one as const ABI per exported contract, plus *Address / *Config for deployed chains |
| src/generated/structs.ts| scripts/generate-types.ts | ABI tuple parameters + expanded struct types |
| src/generated/enums.ts | scripts/generate-types.ts | const objects for each file-level Solidity enum |
| test/generated.assert.ts| scripts/generate-types.ts | compile-time check of the struct types |
scripts/contracts.ts is the single list of exported contracts, read by both
generators. scripts/deployments.ts is the single reader of deployments.toml,
used by wagmi.config.ts to supply addresses.
The wagmi CLI is used for ABIs only — the react and actions plugins are
deliberately not enabled, since the hooks they emit import from wagmi/codegen
and would make wagmi a runtime dependency of this package.
The plugin's includeBroadcasts option, which would lift addresses out of
broadcast/ and remove scripts/deployments.ts, is off for two reasons that were
checked against this project's own broadcast logs. It attributes the implementation
to Core, since Core reaches the chain twice and the address consumers need
belongs to a transaction named ERC1967Proxy; and run-latest.json holds the
latest run rather than accumulated state, so a re-run that only reconciles
configuration overwrites it and every address disappears. A deployment record has
neither problem — it names the proxy, and it describes a chain's current state
rather than the last thing that happened to it.
The struct types are expanded rather than derived from abitype so that the
published .d.ts has no imports. That puts the Solidity-to-TypeScript width
mapping (uint48 and below are number, wider is bigint) in
scripts/generate-types.ts, so test/generated.assert.ts checks it against what
abitype — and therefore viem — infers from the same ABI fragment. abitype is a
devDependency used only by that check.
