Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

31 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Artifacts Hub

Canonical Aragon addresses and ABIs — one JSON per chain, one JSON per contract. Consumed as a git submodule, no NPM, no build step.

Two symmetrical trees:

  • addresses/ — <chainId>.json per chain, with <network>.json symlinks for grep-friendly names. Every file has the same shape (Zod source of truth in scripts/schema.ts).
  • abi/ — <component>/<Contract>.json per contract, plus generated index.ts files with viem-compatible as const bindings. Every version referenced by addresses/ has its own <component>/v<version>/ folder; the component root mirrors the latest one.

Get started

git submodule add https://github.com/aragon/artifacts-hub lib/artifacts-hub

Concrete example — resolve a plugin's current build from the address book, pair it with the ABI, make a typed viem call:

import mainnet from "./lib/artifacts-hub/addresses/mainnet.json";
import { MultisigABI } from "./lib/artifacts-hub/abi/multisig";
import { createPublicClient, http } from "viem";
import { mainnet as mainnetChain } from "viem/chains";

// From the address book:
const currentBuild = mainnet.plugins.multisig.versions.find(v => v.current)!;
// currentBuild.setup + currentBuild.implementation are the current setup contract
// and its plugin implementation on this chain.

const client = createPublicClient({ chain: mainnetChain, transport: http() });
const proposalCount = await client.readContract({
  address: "0x…YourMultisigDAOInstance…",
  abi: MultisigABI,
  functionName: "proposalCount",
});

Alternative import shapes:

// Grab several components under a single namespace import
import { admin, multisig, tokenVoting } from "./lib/artifacts-hub/abi";
admin.AdminABI;  tokenVoting.TokenVotingABI;

// Plain JSON when you don't need viem's `as const` inference
import Multisig from "./lib/artifacts-hub/abi/multisig/Multisig.json";

// A specific version: same shape, under v<release>.<build> (plugins) or v<protocolVersion> (osx)
import { TokenVotingABI } from "./lib/artifacts-hub/abi/token-voting/v1.3";

addresses/ — the address book

Every file has the same top-level shape. Common lookups:

Want Path
Chain identity chainId, network
Current OSx version osx.versions.find(v => v.current)
DAO implementation (cloned by DAOFactory) osx.versions.find(v => v.current).core.daoBase
Management DAO on this chain management.dao
Chain's ENSRegistry ens.registry
Latest plugin build (setup + impl) plugins["<slug>"].versions.find(v => v.current)
All historical plugin builds plugins["<slug>"].versions
Installable plugin builds plugins["<slug>"].versions.filter(v => !v.placeholder)
Governance token base (ERC20) plugins["token-voting"].other.governanceERC20
Current SafeOwner condition factory conditions.factories.find(f => f.current).address
ProtocolFactory (when the chain has one) deployers.protocolFactory

Addresses are lowercased on parse — never rely on checksum casing for identity. Arrays are ascending, and the current entry carries current: true.

Placeholder builds. Chains that start a plugin at a later build publish the earlier build numbers with the OSx PlaceholderSetup, so build numbers line up across chains. Those entries carry placeholder: true: they are not the plugin, can't be installed and are never current. ingest and refresh-plugin detect them on-chain (the setup reverts prepareInstallation with PlaceholderSetupCannotBeUsed()). implementation is omitted when a setup has none (placeholders, zkSync Admin); it is never 0x0.

Schema outline:

  • osx.versions[] — one entry per protocol version deployed on the chain. core.* = the framework contracts, helpers.* = auxiliary singletons (globalExecutor).
  • management.* — the OSx-managing DAO + its multisig plugin instance.
  • ens.* — Aragon's ENS stack (registry, plugin- and dao-subdomain registrars, public resolver).
  • plugins.<slug> — one entry per plugin (admin, multisig, token-voting, spp, lock-to-vote, crosschain) with repo, ens, maintainer, versioned setup+impl, and optional other{} for plugin-scoped auxiliary addresses.
  • conditions.factories[] — versioned condition factories deployed on the chain.
  • deployers.* — one-shot deployment tools (currently just protocolFactory).
  • deprecated: true (optional, top-level) — the chain is retired. Entries stay so consumers can still resolve historical addresses, but just coverage excludes it from the required-slot gate and new plugin ingest should not target it.

See addresses/README.md for the tree layout and file-naming rules; scripts/schema.ts is the Zod source of truth.

