Skip to content

feat: publish contract addresses and a release manifest with each release - #242

Open
re-gius wants to merge 4 commits into
masterfrom
re-gius/publish-deployments-artifact
Open

feat: publish contract addresses and a release manifest with each release#242
re-gius wants to merge 4 commits into
masterfrom
re-gius/publish-deployments-artifact

Conversation

@re-gius

@re-gius re-gius commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Description

Releases currently ship ABIs but no contract addresses, so a consumer gets the interface and still doesn't know how to call the contracts without scraping a doc or hardcoding a set. This publishes deployments.json (addresses per network) and release-manifest.json (what the release contains) as standalone assets and at the root of the zip, generated from the committed deployment manifests. Both are in the pre-publish asset check, so a release either carries them or doesn't publish.

It also removes the repo's two other copies of addresses: the 17-address list in DEPLOYMENTS.md, which had already drifted once (6f1e6fbc), and the PINNED_FACTORY literal in deploy-contracts.yml, now read from the manifest.

deployments/<network>/<chain-id>.json becomes the only tracked copy.

New scripts/js/release-metadata.mjs: it has no dependencies, uses cast for chain reads, with build, validate, changelog and verify. A small new workflow runs validate on PRs touching deployments/** or the contract list, so a duplicate address or malformed manifest fails there rather than when a release is cut.

Type

  • Bug fix
  • Feature
  • Breaking change
  • Documentation
  • Chore
  • Refactor
  • Security

Scope

  • Registration
  • Resolver
  • Store
  • Proof of Personhood
  • Deployment scripts
  • Tests

Related Issues

Follows #222 , which made the ABI artifact trustworthy. This PR extends that artifact with addresses.

Fixes

Fixes #118 .

Checklist

Code

  • Follows project style
  • forge build passes
  • forge test passes
  • No new compiler warnings

Testing

  • New tests added for changed behavior
  • Fuzz tests added where applicable
  • Invariant tests verified

Security

  • No new selfdestruct or delegatecall
  • Access control reviewed
  • No storage layout conflicts (for upgradeable contracts)

Documentation

  • NatSpec updated on changed interfaces
  • README updated if needed

Breaking Changes

  • No breaking changes
  • Breaking changes documented below

Breaking changes: none for on-chain behaviour or for existing release assets, which keep their names and shapes. One thing to be aware of: anything scraping the address list out of DEPLOYMENTS.md will no longer find it and should read deployments/<network>/<chain-id>.json or the published deployments.json.

How to test

Not a contracts change, so forge test exercises none of it. From a checkout of this branch:

# the two release files, from the committed manifests plus the current build
forge build && bun scripts/js/release-metadata.mjs build --tag v0.0.0-test --out /tmp/rel
cat /tmp/rel/deployments.json /tmp/rel/release-manifest.json

# what CI now runs on a manifest change; needs no build
bun scripts/js/release-metadata.mjs validate

# check a network against a live chain (needs an eth-rpc endpoint)
bun run deployments:verify --network paseo-assethub --rpc <eth-rpc-url>

The end-to-end check that matters is a dry run of Publish Beta Package with a throwaway version, the same method used to validate #237. I will run it before merging it.

Notes

The manifest is updated by hand, and stays the single source of truth. A live deploy commits only its markdown report in dotns-releases; nothing writes addresses back here. The next step after this merges is for dotns-releases to open that PR automatically. RELEASE_ARTIFACTS.md states this plainly rather than implying a mechanism that doesn't exist. The manifest is accurate today, the latest report for each live network reproduces the canonical set with no differences.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

CI Summary

Check Result
Deploy Contracts Reproduces the committed manifest; resume verified
PR Title PR Title Valid
Labels Unknown
Secret Scan Passed - No secrets detected

Deploy Contracts

Deployed addresses vs the committed manifest

Expected is the committed manifest; actual is this CI deployment of the same pipeline.

Contract Expected Actual Match
Create3Factory 0x8533c79E058c5a6489CAFeCA86dc600E029D75f5 0x8533c79E058c5a6489CAFeCA86dc600E029D75f5 match
DotnsContentResolver 0x7F74D7CD50f5a834270E2ad395a01b01891AB37d 0x7F74D7CD50f5a834270E2ad395a01b01891AB37d match
DotnsNameEscrow 0x4881Afb78e7C908cAe818168B926229D93376520 0x4881Afb78e7C908cAe818168B926229D93376520 match
DotnsPopController 0xCC932348606cc1f3318cADeC5A5Cd2CA447f8a4b 0xCC932348606cc1f3318cADeC5A5Cd2CA447f8a4b match
DotnsPopResolver 0xDaC984884EcA8Fc44011f1D6C49B27828390A72B 0xDaC984884EcA8Fc44011f1D6C49B27828390A72B match
DotnsProtocolRegistry 0xD19e3D0C97CF501125a04A97405e3e6592fa846E 0xD19e3D0C97CF501125a04A97405e3e6592fa846E match
DotnsRegistrar 0x4f06E818Ba3d987704fd91cf3d868E4b019106Ab 0x4f06E818Ba3d987704fd91cf3d868E4b019106Ab match
DotnsRegistrarController 0xBdaA01bD1bA67d709F2b1fF286Da0d854977EA30 0xBdaA01bD1bA67d709F2b1fF286Da0d854977EA30 match
DotnsRegistry 0xf34054fd76BbF85f216cf9908226D5f0A72E50CA 0xf34054fd76BbF85f216cf9908226D5f0A72E50CA match
DotnsResolver 0xbd1165E549DF96F083c0A16f61590927bC187009 0xbd1165E549DF96F083c0A16f61590927bC187009 match
DotnsReverseResolver 0xee3883d7eB60Ee9BCD7F3bcD8f2f05302A9Cc035 0xee3883d7eB60Ee9BCD7F3bcD8f2f05302A9Cc035 match
LabelStoreBeacon 0xb57Ebc2e7085616d4906D1fE49af1cE13f7dffeF 0xb57Ebc2e7085616d4906D1fE49af1cE13f7dffeF match
Multicall3 0xB4468000abD87D3c56cbFBd153161223D7b109e5 0xB4468000abD87D3c56cbFBd153161223D7b109e5 match
PopRules 0x747B456bE03aec0b42bd85C51513730FBD45DA31 0x747B456bE03aec0b42bd85C51513730FBD45DA31 match
RootGatewayDispatcher 0xa889CCA3Fb4B07b98a11cc54C10f13dDA20bc3db 0xa889CCA3Fb4B07b98a11cc54C10f13dDA20bc3db match
StoreFactory 0x709A027F446a9e2a4BB9cb9a9c754435b19e32B7 0x709A027F446a9e2a4BB9cb9a9c754435b19e32B7 match
UserStoreBeacon 0xb7C995601679840d36F37E86DB2d7dF30797eC5C 0xb7C995601679840d36F37E86DB2d7dF30797eC5C match
_seed 0x0000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000 match

View full logs

Labels

dependencies, other, type: docs

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file other type: docs labels Aug 20, 2026
@re-gius
re-gius marked this pull request as ready for review August 20, 2026 10:10
@re-gius

re-gius commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

New release flow, documented in RELEASE_ARTIFACTS.md. For a release that changes contract code:

  1. Cut a pre-release. It carries the ABIs and release-manifest.json, and no addresses. The code is not deployed yet at this point, so the recorded addresses still belong to the previous deployment.
  2. Deploy that tag from dotns-releases. Deploying a tag rather than a branch is what ties the addresses to the code that produced them.
  3. Record the resulting addresses in deployments/<network>/<chain-id>.json (manually for now).
  4. Cut the release from a commit that differs from the deployed tag only by that record, after running deployments:verify against the network.

A release that changes no contract code skips all of this.

Step 3 is manual today. The next piece of work is for dotns-releases to open that PR itself and make everything automated (checks, recording addresses, ...).

@sphamjoli sphamjoli left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collapsing the three copies of the addresses into one tracked manifest and generating the rest from it fixes the drift from 6f1e6fb. Reading the factory out of the canonical manifest at CI time instead of repeating the literal means the two cannot disagree, and running validate on PRs fails a bad manifest before a release does. A few suggestions below, mostly non-blocking; the validate() tidy-up is the one worth doing.

Comment thread scripts/js/release-metadata.mjs Outdated
const contracts = contractsFromManifest(path);
log(`${network} (chain ${chainId}): ${Object.keys(contracts).length} addresses`);
}
const missing = readContractNames().filter(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small thing: the contract list gets read and parsed here, then again on line 175 just to log the count. Reading it once into a variable and reusing it would avoid the second parse.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 824137d : read only once now

Comment thread scripts/js/release-metadata.mjs Outdated
log(`${network} (chain ${chainId}): ${Object.keys(contracts).length} addresses`);
}
const missing = readContractNames().filter(
(name) => !existsSync(join(ROOT, "out", `${name}.sol`, `${name}.json`)) && !sourceExists(name),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sourceExists runs a separate git ls-files for every contract name. On the new metadata job there is no forge build, so out/ is absent and all 32 listed contracts fall through to their own git call. Listing the tracked sources once and checking membership would do the same work more simply. I checked that a set built from the sources resolves the same 32 names as the per-name globs, so it is behaviour preserving. Happy to make the change if you would like it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 824137d and thanks for checking. trackedSourceNames() now lists contracts once and builds a Set of basenames.

Comment thread scripts/js/release-metadata.mjs Outdated
cwd: ROOT,
encoding: "utf8",
});
return found.trim() !== "";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catches any git failure and returns false, so a genuine git error surfaces as "no source or build artefact", which is a little misleading. It fails safe, so only a clarity point, and folding this into a single sources lookup avoids it anyway.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 824137d - a real git failure reports itself now

Comment thread scripts/js/release-metadata.mjs Outdated
const changed = Object.keys(current).filter(
(name) =>
previous[name] &&
JSON.stringify(previous[name].contracts) !== JSON.stringify(current[name].contracts),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This compares the two address sets by serialising them. It is fine today because Foundry writes the manifests with sorted keys, so both sides serialise in the same order, but it does tie the "addresses changed" note to that ordering. If a manifest were ever hand-edited into a different key order, the notes would announce a move that did not happen. Comparing per address, or sorting keys first, would make it robust. Minor.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 824137d - sameAddresses() compares per label and case-insensitively now

Comment thread scripts/js/release-metadata.mjs Outdated
@@ -0,0 +1,370 @@
#!/usr/bin/env bun

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file has a bun shebang but is always invoked as bun scripts/..., so the shebang is never used and the file is not executable. Worth a glance in case it was meant to run directly, otherwise fine to leave.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good finding, removed in 824137d

Comment thread RELEASE_ARTIFACTS.md Outdated

Two ways to protect yourself. Resolve addresses through the protocol registry at runtime, so the chain is the authority and the published file is only a starting point. Or check a release against a chain yourself with `deployments:verify` before relying on it.

The manual step is deliberate for now, and this file stays the single source of truth for addresses either way. The next step is for a deployment to update it automatically, so recording addresses stops depending on someone remembering. Later, publishing can read the chain and refuse to publish addresses that disagree with it.

@sphamjoli sphamjoli Aug 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: this is a roadmap note in a reference doc. If no issue tracks it, a reader cannot act on it. Drop it, or link the issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, removed and created #245

@re-gius
re-gius requested a review from sphamjoli August 21, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file other type: docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat]: Publish deployments and manifest as part of ABI release artifacts

2 participants