@zodiaceco/create 0.0.0-stage → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/dist/index.mjs +220 -0
- package/package.json +36 -4
- package/templates/constellation/.editorconfig +10 -0
- package/templates/constellation/.prettierignore +2 -0
- package/templates/constellation/.prettierrc +1 -0
- package/templates/constellation/.vscode/extensions.json +3 -0
- package/templates/constellation/.vscode/settings.json +6 -0
- package/templates/constellation/CLAUDE.md +28 -0
- package/templates/constellation/README.md +262 -0
- package/templates/constellation/_gitignore +6 -0
- package/templates/constellation/abis/eth/weth.json +153 -0
- package/templates/constellation/constellation/allowances/index.ts +4 -0
- package/templates/constellation/constellation/index.ts +25 -0
- package/templates/constellation/constellation/roles/index.ts +1 -0
- package/templates/constellation/constellation/roles/treasury_ops/index.ts +2 -0
- package/templates/constellation/constellation/roles/treasury_ops/members.ts +4 -0
- package/templates/constellation/constellation/roles/treasury_ops/permissions.ts +22 -0
- package/templates/constellation/package.json +20 -0
- package/templates/constellation/tsconfig.json +16 -0
- package/templates/constellation/zodiac.config.ts +11 -0
package/README.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zodiaceco/create
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Starts a [Zodiac](https://zodiac.eco) constellation project: your organisation's onchain accounts, roles, and permissions as code.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create @zodiaceco@latest my-org
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
`pnpm dlx @zodiaceco/create` and `bunx @zodiaceco/create` work the same, installing with pnpm or Bun, as does `zodiac create` from [`@zodiaceco/sdk`](https://www.npmjs.com/package/@zodiaceco/sdk).
|
|
10
|
+
|
|
11
|
+
It copies the constellation template into `my-org`, installs its dependencies with the package manager you ran it with, and offers to connect the project to Zodiac: that opens your browser to sign in, mints an API key for the project and pulls your org's accounts. Last, it commits the project to a new git repository.
|
|
12
|
+
|
|
13
|
+
| Option | What it does |
|
|
14
|
+
| -------------- | ---------------------------------------------------------------- |
|
|
15
|
+
| `--pm <name>` | install with `npm`, `pnpm`, `yarn` or `bun` instead |
|
|
16
|
+
| `--no-install` | don't install dependencies |
|
|
17
|
+
| `--no-git` | don't initialize a git repository |
|
|
18
|
+
| `--no-auth` | don't connect the project to Zodiac |
|
|
19
|
+
| `--overwrite` | empty a directory that isn't empty (keeping its `.git`) |
|
|
20
|
+
| `-y, --yes` | don't ask, take the defaults (as without a terminal, e.g. in CI) |
|
|
21
|
+
|
|
22
|
+
Without a terminal, or with `--yes`, it never connects to Zodiac, since that needs a browser. Run `npm run pull` in the project to do it later.
|
|
23
|
+
|
|
24
|
+
The template lives in [`templates/constellation`](./templates/constellation).
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import * as prompts from "@clack/prompts";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { basename, join, relative, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { parseArgs } from "node:util";
|
|
9
|
+
//#region src/packageManager.ts
|
|
10
|
+
const PACKAGE_MANAGERS = [
|
|
11
|
+
"npm",
|
|
12
|
+
"pnpm",
|
|
13
|
+
"yarn",
|
|
14
|
+
"bun"
|
|
15
|
+
];
|
|
16
|
+
const isPackageManager = (name) => PACKAGE_MANAGERS.includes(name);
|
|
17
|
+
/**
|
|
18
|
+
* The package manager this runs under: `npm create`, `pnpm create`,
|
|
19
|
+
* `yarn create` and `bun create` each say so in `npm_config_user_agent`.
|
|
20
|
+
*/
|
|
21
|
+
const detectPackageManager = (userAgent = process.env.npm_config_user_agent) => {
|
|
22
|
+
const name = userAgent?.split("/")[0];
|
|
23
|
+
return isPackageManager(name) ? name : "npm";
|
|
24
|
+
};
|
|
25
|
+
/** The command that runs a `package.json` script with `packageManager`. */
|
|
26
|
+
const runScript = (packageManager, script) => {
|
|
27
|
+
switch (packageManager) {
|
|
28
|
+
case "npm":
|
|
29
|
+
case "bun": return [
|
|
30
|
+
packageManager,
|
|
31
|
+
"run",
|
|
32
|
+
script
|
|
33
|
+
];
|
|
34
|
+
default: return [packageManager, script];
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/scaffold.ts
|
|
39
|
+
/** Copies the template into `targetDir` and makes it the project `name`. */
|
|
40
|
+
const scaffold = ({ templateDir, targetDir, name, sdkVersion }) => {
|
|
41
|
+
mkdirSync(targetDir, { recursive: true });
|
|
42
|
+
cpSync(templateDir, targetDir, { recursive: true });
|
|
43
|
+
renameSync(join(targetDir, "_gitignore"), join(targetDir, ".gitignore"));
|
|
44
|
+
const packageJsonPath = join(targetDir, "package.json");
|
|
45
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
46
|
+
packageJson.name = name;
|
|
47
|
+
packageJson.dependencies["@zodiaceco/sdk"] = `^${sdkVersion}`;
|
|
48
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
|
|
49
|
+
};
|
|
50
|
+
/** Whether `dir` is missing or holds nothing but a git repository. */
|
|
51
|
+
const isEmptyDir = (dir) => !existsSync(dir) || readdirSync(dir).every((entry) => entry === ".git");
|
|
52
|
+
/** Removes everything in `dir` but its git repository. */
|
|
53
|
+
const emptyDir = (dir) => {
|
|
54
|
+
for (const entry of readdirSync(dir)) if (entry !== ".git") rmSync(join(dir, entry), {
|
|
55
|
+
recursive: true,
|
|
56
|
+
force: true
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
/** A valid package name after the directory the project goes in. */
|
|
60
|
+
const toPackageName = (targetDir) => basename(resolve(targetDir)).toLowerCase().replace(/[^a-z0-9._~-]+/g, "-").replace(/^[._-]+|-+$/g, "") || "zodiac-constellation";
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/workspace.ts
|
|
63
|
+
/** The workspace every org starts with, and the one the template names. */
|
|
64
|
+
const DEFAULT_WORKSPACE = "Default workspace";
|
|
65
|
+
const ENTRYPOINT = join("constellation", "index.ts");
|
|
66
|
+
/**
|
|
67
|
+
* The workspaces of the org the project is authorized for, after `zodiac
|
|
68
|
+
* pull` wrote their accounts to `.zodiac/`. None before.
|
|
69
|
+
*/
|
|
70
|
+
const readWorkspaces = (projectDir) => {
|
|
71
|
+
try {
|
|
72
|
+
const codegen = createRequire(import.meta.url)(join(projectDir, ".zodiac"));
|
|
73
|
+
return Object.keys(codegen.accounts ?? {});
|
|
74
|
+
} catch {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
/** Points the template's constellation at `workspace`. */
|
|
79
|
+
const setWorkspace = (projectDir, workspace) => {
|
|
80
|
+
const path = join(projectDir, ENTRYPOINT);
|
|
81
|
+
writeFileSync(path, readFileSync(path, "utf8").replace(`workspace: ${JSON.stringify(DEFAULT_WORKSPACE)}`, () => `workspace: ${JSON.stringify(workspace)}`));
|
|
82
|
+
};
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/index.ts
|
|
85
|
+
const HELP = `Usage: npm create @zodiaceco [directory] [options]
|
|
86
|
+
|
|
87
|
+
Starts a Zodiac constellation project in [directory].
|
|
88
|
+
|
|
89
|
+
Options:
|
|
90
|
+
--pm <name> package manager to install with: ${PACKAGE_MANAGERS.join(", ")} (default: the one running this)
|
|
91
|
+
--no-install don't install dependencies
|
|
92
|
+
--no-git don't initialize a git repository
|
|
93
|
+
--no-auth don't connect the project to Zodiac
|
|
94
|
+
--overwrite empty a directory that isn't empty
|
|
95
|
+
-y, --yes don't ask, take the defaults
|
|
96
|
+
-h, --help print this help
|
|
97
|
+
-v, --version print the version
|
|
98
|
+
`;
|
|
99
|
+
const DEFAULT_DIRECTORY = "zodiac-constellation";
|
|
100
|
+
const ownVersion = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
101
|
+
const templateDir = fileURLToPath(new URL("../templates/constellation", import.meta.url));
|
|
102
|
+
const main = async () => {
|
|
103
|
+
const { values, positionals } = parseArgs({
|
|
104
|
+
allowPositionals: true,
|
|
105
|
+
options: {
|
|
106
|
+
pm: { type: "string" },
|
|
107
|
+
"no-install": { type: "boolean" },
|
|
108
|
+
"no-git": { type: "boolean" },
|
|
109
|
+
"no-auth": { type: "boolean" },
|
|
110
|
+
overwrite: { type: "boolean" },
|
|
111
|
+
yes: {
|
|
112
|
+
type: "boolean",
|
|
113
|
+
short: "y"
|
|
114
|
+
},
|
|
115
|
+
help: {
|
|
116
|
+
type: "boolean",
|
|
117
|
+
short: "h"
|
|
118
|
+
},
|
|
119
|
+
version: {
|
|
120
|
+
type: "boolean",
|
|
121
|
+
short: "v"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
if (values.help) return console.log(HELP);
|
|
126
|
+
if (values.version) return console.log(ownVersion);
|
|
127
|
+
if (values.pm != null && !isPackageManager(values.pm)) throw new UsageError(`--pm must be one of ${PACKAGE_MANAGERS.join(", ")}, not "${values.pm}".`);
|
|
128
|
+
const interactive = process.stdin.isTTY === true && !values.yes;
|
|
129
|
+
const packageManager = values.pm ?? detectPackageManager();
|
|
130
|
+
prompts.intro("Zodiac constellation");
|
|
131
|
+
const directory = positionals[0] ?? (interactive ? orCancel(await prompts.text({
|
|
132
|
+
message: "Where should the project go?",
|
|
133
|
+
placeholder: DEFAULT_DIRECTORY,
|
|
134
|
+
defaultValue: DEFAULT_DIRECTORY
|
|
135
|
+
})) : DEFAULT_DIRECTORY);
|
|
136
|
+
const targetDir = resolve(directory);
|
|
137
|
+
if (!isEmptyDir(targetDir)) {
|
|
138
|
+
if (!(values.overwrite || interactive && orCancel(await prompts.confirm({
|
|
139
|
+
message: `${directory} isn't empty. Remove what's in it?`,
|
|
140
|
+
initialValue: false
|
|
141
|
+
})))) throw new UsageError(`${directory} isn't empty. Pick another directory, or pass --overwrite.`);
|
|
142
|
+
emptyDir(targetDir);
|
|
143
|
+
}
|
|
144
|
+
scaffold({
|
|
145
|
+
templateDir,
|
|
146
|
+
targetDir,
|
|
147
|
+
name: toPackageName(targetDir),
|
|
148
|
+
sdkVersion: ownVersion
|
|
149
|
+
});
|
|
150
|
+
prompts.log.success(`Created the project in ${directory}`);
|
|
151
|
+
const install = !values["no-install"] && (!interactive || orCancel(await prompts.confirm({ message: `Install dependencies with ${packageManager}?` })));
|
|
152
|
+
const installed = install && run([packageManager, "install"], targetDir);
|
|
153
|
+
if (install && !installed) prompts.log.warn(`Installing with ${packageManager} failed.`);
|
|
154
|
+
const connect = installed && interactive && !values["no-auth"] && orCancel(await prompts.confirm({ message: "Connect the project to Zodiac now? This opens your browser to sign in." }));
|
|
155
|
+
const connected = connect && run(runScript(packageManager, "pull"), targetDir);
|
|
156
|
+
if (connect && !connected) prompts.log.warn("Connecting to Zodiac failed.");
|
|
157
|
+
if (connected) await pickWorkspace(targetDir);
|
|
158
|
+
if (!values["no-git"]) initGit(targetDir);
|
|
159
|
+
const next = [
|
|
160
|
+
`cd ${shellQuote(relative(process.cwd(), targetDir) || ".")}`,
|
|
161
|
+
!installed && `${packageManager} install`,
|
|
162
|
+
!connected && runScript(packageManager, "pull").join(" "),
|
|
163
|
+
"edit constellation/index.ts and constellation/roles/",
|
|
164
|
+
runScript(packageManager, "push").join(" ")
|
|
165
|
+
].filter(Boolean);
|
|
166
|
+
prompts.note(next.join("\n"), "Next steps");
|
|
167
|
+
prompts.outro("README.md explains the rest.");
|
|
168
|
+
};
|
|
169
|
+
/** Points the constellation at the org's workspace, asking if it has several. */
|
|
170
|
+
const pickWorkspace = async (targetDir) => {
|
|
171
|
+
const workspaces = readWorkspaces(targetDir);
|
|
172
|
+
if (workspaces.length === 0) return;
|
|
173
|
+
const workspace = workspaces.length === 1 ? workspaces[0] : orCancel(await prompts.select({
|
|
174
|
+
message: "Which workspace is the constellation for?",
|
|
175
|
+
options: workspaces.map((value) => ({
|
|
176
|
+
value,
|
|
177
|
+
label: value
|
|
178
|
+
})),
|
|
179
|
+
initialValue: workspaces.includes("Default workspace") ? DEFAULT_WORKSPACE : workspaces[0]
|
|
180
|
+
}));
|
|
181
|
+
if (workspace !== "Default workspace") {
|
|
182
|
+
setWorkspace(targetDir, workspace);
|
|
183
|
+
prompts.log.success(`The constellation is for the workspace "${workspace}".`);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
/** Commits the new project to a repository of its own, if git is there. */
|
|
187
|
+
const initGit = (targetDir) => {
|
|
188
|
+
const git = (...args) => spawnSync("git", args, {
|
|
189
|
+
cwd: targetDir,
|
|
190
|
+
stdio: "ignore"
|
|
191
|
+
}).status === 0;
|
|
192
|
+
if (!git("--version")) return;
|
|
193
|
+
if (git("rev-parse", "--is-inside-work-tree")) return;
|
|
194
|
+
if (!git("init", "--quiet")) return;
|
|
195
|
+
if (git("add", "--all") && git("commit", "--quiet", "-m", "Initial commit from @zodiaceco/create")) prompts.log.success("Initialized a git repository");
|
|
196
|
+
else prompts.log.info("Initialized a git repository, without committing to it.");
|
|
197
|
+
};
|
|
198
|
+
/** Runs a command in `cwd` with its output shown. True if it succeeded. */
|
|
199
|
+
const run = ([command, ...args], cwd) => spawnSync(command, args, {
|
|
200
|
+
cwd,
|
|
201
|
+
stdio: "inherit",
|
|
202
|
+
shell: process.platform === "win32"
|
|
203
|
+
}).status === 0;
|
|
204
|
+
const shellQuote = (path) => /^[\w./~-]+$/.test(path) ? path : `"${path.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
205
|
+
var UsageError = class extends Error {};
|
|
206
|
+
/** An answer, or the end of the run when it was cancelled with Ctrl-C. */
|
|
207
|
+
const orCancel = (value) => {
|
|
208
|
+
if (prompts.isCancel(value)) {
|
|
209
|
+
prompts.cancel("Cancelled.");
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
return value;
|
|
213
|
+
};
|
|
214
|
+
main().catch((error) => {
|
|
215
|
+
if (error instanceof UsageError) prompts.cancel(error.message);
|
|
216
|
+
else console.error(error);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
});
|
|
219
|
+
//#endregion
|
|
220
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zodiaceco/create",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "2.7.0",
|
|
4
|
+
"description": "Start a Zodiac constellation project: npm create @zodiaceco",
|
|
5
|
+
"author": "Zodiac Labs GmbH",
|
|
6
|
+
"license": "LGPL-3.0-only",
|
|
7
|
+
"homepage": "https://github.com/zodiaceco/zodiac-sdk/tree/main/create",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/zodiaceco/zodiac-sdk.git",
|
|
11
|
+
"directory": "create"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"create": "dist/index.mjs"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"templates",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsdown",
|
|
27
|
+
"check:types": "tsc -p tsconfig.json",
|
|
28
|
+
"test": "bun test"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@clack/prompts": "^1.8.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/bun": "^1.2.21",
|
|
35
|
+
"tsdown": "~0.21.9",
|
|
36
|
+
"typescript": "^5.9.2"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Zodiac Constellation — agent notes
|
|
2
|
+
|
|
3
|
+
This repo is a **Zodiac constellation**: a TypeScript description of an org's smart-account graph (Safes, mods, users, permissions). `npm run push` sends the spec to Zodiac, which diffs it against onchain state and gives a UI to review + sign the transactions.
|
|
4
|
+
|
|
5
|
+
User-facing docs are in `README.md` — read it before suggesting edits to `constellation/`.
|
|
6
|
+
|
|
7
|
+
## Project map
|
|
8
|
+
|
|
9
|
+
- `constellation/index.ts` — entrypoint. **Only exported values get pushed.**
|
|
10
|
+
- `constellation/roles/<role>/` — `members.ts` (addresses) + `permissions.ts`
|
|
11
|
+
(a list of entries: `custom`, `defikit`, `swap`, `transfer` from
|
|
12
|
+
`@zodiaceco/sdk/actions`, or a bare `allow` kit permission).
|
|
13
|
+
- `constellation/allowances/` — reusable Roles allowance objects (key, refill, period, ...).
|
|
14
|
+
A permission draws on one by naming its key: `c.withinAllowance("usdc_payouts")`.
|
|
15
|
+
- `zodiac.config.ts` — contracts the `allow` kit should know about.
|
|
16
|
+
- `.zodiac/` — generated codegen; **never edit by hand**. Re-run `npm run pull` (or `pull-org` / `pull-contracts`) to refresh.
|
|
17
|
+
|
|
18
|
+
## Conventions
|
|
19
|
+
|
|
20
|
+
- Entries **describe, they never compile**. `defikit` stores a protocol, verb
|
|
21
|
+
and parameters; `swap` stores its token lists. The permissions are built when
|
|
22
|
+
the constellation is deployed, so a stored revision never carries a copy of
|
|
23
|
+
what a preset meant on the day it was written.
|
|
24
|
+
|
|
25
|
+
- Import `constellation`, `allow`, `c` and the `Members` / `Permissions` types from `@zodiaceco/sdk`, the entries from `@zodiaceco/sdk/actions`.
|
|
26
|
+
- After editing `zodiac.config.ts` (contracts) or anything that changes referenced accounts/users, run `npm run pull` so the generated types match.
|
|
27
|
+
- Any package manager works: run the scripts with `npm run`, `pnpm`, `yarn` or `bun run`. The `zodiac` CLI loads `.env` itself.
|
|
28
|
+
- `npx zodiac push --dry-run` prints what would be pushed, without pulling or an API key: use it to check an edit.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Zodiac constellation
|
|
2
|
+
|
|
3
|
+
Your organisation's onchain accounts, roles, and permissions as code.
|
|
4
|
+
|
|
5
|
+
## What's a Zodiac constellation?
|
|
6
|
+
|
|
7
|
+
A **constellation** is a declarative description of an organisation's smart account graph: which Safes you operate, what roles each one delegates, who's allowed to do what, and how the pieces are wired together.
|
|
8
|
+
|
|
9
|
+
You write the desired state in TypeScript. Zodiac figures out the diff against what's currently onchain, plans the most efficient set of transactions, and gives you a UI to review and sign them. Anyone with access to the workspace sees the same plan and the same audit trail.
|
|
10
|
+
|
|
11
|
+
## Why manage your accounts as code?
|
|
12
|
+
|
|
13
|
+
Treat your smart account graph the way you treat infrastructure: declare it as code, version it in git, ship it like software. AI-assisted edits, branch + PR review, CI dry runs, and a safe path to onchain via Zodiac's review and signing UI.
|
|
14
|
+
|
|
15
|
+
## Getting started
|
|
16
|
+
|
|
17
|
+
This project starts with one example on Ethereum: a new Safe, a Roles mod on it, and a role that may wrap ETH and supply WETH to Aave.
|
|
18
|
+
|
|
19
|
+
1. `npm install`.
|
|
20
|
+
2. `npm run pull`. On first run, this opens a browser to authorize this directory and mint a Zodiac API key, which it writes to `.env`. It then fetches your org's users and accounts and the ABIs of the contracts in `zodiac.config.ts`, and generates types for them. If you don't have a Zodiac account yet, the browser walks you through sign-up first.
|
|
21
|
+
> `.env` is in `.gitignore`. Don't commit your API key.
|
|
22
|
+
3. Edit `constellation/index.ts` and the role under `constellation/roles/treasury_ops/` to describe the state you want onchain. Start with the placeholder owner and member addresses.
|
|
23
|
+
4. `npm run push`. This sends the constellation to Zodiac and opens a browser tab where you review the diff and sign the transactions.
|
|
24
|
+
|
|
25
|
+
Day-to-day: edit the constellation, then `npm run push`. To permission another contract, add it to `zodiac.config.ts` under its chain prefix and run `npm run pull-contracts`, so the `allow` kit knows it.
|
|
26
|
+
|
|
27
|
+
> **VS Code tip:** if `allow.eth.weth.*` (or whatever you just added) doesn't autocomplete after a pull, restart the TypeScript server: `Cmd+Shift+P` → "TypeScript: Restart TS Server".
|
|
28
|
+
|
|
29
|
+
To check an edit without pushing, `npx zodiac push --dry-run` prints what would be pushed. It needs no API key.
|
|
30
|
+
|
|
31
|
+
Using pnpm, yarn or Bun? Run the same scripts with `pnpm`, `yarn` or `bun run` in place of `npm run`.
|
|
32
|
+
|
|
33
|
+
## Project layout
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
.
|
|
37
|
+
├── constellation/
|
|
38
|
+
│ ├── index.ts # entrypoint — what gets pushed
|
|
39
|
+
│ ├── allowances/ # reusable Roles-allowance objects
|
|
40
|
+
│ └── roles/<role>/ # one folder per role
|
|
41
|
+
│ ├── index.ts
|
|
42
|
+
│ ├── members.ts # addresses allowed to assume the role
|
|
43
|
+
│ └── permissions.ts # what the role can call
|
|
44
|
+
├── zodiac.config.ts # contracts the `allow` kit should know about
|
|
45
|
+
├── abis/ # contract ABIs, fetched by `npm run pull-contracts`
|
|
46
|
+
├── .zodiac/ # generated codegen (git-ignored)
|
|
47
|
+
└── .env # ZODIAC_API_KEY for this directory (git-ignored)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Defining a constellation
|
|
51
|
+
|
|
52
|
+
`constellation/index.ts` is what gets pushed to Zodiac. Start by scoping a constellation to a single workspace and chain:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { constellation } from "@zodiaceco/sdk";
|
|
56
|
+
|
|
57
|
+
const eth = constellation({
|
|
58
|
+
workspace: "Default workspace",
|
|
59
|
+
label: "Backend Operator",
|
|
60
|
+
chain: 1, // ethereum
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`npm run pull-org` regenerates type-level info for your workspace, so workspace name, chain id, account labels, role labels, and user handles all autocomplete.
|
|
65
|
+
|
|
66
|
+
### Existing vs new nodes
|
|
67
|
+
|
|
68
|
+
Bracket access on `.safe`, `.roles`, and `.user` is your editor for both **existing** accounts (vaults already in your workspace + anything previously pushed via a constellation) and **new** ones you're declaring for the first time.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// Reference an existing Safe verbatim — no invocation
|
|
72
|
+
const treasury = eth.safe["GG Treasury"];
|
|
73
|
+
|
|
74
|
+
// Tweak an existing Safe — invoke with overrides; all fields are optional
|
|
75
|
+
export const tweaked = eth.safe["GG Treasury"]({ threshold: 4 });
|
|
76
|
+
|
|
77
|
+
// Declare a new Safe — required fields are enforced at compile time
|
|
78
|
+
export const backendSafe = eth.safe["Backend Safe"]({
|
|
79
|
+
nonce: 0n,
|
|
80
|
+
threshold: 2,
|
|
81
|
+
owners: ["0xcAF075b4102d96CdF9Bc5A2cEDb170a8b69697E8", eth.user["alice"]],
|
|
82
|
+
modules: [eth.roles["Backend Operator"]],
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For Safes the required fields are `nonce`, `threshold`, `owners`. For Roles mods, `nonce` (plus `owner`, `target`, `avatar` if you want anything other than the wired-up defaults). The SDK injects no runtime defaults — missing required fields surface as TypeScript errors.
|
|
87
|
+
|
|
88
|
+
#### Binding to an existing Roles mod by address
|
|
89
|
+
|
|
90
|
+
Bracket access only knows about accounts in your workspace codegen. To configure a Roles mod that's already deployed on-chain but **not** in your workspace, bind it by `address` instead of declaring a `nonce`. Zodiac diffs your spec against the live mod and updates only what changed:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const arb = constellation({
|
|
94
|
+
workspace: "Default workspace",
|
|
95
|
+
label: "Backend Operator",
|
|
96
|
+
chain: 42161, // arbitrum
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export const ops = arb.roles["Existing roles mod"]({
|
|
100
|
+
address: "0x88A51CcB262d04B334065Ad425928dF79c4CB7d7",
|
|
101
|
+
roles: { backend_operator },
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
A roles node therefore takes **either** `nonce` (deploy a new mod) **or** `address` (bind an existing one) — never both.
|
|
106
|
+
|
|
107
|
+
### Forward references between new nodes
|
|
108
|
+
|
|
109
|
+
New nodes can reference each other before either has been invoked — the uninvoked factory works as a forward reference. Both sides just need to be exported so `push()` resolves them by name:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
export const backendSafe = eth.safe["Backend Safe"]({
|
|
113
|
+
nonce: 0n,
|
|
114
|
+
threshold: 2,
|
|
115
|
+
owners: ["0x1234567890123456789012345678901234567890"],
|
|
116
|
+
modules: [eth.roles["Backend Operator"]], // forward ref — not invoked yet
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export const backendRoles = eth.roles["Backend Operator"]({
|
|
120
|
+
nonce: 0n,
|
|
121
|
+
target: backendSafe,
|
|
122
|
+
owner: backendSafe,
|
|
123
|
+
avatar: backendSafe,
|
|
124
|
+
roles: { backend_operator },
|
|
125
|
+
allowances: { usdc_payouts },
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Users
|
|
130
|
+
|
|
131
|
+
`eth.user["Alice Example"]` resolves to the user's personal Safe address on the constellation's chain. Useful in owner lists and role-member arrays without hardcoding addresses.
|
|
132
|
+
|
|
133
|
+
### Pushing
|
|
134
|
+
|
|
135
|
+
Only the values you `export` end up in the constellation.
|
|
136
|
+
|
|
137
|
+
`npm run push` runs `pull` first, walks your exports, sends the spec to Zodiac, and opens your browser to review the diff and sign the transactions.
|
|
138
|
+
|
|
139
|
+
### Roles and permissions
|
|
140
|
+
|
|
141
|
+
Each role lives in its own folder under `constellation/roles/`:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
constellation/roles/backend_operator/
|
|
145
|
+
├── index.ts # combines members + permissions
|
|
146
|
+
├── members.ts # who can act as this role
|
|
147
|
+
└── permissions.ts # what the role can call
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Members are an array of addresses, user accessors, or node references — anything that resolves to an address is accepted:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import type { Members } from "@zodiaceco/sdk";
|
|
154
|
+
|
|
155
|
+
export default [
|
|
156
|
+
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
|
|
157
|
+
eth.user["alice"],
|
|
158
|
+
eth.safe["Trading Bot"],
|
|
159
|
+
] satisfies Members;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
A role's `permissions` is a list of **entries**. Each entry is one thing the role may do, and each carries a `label` that names it as a single card in the Zodiac UI — labels never reach the chain.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { allow, type Permissions } from "@zodiaceco/sdk";
|
|
166
|
+
import { custom, defikit, swap, transfer } from "@zodiaceco/sdk/actions";
|
|
167
|
+
|
|
168
|
+
export default [
|
|
169
|
+
// A labelled bag of `allow`-kit permissions, for anything the
|
|
170
|
+
// other entries don't cover.
|
|
171
|
+
custom({
|
|
172
|
+
label: "Wrap and unwrap ETH",
|
|
173
|
+
permissions: [
|
|
174
|
+
allow.eth.weth.deposit({ send: true }),
|
|
175
|
+
allow.eth.weth.withdraw(),
|
|
176
|
+
],
|
|
177
|
+
}),
|
|
178
|
+
|
|
179
|
+
// A DeFi Kit preset, by protocol and verb.
|
|
180
|
+
defikit.aave_v3.deposit({
|
|
181
|
+
label: "Supply WETH to Aave v3",
|
|
182
|
+
market: "Core",
|
|
183
|
+
targets: ["WETH"],
|
|
184
|
+
}),
|
|
185
|
+
|
|
186
|
+
// Sign CoW orders selling any of `sell` for any of `buy`.
|
|
187
|
+
swap({
|
|
188
|
+
label: "Rebalance stables",
|
|
189
|
+
sell: [USDC, DAI],
|
|
190
|
+
buy: [USDC, DAI],
|
|
191
|
+
}),
|
|
192
|
+
|
|
193
|
+
// Send tokens to fixed recipients, optionally bridged over Across.
|
|
194
|
+
transfer({
|
|
195
|
+
label: "Payouts",
|
|
196
|
+
tokens: [USDC],
|
|
197
|
+
to: [eth.safe["Payouts"]],
|
|
198
|
+
allowance: usdc_payouts,
|
|
199
|
+
}),
|
|
200
|
+
] satisfies Permissions;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A bare `allow`-kit permission is still a valid entry — it just shows up unlabelled:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
export default [allow.eth.weth.withdraw()] satisfies Permissions;
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Entries describe, they never compile.** A `defikit` entry stores its protocol, verb and parameters; a `swap` stores its token lists. The permissions are built when the constellation is deployed, so a stored revision always compiles through the current compilers instead of replaying a copy made when it was pushed. A compiled `PermissionSet` — what calling `defi-kit` directly returns — is therefore not an entry; reach for `defikit` instead.
|
|
210
|
+
|
|
211
|
+
#### Allowances
|
|
212
|
+
|
|
213
|
+
An allowance is a refilling budget the Roles modifier tracks on-chain. Define it once under `constellation/allowances/`, hand it to the roles node, and reference it from any permission that should draw on it:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
// constellation/allowances/index.ts
|
|
217
|
+
export const usdc_payouts = {
|
|
218
|
+
key: "usdc_payouts",
|
|
219
|
+
refill: 10_000n * 10n ** 6n, // added each period
|
|
220
|
+
maxRefill: 10_000n * 10n ** 6n, // cap the balance refills to
|
|
221
|
+
period: 60n * 60n * 24n, // seconds
|
|
222
|
+
balance: 10_000n * 10n ** 6n, // starting balance
|
|
223
|
+
timestamp: 0n,
|
|
224
|
+
};
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
// constellation/roles/<role>/permissions.ts
|
|
229
|
+
import { allow, c } from "@zodiaceco/sdk";
|
|
230
|
+
|
|
231
|
+
allow.eth.usdc.transfer(undefined, c.withinAllowance("usdc_payouts"));
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
A `key` is at most 31 bytes.
|
|
235
|
+
|
|
236
|
+
#### Adding contracts
|
|
237
|
+
|
|
238
|
+
The `allow` kit only knows about contracts listed in `zodiac.config.ts`. Add the address under the right chain prefix (see [Getting started](#getting-started) for an example), then run `npm run pull-contracts`. If a contract isn't verified on the explorer, the CLI prints the path where you should drop the ABI JSON by hand and re-run.
|
|
239
|
+
|
|
240
|
+
#### Overloaded functions
|
|
241
|
+
|
|
242
|
+
The `allow` kit names each member after its ABI function, so a contract that
|
|
243
|
+
overloads a name — say sUSDS, with both `deposit(uint256,address)` and
|
|
244
|
+
`deposit(uint256,address,uint16)` — has no single member that could mean either.
|
|
245
|
+
Address the one you want by its full signature:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
allow.eth.susds["deposit(uint256,address)"](
|
|
249
|
+
c.withinAllowance("sky_savings"),
|
|
250
|
+
c.avatar,
|
|
251
|
+
);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Commands
|
|
255
|
+
|
|
256
|
+
| Command | What it does |
|
|
257
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
258
|
+
| `npm run pull` | Refresh codegen: org data and contract ABIs. On first run, also authorizes this directory (mints an API key, writes `.env`). |
|
|
259
|
+
| `npm run pull-org` | Just the org data (users, accounts) |
|
|
260
|
+
| `npm run pull-contracts` | Just the contract ABIs and `allow` kit types. Run this after you add a contract to `zodiac.config.ts`. |
|
|
261
|
+
| `npm run push` | Send the constellation to Zodiac and open the review URL. Runs `pull` first, so you push against fresh org state and contracts. |
|
|
262
|
+
| `npm run format` | Prettier across the project |
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "name",
|
|
4
|
+
"type": "function",
|
|
5
|
+
"inputs": [],
|
|
6
|
+
"outputs": [{ "name": "", "type": "string" }],
|
|
7
|
+
"payable": false,
|
|
8
|
+
"constant": true,
|
|
9
|
+
"stateMutability": "view"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "approve",
|
|
13
|
+
"type": "function",
|
|
14
|
+
"inputs": [
|
|
15
|
+
{ "name": "guy", "type": "address" },
|
|
16
|
+
{ "name": "wad", "type": "uint256" }
|
|
17
|
+
],
|
|
18
|
+
"outputs": [{ "name": "", "type": "bool" }],
|
|
19
|
+
"payable": false,
|
|
20
|
+
"constant": false,
|
|
21
|
+
"stateMutability": "nonpayable"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "totalSupply",
|
|
25
|
+
"type": "function",
|
|
26
|
+
"inputs": [],
|
|
27
|
+
"outputs": [{ "name": "", "type": "uint256" }],
|
|
28
|
+
"payable": false,
|
|
29
|
+
"constant": true,
|
|
30
|
+
"stateMutability": "view"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "transferFrom",
|
|
34
|
+
"type": "function",
|
|
35
|
+
"inputs": [
|
|
36
|
+
{ "name": "src", "type": "address" },
|
|
37
|
+
{ "name": "dst", "type": "address" },
|
|
38
|
+
{ "name": "wad", "type": "uint256" }
|
|
39
|
+
],
|
|
40
|
+
"outputs": [{ "name": "", "type": "bool" }],
|
|
41
|
+
"payable": false,
|
|
42
|
+
"constant": false,
|
|
43
|
+
"stateMutability": "nonpayable"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "withdraw",
|
|
47
|
+
"type": "function",
|
|
48
|
+
"inputs": [{ "name": "wad", "type": "uint256" }],
|
|
49
|
+
"outputs": [],
|
|
50
|
+
"payable": false,
|
|
51
|
+
"constant": false,
|
|
52
|
+
"stateMutability": "nonpayable"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "decimals",
|
|
56
|
+
"type": "function",
|
|
57
|
+
"inputs": [],
|
|
58
|
+
"outputs": [{ "name": "", "type": "uint8" }],
|
|
59
|
+
"payable": false,
|
|
60
|
+
"constant": true,
|
|
61
|
+
"stateMutability": "view"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "balanceOf",
|
|
65
|
+
"type": "function",
|
|
66
|
+
"inputs": [{ "name": "", "type": "address" }],
|
|
67
|
+
"outputs": [{ "name": "", "type": "uint256" }],
|
|
68
|
+
"payable": false,
|
|
69
|
+
"constant": true,
|
|
70
|
+
"stateMutability": "view"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "symbol",
|
|
74
|
+
"type": "function",
|
|
75
|
+
"inputs": [],
|
|
76
|
+
"outputs": [{ "name": "", "type": "string" }],
|
|
77
|
+
"payable": false,
|
|
78
|
+
"constant": true,
|
|
79
|
+
"stateMutability": "view"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "transfer",
|
|
83
|
+
"type": "function",
|
|
84
|
+
"inputs": [
|
|
85
|
+
{ "name": "dst", "type": "address" },
|
|
86
|
+
{ "name": "wad", "type": "uint256" }
|
|
87
|
+
],
|
|
88
|
+
"outputs": [{ "name": "", "type": "bool" }],
|
|
89
|
+
"payable": false,
|
|
90
|
+
"constant": false,
|
|
91
|
+
"stateMutability": "nonpayable"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "deposit",
|
|
95
|
+
"type": "function",
|
|
96
|
+
"inputs": [],
|
|
97
|
+
"outputs": [],
|
|
98
|
+
"payable": true,
|
|
99
|
+
"constant": false,
|
|
100
|
+
"stateMutability": "payable"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "allowance",
|
|
104
|
+
"type": "function",
|
|
105
|
+
"inputs": [
|
|
106
|
+
{ "name": "", "type": "address" },
|
|
107
|
+
{ "name": "", "type": "address" }
|
|
108
|
+
],
|
|
109
|
+
"outputs": [{ "name": "", "type": "uint256" }],
|
|
110
|
+
"payable": false,
|
|
111
|
+
"constant": true,
|
|
112
|
+
"stateMutability": "view"
|
|
113
|
+
},
|
|
114
|
+
{ "type": "fallback", "payable": true, "stateMutability": "payable" },
|
|
115
|
+
{
|
|
116
|
+
"name": "Approval",
|
|
117
|
+
"type": "event",
|
|
118
|
+
"inputs": [
|
|
119
|
+
{ "name": "src", "type": "address", "indexed": true },
|
|
120
|
+
{ "name": "guy", "type": "address", "indexed": true },
|
|
121
|
+
{ "name": "wad", "type": "uint256", "indexed": false }
|
|
122
|
+
],
|
|
123
|
+
"anonymous": false
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "Transfer",
|
|
127
|
+
"type": "event",
|
|
128
|
+
"inputs": [
|
|
129
|
+
{ "name": "src", "type": "address", "indexed": true },
|
|
130
|
+
{ "name": "dst", "type": "address", "indexed": true },
|
|
131
|
+
{ "name": "wad", "type": "uint256", "indexed": false }
|
|
132
|
+
],
|
|
133
|
+
"anonymous": false
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"name": "Deposit",
|
|
137
|
+
"type": "event",
|
|
138
|
+
"inputs": [
|
|
139
|
+
{ "name": "dst", "type": "address", "indexed": true },
|
|
140
|
+
{ "name": "wad", "type": "uint256", "indexed": false }
|
|
141
|
+
],
|
|
142
|
+
"anonymous": false
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"name": "Withdrawal",
|
|
146
|
+
"type": "event",
|
|
147
|
+
"inputs": [
|
|
148
|
+
{ "name": "src", "type": "address", "indexed": true },
|
|
149
|
+
{ "name": "wad", "type": "uint256", "indexed": false }
|
|
150
|
+
],
|
|
151
|
+
"anonymous": false
|
|
152
|
+
}
|
|
153
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { constellation } from "@zodiaceco/sdk";
|
|
2
|
+
import { treasury_ops } from "./roles";
|
|
3
|
+
|
|
4
|
+
const eth = constellation({
|
|
5
|
+
workspace: "Default workspace",
|
|
6
|
+
label: "Treasury",
|
|
7
|
+
chain: 1, // Ethereum
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// A new Safe: replace the owner with your signers, then push.
|
|
11
|
+
export const treasury = eth.safe["Treasury"]({
|
|
12
|
+
nonce: 0n,
|
|
13
|
+
threshold: 1,
|
|
14
|
+
owners: ["0x0000000000000000000000000000000000000001"],
|
|
15
|
+
modules: [eth.roles["Treasury Roles"]],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// A Roles mod on the Safe, holding the roles under `./roles`.
|
|
19
|
+
export const treasuryRoles = eth.roles["Treasury Roles"]({
|
|
20
|
+
nonce: 0n,
|
|
21
|
+
target: treasury,
|
|
22
|
+
owner: treasury,
|
|
23
|
+
avatar: treasury,
|
|
24
|
+
roles: { treasury_ops },
|
|
25
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as treasury_ops from "./treasury_ops";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { allow, type Permissions } from "@zodiaceco/sdk";
|
|
2
|
+
import { custom, defikit } from "@zodiaceco/sdk/actions";
|
|
3
|
+
|
|
4
|
+
// Each entry is one thing the role may do. Its label names it in the Zodiac
|
|
5
|
+
// app and never reaches the chain.
|
|
6
|
+
export default [
|
|
7
|
+
// `allow` kit permissions for the contracts in zodiac.config.ts.
|
|
8
|
+
custom({
|
|
9
|
+
label: "Wrap and unwrap ETH",
|
|
10
|
+
permissions: [
|
|
11
|
+
allow.eth.weth.deposit({ send: true }),
|
|
12
|
+
allow.eth.weth.withdraw(),
|
|
13
|
+
],
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
// A DeFi Kit preset, compiled when the constellation is deployed.
|
|
17
|
+
defikit.aave_v3.deposit({
|
|
18
|
+
label: "Supply WETH to Aave v3",
|
|
19
|
+
market: "Core",
|
|
20
|
+
targets: ["WETH"],
|
|
21
|
+
}),
|
|
22
|
+
] satisfies Permissions;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zodiac-constellation",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"format": "prettier --write .",
|
|
7
|
+
"pull": "zodiac pull",
|
|
8
|
+
"pull-org": "zodiac pull-org",
|
|
9
|
+
"pull-contracts": "zodiac pull-contracts",
|
|
10
|
+
"push": "zodiac pull && zodiac push"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@zodiaceco/sdk": "^2.7.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/node": "^22",
|
|
17
|
+
"prettier": "^3.6.2",
|
|
18
|
+
"typescript": "^5"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// "incremental": true, /* Enable incremental compilation */
|
|
4
|
+
"target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
|
|
5
|
+
"module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
|
|
6
|
+
"strict": true /* Enable all strict type-checking options. */,
|
|
7
|
+
"moduleResolution": "bundler" /* Respect package.json `exports` subpaths (required for @zodiaceco/sdk/allow etc). */,
|
|
8
|
+
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
|
|
12
|
+
"typeRoots": ["./node_modules/@types"],
|
|
13
|
+
"lib": ["ES2023"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["constellation/**/*", "zodiac.config.ts", ".zodiac/**/*"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineConfig } from "@zodiaceco/sdk/cli/config";
|
|
2
|
+
|
|
3
|
+
// The contracts the `allow` kit knows, by chain prefix. After changing them,
|
|
4
|
+
// run `npm run pull-contracts` to fetch their ABIs into `abis/`.
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
contracts: {
|
|
7
|
+
eth: {
|
|
8
|
+
weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
});
|