abi/ — the ABI tree

One folder per source component; one JSON per contract. Slugs match addresses/ where applicable:

slug source repo
osx aragon/osx
admin aragon/admin-plugin
multisig aragon/multisig-plugin
token-voting aragon/token-voting-plugin
spp aragon/staged-proposal-processor-plugin
lock-to-vote aragon/lock-to-vote-plugin
crosschain aragon/crosschain
conditions aragon/condition-library
protocol-factory aragon/protocol-factory

Versions live next to the latest:

abi/<slug>/v<version>/<Contract>.json   # the ABI at that version
abi/<slug>/v<version>/index.ts          # generated, same exports as the root
abi/<slug>/<Contract>.json              # generated copy of the latest version folder
abi/<slug>/index.ts                     # export * from "./<latest>/index.ts"

Plugins use v<release>.<build>, matching plugins.<slug>.versions in the address book (abi/multisig/v1.2). OSx uses its protocol version (abi/osx/v1.4.0). Components without an on-chain version (crosschain, conditions, protocol-factory) start at v1.1. A version folder can gain contracts over time, but an ABI already in it never changes: a changed ABI means a new version.

Placeholder builds (placeholder: true in the address book) point to the OSx PlaceholderSetup, not to the plugin, so abi/<slug>/v<version> does not describe them. just verify-abi skips and lists them per chain.

Each folder carries a generated index.ts exporting every contract as <Contract>ABI with as const (viem-friendly). The top-level abi/index.ts re-exports each component under a camelCase namespace (lock-to-vote → lockToVote, etc.).

Bytecode is deliberately omitted — pair a JSON ABI with the on-chain address from addresses/, or forge build the source repo when you need artefacts.

See abi/README.md for consuming patterns.

Adding a new chain

Every chain deployed via ProtocolFactory reconstructs from a single address:

just ingest <chainId> <rpcUrl> <protocolFactoryAddress> [network]

That reads getDeployment() on the PF, calls protocolVersion() on the DAOFactory, enumerates every plugin repo's versions on-chain, resolves ENS, and writes addresses/<chainId>.json + the network symlink. Re-running is idempotent — same PF address, same output (modulo new plugin builds).

For chains that predate the ProtocolFactory (mainnet, arbitrum, base, …), the address book is hand-managed. Edit the JSON directly and let just validate + just coverage gate the changes.

Per-deployment artifacts from plugin repos

Plugin repos emit a self-describing envelope after each on-chain deployment:

<plugin-repo>/artifacts/artifacts-<network>-<timestamp>.json

The envelope's plugin subtree is exactly the AddressBook Plugin schema, so a future ingest step can drop it straight into addresses/<chainId>.json under plugins.<slug> — no re-derivation, no per-plugin adapter code.

{
  "chainId": 11155111,
  "network": "sepolia",
  "timestamp": 1789660800,
  "slug": "crosschain",
  "plugin": {
    "repo":  "0xaa77…2603",
    "ens":   "crosschain.plugin.dao.eth",
    "maintainer": "0xca83…d324",
    "versions": [
      { "release": 1, "build": 1,
        "setup": "0xdce3…ae2e",
        "implementation": "0x817b…866f",
        "current": true }
    ]
  }
}

Zod source of truth: PluginArtifact in scripts/schema.ts. Reserved slugs (canonical ENS + display label) live in scripts/lib/plugin-catalog.ts.

Ingesting plugin deployments

just import-plugin merges those envelopes into addresses/<chainId>.json under plugins.<slug>:

just import-plugin path/to/artifacts-<network>-<timestamp>.json   # one file
just import-plugin path/to/plugin-artifacts-directory             # sweep every *.json in it (timestamp-ascending)
just import-plugin --dry-run <path>                               # preview only, no writes

Invariants the script enforces (source: scripts/import-plugin.ts):

  • Identity fields (repo, ens, maintainer) are constants per (chainId, slug). Any mismatch against an existing entry is a hard error — ownership transfers or address changes belong in a reviewed PR, not through import.
  • Versions are additive. Same (release, build) with the same (setup, implementation, other) is a no-op (re-runs are safe). Same (release, build) with different addresses is a hard error.
  • current: true is derived: after any merge, only the highest non-placeholder (release, build) carries it. versions[] is re-sorted ascending.
  • Missing fields are adopted. A known version that lacks implementation or placeholder: true picks it up from the incoming data (reported as an update). A placeholder with an implementation in the book is a hard error.
  • Chain must be tracked (addresses/<chainId>.json must exist) and must not be marked deprecated: true. Ingest new chains with just ingest first.
  • Final book is re-parsed through Zod before writing, so addresses land canonically lowercased regardless of the artifact's casing.

