Skip to content
This repository was archived by the owner on Jul 10, 2026. It is now read-only.

Commit 8d5999f

Browse files
committed
feat: registering to aztec registry on deployment
1 parent 2149349 commit 8d5999f

3 files changed

Lines changed: 145 additions & 0 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ yarn test # Run tests against existing sandbox
6868

6969
The sandbox runs on `http://localhost:8080` by default.
7070

71+
## Artifact registry (devnet.aztec-registry.xyz)
72+
73+
This boilerplate can optionally upload your compiled contract artifact JSONs to the Aztec **artifact registry** during deployment.
74+
75+
- **Registry URL**: set `AZTEC_ARTIFACT_REGISTRY_URL` (defaults to `https://devnet.aztec-registry.xyz/`)
76+
- **Enable upload**: set `AZTEC_ARTIFACT_REGISTRY_UPLOAD=1`
77+
- **Fail deploy on upload errors** (optional): set `AZTEC_ARTIFACT_REGISTRY_STRICT=1`
78+
79+
### Deploy the Counter contract
80+
81+
You can deploy contracts programmatically using the `deployCounter` utility function from `src/ts/utils.ts`. The deployment automatically uploads artifacts to the registry if `AZTEC_ARTIFACT_REGISTRY_UPLOAD=1` is set. See `src/ts/counter.test.ts` for an example of how to use it.
82+
7183
### All tests
7284
Run both Noir contract tests and TypeScript integration tests:
7385

src/ts/artifactRegistry.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { readFile } from "node:fs/promises";
2+
3+
export type ArtifactRegistryUploadResponse =
4+
| {
5+
success: true;
6+
filename?: string;
7+
classId?: string;
8+
contractName?: string;
9+
functionCount?: number;
10+
[key: string]: unknown;
11+
}
12+
| {
13+
success: false;
14+
error?: string;
15+
message?: string;
16+
[key: string]: unknown;
17+
};
18+
19+
function normalizeBaseUrl(baseUrl: string): string {
20+
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
21+
}
22+
23+
export function getArtifactRegistryBaseUrl(): string {
24+
return (
25+
process.env.AZTEC_ARTIFACT_REGISTRY_URL ??
26+
"https://devnet.aztec-registry.xyz/"
27+
);
28+
}
29+
30+
export function shouldUploadArtifacts(): boolean {
31+
const v = process.env.AZTEC_ARTIFACT_REGISTRY_UPLOAD ?? "";
32+
return v === "1" || v.toLowerCase() === "true";
33+
}
34+
35+
export function isStrictUpload(): boolean {
36+
const v = process.env.AZTEC_ARTIFACT_REGISTRY_STRICT ?? "";
37+
return v === "1" || v.toLowerCase() === "true";
38+
}
39+
40+
export async function uploadArtifactToRegistry(params: {
41+
artifact: unknown;
42+
filename: string;
43+
registryBaseUrl?: string;
44+
}): Promise<ArtifactRegistryUploadResponse> {
45+
const base = normalizeBaseUrl(
46+
params.registryBaseUrl ?? getArtifactRegistryBaseUrl(),
47+
);
48+
const uploadUrl = new URL("api/upload", base).toString();
49+
50+
const body = new FormData();
51+
const payload = JSON.stringify(params.artifact);
52+
body.set(
53+
"file",
54+
new Blob([payload], { type: "application/json" }),
55+
params.filename,
56+
);
57+
58+
const res = await fetch(uploadUrl, { method: "POST", body });
59+
60+
// The registry commonly returns JSON even on 409; treat duplicates as non-fatal by default.
61+
const text = await res.text();
62+
const parsed: unknown = text ? safeJsonParse(text) : { success: res.ok };
63+
64+
if (res.ok) {
65+
return (parsed as ArtifactRegistryUploadResponse) ?? { success: true };
66+
}
67+
68+
// Duplicate artifact (already uploaded) should not break deploys unless strict mode is enabled.
69+
if (res.status === 409) {
70+
return (
71+
(parsed as ArtifactRegistryUploadResponse) ?? {
72+
success: true,
73+
message: "Artifact already exists in registry",
74+
}
75+
);
76+
}
77+
78+
const msg =
79+
typeof parsed === "object" && parsed
80+
? JSON.stringify(parsed)
81+
: text || `HTTP ${res.status} ${res.statusText}`;
82+
throw new Error(`Artifact registry upload failed (${res.status}): ${msg}`);
83+
}
84+
85+
export async function uploadArtifactFileToRegistry(params: {
86+
artifactPath: string;
87+
filename?: string;
88+
registryBaseUrl?: string;
89+
}): Promise<ArtifactRegistryUploadResponse> {
90+
const buf = await readFile(params.artifactPath, "utf8");
91+
const artifact = JSON.parse(buf) as unknown;
92+
const filename =
93+
params.filename ?? params.artifactPath.split("/").pop() ?? "artifact.json";
94+
return await uploadArtifactToRegistry({
95+
artifact,
96+
filename,
97+
registryBaseUrl: params.registryBaseUrl,
98+
});
99+
}
100+
101+
export async function maybeUploadArtifactToRegistry(params: {
102+
artifact: unknown;
103+
filename: string;
104+
registryBaseUrl?: string;
105+
}): Promise<ArtifactRegistryUploadResponse | null> {
106+
if (!shouldUploadArtifacts()) return null;
107+
try {
108+
const resp = await uploadArtifactToRegistry(params);
109+
return resp;
110+
} catch (err) {
111+
if (isStrictUpload()) throw err;
112+
// Best-effort upload; do not fail deployments by default.
113+
console.warn(
114+
`[artifact-registry] Upload failed (continuing): ${
115+
err instanceof Error ? err.message : String(err)
116+
}`,
117+
);
118+
return null;
119+
}
120+
}
121+
122+
function safeJsonParse(text: string): unknown {
123+
try {
124+
return JSON.parse(text) as unknown;
125+
} catch {
126+
return text;
127+
}
128+
}

src/ts/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from "../artifacts/Counter.js";
66
import { AztecAddress } from "@aztec/stdlib/aztec-address";
77
import { Contract } from "@aztec/aztec.js/contracts";
8+
import { maybeUploadArtifactToRegistry } from "./artifactRegistry.js";
89

910
/**
1011
* Deploys the Counter contract.
@@ -27,5 +28,9 @@ export async function deployCounter(
2728
from: deployerAddress,
2829
});
2930
const contract = await tx.deployed();
31+
await maybeUploadArtifactToRegistry({
32+
artifact: CounterContractArtifact,
33+
filename: "counter_contract-Counter.json",
34+
});
3035
return contract as CounterContract;
3136
}

0 commit comments

Comments
 (0)