Directory mode + timestamp-ascending order means re-sweeping the same folder is a total no-op — safe to run in CI on every push, or as a follow-up to a batch of deploys.

Refreshing a plugin's versions from on-chain

just import-plugin only ever adds what's in the envelope, which is fine when you've captured every deploy through the artifact flow. When a plugin repo already has versions on-chain that predate the envelope flow (or that were published through some other path), just refresh-plugin reads them straight from the PluginRepo and merges them in:

just refresh-plugin <slug> <chainId> <rpcUrl>
just refresh-plugin --dry-run <slug> <chainId> <rpcUrl>

It looks up plugins.<slug>.repo in addresses/<chainId>.json, calls the same on-chain enumerator just ingest uses (walking latestRelease() + buildCount(release) and reading getVersion(tag) per build), then hands the result to the same merge core as just import-plugin — so identity mismatches, (release, build) collisions with different addresses, and deprecated-chain refusals all behave identically. New builds append and current: true re-stamps on the highest non-placeholder (release, build), and placeholder builds are flagged as they are read.

Use it whenever a chain's plugins.<slug>.versions looks short — typically right after adopting the envelope flow on a plugin that has historical builds, or as a periodic sanity sweep across all chains.

Refreshing ABIs

Every version folder is pinned in abi/sources.json:

"spp": {
  "v1.3": { "source": "git:https://github.com/aragon/staged-proposal-processor-plugin#5715646f…" }
},
"multisig": {
  "v1.2": { "source": "npm:@aragon/osx-ethers@1.3.0", "contracts": ["Multisig", "MultisigSetup", "IMultisig"] }
}
  • git:<https url>#<sha>: cloned into a temp dir, forge build, and every contract, interface and library declared under src/ with a non-empty ABI is taken. Abstract contracts are skipped: they can't be deployed, and their functions are already in the ABI of every contract that extends them. Needs git and forge. A repo or submodule that isn't reachable over HTTPS (private) is cloned from the sibling checkout ../<repo> instead, so keep one next to this repo.
  • npm:<package>@<version>: for builds that predate the Foundry repos (Hardhat era). Reads <Name>ABI exports or legacy TypeChain <Name>__factory.abi.
  • contracts (optional) narrows a source that bundles other components. npm packages carry no "abstract" information, so their list must leave abstract contracts out.

To add a version: add its entry to sources.json, then

just import-abi <slug> <version>   # fetch into abi/<slug>/<version>/, regenerate index.ts + root copies
just verify-abi                    # check the new ABI against every deployed setup/implementation

import-abi never changes an existing ABI: identical files are a no-op, new contracts are added, and a different ABI for an existing contract is a hard error. To replace a wrong file on purpose, delete it in a reviewed commit and re-import.

verify-abi takes RPC URLs from a temporary clone of aragon/just-foundry (networks/<network>.env) and checks that every function selector of the version's ABI is present in the deployed code. Deprecated chains are skipped.

just generate-abi rebuilds every generated file from the version folders; just generate-abi --check (part of just validate) fails when one is stale.

Commands

just validate           # Zod-validate every addresses/*.json + abi/ generated files up to date
just test               # unit tests for scripts/
just coverage           # per-network table + section gap summary
just coverage --gaps    # invert to slot-centric view
just coverage --json    # machine-readable output for scripting
just ingest <chainId> <rpcUrl> <pfAddress> [network]
just import-plugin <path>            # merge PluginArtifact envelope(s) into addresses/<chainId>.json
just refresh-plugin <slug> <chainId> <rpcUrl>   # enumerate all on-chain versions of a plugin, merge into the book
just import-abi [slug] [version]     # fetch the ABI versions pinned in abi/sources.json
just verify-abi [network]            # check versioned ABIs against deployed plugin contracts
just generate-abi [--check]          # regenerate abi/**/index.ts + root copies of the latest version

just coverage exits non-zero when a chain is missing a required slot; optional gaps (e.g. deployers.protocolFactory on chains that predate the PF concept) don't fail the gate. See scripts/coverage.ts for the exact required/optional split.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages