@zodiac-os/sdk 1.2.0 → 1.3.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 +34 -0
- package/dist/allow/index.d.mts +2 -0
- package/dist/allow/index.mjs +3 -0
- package/dist/allow-Dzh6t_l8.mjs +122 -0
- package/dist/allow-Dzh6t_l8.mjs.map +1 -0
- package/dist/cli/config.d.mts +35 -0
- package/dist/cli/config.mjs +31 -0
- package/dist/cli/config.mjs.map +1 -0
- package/dist/cli.mjs +224 -35
- package/dist/cli.mjs.map +1 -1
- package/dist/index-DTBaxN7b.d.mts +63 -0
- package/dist/index.d.mts +24 -22
- package/dist/index.mjs +48 -15
- package/dist/index.mjs.map +1 -1
- package/dist/networks-BTW1qAAa.mjs +77 -0
- package/dist/networks-BTW1qAAa.mjs.map +1 -0
- package/dist/zodiac-os-codegen.d.ts +10 -3
- package/package.json +25 -12
package/README.md
CHANGED
|
@@ -99,6 +99,40 @@ const newRoles = eth.roles['New Roles']({
|
|
|
99
99
|
})
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
### Canonical Roles mods
|
|
103
|
+
|
|
104
|
+
Every Safe has a canonical Roles mod hosting policies applied through the app. When you use the Safe label on the `roles` accessor, it resolves to that Safe's canonical Roles mod automatically.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
// Enable roles on an existing Safe in your org
|
|
108
|
+
const daoRoles = eth.roles['GG DAO']({
|
|
109
|
+
roles: [
|
|
110
|
+
/* ... */
|
|
111
|
+
],
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Circular references between new nodes
|
|
116
|
+
|
|
117
|
+
New nodes can reference each other before either has been invoked — use the uninvoked factory as a forward reference:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const safe = eth.safe['New Safe']({
|
|
121
|
+
nonce: 0n,
|
|
122
|
+
threshold: 1,
|
|
123
|
+
owners: [eth.user['Alice Sample']],
|
|
124
|
+
// Forward reference to a Roles mod that doesn't exist yet
|
|
125
|
+
modules: [eth.roles['New Roles']],
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const roles = eth.roles['New Roles']({
|
|
129
|
+
nonce: 0n,
|
|
130
|
+
target: safe,
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
References are resolved by label at `apply()` time, so both sides of the cycle must be included in the call.
|
|
135
|
+
|
|
102
136
|
### Referencing users
|
|
103
137
|
|
|
104
138
|
`eth.user[handle]` resolves a user to their personal Safe address on the current chain:
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as EVERYTHING, c as Scoping, i as ConditionFunction, l as TargetPermission, n as ChainPrefix, o as FunctionPermission, r as chainIdFor, s as Options, t as CHAIN_IDS, u as buildAllowKit } from "../index-DTBaxN7b.mjs";
|
|
2
|
+
export { CHAIN_IDS, ChainPrefix, ConditionFunction, EVERYTHING, FunctionPermission, Options, Scoping, TargetPermission, buildAllowKit, chainIdFor };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { a as walkContracts, i as readAbi } from "./networks-BTW1qAAa.mjs";
|
|
2
|
+
import { c, coercePermission } from "zodiac-roles-sdk";
|
|
3
|
+
import { Interface, isError } from "ethers";
|
|
4
|
+
import { Operator, ParameterType } from "zodiac-roles-deployments";
|
|
5
|
+
//#region src/allow/types.ts
|
|
6
|
+
const EVERYTHING = Symbol.for("@zodiac-os/allow-kit/EVERYTHING");
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/allow/runtime.ts
|
|
9
|
+
function buildAllowKit(abisDir, contractsConfig) {
|
|
10
|
+
const kit = {};
|
|
11
|
+
for (const node of walkContracts(contractsConfig)) {
|
|
12
|
+
const abi = readAbi(abisDir, node);
|
|
13
|
+
if (!abi) {
|
|
14
|
+
attachAt(kit, [node.chain, ...node.segments], missingAbiProxy(node));
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
attachAt(kit, [node.chain, ...node.segments], makeAllowContract(node.address, abi));
|
|
18
|
+
}
|
|
19
|
+
return kit;
|
|
20
|
+
}
|
|
21
|
+
function attachAt(root, segments, value) {
|
|
22
|
+
let cursor = root;
|
|
23
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
24
|
+
const seg = segments[i];
|
|
25
|
+
if (!(seg in cursor)) cursor[seg] = {};
|
|
26
|
+
cursor = cursor[seg];
|
|
27
|
+
}
|
|
28
|
+
cursor[segments[segments.length - 1]] = value;
|
|
29
|
+
}
|
|
30
|
+
function missingAbiProxy(node) {
|
|
31
|
+
const explain = () => {
|
|
32
|
+
throw new Error(`ABI missing for ${node.chain}.${node.segments.join(".")} (${node.address}). Run \`zodiac-os pull-contracts\` to fetch it, or paste the ABI JSON manually at <abisDir>/${node.chain}/${node.segments.join("/")}.json`);
|
|
33
|
+
};
|
|
34
|
+
return new Proxy({}, {
|
|
35
|
+
get: explain,
|
|
36
|
+
has: explain
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function makeAllowContract(address, abi) {
|
|
40
|
+
const iface = Interface.from(abi);
|
|
41
|
+
const lowerAddr = address.toLowerCase();
|
|
42
|
+
const allowEverything = (options) => ({
|
|
43
|
+
targetAddress: lowerAddr,
|
|
44
|
+
send: options?.send,
|
|
45
|
+
delegatecall: options?.delegatecall
|
|
46
|
+
});
|
|
47
|
+
const has = (name) => {
|
|
48
|
+
try {
|
|
49
|
+
const fn = iface.getFunction(name);
|
|
50
|
+
if (!fn) return false;
|
|
51
|
+
return fn.stateMutability !== "view" && fn.stateMutability !== "pure";
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (!isError(error, "INVALID_ARGUMENT")) throw error;
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
return new Proxy({}, {
|
|
58
|
+
get: (_target, prop) => {
|
|
59
|
+
if (prop === EVERYTHING) return allowEverything;
|
|
60
|
+
if (typeof prop !== "string") return void 0;
|
|
61
|
+
if (!has(prop)) return void 0;
|
|
62
|
+
return makeAllowFunction(iface.getFunction(prop), lowerAddr);
|
|
63
|
+
},
|
|
64
|
+
has: (_target, prop) => {
|
|
65
|
+
if (prop === EVERYTHING) return true;
|
|
66
|
+
return typeof prop === "string" && has(prop);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function makeAllowFunction(fn, targetAddress) {
|
|
71
|
+
const inputs = fn.inputs;
|
|
72
|
+
return (...args) => {
|
|
73
|
+
const scopings = args.slice(0, inputs.length);
|
|
74
|
+
const hasScopings = scopings.some((s) => s !== void 0 && s !== null);
|
|
75
|
+
const options = args[inputs.length] ?? {};
|
|
76
|
+
const condition = hasScopings ? c.calldataMatches(scopings, inputs)() : void 0;
|
|
77
|
+
return applyOptions(coercePermission({
|
|
78
|
+
targetAddress,
|
|
79
|
+
signature: fn.format("sighash"),
|
|
80
|
+
condition
|
|
81
|
+
}), options);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const emptyCalldataMatches = {
|
|
85
|
+
paramType: ParameterType.Calldata,
|
|
86
|
+
operator: Operator.Matches,
|
|
87
|
+
children: []
|
|
88
|
+
};
|
|
89
|
+
const applyGlobalAllowance = (condition, allowanceCondition) => {
|
|
90
|
+
const base = condition ?? emptyCalldataMatches;
|
|
91
|
+
if (base.paramType !== ParameterType.Calldata || base.operator !== Operator.Matches) throw new Error("Global allowance can only be applied to calldata matches nodes");
|
|
92
|
+
return {
|
|
93
|
+
...base,
|
|
94
|
+
children: [...base.children ?? [], allowanceCondition]
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
const applyOptions = (permission, options) => {
|
|
98
|
+
let condition = permission.condition;
|
|
99
|
+
if (options.etherWithinAllowance) {
|
|
100
|
+
if (!options.send) throw new Error("`etherWithinAllowance` can only be used if `send` is allowed");
|
|
101
|
+
condition = applyGlobalAllowance(condition, {
|
|
102
|
+
paramType: ParameterType.None,
|
|
103
|
+
operator: Operator.EtherWithinAllowance,
|
|
104
|
+
compValue: options.etherWithinAllowance
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (options.callWithinAllowance) condition = applyGlobalAllowance(condition, {
|
|
108
|
+
paramType: ParameterType.None,
|
|
109
|
+
operator: Operator.CallWithinAllowance,
|
|
110
|
+
compValue: options.callWithinAllowance
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
...permission,
|
|
114
|
+
send: options.send,
|
|
115
|
+
delegatecall: options.delegatecall,
|
|
116
|
+
condition
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
//#endregion
|
|
120
|
+
export { EVERYTHING as n, buildAllowKit as t };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=allow-Dzh6t_l8.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allow-Dzh6t_l8.mjs","names":[],"sources":["../src/allow/types.ts","../src/allow/runtime.ts"],"sourcesContent":["import type { BigNumberish, BytesLike, ParamType } from 'ethers'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\n\nexport type Options = {\n send?: boolean\n delegatecall?: boolean\n etherWithinAllowance?: `0x${string}`\n callWithinAllowance?: `0x${string}`\n}\n\nexport type PrimitiveValue = BigNumberish | BytesLike | string | boolean\n\n// Signature matches `zodiac-roles-sdk` so values from `c.*` are assignable.\nexport type ConditionFunction<T = unknown> = (\n abiType: ParamType,\n _?: T\n) => Condition\n\ntype RequireAtLeastOne<T> = {\n [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>\n}[keyof T]\n\ntype ArrayElement<T extends readonly unknown[]> = T extends readonly (infer U)[]\n ? U\n : never\n\nexport type PrimitiveScoping<T extends PrimitiveValue> =\n | T\n | ConditionFunction<T>\n\nexport type ArrayScoping<T extends readonly any[]> =\n | readonly Scoping<ArrayElement<T>>[]\n | ConditionFunction<T>\n\nexport type StructScoping<T extends { [key: string]: any }> =\n | RequireAtLeastOne<{ [K in keyof T]?: Scoping<T[K]> }>\n | ConditionFunction<T>\n\nexport type Scoping<T> = T extends PrimitiveValue\n ? PrimitiveScoping<T>\n : T extends readonly any[]\n ? ArrayScoping<T>\n : T extends { [key: string]: any }\n ? StructScoping<T>\n : unknown\n\nexport type { FunctionPermission, TargetPermission }\n\nexport const EVERYTHING = Symbol.for('@zodiac-os/allow-kit/EVERYTHING')\nexport type EVERYTHING = typeof EVERYTHING\n","import { Interface, FunctionFragment, isError, type InterfaceAbi } from 'ethers'\nimport { c, coercePermission } from 'zodiac-roles-sdk'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\nimport { ParameterType, Operator } from 'zodiac-roles-deployments'\nimport { readAbi, walkContracts, type ContractNode } from './abi'\nimport { EVERYTHING, type Options } from './types'\n\nexport function buildAllowKit(\n abisDir: string,\n contractsConfig: Record<string, any>\n): Record<string, any> {\n const kit: Record<string, any> = {}\n for (const node of walkContracts(contractsConfig)) {\n const abi = readAbi(abisDir, node)\n if (!abi) {\n // Defer the error until the user touches this contract — otherwise an\n // ABI missing for one chain crashes all unrelated role definitions.\n attachAt(kit, [node.chain, ...node.segments], missingAbiProxy(node))\n continue\n }\n attachAt(\n kit,\n [node.chain, ...node.segments],\n makeAllowContract(node.address, abi as InterfaceAbi)\n )\n }\n return kit\n}\n\nfunction attachAt(root: Record<string, any>, segments: string[], value: any) {\n let cursor = root\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!\n if (!(seg in cursor)) cursor[seg] = {}\n cursor = cursor[seg]\n }\n cursor[segments[segments.length - 1]!] = value\n}\n\nfunction missingAbiProxy(node: ContractNode) {\n const explain = () => {\n throw new Error(\n `ABI missing for ${node.chain}.${node.segments.join('.')} ` +\n `(${node.address}). Run \\`zodiac-os pull-contracts\\` to fetch it, or ` +\n `paste the ABI JSON manually at <abisDir>/${node.chain}/${node.segments.join('/')}.json`\n )\n }\n return new Proxy(\n {},\n {\n get: explain,\n has: explain,\n }\n )\n}\n\nfunction makeAllowContract(\n address: `0x${string}`,\n abi: InterfaceAbi\n): Record<string | symbol, any> {\n const iface = Interface.from(abi)\n const lowerAddr = address.toLowerCase() as `0x${string}`\n\n const allowEverything = (options?: Options): TargetPermission => ({\n targetAddress: lowerAddr,\n send: options?.send,\n delegatecall: options?.delegatecall,\n })\n\n const has = (name: string) => {\n try {\n const fn = iface.getFunction(name)\n if (!fn) return false\n return fn.stateMutability !== 'view' && fn.stateMutability !== 'pure'\n } catch (error) {\n if (!isError(error as any, 'INVALID_ARGUMENT')) throw error\n return false\n }\n }\n\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === EVERYTHING) return allowEverything\n if (typeof prop !== 'string') return undefined\n if (!has(prop)) return undefined\n const fn = iface.getFunction(prop)!\n return makeAllowFunction(fn, lowerAddr)\n },\n has: (_target, prop) => {\n if (prop === EVERYTHING) return true\n return typeof prop === 'string' && has(prop)\n },\n }\n )\n}\n\nfunction makeAllowFunction(\n fn: FunctionFragment,\n targetAddress: `0x${string}`\n): (...args: any[]) => FunctionPermission {\n const inputs = fn.inputs\n return (...args: any[]) => {\n const scopings = args.slice(0, inputs.length)\n const hasScopings = scopings.some((s) => s !== undefined && s !== null)\n const options: Options = args[inputs.length] ?? {}\n const condition = hasScopings\n ? c.calldataMatches(scopings, inputs)()\n : undefined\n const preset = {\n targetAddress,\n signature: fn.format('sighash'),\n condition,\n }\n return applyOptions(coercePermission(preset as any) as any, options)\n }\n}\n\nconst emptyCalldataMatches: Condition = {\n paramType: ParameterType.Calldata,\n operator: Operator.Matches,\n children: [],\n}\n\nconst applyGlobalAllowance = (\n condition: Condition | undefined,\n allowanceCondition: Condition\n): Condition => {\n const base = condition ?? emptyCalldataMatches\n if (\n base.paramType !== ParameterType.Calldata ||\n base.operator !== Operator.Matches\n ) {\n throw new Error(\n 'Global allowance can only be applied to calldata matches nodes'\n )\n }\n return {\n ...base,\n children: [...(base.children ?? []), allowanceCondition],\n }\n}\n\nconst applyOptions = (\n permission: FunctionPermission & { condition?: Condition },\n options: Options\n): FunctionPermission => {\n let condition = permission.condition\n if (options.etherWithinAllowance) {\n if (!options.send) {\n throw new Error(\n '`etherWithinAllowance` can only be used if `send` is allowed'\n )\n }\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.EtherWithinAllowance,\n compValue: options.etherWithinAllowance,\n })\n }\n if (options.callWithinAllowance) {\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.CallWithinAllowance,\n compValue: options.callWithinAllowance,\n })\n }\n return {\n ...permission,\n send: options.send,\n delegatecall: options.delegatecall,\n condition,\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/cli/config.d.ts
|
|
2
|
+
type Contracts = {
|
|
3
|
+
[chain: string]: ContractsNode;
|
|
4
|
+
};
|
|
5
|
+
type ContractsNode = `0x${string}` | {
|
|
6
|
+
[name: string]: ContractsNode;
|
|
7
|
+
};
|
|
8
|
+
interface ZodiacConfig {
|
|
9
|
+
apiKey: `zodiac_${string}`;
|
|
10
|
+
/**
|
|
11
|
+
* Contracts the `allow` kit should know about, keyed by chain prefix.
|
|
12
|
+
* Nested objects are allowed for grouping related addresses.
|
|
13
|
+
*/
|
|
14
|
+
contracts?: Contracts;
|
|
15
|
+
/**
|
|
16
|
+
* Directory where fetched ABIs are stored and read from.
|
|
17
|
+
* Resolved relative to the project root (cwd). Defaults to `./abis`.
|
|
18
|
+
*/
|
|
19
|
+
abisDir?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Directory where generated type declarations (e.g. `allow.d.ts`) are
|
|
22
|
+
* written. Resolved relative to the project root (cwd).
|
|
23
|
+
* Defaults to `./.zodiac-os`.
|
|
24
|
+
*/
|
|
25
|
+
typesDir?: string;
|
|
26
|
+
}
|
|
27
|
+
declare const defineConfig: (config: ZodiacConfig) => ZodiacConfig;
|
|
28
|
+
declare function loadConfig(configPath?: string): Promise<ZodiacConfig>;
|
|
29
|
+
declare const DEFAULT_ABIS_DIR = "abis";
|
|
30
|
+
declare const DEFAULT_TYPES_DIR = ".zodiac-os";
|
|
31
|
+
declare function resolveAbisDir(config: ZodiacConfig): string;
|
|
32
|
+
declare function resolveTypesDir(config: ZodiacConfig): string;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { Contracts, ContractsNode, DEFAULT_ABIS_DIR, DEFAULT_TYPES_DIR, ZodiacConfig, defineConfig, loadConfig, resolveAbisDir, resolveTypesDir };
|
|
35
|
+
//# sourceMappingURL=config.d.mts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { pathToFileURL } from "url";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
//#region src/cli/config.ts
|
|
4
|
+
const defineConfig = (config) => config;
|
|
5
|
+
const DEFAULT_CONFIG_PATH = "zodiac.config.ts";
|
|
6
|
+
async function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
|
|
7
|
+
const absolutePath = resolve(process.cwd(), configPath);
|
|
8
|
+
let mod;
|
|
9
|
+
try {
|
|
10
|
+
mod = await import(pathToFileURL(absolutePath).href);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
if (error?.code === "ERR_MODULE_NOT_FOUND" || error?.code === "ENOENT") throw new Error(`Config file not found: ${absolutePath}`);
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
const config = mod.default ?? mod.config;
|
|
16
|
+
if (!config) throw new Error(`Config file must export a default value or a named "config" export: ${absolutePath}`);
|
|
17
|
+
if (!config.apiKey) throw new Error(`Config is missing required field "apiKey"`);
|
|
18
|
+
return config;
|
|
19
|
+
}
|
|
20
|
+
const DEFAULT_ABIS_DIR = "abis";
|
|
21
|
+
const DEFAULT_TYPES_DIR = ".zodiac-os";
|
|
22
|
+
function resolveAbisDir(config) {
|
|
23
|
+
return resolve(process.cwd(), config.abisDir ?? "abis");
|
|
24
|
+
}
|
|
25
|
+
function resolveTypesDir(config) {
|
|
26
|
+
return resolve(process.cwd(), config.typesDir ?? ".zodiac-os");
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { DEFAULT_ABIS_DIR, DEFAULT_TYPES_DIR, defineConfig, loadConfig, resolveAbisDir, resolveTypesDir };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=config.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../../src/cli/config.ts"],"sourcesContent":["import { pathToFileURL } from 'url'\nimport { resolve } from 'path'\n\nexport type Contracts = {\n [chain: string]: ContractsNode\n}\nexport type ContractsNode = `0x${string}` | { [name: string]: ContractsNode }\n\nexport interface ZodiacConfig {\n apiKey: `zodiac_${string}`\n /**\n * Contracts the `allow` kit should know about, keyed by chain prefix.\n * Nested objects are allowed for grouping related addresses.\n */\n contracts?: Contracts\n /**\n * Directory where fetched ABIs are stored and read from.\n * Resolved relative to the project root (cwd). Defaults to `./abis`.\n */\n abisDir?: string\n /**\n * Directory where generated type declarations (e.g. `allow.d.ts`) are\n * written. Resolved relative to the project root (cwd).\n * Defaults to `./.zodiac-os`.\n */\n typesDir?: string\n}\n\nexport const defineConfig = (config: ZodiacConfig): ZodiacConfig => config\n\nconst DEFAULT_CONFIG_PATH = 'zodiac.config.ts'\n\nexport async function loadConfig(\n configPath: string = DEFAULT_CONFIG_PATH\n): Promise<ZodiacConfig> {\n const absolutePath = resolve(process.cwd(), configPath)\n\n let mod: Record<string, unknown>\n try {\n mod = await import(pathToFileURL(absolutePath).href)\n } catch (error: any) {\n if (error?.code === 'ERR_MODULE_NOT_FOUND' || error?.code === 'ENOENT') {\n throw new Error(`Config file not found: ${absolutePath}`)\n }\n throw error\n }\n\n const config = (mod.default ?? mod.config) as ZodiacConfig | undefined\n if (!config) {\n throw new Error(\n `Config file must export a default value or a named \"config\" export: ${absolutePath}`\n )\n }\n\n if (!config.apiKey) {\n throw new Error(`Config is missing required field \"apiKey\"`)\n }\n\n return config\n}\n\nexport const DEFAULT_ABIS_DIR = 'abis'\nexport const DEFAULT_TYPES_DIR = '.zodiac-os'\n\nexport function resolveAbisDir(config: ZodiacConfig): string {\n return resolve(process.cwd(), config.abisDir ?? DEFAULT_ABIS_DIR)\n}\n\nexport function resolveTypesDir(config: ZodiacConfig): string {\n return resolve(process.cwd(), config.typesDir ?? DEFAULT_TYPES_DIR)\n}\n"],"mappings":";;;AA4BA,MAAa,gBAAgB,WAAuC;AAEpE,MAAM,sBAAsB;AAE5B,eAAsB,WACpB,aAAqB,qBACE;CACvB,MAAM,eAAe,QAAQ,QAAQ,KAAK,EAAE,WAAW;CAEvD,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,OAAO,cAAc,aAAa,CAAC;UACxC,OAAY;AACnB,MAAI,OAAO,SAAS,0BAA0B,OAAO,SAAS,SAC5D,OAAM,IAAI,MAAM,0BAA0B,eAAe;AAE3D,QAAM;;CAGR,MAAM,SAAU,IAAI,WAAW,IAAI;AACnC,KAAI,CAAC,OACH,OAAM,IAAI,MACR,uEAAuE,eACxE;AAGH,KAAI,CAAC,OAAO,OACV,OAAM,IAAI,MAAM,4CAA4C;AAG9D,QAAO;;AAGT,MAAa,mBAAmB;AAChC,MAAa,oBAAoB;AAEjC,SAAgB,eAAe,QAA8B;AAC3D,QAAO,QAAQ,QAAQ,KAAK,EAAE,OAAO,WAAA,OAA4B;;AAGnE,SAAgB,gBAAgB,QAA8B;AAC5D,QAAO,QAAQ,QAAQ,KAAK,EAAE,OAAO,YAAA,aAA8B"}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,33 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as ApiClient } from "./api-D6ee2Q2b.mjs";
|
|
3
|
+
import { a as walkContracts, i as readAbi, n as chainIdFor, o as writeAbi, r as abiFilePath } from "./networks-BTW1qAAa.mjs";
|
|
4
|
+
import { loadConfig, resolveAbisDir, resolveTypesDir } from "./cli/config.mjs";
|
|
3
5
|
import { invariant } from "@epic-web/invariant";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
4
8
|
import { Command } from "commander";
|
|
5
|
-
import { fileURLToPath
|
|
6
|
-
import { join
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
import { join } from "path";
|
|
7
11
|
import { ModuleKind, Project, ScriptTarget, VariableDeclarationKind } from "ts-morph";
|
|
8
12
|
import { mkdirSync, writeFileSync } from "fs";
|
|
9
|
-
import { defineConfig } from "@gnosis-guild/eth-sdk";
|
|
10
|
-
import { gatherABIs } from "@gnosis-guild/eth-sdk/dist/abi-management/index.js";
|
|
11
|
-
import { generateSdk } from "@gnosis-guild/eth-sdk/dist/client/index.js";
|
|
12
|
-
import { createEthSdkConfig } from "@gnosis-guild/eth-sdk/dist/config/types.js";
|
|
13
|
-
import { realFs } from "@gnosis-guild/eth-sdk/dist/peripherals/fs.js";
|
|
14
|
-
//#region src/cli/config.ts
|
|
15
|
-
const DEFAULT_CONFIG_PATH = "zodiac.config.ts";
|
|
16
|
-
async function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
|
|
17
|
-
const absolutePath = resolve(process.cwd(), configPath);
|
|
18
|
-
let mod;
|
|
19
|
-
try {
|
|
20
|
-
mod = await import(pathToFileURL(absolutePath).href);
|
|
21
|
-
} catch (error) {
|
|
22
|
-
if (error?.code === "ERR_MODULE_NOT_FOUND" || error?.code === "ENOENT") throw new Error(`Config file not found: ${absolutePath}`);
|
|
23
|
-
throw error;
|
|
24
|
-
}
|
|
25
|
-
const config = mod.default ?? mod.config;
|
|
26
|
-
if (!config) throw new Error(`Config file must export a default value or a named "config" export: ${absolutePath}`);
|
|
27
|
-
if (!config.apiKey) throw new Error(`Config is missing required field "apiKey"`);
|
|
28
|
-
return config;
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
13
|
//#region src/cli/commands/pullOrg.ts
|
|
32
14
|
function resolveNodeModulesDir() {
|
|
33
15
|
const match = fileURLToPath(import.meta.url).match(/^(.+[/\\]node_modules)[/\\]/);
|
|
@@ -132,24 +114,231 @@ const pullOrg = async (config) => {
|
|
|
132
114
|
for (const outputFile of emitResult.getOutputFiles()) writeFileSync(join(outDir, outputFile.getFilePath().includes(".d.ts") ? "index.d.ts" : "index.js"), outputFile.getText());
|
|
133
115
|
};
|
|
134
116
|
//#endregion
|
|
117
|
+
//#region src/allow/fetch.ts
|
|
118
|
+
async function fetchAbi(chainId, address) {
|
|
119
|
+
const url = `https://api.abi.pub/v1/chains/${chainId}/etherscan?module=contract&action=getabi&address=${address}`;
|
|
120
|
+
let body;
|
|
121
|
+
try {
|
|
122
|
+
const resp = await fetch(url);
|
|
123
|
+
if (!resp.ok) return null;
|
|
124
|
+
body = await resp.json();
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
if (body.status !== "1" || typeof body.result !== "string") return null;
|
|
129
|
+
try {
|
|
130
|
+
const parsed = JSON.parse(body.result);
|
|
131
|
+
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
132
|
+
return parsed;
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/allow/codegen.ts
|
|
139
|
+
function generateAllowTypes(abisDir, contractsConfig) {
|
|
140
|
+
const root = /* @__PURE__ */ new Map();
|
|
141
|
+
for (const node of walkContracts(contractsConfig)) {
|
|
142
|
+
const abi = readAbi(abisDir, node);
|
|
143
|
+
if (!abi) continue;
|
|
144
|
+
insertIntoTree(root, [node.chain, ...node.segments], {
|
|
145
|
+
node,
|
|
146
|
+
abi
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const out = [];
|
|
150
|
+
out.push("// AUTO-GENERATED by `zodiac-os pull-contracts`. Do not edit.");
|
|
151
|
+
out.push("/* eslint-disable */");
|
|
152
|
+
out.push("");
|
|
153
|
+
out.push(`import type { FunctionPermission, TargetPermission } from "zodiac-roles-sdk";`);
|
|
154
|
+
out.push(`import type { Scoping, Options, EVERYTHING } from "@zodiac-os/sdk/allow";`);
|
|
155
|
+
out.push("");
|
|
156
|
+
out.push("declare global {");
|
|
157
|
+
out.push(" interface AllowKit " + renderTree(root, " "));
|
|
158
|
+
out.push("}");
|
|
159
|
+
out.push("");
|
|
160
|
+
out.push("export {};");
|
|
161
|
+
out.push("");
|
|
162
|
+
return out.join("\n");
|
|
163
|
+
}
|
|
164
|
+
function insertIntoTree(tree, segments, leaf) {
|
|
165
|
+
const [first, ...rest] = segments;
|
|
166
|
+
if (!first) throw new Error("empty segments");
|
|
167
|
+
if (rest.length === 0) {
|
|
168
|
+
tree.set(first, leaf);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
let child = tree.get(first);
|
|
172
|
+
if (!child || child.node) {
|
|
173
|
+
child = /* @__PURE__ */ new Map();
|
|
174
|
+
tree.set(first, child);
|
|
175
|
+
}
|
|
176
|
+
insertIntoTree(child, rest, leaf);
|
|
177
|
+
}
|
|
178
|
+
function renderTree(tree, indent) {
|
|
179
|
+
const lines = ["{"];
|
|
180
|
+
for (const [key, value] of tree) {
|
|
181
|
+
const safeKey = renderPropKey(key);
|
|
182
|
+
if (value instanceof Map) lines.push(`${indent} ${safeKey}: ${renderTree(value, indent + " ")};`);
|
|
183
|
+
else {
|
|
184
|
+
const { node, abi } = value;
|
|
185
|
+
lines.push(`${indent} ${safeKey}: ${renderContractType(node, abi, indent + " ")};`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
lines.push(`${indent}}`);
|
|
189
|
+
return lines.join("\n");
|
|
190
|
+
}
|
|
191
|
+
function renderPropKey(name) {
|
|
192
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
193
|
+
}
|
|
194
|
+
function renderContractType(node, abi, indent) {
|
|
195
|
+
const members = [];
|
|
196
|
+
const seen = /* @__PURE__ */ new Set();
|
|
197
|
+
members.push(`${indent} [EVERYTHING]: (options?: Options) => TargetPermission;`);
|
|
198
|
+
for (const fragment of abi) {
|
|
199
|
+
if (fragment.type !== "function") continue;
|
|
200
|
+
if (fragment.stateMutability === "view" || fragment.stateMutability === "pure") continue;
|
|
201
|
+
const name = fragment.name;
|
|
202
|
+
if (!name || seen.has(name)) continue;
|
|
203
|
+
seen.add(name);
|
|
204
|
+
members.push(renderFunctionSignature(fragment, indent + " "));
|
|
205
|
+
}
|
|
206
|
+
return [
|
|
207
|
+
"{",
|
|
208
|
+
...members,
|
|
209
|
+
`${indent}}`
|
|
210
|
+
].join("\n");
|
|
211
|
+
}
|
|
212
|
+
function renderFunctionSignature(fragment, indent) {
|
|
213
|
+
const name = renderPropKey(fragment.name);
|
|
214
|
+
const params = [];
|
|
215
|
+
for (const input of fragment.inputs ?? []) {
|
|
216
|
+
const paramName = sanitizeParamName(input.name || `arg${params.length}`, params.length);
|
|
217
|
+
params.push(`${paramName}?: Scoping<${tsTypeFor(input)}>`);
|
|
218
|
+
}
|
|
219
|
+
params.push(`options?: Options`);
|
|
220
|
+
return `${indent}${name}: (${params.join(", ")}) => FunctionPermission;`;
|
|
221
|
+
}
|
|
222
|
+
function sanitizeParamName(name, index) {
|
|
223
|
+
const cleaned = name.replace(/[^A-Za-z0-9_$]/g, "_");
|
|
224
|
+
if (!cleaned || /^[0-9]/.test(cleaned)) return `arg${index}`;
|
|
225
|
+
return new Set([
|
|
226
|
+
"function",
|
|
227
|
+
"class",
|
|
228
|
+
"new",
|
|
229
|
+
"number",
|
|
230
|
+
"string",
|
|
231
|
+
"object",
|
|
232
|
+
"boolean",
|
|
233
|
+
"symbol",
|
|
234
|
+
"default",
|
|
235
|
+
"return",
|
|
236
|
+
"this",
|
|
237
|
+
"void",
|
|
238
|
+
"delete",
|
|
239
|
+
"in",
|
|
240
|
+
"of",
|
|
241
|
+
"for",
|
|
242
|
+
"while",
|
|
243
|
+
"switch",
|
|
244
|
+
"case",
|
|
245
|
+
"if",
|
|
246
|
+
"else",
|
|
247
|
+
"null",
|
|
248
|
+
"true",
|
|
249
|
+
"false",
|
|
250
|
+
"undefined",
|
|
251
|
+
"any",
|
|
252
|
+
"never",
|
|
253
|
+
"unknown"
|
|
254
|
+
]).has(cleaned) ? `_${cleaned}` : cleaned;
|
|
255
|
+
}
|
|
256
|
+
function tsTypeFor(fragment) {
|
|
257
|
+
const type = fragment.type;
|
|
258
|
+
const arrayMatch = /^(.*)\[(\d*)\]$/.exec(type);
|
|
259
|
+
if (arrayMatch) return `readonly (${tsTypeFor({
|
|
260
|
+
...fragment,
|
|
261
|
+
type: arrayMatch[1]
|
|
262
|
+
})})[]`;
|
|
263
|
+
if (type === "tuple") {
|
|
264
|
+
const components = fragment.components ?? [];
|
|
265
|
+
if (components.length === 0) return "Record<string, unknown>";
|
|
266
|
+
return `{ ${components.map((c, i) => {
|
|
267
|
+
return `${renderPropKey(sanitizeParamName(c.name || `f${i}`, i))}: ${tsTypeFor(c)}`;
|
|
268
|
+
}).join("; ")} }`;
|
|
269
|
+
}
|
|
270
|
+
if (type === "address") return "`0x${string}`";
|
|
271
|
+
if (type === "bool") return "boolean";
|
|
272
|
+
if (type === "string") return "string";
|
|
273
|
+
if (type === "bytes") return "import(\"ethers\").BytesLike";
|
|
274
|
+
if (/^bytes\d+$/.test(type)) return "`0x${string}`";
|
|
275
|
+
if (/^u?int\d*$/.test(type)) return "import(\"ethers\").BigNumberish";
|
|
276
|
+
return "unknown";
|
|
277
|
+
}
|
|
278
|
+
function writeGenerated(outFile, source) {
|
|
279
|
+
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
280
|
+
fs.writeFileSync(outFile, source, "utf8");
|
|
281
|
+
}
|
|
282
|
+
//#endregion
|
|
135
283
|
//#region src/cli/commands/pullContracts.ts
|
|
136
284
|
const pullContracts = async (config) => {
|
|
137
285
|
if (!config.contracts || Object.keys(config.contracts).length === 0) {
|
|
138
286
|
console.log("No contracts defined in config, skipping.");
|
|
139
287
|
return;
|
|
140
288
|
}
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
289
|
+
const abisDir = resolveAbisDir(config);
|
|
290
|
+
const generatedFile = path.join(resolveTypesDir(config), "allow.d.ts");
|
|
291
|
+
let missing = 0;
|
|
292
|
+
let fetched = 0;
|
|
293
|
+
let existing = 0;
|
|
294
|
+
for (const node of walkContracts(config.contracts)) {
|
|
295
|
+
const file = abiFilePath(abisDir, node);
|
|
296
|
+
if (fs.existsSync(file)) {
|
|
297
|
+
existing++;
|
|
298
|
+
report(node.chain, node.segments, node.address, "ok", file);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
let chainId;
|
|
302
|
+
try {
|
|
303
|
+
chainId = chainIdFor(node.chain);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
missing++;
|
|
306
|
+
report(node.chain, node.segments, node.address, "missing", file, error.message);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const abi = await fetchAbi(chainId, node.address);
|
|
310
|
+
if (!abi) {
|
|
311
|
+
missing++;
|
|
312
|
+
report(node.chain, node.segments, node.address, "missing", file, `api.abi.pub returned no ABI for chain ${chainId}`);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
writeAbi(abisDir, node, abi);
|
|
316
|
+
fetched++;
|
|
317
|
+
report(node.chain, node.segments, node.address, "fetched", file);
|
|
318
|
+
}
|
|
319
|
+
console.log("");
|
|
320
|
+
console.log(`Contracts summary: ${existing} existing, ${fetched} fetched, ${missing} missing.`);
|
|
321
|
+
if (missing > 0) {
|
|
322
|
+
console.log("");
|
|
323
|
+
console.log("Missing ABIs must be provided manually. Paste the contract");
|
|
324
|
+
console.log("ABI JSON at the paths listed above and re-run `zodiac-os pull-contracts`.");
|
|
325
|
+
}
|
|
326
|
+
writeGenerated(generatedFile, generateAllowTypes(abisDir, config.contracts));
|
|
327
|
+
console.log("");
|
|
328
|
+
console.log(`Wrote typings to ${path.relative(process.cwd(), generatedFile)}`);
|
|
329
|
+
if (missing > 0) process.exit(1);
|
|
152
330
|
};
|
|
331
|
+
function report(chain, segments, address, status, file, reason) {
|
|
332
|
+
const label = `${chain}.${segments.join(".")}`.padEnd(40, " ");
|
|
333
|
+
const tag = {
|
|
334
|
+
ok: " cached ",
|
|
335
|
+
fetched: " fetched ",
|
|
336
|
+
missing: " MISSING "
|
|
337
|
+
}[status];
|
|
338
|
+
const suffix = reason ? ` — ${reason}` : "";
|
|
339
|
+
console.log(`${tag} ${label} ${address}${suffix}`);
|
|
340
|
+
if (status === "missing") console.log(` → paste ABI at ${file}`);
|
|
341
|
+
}
|
|
153
342
|
//#endregion
|
|
154
343
|
//#region src/cli/run.ts
|
|
155
344
|
const run = async (argv = process.argv) => {
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli/config.ts","../src/cli/commands/pullOrg.ts","../src/cli/commands/pullContracts.ts","../src/cli/run.ts","../src/cli/index.ts"],"sourcesContent":["import type { EthSdkContracts } from '@gnosis-guild/eth-sdk'\nimport { pathToFileURL } from 'url'\nimport { resolve } from 'path'\n\nexport interface ZodiacConfig {\n apiKey: `zodiac_${string}`\n contracts?: EthSdkContracts\n}\n\nexport const defineConfig = (config: ZodiacConfig): ZodiacConfig => config\n\nconst DEFAULT_CONFIG_PATH = 'zodiac.config.ts'\n\nexport async function loadConfig(\n configPath: string = DEFAULT_CONFIG_PATH\n): Promise<ZodiacConfig> {\n const absolutePath = resolve(process.cwd(), configPath)\n\n let mod: Record<string, unknown>\n try {\n mod = await import(pathToFileURL(absolutePath).href)\n } catch (error: any) {\n if (error?.code === 'ERR_MODULE_NOT_FOUND' || error?.code === 'ENOENT') {\n throw new Error(`Config file not found: ${absolutePath}`)\n }\n throw error\n }\n\n const config = (mod.default ?? mod.config) as ZodiacConfig | undefined\n if (!config) {\n throw new Error(\n `Config file must export a default value or a named \"config\" export: ${absolutePath}`\n )\n }\n\n if (!config.apiKey) {\n throw new Error(`Config is missing required field \"apiKey\"`)\n }\n\n return config\n}\n","import type { ZodiacConfig } from '../config'\nimport { ApiClient } from '../../api'\nimport { invariant } from '@epic-web/invariant'\nimport {\n ModuleKind,\n Project,\n ScriptTarget,\n VariableDeclarationKind,\n} from 'ts-morph'\nimport { mkdirSync, writeFileSync } from 'fs'\nimport { join } from 'path'\nimport { fileURLToPath } from 'url'\n\nfunction resolveNodeModulesDir(): string {\n const selfPath = fileURLToPath(import.meta.url)\n const match = selfPath.match(/^(.+[/\\\\]node_modules)[/\\\\]/)\n if (match) return match[1]\n // Fallback for development (running from source, not from node_modules)\n return join(process.cwd(), 'node_modules')\n}\n\nconst toLiteral = (value: unknown, indent = 0): string => {\n const pad = ' '.repeat(indent)\n const childPad = ' '.repeat(indent + 1)\n\n if (value === null) return 'null'\n if (typeof value === 'bigint') return `${value}n`\n if (typeof value === 'string') return JSON.stringify(value)\n if (typeof value === 'number' || typeof value === 'boolean')\n return String(value)\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]'\n return `[\\n${value.map((v) => `${childPad}${toLiteral(v, indent + 1)}`).join(',\\n')},\\n${pad}]`\n }\n if (typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n if (entries.length === 0) return '{}'\n const props = entries.map(\n ([k, v]) =>\n `${childPad}${/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k)}: ${toLiteral(v, indent + 1)}`\n )\n return `{\\n${props.join(',\\n')},\\n${pad}}`\n }\n return String(value)\n}\n\nexport const pullOrg = async (config: ZodiacConfig) => {\n const client = new ApiClient({\n apiKey: config.apiKey,\n })\n\n const [users, workspaceVaults] = await Promise.all([\n client.listUsers(),\n client.listVaults(),\n ])\n\n const allRawVaults = workspaceVaults.flatMap((ws) => ws.vaults)\n\n const { result: accounts } = await client.resolveConstellation(\n workspaceVaults[0].workspaceId, // can just use any workspace to resolve\n {\n specification: allRawVaults.map((vault) => ({\n type: 'SAFE',\n chain: vault.chain,\n address: vault.address,\n })),\n }\n )\n\n let accountIndex = 0\n const vaultsRecord: Record<string, unknown> = {}\n for (const ws of workspaceVaults) {\n const wsVaults: Record<string, unknown> = {}\n for (const vault of ws.vaults) {\n const account = accounts[accountIndex++]\n invariant(\n account.type === 'SAFE',\n `Expected SAFE account for vault ${vault.id}`\n )\n wsVaults[vault.label] = {\n id: vault.id,\n label: vault.label,\n address: account.address,\n chain: vault.chain,\n threshold: account.threshold,\n owners: [...account.owners],\n modules: [...account.modules],\n }\n }\n vaultsRecord[ws.workspaceName] = {\n workspaceId: ws.workspaceId,\n workspaceName: ws.workspaceName,\n vaults: wsVaults,\n }\n }\n\n const nameCount = new Map<string, number>()\n for (const user of users) {\n nameCount.set(user.fullName, (nameCount.get(user.fullName) ?? 0) + 1)\n }\n\n const usersRecord: Record<string, unknown> = {}\n for (const user of users) {\n const handle =\n nameCount.get(user.fullName)! > 1\n ? `${user.fullName} (${user.id})`\n : user.fullName\n usersRecord[handle] = {\n id: user.id,\n fullName: user.fullName,\n personalSafes: user.personalSafes,\n }\n }\n\n const outDir = join(resolveNodeModulesDir(), '.zodiac-os')\n\n mkdirSync(outDir, { recursive: true })\n\n // Write package.json so .zodiac-os is importable\n writeFileSync(\n join(outDir, 'package.json'),\n JSON.stringify(\n {\n name: '.zodiac-os',\n type: 'module',\n main: 'index.js',\n types: 'index.d.ts',\n },\n null,\n 2\n )\n )\n\n // Use ts-morph to generate TS, then emit JS + d.ts\n const project = new Project({\n compilerOptions: {\n declaration: true,\n module: ModuleKind.ESNext,\n target: ScriptTarget.ESNext,\n outDir,\n },\n useInMemoryFileSystem: true,\n })\n\n const sourceFile = project.createSourceFile('index.ts', '')\n\n sourceFile.addVariableStatement({\n isExported: true,\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'users',\n initializer: `${toLiteral(usersRecord)} as const`,\n },\n ],\n })\n\n sourceFile.addVariableStatement({\n isExported: true,\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'vaults',\n initializer: `${toLiteral(vaultsRecord)} as const`,\n },\n ],\n })\n\n const emitResult = sourceFile.getEmitOutput()\n for (const outputFile of emitResult.getOutputFiles()) {\n const filePath = outputFile.getFilePath()\n const fileName = filePath.includes('.d.ts') ? 'index.d.ts' : 'index.js'\n writeFileSync(join(outDir, fileName), outputFile.getText())\n }\n}\n","import type { ZodiacConfig } from '../config'\nimport { defineConfig } from '@gnosis-guild/eth-sdk'\nimport { gatherABIs } from '@gnosis-guild/eth-sdk/dist/abi-management'\nimport { generateSdk } from '@gnosis-guild/eth-sdk/dist/client'\nimport { createEthSdkConfig } from '@gnosis-guild/eth-sdk/dist/config/types'\nimport { realFs } from '@gnosis-guild/eth-sdk/dist/peripherals/fs'\n\nexport const pullContracts = async (config: ZodiacConfig) => {\n if (!config.contracts || Object.keys(config.contracts).length === 0) {\n console.log('No contracts defined in config, skipping.')\n return\n }\n\n const cwd = process.cwd()\n const ethSdkConfig = createEthSdkConfig(\n defineConfig({\n contracts: config.contracts,\n })\n )\n\n const ctx = {\n cliArgs: { workingDirPath: cwd },\n config: ethSdkConfig,\n fs: realFs,\n }\n\n console.log('Fetching contract ABIs...')\n await gatherABIs(ctx)\n\n console.log('Generating typed SDK...')\n await generateSdk(ctx)\n}\n","import { Command } from 'commander'\nimport { loadConfig } from './config'\nimport { pullOrg } from './commands/pullOrg'\nimport { pullContracts } from './commands/pullContracts'\n\nexport const run = async (argv: string[] = process.argv) => {\n const program = new Command()\n\n program\n .name('zodiac-os')\n .description('Zodiac OS SDK CLI – pull org data and contract ABIs')\n .version('1.0.0')\n .option(\n '-c, --config <path>',\n 'path to the config file',\n 'zodiac.config.ts'\n )\n\n program\n .command('pull-org')\n .description('Fetch Zodiac users and vaults, generate TypeScript types')\n .action(async (_opts, cmd) => {\n const config = await loadConfig(cmd.optsWithGlobals().config)\n await pullOrg(config)\n })\n\n program\n .command('pull-contracts')\n .description('Fetch contract ABIs, generate typed permissions kit')\n .action(async (_opts, cmd) => {\n const config = await loadConfig(cmd.optsWithGlobals().config)\n await pullContracts(config)\n })\n\n program\n .command('pull')\n .description('Fetch Zodiac org and contracts ABI, generate SDK functions')\n .action(async (_opts, cmd) => {\n const config = await loadConfig(cmd.optsWithGlobals().config)\n await Promise.all([pullOrg(config), pullContracts(config)])\n })\n\n await program.parseAsync(argv)\n}\n","#!/usr/bin/env node\nimport { run } from './run'\n\nrun().then(\n () => {\n process.exit(0)\n },\n (error: unknown) => {\n if (error) console.error(error)\n process.exit(1)\n }\n)\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli/commands/pullOrg.ts","../src/allow/fetch.ts","../src/allow/codegen.ts","../src/cli/commands/pullContracts.ts","../src/cli/run.ts","../src/cli/index.ts"],"sourcesContent":["import type { ZodiacConfig } from '../config'\nimport { ApiClient } from '../../api'\nimport { invariant } from '@epic-web/invariant'\nimport {\n ModuleKind,\n Project,\n ScriptTarget,\n VariableDeclarationKind,\n} from 'ts-morph'\nimport { mkdirSync, writeFileSync } from 'fs'\nimport { join } from 'path'\nimport { fileURLToPath } from 'url'\n\nfunction resolveNodeModulesDir(): string {\n const selfPath = fileURLToPath(import.meta.url)\n const match = selfPath.match(/^(.+[/\\\\]node_modules)[/\\\\]/)\n if (match) return match[1]\n // Fallback for development (running from source, not from node_modules)\n return join(process.cwd(), 'node_modules')\n}\n\nconst toLiteral = (value: unknown, indent = 0): string => {\n const pad = ' '.repeat(indent)\n const childPad = ' '.repeat(indent + 1)\n\n if (value === null) return 'null'\n if (typeof value === 'bigint') return `${value}n`\n if (typeof value === 'string') return JSON.stringify(value)\n if (typeof value === 'number' || typeof value === 'boolean')\n return String(value)\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]'\n return `[\\n${value.map((v) => `${childPad}${toLiteral(v, indent + 1)}`).join(',\\n')},\\n${pad}]`\n }\n if (typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n if (entries.length === 0) return '{}'\n const props = entries.map(\n ([k, v]) =>\n `${childPad}${/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k)}: ${toLiteral(v, indent + 1)}`\n )\n return `{\\n${props.join(',\\n')},\\n${pad}}`\n }\n return String(value)\n}\n\nexport const pullOrg = async (config: ZodiacConfig) => {\n const client = new ApiClient({\n apiKey: config.apiKey,\n })\n\n const [users, workspaceVaults] = await Promise.all([\n client.listUsers(),\n client.listVaults(),\n ])\n\n const allRawVaults = workspaceVaults.flatMap((ws) => ws.vaults)\n\n const { result: accounts } = await client.resolveConstellation(\n workspaceVaults[0].workspaceId, // can just use any workspace to resolve\n {\n specification: allRawVaults.map((vault) => ({\n type: 'SAFE',\n chain: vault.chain,\n address: vault.address,\n })),\n }\n )\n\n let accountIndex = 0\n const vaultsRecord: Record<string, unknown> = {}\n for (const ws of workspaceVaults) {\n const wsVaults: Record<string, unknown> = {}\n for (const vault of ws.vaults) {\n const account = accounts[accountIndex++]\n invariant(\n account.type === 'SAFE',\n `Expected SAFE account for vault ${vault.id}`\n )\n wsVaults[vault.label] = {\n id: vault.id,\n label: vault.label,\n address: account.address,\n chain: vault.chain,\n threshold: account.threshold,\n owners: [...account.owners],\n modules: [...account.modules],\n }\n }\n vaultsRecord[ws.workspaceName] = {\n workspaceId: ws.workspaceId,\n workspaceName: ws.workspaceName,\n vaults: wsVaults,\n }\n }\n\n const nameCount = new Map<string, number>()\n for (const user of users) {\n nameCount.set(user.fullName, (nameCount.get(user.fullName) ?? 0) + 1)\n }\n\n const usersRecord: Record<string, unknown> = {}\n for (const user of users) {\n const handle =\n nameCount.get(user.fullName)! > 1\n ? `${user.fullName} (${user.id})`\n : user.fullName\n usersRecord[handle] = {\n id: user.id,\n fullName: user.fullName,\n personalSafes: user.personalSafes,\n }\n }\n\n const outDir = join(resolveNodeModulesDir(), '.zodiac-os')\n\n mkdirSync(outDir, { recursive: true })\n\n // Write package.json so .zodiac-os is importable\n writeFileSync(\n join(outDir, 'package.json'),\n JSON.stringify(\n {\n name: '.zodiac-os',\n type: 'module',\n main: 'index.js',\n types: 'index.d.ts',\n },\n null,\n 2\n )\n )\n\n // Use ts-morph to generate TS, then emit JS + d.ts\n const project = new Project({\n compilerOptions: {\n declaration: true,\n module: ModuleKind.ESNext,\n target: ScriptTarget.ESNext,\n outDir,\n },\n useInMemoryFileSystem: true,\n })\n\n const sourceFile = project.createSourceFile('index.ts', '')\n\n sourceFile.addVariableStatement({\n isExported: true,\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'users',\n initializer: `${toLiteral(usersRecord)} as const`,\n },\n ],\n })\n\n sourceFile.addVariableStatement({\n isExported: true,\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'vaults',\n initializer: `${toLiteral(vaultsRecord)} as const`,\n },\n ],\n })\n\n const emitResult = sourceFile.getEmitOutput()\n for (const outputFile of emitResult.getOutputFiles()) {\n const filePath = outputFile.getFilePath()\n const fileName = filePath.includes('.d.ts') ? 'index.d.ts' : 'index.js'\n writeFileSync(join(outDir, fileName), outputFile.getText())\n }\n}\n","import { chainIdFor, type ChainPrefix } from './networks'\n\nexport type AbiFragment = Record<string, any>\nexport type Abi = AbiFragment[]\n\n// Returns null on any failure so callers can fall back to a manual ABI file.\nexport async function fetchAbi(\n chainId: number,\n address: `0x${string}`\n): Promise<Abi | null> {\n const url = `https://api.abi.pub/v1/chains/${chainId}/etherscan?module=contract&action=getabi&address=${address}`\n let body: { status?: string; message?: string; result?: string }\n try {\n const resp = await fetch(url)\n if (!resp.ok) return null\n body = (await resp.json()) as typeof body\n } catch {\n return null\n }\n if (body.status !== '1' || typeof body.result !== 'string') return null\n try {\n const parsed = JSON.parse(body.result)\n if (!Array.isArray(parsed) || parsed.length === 0) return null\n return parsed as Abi\n } catch {\n return null\n }\n}\n\nexport const fetchAbiForPrefix = (\n prefix: ChainPrefix,\n address: `0x${string}`\n) => fetchAbi(chainIdFor(prefix), address)\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { walkContracts, readAbi, type ContractNode } from './abi'\nimport type { Abi, AbiFragment } from './fetch'\n\n// Emits named tuple elements per ABI input — this is the reason we don't rely\n// on viem/abitype, which produces unnamed tuples (microsoft/TypeScript#44939).\nexport function generateAllowTypes(\n abisDir: string,\n contractsConfig: Record<string, any>\n): string {\n type Tree = Map<string, Tree | { node: ContractNode; abi: Abi }>\n\n const root: Tree = new Map()\n for (const node of walkContracts(contractsConfig)) {\n const abi = readAbi(abisDir, node)\n if (!abi) continue\n insertIntoTree(root, [node.chain, ...node.segments], { node, abi })\n }\n\n const out: string[] = []\n out.push('// AUTO-GENERATED by `zodiac-os pull-contracts`. Do not edit.')\n out.push('/* eslint-disable */')\n out.push('')\n out.push(\n `import type { FunctionPermission, TargetPermission } from \"zodiac-roles-sdk\";`\n )\n out.push(\n `import type { Scoping, Options, EVERYTHING } from \"@zodiac-os/sdk/allow\";`\n )\n out.push('')\n out.push('declare global {')\n out.push(' interface AllowKit ' + renderTree(root, ' '))\n out.push('}')\n out.push('')\n out.push('export {};')\n out.push('')\n return out.join('\\n')\n}\n\nfunction insertIntoTree(\n tree: Map<string, any>,\n segments: string[],\n leaf: { node: ContractNode; abi: Abi }\n): void {\n const [first, ...rest] = segments\n if (!first) throw new Error('empty segments')\n if (rest.length === 0) {\n tree.set(first, leaf)\n return\n }\n let child = tree.get(first)\n if (!child || child.node) {\n child = new Map()\n tree.set(first, child)\n }\n insertIntoTree(child, rest, leaf)\n}\n\nfunction renderTree(tree: Map<string, any>, indent: string): string {\n const lines: string[] = ['{']\n for (const [key, value] of tree) {\n const safeKey = renderPropKey(key)\n if (value instanceof Map) {\n lines.push(`${indent} ${safeKey}: ${renderTree(value, indent + ' ')};`)\n } else {\n const { node, abi } = value as { node: ContractNode; abi: Abi }\n lines.push(\n `${indent} ${safeKey}: ${renderContractType(node, abi, indent + ' ')};`\n )\n }\n }\n lines.push(`${indent}}`)\n return lines.join('\\n')\n}\n\nfunction renderPropKey(name: string): string {\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name)\n}\n\nfunction renderContractType(\n node: ContractNode,\n abi: Abi,\n indent: string\n): string {\n const members: string[] = []\n const seen = new Set<string>()\n members.push(\n `${indent} [EVERYTHING]: (options?: Options) => TargetPermission;`\n )\n for (const fragment of abi) {\n if (fragment.type !== 'function') continue\n if (\n fragment.stateMutability === 'view' ||\n fragment.stateMutability === 'pure'\n ) {\n continue\n }\n const name = fragment.name as string\n if (!name || seen.has(name)) continue\n seen.add(name)\n members.push(renderFunctionSignature(fragment, indent + ' '))\n }\n return ['{', ...members, `${indent}}`].join('\\n')\n}\n\nfunction renderFunctionSignature(\n fragment: AbiFragment,\n indent: string\n): string {\n const name = renderPropKey(fragment.name as string)\n const params: string[] = []\n for (const input of (fragment.inputs as AbiFragment[]) ?? []) {\n const paramName = sanitizeParamName(\n input.name || `arg${params.length}`,\n params.length\n )\n params.push(`${paramName}?: Scoping<${tsTypeFor(input)}>`)\n }\n params.push(`options?: Options`)\n return `${indent}${name}: (${params.join(', ')}) => FunctionPermission;`\n}\n\nfunction sanitizeParamName(name: string, index: number): string {\n const cleaned = name.replace(/[^A-Za-z0-9_$]/g, '_')\n if (!cleaned || /^[0-9]/.test(cleaned)) return `arg${index}`\n // Named tuple element labels can't collide with TS reserved words.\n const reserved = new Set([\n 'function',\n 'class',\n 'new',\n 'number',\n 'string',\n 'object',\n 'boolean',\n 'symbol',\n 'default',\n 'return',\n 'this',\n 'void',\n 'delete',\n 'in',\n 'of',\n 'for',\n 'while',\n 'switch',\n 'case',\n 'if',\n 'else',\n 'null',\n 'true',\n 'false',\n 'undefined',\n 'any',\n 'never',\n 'unknown',\n ])\n return reserved.has(cleaned) ? `_${cleaned}` : cleaned\n}\n\nfunction tsTypeFor(fragment: AbiFragment): string {\n const type = fragment.type as string\n\n const arrayMatch = /^(.*)\\[(\\d*)\\]$/.exec(type)\n if (arrayMatch) {\n const inner: AbiFragment = { ...fragment, type: arrayMatch[1] }\n return `readonly (${tsTypeFor(inner)})[]`\n }\n\n if (type === 'tuple') {\n const components = (fragment.components as AbiFragment[]) ?? []\n if (components.length === 0) return 'Record<string, unknown>'\n const fields = components.map((c, i) => {\n const key = sanitizeParamName(c.name || `f${i}`, i)\n return `${renderPropKey(key)}: ${tsTypeFor(c)}`\n })\n return `{ ${fields.join('; ')} }`\n }\n\n if (type === 'address') return '`0x${string}`'\n if (type === 'bool') return 'boolean'\n if (type === 'string') return 'string'\n if (type === 'bytes') return 'import(\"ethers\").BytesLike'\n if (/^bytes\\d+$/.test(type)) return '`0x${string}`'\n if (/^u?int\\d*$/.test(type)) return 'import(\"ethers\").BigNumberish'\n return 'unknown'\n}\n\nexport function writeGenerated(outFile: string, source: string): void {\n fs.mkdirSync(path.dirname(outFile), { recursive: true })\n fs.writeFileSync(outFile, source, 'utf8')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { ZodiacConfig } from '../config'\nimport { resolveAbisDir, resolveTypesDir } from '../config'\nimport { abiFilePath, walkContracts, writeAbi } from '../../allow/abi'\nimport { fetchAbi } from '../../allow/fetch'\nimport { chainIdFor } from '../../allow/networks'\nimport { generateAllowTypes, writeGenerated } from '../../allow/codegen'\n\ntype Status = 'ok' | 'fetched' | 'missing'\n\nexport const pullContracts = async (config: ZodiacConfig) => {\n if (!config.contracts || Object.keys(config.contracts).length === 0) {\n console.log('No contracts defined in config, skipping.')\n return\n }\n\n const abisDir = resolveAbisDir(config)\n const generatedFile = path.join(resolveTypesDir(config), 'allow.d.ts')\n\n let missing = 0\n let fetched = 0\n let existing = 0\n\n for (const node of walkContracts(config.contracts)) {\n const file = abiFilePath(abisDir, node)\n\n if (fs.existsSync(file)) {\n existing++\n report(node.chain, node.segments, node.address, 'ok', file)\n continue\n }\n\n let chainId: number\n try {\n chainId = chainIdFor(node.chain)\n } catch (error) {\n missing++\n report(\n node.chain,\n node.segments,\n node.address,\n 'missing',\n file,\n (error as Error).message\n )\n continue\n }\n\n const abi = await fetchAbi(chainId, node.address)\n if (!abi) {\n missing++\n report(\n node.chain,\n node.segments,\n node.address,\n 'missing',\n file,\n `api.abi.pub returned no ABI for chain ${chainId}`\n )\n continue\n }\n writeAbi(abisDir, node, abi)\n fetched++\n report(node.chain, node.segments, node.address, 'fetched', file)\n }\n\n console.log('')\n console.log(\n `Contracts summary: ${existing} existing, ${fetched} fetched, ${missing} missing.`\n )\n if (missing > 0) {\n console.log('')\n console.log('Missing ABIs must be provided manually. Paste the contract')\n console.log(\n 'ABI JSON at the paths listed above and re-run `zodiac-os pull-contracts`.'\n )\n }\n\n const source = generateAllowTypes(abisDir, config.contracts)\n writeGenerated(generatedFile, source)\n console.log('')\n console.log(`Wrote typings to ${path.relative(process.cwd(), generatedFile)}`)\n\n if (missing > 0) process.exit(1)\n}\n\nfunction report(\n chain: string,\n segments: string[],\n address: string,\n status: Status,\n file: string,\n reason?: string\n) {\n const label = `${chain}.${segments.join('.')}`.padEnd(40, ' ')\n const tag = {\n ok: ' cached ',\n fetched: ' fetched ',\n missing: ' MISSING ',\n }[status]\n const suffix = reason ? ` — ${reason}` : ''\n console.log(`${tag} ${label} ${address}${suffix}`)\n if (status === 'missing') {\n console.log(` → paste ABI at ${file}`)\n }\n}\n","import { Command } from 'commander'\nimport { loadConfig } from './config'\nimport { pullOrg } from './commands/pullOrg'\nimport { pullContracts } from './commands/pullContracts'\n\nexport const run = async (argv: string[] = process.argv) => {\n const program = new Command()\n\n program\n .name('zodiac-os')\n .description('Zodiac OS SDK CLI – pull org data and contract ABIs')\n .version('1.0.0')\n .option(\n '-c, --config <path>',\n 'path to the config file',\n 'zodiac.config.ts'\n )\n\n program\n .command('pull-org')\n .description('Fetch Zodiac users and vaults, generate TypeScript types')\n .action(async (_opts, cmd) => {\n const config = await loadConfig(cmd.optsWithGlobals().config)\n await pullOrg(config)\n })\n\n program\n .command('pull-contracts')\n .description('Fetch contract ABIs, generate typed permissions kit')\n .action(async (_opts, cmd) => {\n const config = await loadConfig(cmd.optsWithGlobals().config)\n await pullContracts(config)\n })\n\n program\n .command('pull')\n .description('Fetch Zodiac org and contracts ABI, generate SDK functions')\n .action(async (_opts, cmd) => {\n const config = await loadConfig(cmd.optsWithGlobals().config)\n await Promise.all([pullOrg(config), pullContracts(config)])\n })\n\n await program.parseAsync(argv)\n}\n","#!/usr/bin/env node\nimport { run } from './run'\n\nrun().then(\n () => {\n process.exit(0)\n },\n (error: unknown) => {\n if (error) console.error(error)\n process.exit(1)\n }\n)\n"],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Condition, FunctionPermission, TargetPermission } from "zodiac-roles-sdk";
|
|
2
|
+
import { BigNumberish, BytesLike, ParamType } from "ethers";
|
|
3
|
+
|
|
4
|
+
//#region src/allow/runtime.d.ts
|
|
5
|
+
declare function buildAllowKit(abisDir: string, contractsConfig: Record<string, any>): Record<string, any>;
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/allow/types.d.ts
|
|
8
|
+
type Options = {
|
|
9
|
+
send?: boolean;
|
|
10
|
+
delegatecall?: boolean;
|
|
11
|
+
etherWithinAllowance?: `0x${string}`;
|
|
12
|
+
callWithinAllowance?: `0x${string}`;
|
|
13
|
+
};
|
|
14
|
+
type PrimitiveValue = BigNumberish | BytesLike | string | boolean;
|
|
15
|
+
type ConditionFunction<T = unknown> = (abiType: ParamType, _?: T) => Condition;
|
|
16
|
+
type RequireAtLeastOne<T> = { [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>> }[keyof T];
|
|
17
|
+
type ArrayElement<T extends readonly unknown[]> = T extends readonly (infer U)[] ? U : never;
|
|
18
|
+
type PrimitiveScoping<T extends PrimitiveValue> = T | ConditionFunction<T>;
|
|
19
|
+
type ArrayScoping<T extends readonly any[]> = readonly Scoping<ArrayElement<T>>[] | ConditionFunction<T>;
|
|
20
|
+
type StructScoping<T extends {
|
|
21
|
+
[key: string]: any;
|
|
22
|
+
}> = RequireAtLeastOne<{ [K in keyof T]?: Scoping<T[K]> }> | ConditionFunction<T>;
|
|
23
|
+
type Scoping<T> = T extends PrimitiveValue ? PrimitiveScoping<T> : T extends readonly any[] ? ArrayScoping<T> : T extends {
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
} ? StructScoping<T> : unknown;
|
|
26
|
+
declare const EVERYTHING: unique symbol;
|
|
27
|
+
type EVERYTHING = typeof EVERYTHING;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/allow/networks.d.ts
|
|
30
|
+
declare const CHAIN_IDS: {
|
|
31
|
+
readonly eth: 1;
|
|
32
|
+
readonly oeth: 10;
|
|
33
|
+
readonly gno: 100;
|
|
34
|
+
readonly sep: 11155111;
|
|
35
|
+
readonly matic: 137;
|
|
36
|
+
readonly zkevm: 1101;
|
|
37
|
+
readonly arb1: 42161;
|
|
38
|
+
readonly avax: 43114;
|
|
39
|
+
readonly base: 8453;
|
|
40
|
+
readonly basesep: 84532;
|
|
41
|
+
readonly bnb: 56;
|
|
42
|
+
readonly celo: 42220;
|
|
43
|
+
readonly sonic: 146;
|
|
44
|
+
readonly berachain: 80094;
|
|
45
|
+
readonly unichain: 130;
|
|
46
|
+
readonly worldchain: 480;
|
|
47
|
+
readonly bob: 60808;
|
|
48
|
+
readonly mantle: 5000;
|
|
49
|
+
readonly hemi: 43111;
|
|
50
|
+
readonly katana: 747474;
|
|
51
|
+
readonly linea: 59144;
|
|
52
|
+
readonly ink: 57073;
|
|
53
|
+
readonly hyperevm: 999;
|
|
54
|
+
readonly flare: 14;
|
|
55
|
+
readonly scroll: 534352;
|
|
56
|
+
readonly plasma: 9745;
|
|
57
|
+
readonly megaeth: 4326;
|
|
58
|
+
};
|
|
59
|
+
type ChainPrefix = keyof typeof CHAIN_IDS;
|
|
60
|
+
declare const chainIdFor: (prefix: string) => number;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { EVERYTHING as a, Scoping as c, ConditionFunction as i, TargetPermission as l, ChainPrefix as n, FunctionPermission as o, chainIdFor as r, Options as s, CHAIN_IDS as t, buildAllowKit as u };
|
|
63
|
+
//# sourceMappingURL=index-DTBaxN7b.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference path="./zodiac-os-codegen.d.ts" />
|
|
2
|
-
import { EVERYTHING,
|
|
2
|
+
import { a as EVERYTHING, c as Scoping, i as ConditionFunction, l as TargetPermission, n as ChainPrefix, o as FunctionPermission, s as Options, u as buildAllowKit } from "./index-DTBaxN7b.mjs";
|
|
3
3
|
import { c, forAll } from "zodiac-roles-sdk";
|
|
4
4
|
import { Address, ApplyConstellationPayload, ApplyConstellationResult, ChainId, ListUsersResult, ListVaultsResult, ResolveConstellationPayload, ResolveConstellationResult } from "@zodiac-os/api-types";
|
|
5
5
|
import * as ZodiacOsCodegen from ".zodiac-os";
|
|
@@ -73,27 +73,27 @@ type NodeRef = Readonly<{
|
|
|
73
73
|
/** A blockchain address or a reference to another node in the constellation. */
|
|
74
74
|
type AddressOrRef = Lowercase<Address> | NodeRef;
|
|
75
75
|
type NodeBase = Readonly<{
|
|
76
|
-
label: string;
|
|
76
|
+
/** Human-readable identifier, unique within the constellation. */label: string; /** Chain the node is deployed on. */
|
|
77
77
|
chain: ChainId; /** Set for existing nodes from codegen, absent for new nodes. */
|
|
78
78
|
address?: Lowercase<Address>; /** Deployment nonce — required for new nodes, optional for existing. */
|
|
79
79
|
nonce?: bigint;
|
|
80
80
|
}>;
|
|
81
81
|
/** A safe node spec — existing vault ref or new safe with required config. */
|
|
82
82
|
type SafeNode = NodeBase & Readonly<{
|
|
83
|
-
type: 'SAFE';
|
|
84
|
-
threshold: number;
|
|
85
|
-
owners: readonly (string | NodeRef)[];
|
|
86
|
-
modules?: readonly (string | NodeRef)[];
|
|
83
|
+
/** Discriminator identifying this node as a Safe. */type: 'SAFE'; /** Number of owner signatures required to execute a transaction. */
|
|
84
|
+
threshold: number; /** Safe owner addresses or node references. */
|
|
85
|
+
owners: readonly (string | NodeRef)[]; /** Module addresses or node references enabled on the safe. */
|
|
86
|
+
modules?: readonly (string | NodeRef)[]; /** Whether this safe shall appear as a vault in the workspace. @default false */
|
|
87
87
|
vault?: boolean;
|
|
88
88
|
}>;
|
|
89
89
|
/** A roles modifier node spec — existing vault ref or new roles with modifier config. */
|
|
90
90
|
type RolesNode = NodeBase & Readonly<{
|
|
91
|
-
type: 'ROLES';
|
|
92
|
-
target?: AddressOrRef;
|
|
93
|
-
owner?: AddressOrRef;
|
|
94
|
-
avatar?: AddressOrRef;
|
|
95
|
-
multisend?: readonly Lowercase<Address>[];
|
|
96
|
-
roles?: readonly RoleSpec[];
|
|
91
|
+
/** Discriminator identifying this node as a Roles modifier. */type: 'ROLES'; /** The safe that this roles modifier controls. */
|
|
92
|
+
target?: AddressOrRef; /** The account that is allowed to update the configuration of the Roles mod. */
|
|
93
|
+
owner?: AddressOrRef; /** The account that calls will be executed from. */
|
|
94
|
+
avatar?: AddressOrRef; /** MultiSend contract addresses for batched transactions. */
|
|
95
|
+
multisend?: readonly Lowercase<Address>[]; /** Role definitions configured on this modifier. */
|
|
96
|
+
roles?: readonly RoleSpec[]; /** Spending allowances configured on this modifier. */
|
|
97
97
|
allowances?: readonly AllowanceSpec[];
|
|
98
98
|
}>;
|
|
99
99
|
/** Any complete node that can be passed to `apply()`. */
|
|
@@ -106,8 +106,8 @@ type NewSafeProps = {
|
|
|
106
106
|
vault?: boolean;
|
|
107
107
|
};
|
|
108
108
|
type NewRolesProps = {
|
|
109
|
-
/** Deployment nonce for CREATE2 address derivation. */nonce
|
|
110
|
-
target
|
|
109
|
+
/** Deployment nonce for CREATE2 address derivation. Defaults to `0n` when omitted. */nonce?: bigint; /** The safe that this roles modifier controls. Defaults to the new safe with the same label, when one exists. */
|
|
110
|
+
target?: AddressOrRef; /** The account that calls will be executed from. Defaults to `target` value */
|
|
111
111
|
avatar?: AddressOrRef; /** The account that is allowed to update the configuration of the Roles Mod. Defaults to `target` value */
|
|
112
112
|
owner?: AddressOrRef; /** MultiSend contract addresses for batched transactions. Defaults to `['0x38869bf66a61cf6bdb996a6ae40d5853fd43b526', '0x9641d764fc13c8b624c04430c7356c1c7c8102e2']` */
|
|
113
113
|
multisend?: readonly Lowercase<Address>[]; /** Role definitions to configure on this modifier. */
|
|
@@ -118,17 +118,19 @@ type EntityAccessor<Type extends string, Entries extends Record<string, any>, Ch
|
|
|
118
118
|
type: Type;
|
|
119
119
|
label: K;
|
|
120
120
|
chain: Ch;
|
|
121
|
-
}>> & (<O extends { [P in Exclude<keyof Entries[K] & string, 'id' | 'label'>]?: any } = {}>(overrides?: { [P in Exclude<keyof Entries[K] & string, 'id' | 'label'>]?: any } & O) => Readonly<Prettify<Omit<Entries[K], keyof O> & O & {
|
|
121
|
+
}>> & (<O extends { [P in Exclude<keyof Entries[K] & string, 'id' | 'label'>]?: any } & Partial<NP> = {}>(overrides?: { [P in Exclude<keyof Entries[K] & string, 'id' | 'label'>]?: any } & Partial<NP> & O) => Readonly<Prettify<Omit<Entries[K], keyof O> & O & Partial<NP> & {
|
|
122
122
|
type: Type;
|
|
123
123
|
label: K;
|
|
124
124
|
chain: Ch;
|
|
125
|
-
}>>) : <
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
}>>) : Readonly<Prettify<{
|
|
126
|
+
type: Type;
|
|
127
|
+
label: string;
|
|
128
|
+
chain: Ch;
|
|
129
|
+
}>> & ((props: NP) => Readonly<Prettify<NP & {
|
|
128
130
|
type: Type;
|
|
129
131
|
label: string;
|
|
130
132
|
chain: Ch;
|
|
131
|
-
}>> };
|
|
133
|
+
}>>) };
|
|
132
134
|
type UserAccessor<C extends CodegenData, Ch extends number> = { readonly [K in keyof C['users'] & string]: C['users'][K]['personalSafes'][Ch]['address'] };
|
|
133
135
|
type ConstellationResult<C extends CodegenData, W extends keyof C['vaults'] = keyof C['vaults'], Ch extends ChainId = ChainId> = {
|
|
134
136
|
/** Access existing safes by label or create new ones with a new label. */safe: EntityAccessor<'SAFE', WorkspaceVaultEntries<C, W>, Ch, NewSafeProps>; /** Access existing roles modifiers by label or create new ones with a new label. */
|
|
@@ -154,7 +156,7 @@ declare function constellation<const C extends CodegenData = GeneratedCodegen, c
|
|
|
154
156
|
}, internal?: ConstellationInternalOpts<C>): ConstellationResult<C, W, Ch>;
|
|
155
157
|
//#endregion
|
|
156
158
|
//#region src/api.d.ts
|
|
157
|
-
type Options = {
|
|
159
|
+
type Options$1 = {
|
|
158
160
|
workspace?: string;
|
|
159
161
|
apiKey?: string;
|
|
160
162
|
baseUrl?: string;
|
|
@@ -171,7 +173,7 @@ declare class ApiClient {
|
|
|
171
173
|
fetch: customFetch,
|
|
172
174
|
headers,
|
|
173
175
|
apiKey
|
|
174
|
-
}?: Options);
|
|
176
|
+
}?: Options$1);
|
|
175
177
|
protected postJson(endpoint: string, payload: unknown): Promise<any>;
|
|
176
178
|
protected get(endpoint: string): Promise<any>;
|
|
177
179
|
listVaults(): Promise<ListVaultsResult>;
|
|
@@ -206,5 +208,5 @@ declare function apply(nodes: ConstellationNode[] | {
|
|
|
206
208
|
[key: string]: ConstellationNode;
|
|
207
209
|
}, opts?: ApplyOpts): Promise<ApplyConstellationResult[]>;
|
|
208
210
|
//#endregion
|
|
209
|
-
export { EVERYTHING,
|
|
211
|
+
export { type ChainPrefix, type ConditionFunction, EVERYTHING, type FunctionPermission, type Options, type Scoping, type TargetPermission, apply, buildAllowKit, c, constellation, forAll };
|
|
210
212
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as ApiClient } from "./api-D6ee2Q2b.mjs";
|
|
2
|
+
import { n as EVERYTHING, t as buildAllowKit } from "./allow-Dzh6t_l8.mjs";
|
|
2
3
|
import { createRequire } from "module";
|
|
3
4
|
import { invariant } from "@epic-web/invariant";
|
|
4
|
-
import { EVERYTHING, allow } from "zodiac-roles-sdk/kit";
|
|
5
5
|
import { c, forAll } from "zodiac-roles-sdk";
|
|
6
6
|
//#region src/constellation.ts
|
|
7
7
|
function loadCodegen() {
|
|
@@ -29,18 +29,34 @@ function constellation(opts, internal) {
|
|
|
29
29
|
chain: opts.chain,
|
|
30
30
|
workspaceId: ws?.workspaceId ?? ""
|
|
31
31
|
};
|
|
32
|
+
const newSafes = /* @__PURE__ */ new Map();
|
|
32
33
|
function makeNodeRef(data) {
|
|
33
|
-
|
|
34
|
+
const ref = Object.freeze({
|
|
34
35
|
...data,
|
|
35
36
|
chain: opts.chain,
|
|
36
37
|
_constellation: meta
|
|
37
38
|
});
|
|
39
|
+
if (ref.type === "SAFE" && typeof ref.label === "string") newSafes.set(ref.label, ref);
|
|
40
|
+
return ref;
|
|
38
41
|
}
|
|
39
|
-
function entityAccessor(registry, type) {
|
|
42
|
+
function entityAccessor(registry, type, resolveCanonicalSafe) {
|
|
43
|
+
const cache = /* @__PURE__ */ new Map();
|
|
40
44
|
return new Proxy({}, { get(_target, name) {
|
|
41
45
|
if (typeof name !== "string") return void 0;
|
|
46
|
+
const cached = cache.get(name);
|
|
47
|
+
if (cached) return cached;
|
|
42
48
|
const existing = registry[name];
|
|
43
49
|
const fn = (overrides) => {
|
|
50
|
+
const canonicalSafe = resolveCanonicalSafe && !overrides?.target ? resolveCanonicalSafe(name) : void 0;
|
|
51
|
+
if (canonicalSafe) return makeNodeRef({
|
|
52
|
+
type,
|
|
53
|
+
nonce: 0n,
|
|
54
|
+
target: canonicalSafe,
|
|
55
|
+
owner: canonicalSafe,
|
|
56
|
+
avatar: canonicalSafe,
|
|
57
|
+
...overrides,
|
|
58
|
+
label: name
|
|
59
|
+
});
|
|
44
60
|
return makeNodeRef({
|
|
45
61
|
type,
|
|
46
62
|
...existing || {},
|
|
@@ -48,13 +64,14 @@ function constellation(opts, internal) {
|
|
|
48
64
|
label: name
|
|
49
65
|
});
|
|
50
66
|
};
|
|
51
|
-
|
|
67
|
+
Object.assign(fn, {
|
|
52
68
|
type,
|
|
53
|
-
...existing,
|
|
69
|
+
...existing || {},
|
|
54
70
|
label: name,
|
|
55
71
|
chain: opts.chain,
|
|
56
72
|
_constellation: meta
|
|
57
73
|
});
|
|
74
|
+
cache.set(name, fn);
|
|
58
75
|
return fn;
|
|
59
76
|
} });
|
|
60
77
|
}
|
|
@@ -67,9 +84,14 @@ function constellation(opts, internal) {
|
|
|
67
84
|
return personalSafe.address;
|
|
68
85
|
} });
|
|
69
86
|
}
|
|
87
|
+
const safe = entityAccessor(vaultsByLabel, "SAFE");
|
|
70
88
|
return {
|
|
71
|
-
safe
|
|
72
|
-
roles: entityAccessor(vaultsByLabel, "ROLES")
|
|
89
|
+
safe,
|
|
90
|
+
roles: entityAccessor(vaultsByLabel, "ROLES", (name) => {
|
|
91
|
+
const invoked = newSafes.get(name);
|
|
92
|
+
if (invoked) return invoked;
|
|
93
|
+
if (name in vaultsByLabel) return safe[name];
|
|
94
|
+
}),
|
|
73
95
|
user: userAccessor()
|
|
74
96
|
};
|
|
75
97
|
}
|
|
@@ -91,7 +113,7 @@ async function apply(nodes, opts) {
|
|
|
91
113
|
const api = opts?.api ?? new ApiClient();
|
|
92
114
|
const refs = deriveRefs(nodes);
|
|
93
115
|
const groups = /* @__PURE__ */ new Map();
|
|
94
|
-
for (const node of refs.keys()) {
|
|
116
|
+
for (const node of refs.byIdentity.keys()) {
|
|
95
117
|
const meta = node._constellation;
|
|
96
118
|
invariant(meta, `Node "${node.label}" is not associated with a constellation`);
|
|
97
119
|
const key = `${meta.workspaceId}:${meta.chain}:${meta.label}`;
|
|
@@ -117,30 +139,41 @@ async function apply(nodes, opts) {
|
|
|
117
139
|
}
|
|
118
140
|
return results;
|
|
119
141
|
}
|
|
142
|
+
function labelKey(node) {
|
|
143
|
+
return `${node._constellation.workspaceId}:${node.type}:${node.label}`;
|
|
144
|
+
}
|
|
120
145
|
function deriveRefs(nodes) {
|
|
121
|
-
const
|
|
146
|
+
const byIdentity = /* @__PURE__ */ new Map();
|
|
147
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
148
|
+
const register = (node, ref) => {
|
|
149
|
+
byIdentity.set(node, ref);
|
|
150
|
+
byLabel.set(labelKey(node), ref);
|
|
151
|
+
};
|
|
122
152
|
if (Array.isArray(nodes)) for (let i = 0; i < nodes.length; i++) {
|
|
123
153
|
const node = nodes[i];
|
|
124
154
|
invariant(isConstellationNode(node), `unexpected node input at index: ${i}`);
|
|
125
|
-
|
|
155
|
+
register(node, `${i}`);
|
|
126
156
|
}
|
|
127
157
|
else for (const [key, node] of Object.entries(nodes)) {
|
|
128
158
|
invariant(isConstellationNode(node), `unexpected node input under key: ${key}`);
|
|
129
|
-
|
|
159
|
+
register(node, key);
|
|
130
160
|
}
|
|
131
|
-
return
|
|
161
|
+
return {
|
|
162
|
+
byIdentity,
|
|
163
|
+
byLabel
|
|
164
|
+
};
|
|
132
165
|
}
|
|
133
166
|
function nodeToSpec(node, refs) {
|
|
134
167
|
const { id, _constellation, ...rest } = node;
|
|
135
168
|
const spec = {};
|
|
136
169
|
for (const [key, value] of Object.entries(rest)) spec[key] = resolveRefs(value, refs);
|
|
137
|
-
spec.ref = refs.get(node);
|
|
170
|
+
spec.ref = refs.byIdentity.get(node);
|
|
138
171
|
invariant(spec.ref != null, "ref not found");
|
|
139
172
|
return stringifyBigints(spec);
|
|
140
173
|
}
|
|
141
174
|
function resolveRefs(value, refs) {
|
|
142
175
|
if (isConstellationNode(value)) {
|
|
143
|
-
const ref = refs.get(value);
|
|
176
|
+
const ref = refs.byIdentity.get(value) ?? refs.byLabel.get(labelKey(value));
|
|
144
177
|
invariant(ref != null, `Node "${value.label}" is referenced not included in the apply() call`);
|
|
145
178
|
return `$${ref}`;
|
|
146
179
|
}
|
|
@@ -170,6 +203,6 @@ function isConstellationNode(value) {
|
|
|
170
203
|
return false;
|
|
171
204
|
}
|
|
172
205
|
//#endregion
|
|
173
|
-
export { EVERYTHING,
|
|
206
|
+
export { EVERYTHING, apply, buildAllowKit, c, constellation, forAll };
|
|
174
207
|
|
|
175
208
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/constellation.ts","../src/apply.ts"],"sourcesContent":["/// <reference path=\"./zodiac-os-codegen.d.ts\" />\nimport type { Address, ChainId } from '@zodiac-os/api-types'\nimport type { AllowanceSpec, RoleSpec } from './types'\nimport { createRequire } from 'module'\nimport type * as ZodiacOsCodegen from '.zodiac-os'\nimport { UUID } from 'crypto'\n\ntype User = {\n id: UUID\n fullName: string\n personalSafes: Record<\n number,\n { address: Lowercase<Address>; active: boolean }\n >\n}\n\ntype Vault = {\n id: UUID\n label: string\n address: Lowercase<Address>\n chain: ChainId\n threshold: number\n owners: readonly string[]\n modules: readonly string[]\n}\n\ntype WorkspaceVaults = {\n workspaceId: UUID\n workspaceName: string\n vaults: Readonly<Record<string, Vault>>\n}\n\n/** Shape of the codegen data produced by `zodiac-os pull-org`. */\nexport type CodegenData = {\n users: Readonly<Record<string, User>>\n vaults: Readonly<Record<string, WorkspaceVaults>>\n}\n\ntype GeneratedCodegen = {\n users: typeof ZodiacOsCodegen.users\n vaults: typeof ZodiacOsCodegen.vaults\n}\n\ntype ConstellationOpts<C extends CodegenData> = {\n /** Workspace to scope vaults and roles to. */\n workspace: keyof C['vaults'] & string\n /** Human-readable label for this constellation. */\n label: string\n /** Target chain for all nodes in this constellation. */\n chain: ChainId\n}\n\ntype ConstellationInternalOpts<C extends CodegenData> = {\n /** Injected codegen data (used for testing). */\n codegen?: C\n}\n\ntype Prettify<T> = { readonly [K in keyof T]: T[K] } & {}\n\ntype WorkspaceVaultEntries<\n C extends CodegenData,\n W extends keyof C['vaults'],\n> = C['vaults'][W]['vaults']\n\ntype NodeType = 'SAFE' | 'ROLES' | 'DELAY'\n\n/** A reference to a node used in `owners`, `modules`, `target`, etc. */\ntype NodeRef = Readonly<{ type: NodeType; label: string; chain: ChainId }>\n\n/** A blockchain address or a reference to another node in the constellation. */\ntype AddressOrRef = Lowercase<Address> | NodeRef\n\ntype NodeBase = Readonly<{\n label: string\n chain: ChainId\n /** Set for existing nodes from codegen, absent for new nodes. */\n address?: Lowercase<Address>\n /** Deployment nonce — required for new nodes, optional for existing. */\n nonce?: bigint\n}>\n\n/** A safe node spec — existing vault ref or new safe with required config. */\nexport type SafeNode = NodeBase &\n Readonly<{\n type: 'SAFE'\n threshold: number\n owners: readonly (string | NodeRef)[]\n modules?: readonly (string | NodeRef)[]\n vault?: boolean\n }>\n\n/** A roles modifier node spec — existing vault ref or new roles with modifier config. */\nexport type RolesNode = NodeBase &\n Readonly<{\n type: 'ROLES'\n target?: AddressOrRef\n owner?: AddressOrRef\n avatar?: AddressOrRef\n multisend?: readonly Lowercase<Address>[]\n roles?: readonly RoleSpec[]\n allowances?: readonly AllowanceSpec[]\n }>\n\n/** Any complete node that can be passed to `apply()`. */\nexport type ConstellationNode = SafeNode | RolesNode\nexport type ConstellationNodeInternal = ConstellationNode & {\n _constellation: ConstellationMeta\n}\n\ntype NewSafeProps = {\n /** Deployment nonce for CREATE2 address derivation. */\n nonce: bigint\n /** Number of owner signatures required to execute a transaction. */\n threshold: number\n /** Safe owner addresses or node references. */\n owners: readonly AddressOrRef[]\n /** Module addresses or node references to enable on the safe. */\n modules?: readonly AddressOrRef[]\n /** Whether this safe is a workspace vault. @default false */\n vault?: boolean\n}\n\ntype NewRolesProps = {\n /** Deployment nonce for CREATE2 address derivation. */\n nonce: bigint\n /** The safe that this roles modifier controls. */\n target: AddressOrRef\n /** The account that calls will be executed from. Defaults to `target` value */\n avatar?: AddressOrRef\n /** The account that is allowed to update the configuration of the Roles Mod. Defaults to `target` value */\n owner?: AddressOrRef\n /** MultiSend contract addresses for batched transactions. Defaults to `['0x38869bf66a61cf6bdb996a6ae40d5853fd43b526', '0x9641d764fc13c8b624c04430c7356c1c7c8102e2']` */\n multisend?: readonly Lowercase<Address>[]\n /** Role definitions to configure on this modifier. */\n roles?: readonly RoleSpec[]\n /** Spending allowances to configure on this modifier. */\n allowances?: readonly AllowanceSpec[]\n}\n\ntype EntityAccessor<\n Type extends string,\n Entries extends Record<string, any>,\n Ch extends ChainId = ChainId,\n NP extends Record<string, any> = Record<string, any>,\n> = {\n readonly [K in\n | (keyof Entries & string)\n | (string & {})]: K extends keyof Entries & string\n ? Readonly<Prettify<Entries[K] & { type: Type; label: K; chain: Ch }>> &\n (<\n O extends {\n [P in Exclude<keyof Entries[K] & string, 'id' | 'label'>]?: any\n } = {},\n >(\n overrides?: {\n [P in Exclude<keyof Entries[K] & string, 'id' | 'label'>]?: any\n } & O\n ) => Readonly<\n Prettify<\n Omit<Entries[K], keyof O> & O & { type: Type; label: K; chain: Ch }\n >\n >)\n : <P extends Record<string, any>>(\n props: NP & { [key: string & {}]: any } & P\n ) => Readonly<Prettify<P & { type: Type; label: string; chain: Ch }>>\n}\n\ntype UserAccessor<C extends CodegenData, Ch extends number> = {\n readonly [K in keyof C['users'] &\n string]: C['users'][K]['personalSafes'][Ch]['address']\n}\n\ntype ConstellationResult<\n C extends CodegenData,\n W extends keyof C['vaults'] = keyof C['vaults'],\n Ch extends ChainId = ChainId,\n> = {\n /** Access existing safes by label or create new ones with a new label. */\n safe: EntityAccessor<'SAFE', WorkspaceVaultEntries<C, W>, Ch, NewSafeProps>\n /** Access existing roles modifiers by label or create new ones with a new label. */\n roles: EntityAccessor<'ROLES', WorkspaceVaultEntries<C, W>, Ch, NewRolesProps>\n /** Resolve a user's personal safe address on the constellation's chain. */\n user: UserAccessor<C, Ch>\n}\n\n/** @internal */\nexport type ConstellationMeta = {\n label: string\n chain: ChainId\n workspaceId: UUID\n}\n\nfunction loadCodegen(): CodegenData {\n const require = createRequire(import.meta.url)\n return require('.zodiac-os') as CodegenData\n}\n\n/**\n * Creates a constellation scoped to a workspace and chain.\n *\n * Use bracket access to reference existing vaults or define new nodes:\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n *\n * const dao = eth.safe['GG DAO'] // existing vault ref\n * const roles = eth.roles['GG DAO'] // existing roles ref\n * const newSafe = eth.safe['New Safe']({ nonce: 0n, threshold: 2, owners: [...], modules: [...] })\n * ```\n */\nexport function constellation<\n const C extends CodegenData = GeneratedCodegen,\n const W extends keyof C['vaults'] & string = keyof C['vaults'] & string,\n const Ch extends ChainId = ChainId,\n>(\n opts: ConstellationOpts<C> & { workspace: W; chain: Ch },\n internal?: ConstellationInternalOpts<C>\n): ConstellationResult<C, W, Ch> {\n const codegen: CodegenData = internal?.codegen ?? loadCodegen()\n\n const ws = codegen.vaults[opts.workspace]\n const vaultsByLabel: Record<string, Vault> = {}\n if (ws) {\n for (const [label, vault] of Object.entries(ws.vaults)) {\n vaultsByLabel[label] = vault\n }\n }\n\n const meta: ConstellationMeta = {\n label: opts.label,\n chain: opts.chain,\n workspaceId: (ws?.workspaceId ?? '') as UUID,\n }\n\n function makeNodeRef(\n data: Record<string, any>\n ): Readonly<Record<string, any>> {\n const ref = Object.freeze({\n ...data,\n chain: opts.chain,\n _constellation: meta,\n })\n return ref\n }\n\n function entityAccessor(\n registry: Record<string, Record<string, any>>,\n type: string\n ) {\n return new Proxy({} as Record<string, any>, {\n get(_target: any, name: string) {\n if (typeof name !== 'string') return undefined\n const existing = registry[name]\n const fn = (overrides?: Record<string, any>) => {\n return makeNodeRef({\n type,\n ...(existing || {}),\n ...overrides,\n label: name,\n })\n }\n if (existing) {\n Object.assign(fn, {\n type,\n ...existing,\n label: name,\n chain: opts.chain,\n _constellation: meta,\n })\n }\n return fn\n },\n })\n }\n\n function userAccessor() {\n return new Proxy(\n {},\n {\n get(_target: any, name: string) {\n const user = codegen.users[name]\n if (!user) throw new Error(`Unknown user: ${name}`)\n const personalSafe = user.personalSafes[opts.chain]\n if (!personalSafe) {\n throw new Error(\n `User ${name} has no personal safe on chain ${opts.chain}`\n )\n }\n return personalSafe.address\n },\n }\n )\n }\n\n return {\n safe: entityAccessor(vaultsByLabel, 'SAFE'),\n roles: entityAccessor(vaultsByLabel, 'ROLES'),\n user: userAccessor(),\n } as ConstellationResult<C, W, Ch>\n}\n","import type {\n ApplyConstellationPayload,\n ApplyConstellationResult,\n} from '@zodiac-os/api-types'\nimport { invariant } from '@epic-web/invariant'\nimport { ApiClient } from './api'\nimport type {\n ConstellationMeta,\n ConstellationNode,\n ConstellationNodeInternal,\n} from './constellation'\n\ntype ApplyOpts = {\n /** API client instance. Defaults to a client configured from environment variables. */\n api?: ApiClient\n}\n\n/**\n * Resolves node references and applies the constellation specification via the API.\n *\n *\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n * const dao = eth.safe['GG DAO']\n * const roles = eth.roles['New Roles']({ nonce: 0n, target: dao, owner: dao, avatar: dao })\n *\n * await apply([dao, roles])\n * ```\n */\nexport async function apply(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode },\n opts?: ApplyOpts\n): Promise<ApplyConstellationResult[]> {\n const api = opts?.api ?? new ApiClient()\n const refs = deriveRefs(nodes)\n\n // Group nodes by constellation (multiple constellations can be applied with a single call)\n const groups = new Map<\n string,\n { meta: ConstellationMeta; nodes: ConstellationNodeInternal[] }\n >()\n\n for (const node of refs.keys()) {\n const meta = node._constellation\n invariant(\n meta,\n `Node \"${node.label}\" is not associated with a constellation`\n )\n const key = `${meta.workspaceId}:${meta.chain}:${meta.label}`\n let group = groups.get(key)\n if (!group) {\n group = { meta, nodes: [] }\n groups.set(key, group)\n }\n group.nodes.push(node)\n }\n\n const results: ApplyConstellationResult[] = []\n for (const { meta, nodes: groupNodes } of groups.values()) {\n const specification = groupNodes.map((n) => nodeToSpec(n, refs))\n const result = await api.applyConstellation(meta.workspaceId, {\n label: meta.label,\n chain: meta.chain,\n specification,\n })\n results.push(result)\n }\n\n return results\n}\n\nfunction deriveRefs(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode }\n): Map<ConstellationNodeInternal, string> {\n const refs = new Map<ConstellationNodeInternal, string>()\n\n if (Array.isArray(nodes)) {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n invariant(\n isConstellationNode(node),\n `unexpected node input at index: ${i}`\n )\n refs.set(node, `${i}`)\n }\n } else {\n for (const [key, node] of Object.entries(nodes)) {\n invariant(\n isConstellationNode(node),\n `unexpected node input under key: ${key}`\n )\n refs.set(node, key)\n }\n }\n\n return refs\n}\n\nfunction nodeToSpec(\n node: ConstellationNodeInternal,\n refs: Map<ConstellationNodeInternal, string>\n): ApplyConstellationPayload['specification'][number] {\n const { id, _constellation, ...rest } = node as Record<string, any>\n const spec: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(rest)) {\n spec[key] = resolveRefs(value, refs)\n }\n\n spec.ref = refs.get(node)\n invariant(spec.ref != null, 'ref not found')\n\n return stringifyBigints(\n spec\n ) as ApplyConstellationPayload['specification'][number]\n}\n\nfunction resolveRefs(\n value: unknown,\n refs: Map<ConstellationNodeInternal, string>\n): unknown {\n if (isConstellationNode(value)) {\n const ref = refs.get(value)\n invariant(\n ref != null,\n `Node \"${value.label}\" is referenced not included in the apply() call`\n )\n return `$${ref}`\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => resolveRefs(v, refs))\n }\n\n if (typeof value === 'object' && value !== null) {\n const resolved: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n resolved[k] = resolveRefs(v, refs)\n }\n return resolved\n }\n\n return value\n}\n\nfunction stringifyBigints(value: unknown): unknown {\n if (typeof value === 'bigint') {\n return value.toString()\n }\n\n if (Array.isArray(value)) {\n return value.map(stringifyBigints)\n }\n\n if (typeof value === 'object' && value !== null) {\n const result: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n result[k] = stringifyBigints(v)\n }\n return result\n }\n\n return value\n}\n\nfunction isConstellationNode(\n value: unknown\n): value is ConstellationNodeInternal {\n if (typeof value === 'function' || typeof value === 'object') {\n const obj = value as any\n return (\n obj != null &&\n typeof obj.type === 'string' &&\n typeof obj.chain === 'number' &&\n typeof obj._constellation === 'object'\n )\n }\n return false\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/constellation.ts","../src/apply.ts"],"sourcesContent":["/// <reference path=\"./zodiac-os-codegen.d.ts\" />\nimport type { Address, ChainId } from '@zodiac-os/api-types'\nimport type { AllowanceSpec, RoleSpec } from './types'\nimport { createRequire } from 'module'\nimport type * as ZodiacOsCodegen from '.zodiac-os'\nimport { UUID } from 'crypto'\n\ntype User = {\n id: UUID\n fullName: string\n personalSafes: Record<\n number,\n { address: Lowercase<Address>; active: boolean }\n >\n}\n\ntype Vault = {\n id: UUID\n label: string\n address: Lowercase<Address>\n chain: ChainId\n threshold: number\n owners: readonly string[]\n modules: readonly string[]\n}\n\ntype WorkspaceVaults = {\n workspaceId: UUID\n workspaceName: string\n vaults: Readonly<Record<string, Vault>>\n}\n\n/** Shape of the codegen data produced by `zodiac-os pull-org`. */\nexport type CodegenData = {\n users: Readonly<Record<string, User>>\n vaults: Readonly<Record<string, WorkspaceVaults>>\n}\n\ntype GeneratedCodegen = {\n users: typeof ZodiacOsCodegen.users\n vaults: typeof ZodiacOsCodegen.vaults\n}\n\ntype ConstellationOpts<C extends CodegenData> = {\n /** Workspace to scope vaults and roles to. */\n workspace: keyof C['vaults'] & string\n /** Human-readable label for this constellation. */\n label: string\n /** Target chain for all nodes in this constellation. */\n chain: ChainId\n}\n\ntype ConstellationInternalOpts<C extends CodegenData> = {\n /** Injected codegen data (used for testing). */\n codegen?: C\n}\n\ntype Prettify<T> = { readonly [K in keyof T]: T[K] } & {}\n\ntype WorkspaceVaultEntries<\n C extends CodegenData,\n W extends keyof C['vaults'],\n> = C['vaults'][W]['vaults']\n\ntype NodeType = 'SAFE' | 'ROLES' | 'DELAY'\n\n/** A reference to a node used in `owners`, `modules`, `target`, etc. */\ntype NodeRef = Readonly<{ type: NodeType; label: string; chain: ChainId }>\n\n/** A blockchain address or a reference to another node in the constellation. */\ntype AddressOrRef = Lowercase<Address> | NodeRef\n\ntype NodeBase = Readonly<{\n /** Human-readable identifier, unique within the constellation. */\n label: string\n /** Chain the node is deployed on. */\n chain: ChainId\n /** Set for existing nodes from codegen, absent for new nodes. */\n address?: Lowercase<Address>\n /** Deployment nonce — required for new nodes, optional for existing. */\n nonce?: bigint\n}>\n\n/** A safe node spec — existing vault ref or new safe with required config. */\nexport type SafeNode = NodeBase &\n Readonly<{\n /** Discriminator identifying this node as a Safe. */\n type: 'SAFE'\n /** Number of owner signatures required to execute a transaction. */\n threshold: number\n /** Safe owner addresses or node references. */\n owners: readonly (string | NodeRef)[]\n /** Module addresses or node references enabled on the safe. */\n modules?: readonly (string | NodeRef)[]\n /** Whether this safe shall appear as a vault in the workspace. @default false */\n vault?: boolean\n }>\n\n/** A roles modifier node spec — existing vault ref or new roles with modifier config. */\nexport type RolesNode = NodeBase &\n Readonly<{\n /** Discriminator identifying this node as a Roles modifier. */\n type: 'ROLES'\n /** The safe that this roles modifier controls. */\n target?: AddressOrRef\n /** The account that is allowed to update the configuration of the Roles mod. */\n owner?: AddressOrRef\n /** The account that calls will be executed from. */\n avatar?: AddressOrRef\n /** MultiSend contract addresses for batched transactions. */\n multisend?: readonly Lowercase<Address>[]\n /** Role definitions configured on this modifier. */\n roles?: readonly RoleSpec[]\n /** Spending allowances configured on this modifier. */\n allowances?: readonly AllowanceSpec[]\n }>\n\n/** Any complete node that can be passed to `apply()`. */\nexport type ConstellationNode = SafeNode | RolesNode\nexport type ConstellationNodeInternal = ConstellationNode & {\n _constellation: ConstellationMeta\n}\n\ntype NewSafeProps = {\n /** Deployment nonce for CREATE2 address derivation. */\n nonce: bigint\n /** Number of owner signatures required to execute a transaction. */\n threshold: number\n /** Safe owner addresses or node references. */\n owners: readonly AddressOrRef[]\n /** Module addresses or node references to enable on the safe. */\n modules?: readonly AddressOrRef[]\n /** Whether this safe is a workspace vault. @default false */\n vault?: boolean\n}\n\ntype NewRolesProps = {\n /** Deployment nonce for CREATE2 address derivation. Defaults to `0n` when omitted. */\n nonce?: bigint\n /** The safe that this roles modifier controls. Defaults to the new safe with the same label, when one exists. */\n target?: AddressOrRef\n /** The account that calls will be executed from. Defaults to `target` value */\n avatar?: AddressOrRef\n /** The account that is allowed to update the configuration of the Roles Mod. Defaults to `target` value */\n owner?: AddressOrRef\n /** MultiSend contract addresses for batched transactions. Defaults to `['0x38869bf66a61cf6bdb996a6ae40d5853fd43b526', '0x9641d764fc13c8b624c04430c7356c1c7c8102e2']` */\n multisend?: readonly Lowercase<Address>[]\n /** Role definitions to configure on this modifier. */\n roles?: readonly RoleSpec[]\n /** Spending allowances to configure on this modifier. */\n allowances?: readonly AllowanceSpec[]\n}\n\ntype EntityAccessor<\n Type extends string,\n Entries extends Record<string, any>,\n Ch extends ChainId = ChainId,\n NP extends Record<string, any> = Record<string, any>,\n> = {\n readonly [K in\n | (keyof Entries & string)\n | (string & {})]: K extends keyof Entries & string\n ? Readonly<Prettify<Entries[K] & { type: Type; label: K; chain: Ch }>> &\n (<\n O extends {\n [P in Exclude<keyof Entries[K] & string, 'id' | 'label'>]?: any\n } & Partial<NP> = {},\n >(\n overrides?: {\n [P in Exclude<keyof Entries[K] & string, 'id' | 'label'>]?: any\n } & Partial<NP> &\n O\n ) => Readonly<\n Prettify<\n Omit<Entries[K], keyof O> &\n O &\n Partial<NP> & { type: Type; label: K; chain: Ch }\n >\n >)\n : Readonly<Prettify<{ type: Type; label: string; chain: Ch }>> &\n ((\n props: NP\n ) => Readonly<Prettify<NP & { type: Type; label: string; chain: Ch }>>)\n}\n\ntype UserAccessor<C extends CodegenData, Ch extends number> = {\n readonly [K in keyof C['users'] &\n string]: C['users'][K]['personalSafes'][Ch]['address']\n}\n\ntype ConstellationResult<\n C extends CodegenData,\n W extends keyof C['vaults'] = keyof C['vaults'],\n Ch extends ChainId = ChainId,\n> = {\n /** Access existing safes by label or create new ones with a new label. */\n safe: EntityAccessor<'SAFE', WorkspaceVaultEntries<C, W>, Ch, NewSafeProps>\n /** Access existing roles modifiers by label or create new ones with a new label. */\n roles: EntityAccessor<'ROLES', WorkspaceVaultEntries<C, W>, Ch, NewRolesProps>\n /** Resolve a user's personal safe address on the constellation's chain. */\n user: UserAccessor<C, Ch>\n}\n\n/** @internal */\nexport type ConstellationMeta = {\n label: string\n chain: ChainId\n workspaceId: UUID\n}\n\nfunction loadCodegen(): CodegenData {\n const require = createRequire(import.meta.url)\n return require('.zodiac-os') as CodegenData\n}\n\n/**\n * Creates a constellation scoped to a workspace and chain.\n *\n * Use bracket access to reference existing vaults or define new nodes:\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n *\n * const dao = eth.safe['GG DAO'] // existing vault ref\n * const roles = eth.roles['GG DAO'] // existing roles ref\n * const newSafe = eth.safe['New Safe']({ nonce: 0n, threshold: 2, owners: [...], modules: [...] })\n * ```\n */\nexport function constellation<\n const C extends CodegenData = GeneratedCodegen,\n const W extends keyof C['vaults'] & string = keyof C['vaults'] & string,\n const Ch extends ChainId = ChainId,\n>(\n opts: ConstellationOpts<C> & { workspace: W; chain: Ch },\n internal?: ConstellationInternalOpts<C>\n): ConstellationResult<C, W, Ch> {\n const codegen: CodegenData = internal?.codegen ?? loadCodegen()\n\n const ws = codegen.vaults[opts.workspace]\n const vaultsByLabel: Record<string, Vault> = {}\n if (ws) {\n for (const [label, vault] of Object.entries(ws.vaults)) {\n vaultsByLabel[label] = vault\n }\n }\n\n const meta: ConstellationMeta = {\n label: opts.label,\n chain: opts.chain,\n workspaceId: (ws?.workspaceId ?? '') as UUID,\n }\n\n const newSafes = new Map<string, Readonly<Record<string, any>>>()\n\n function makeNodeRef(\n data: Record<string, any>\n ): Readonly<Record<string, any>> {\n const ref: Record<string, any> = Object.freeze({\n ...data,\n chain: opts.chain,\n _constellation: meta,\n })\n if (ref.type === 'SAFE' && typeof ref.label === 'string') {\n newSafes.set(ref.label, ref)\n }\n return ref\n }\n\n function entityAccessor(\n registry: Record<string, Record<string, any>>,\n type: string,\n resolveCanonicalSafe?: (name: string) => Record<string, any> | undefined\n ) {\n const cache = new Map<string, Record<string, any>>()\n return new Proxy({} as Record<string, any>, {\n get(_target: any, name: string) {\n if (typeof name !== 'string') return undefined\n const cached = cache.get(name)\n if (cached) return cached\n const existing = registry[name]\n const fn = (overrides?: Record<string, any>) => {\n const canonicalSafe =\n resolveCanonicalSafe && !overrides?.target\n ? resolveCanonicalSafe(name)\n : undefined\n if (canonicalSafe) {\n return makeNodeRef({\n type,\n nonce: 0n,\n target: canonicalSafe,\n owner: canonicalSafe,\n avatar: canonicalSafe,\n ...overrides,\n label: name,\n })\n }\n return makeNodeRef({\n type,\n ...(existing || {}),\n ...overrides,\n label: name,\n })\n }\n Object.assign(fn, {\n type,\n ...(existing || {}),\n label: name,\n chain: opts.chain,\n _constellation: meta,\n })\n cache.set(name, fn)\n return fn\n },\n })\n }\n\n function userAccessor() {\n return new Proxy(\n {},\n {\n get(_target: any, name: string) {\n const user = codegen.users[name]\n if (!user) throw new Error(`Unknown user: ${name}`)\n const personalSafe = user.personalSafes[opts.chain]\n if (!personalSafe) {\n throw new Error(\n `User ${name} has no personal safe on chain ${opts.chain}`\n )\n }\n return personalSafe.address\n },\n }\n )\n }\n\n const safe = entityAccessor(vaultsByLabel, 'SAFE')\n const roles = entityAccessor(vaultsByLabel, 'ROLES', (name) => {\n const invoked = newSafes.get(name)\n if (invoked) return invoked\n if (name in vaultsByLabel) return safe[name]\n return undefined\n })\n\n return {\n safe,\n roles,\n user: userAccessor(),\n } as ConstellationResult<C, W, Ch>\n}\n","import type {\n ApplyConstellationPayload,\n ApplyConstellationResult,\n} from '@zodiac-os/api-types'\nimport { invariant } from '@epic-web/invariant'\nimport { ApiClient } from './api'\nimport type {\n ConstellationMeta,\n ConstellationNode,\n ConstellationNodeInternal,\n} from './constellation'\n\ntype ApplyOpts = {\n /** API client instance. Defaults to a client configured from environment variables. */\n api?: ApiClient\n}\n\n/**\n * Resolves node references and applies the constellation specification via the API.\n *\n *\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n * const dao = eth.safe['GG DAO']\n * const roles = eth.roles['New Roles']({ nonce: 0n, target: dao, owner: dao, avatar: dao })\n *\n * await apply([dao, roles])\n * ```\n */\nexport async function apply(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode },\n opts?: ApplyOpts\n): Promise<ApplyConstellationResult[]> {\n const api = opts?.api ?? new ApiClient()\n const refs = deriveRefs(nodes)\n\n // Group nodes by constellation (multiple constellations can be applied with a single call)\n const groups = new Map<\n string,\n { meta: ConstellationMeta; nodes: ConstellationNodeInternal[] }\n >()\n\n for (const node of refs.byIdentity.keys()) {\n const meta = node._constellation\n invariant(\n meta,\n `Node \"${node.label}\" is not associated with a constellation`\n )\n const key = `${meta.workspaceId}:${meta.chain}:${meta.label}`\n let group = groups.get(key)\n if (!group) {\n group = { meta, nodes: [] }\n groups.set(key, group)\n }\n group.nodes.push(node)\n }\n\n const results: ApplyConstellationResult[] = []\n for (const { meta, nodes: groupNodes } of groups.values()) {\n const specification = groupNodes.map((n) => nodeToSpec(n, refs))\n const result = await api.applyConstellation(meta.workspaceId, {\n label: meta.label,\n chain: meta.chain,\n specification,\n })\n results.push(result)\n }\n\n return results\n}\n\ntype RefsIndex = {\n byIdentity: Map<ConstellationNodeInternal, string>\n byLabel: Map<string, string>\n}\n\nfunction labelKey(node: ConstellationNodeInternal): string {\n return `${node._constellation.workspaceId}:${node.type}:${node.label}`\n}\n\nfunction deriveRefs(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode }\n): RefsIndex {\n const byIdentity = new Map<ConstellationNodeInternal, string>()\n const byLabel = new Map<string, string>()\n\n const register = (node: ConstellationNodeInternal, ref: string) => {\n byIdentity.set(node, ref)\n byLabel.set(labelKey(node), ref)\n }\n\n if (Array.isArray(nodes)) {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n invariant(\n isConstellationNode(node),\n `unexpected node input at index: ${i}`\n )\n register(node, `${i}`)\n }\n } else {\n for (const [key, node] of Object.entries(nodes)) {\n invariant(\n isConstellationNode(node),\n `unexpected node input under key: ${key}`\n )\n register(node, key)\n }\n }\n\n return { byIdentity, byLabel }\n}\n\nfunction nodeToSpec(\n node: ConstellationNodeInternal,\n refs: RefsIndex\n): ApplyConstellationPayload['specification'][number] {\n const { id, _constellation, ...rest } = node as Record<string, any>\n const spec: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(rest)) {\n spec[key] = resolveRefs(value, refs)\n }\n\n spec.ref = refs.byIdentity.get(node)\n invariant(spec.ref != null, 'ref not found')\n\n return stringifyBigints(\n spec\n ) as ApplyConstellationPayload['specification'][number]\n}\n\nfunction resolveRefs(value: unknown, refs: RefsIndex): unknown {\n if (isConstellationNode(value)) {\n const ref = refs.byIdentity.get(value) ?? refs.byLabel.get(labelKey(value))\n invariant(\n ref != null,\n `Node \"${value.label}\" is referenced not included in the apply() call`\n )\n return `$${ref}`\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => resolveRefs(v, refs))\n }\n\n if (typeof value === 'object' && value !== null) {\n const resolved: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n resolved[k] = resolveRefs(v, refs)\n }\n return resolved\n }\n\n return value\n}\n\nfunction stringifyBigints(value: unknown): unknown {\n if (typeof value === 'bigint') {\n return value.toString()\n }\n\n if (Array.isArray(value)) {\n return value.map(stringifyBigints)\n }\n\n if (typeof value === 'object' && value !== null) {\n const result: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n result[k] = stringifyBigints(v)\n }\n return result\n }\n\n return value\n}\n\nfunction isConstellationNode(\n value: unknown\n): value is ConstellationNodeInternal {\n if (typeof value === 'function' || typeof value === 'object') {\n const obj = value as any\n return (\n obj != null &&\n typeof obj.type === 'string' &&\n typeof obj.chain === 'number' &&\n typeof obj._constellation === 'object'\n )\n }\n return false\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/allow/abi.ts
|
|
4
|
+
function* walkContracts(config) {
|
|
5
|
+
for (const [chain, contracts] of Object.entries(config)) yield* walkLevel(chain, [], contracts);
|
|
6
|
+
}
|
|
7
|
+
function* walkLevel(chain, segments, node) {
|
|
8
|
+
if (typeof node === "string") {
|
|
9
|
+
if (segments.length === 0) throw new Error(`Contract at ${chain} is missing a name`);
|
|
10
|
+
if (!node.startsWith("0x")) throw new Error(`Invalid address for ${chain}.${segments.join(".")}: ${node}`);
|
|
11
|
+
yield {
|
|
12
|
+
chain,
|
|
13
|
+
segments,
|
|
14
|
+
address: node
|
|
15
|
+
};
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (node && typeof node === "object") {
|
|
19
|
+
for (const [key, value] of Object.entries(node)) yield* walkLevel(chain, [...segments, key], value);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Invalid contracts entry at ${chain}.${segments.join(".")}: ${JSON.stringify(node)}`);
|
|
23
|
+
}
|
|
24
|
+
const abiFilePath = (abisDir, node) => path.join(abisDir, node.chain, ...node.segments) + ".json";
|
|
25
|
+
const readAbi = (abisDir, node) => {
|
|
26
|
+
const file = abiFilePath(abisDir, node);
|
|
27
|
+
if (!fs.existsSync(file)) return null;
|
|
28
|
+
const raw = fs.readFileSync(file, "utf8");
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
if (!Array.isArray(parsed)) throw new Error(`ABI at ${file} is not a JSON array`);
|
|
31
|
+
return parsed;
|
|
32
|
+
};
|
|
33
|
+
const writeAbi = (abisDir, node, abi) => {
|
|
34
|
+
const file = abiFilePath(abisDir, node);
|
|
35
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
36
|
+
fs.writeFileSync(file, JSON.stringify(abi, null, 2) + "\n", "utf8");
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/allow/networks.ts
|
|
40
|
+
const CHAIN_IDS = {
|
|
41
|
+
eth: 1,
|
|
42
|
+
oeth: 10,
|
|
43
|
+
gno: 100,
|
|
44
|
+
sep: 11155111,
|
|
45
|
+
matic: 137,
|
|
46
|
+
zkevm: 1101,
|
|
47
|
+
arb1: 42161,
|
|
48
|
+
avax: 43114,
|
|
49
|
+
base: 8453,
|
|
50
|
+
basesep: 84532,
|
|
51
|
+
bnb: 56,
|
|
52
|
+
celo: 42220,
|
|
53
|
+
sonic: 146,
|
|
54
|
+
berachain: 80094,
|
|
55
|
+
unichain: 130,
|
|
56
|
+
worldchain: 480,
|
|
57
|
+
bob: 60808,
|
|
58
|
+
mantle: 5e3,
|
|
59
|
+
hemi: 43111,
|
|
60
|
+
katana: 747474,
|
|
61
|
+
linea: 59144,
|
|
62
|
+
ink: 57073,
|
|
63
|
+
hyperevm: 999,
|
|
64
|
+
flare: 14,
|
|
65
|
+
scroll: 534352,
|
|
66
|
+
plasma: 9745,
|
|
67
|
+
megaeth: 4326
|
|
68
|
+
};
|
|
69
|
+
const chainIdFor = (prefix) => {
|
|
70
|
+
const id = CHAIN_IDS[prefix];
|
|
71
|
+
if (id === void 0) throw new Error(`Unknown chain prefix: ${prefix}`);
|
|
72
|
+
return id;
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
export { walkContracts as a, readAbi as i, chainIdFor as n, writeAbi as o, abiFilePath as r, CHAIN_IDS as t };
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=networks-BTW1qAAa.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"networks-BTW1qAAa.mjs","names":[],"sources":["../src/allow/abi.ts","../src/allow/networks.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Abi } from './fetch'\n\nexport type ContractAddress = `0x${string}`\n\nexport type ContractNode = {\n chain: string\n segments: string[]\n address: ContractAddress\n}\n\nexport function* walkContracts(\n config: Record<string, any>\n): Generator<ContractNode> {\n for (const [chain, contracts] of Object.entries(config)) {\n yield* walkLevel(chain, [], contracts)\n }\n}\n\nfunction* walkLevel(\n chain: string,\n segments: string[],\n node: unknown\n): Generator<ContractNode> {\n if (typeof node === 'string') {\n if (segments.length === 0) {\n throw new Error(`Contract at ${chain} is missing a name`)\n }\n if (!node.startsWith('0x')) {\n throw new Error(\n `Invalid address for ${chain}.${segments.join('.')}: ${node}`\n )\n }\n yield { chain, segments, address: node as ContractAddress }\n return\n }\n if (node && typeof node === 'object') {\n for (const [key, value] of Object.entries(node as Record<string, any>)) {\n yield* walkLevel(chain, [...segments, key], value)\n }\n return\n }\n throw new Error(\n `Invalid contracts entry at ${chain}.${segments.join('.')}: ${JSON.stringify(node)}`\n )\n}\n\nexport const abiFilePath = (abisDir: string, node: ContractNode): string =>\n path.join(abisDir, node.chain, ...node.segments) + '.json'\n\nexport const readAbi = (abisDir: string, node: ContractNode): Abi | null => {\n const file = abiFilePath(abisDir, node)\n if (!fs.existsSync(file)) return null\n const raw = fs.readFileSync(file, 'utf8')\n const parsed = JSON.parse(raw)\n if (!Array.isArray(parsed)) {\n throw new Error(`ABI at ${file} is not a JSON array`)\n }\n return parsed as Abi\n}\n\nexport const writeAbi = (\n abisDir: string,\n node: ContractNode,\n abi: Abi\n): void => {\n const file = abiFilePath(abisDir, node)\n fs.mkdirSync(path.dirname(file), { recursive: true })\n fs.writeFileSync(file, JSON.stringify(abi, null, 2) + '\\n', 'utf8')\n}\n","export const CHAIN_IDS = {\n eth: 1,\n oeth: 10,\n gno: 100,\n sep: 11155111,\n matic: 137,\n zkevm: 1101,\n arb1: 42161,\n avax: 43114,\n base: 8453,\n basesep: 84532,\n bnb: 56,\n celo: 42220,\n sonic: 146,\n berachain: 80094,\n unichain: 130,\n worldchain: 480,\n bob: 60808,\n mantle: 5000,\n hemi: 43111,\n katana: 747474,\n linea: 59144,\n ink: 57073,\n hyperevm: 999,\n flare: 14,\n scroll: 534352,\n plasma: 9745,\n megaeth: 4326,\n} as const satisfies Record<string, number>\n\nexport type ChainPrefix = keyof typeof CHAIN_IDS\n\nexport const chainIdFor = (prefix: string): number => {\n const id = (CHAIN_IDS as Record<string, number>)[prefix]\n if (id === undefined) {\n throw new Error(`Unknown chain prefix: ${prefix}`)\n }\n return id\n}\n"],"mappings":";;;AAYA,UAAiB,cACf,QACyB;AACzB,MAAK,MAAM,CAAC,OAAO,cAAc,OAAO,QAAQ,OAAO,CACrD,QAAO,UAAU,OAAO,EAAE,EAAE,UAAU;;AAI1C,UAAU,UACR,OACA,UACA,MACyB;AACzB,KAAI,OAAO,SAAS,UAAU;AAC5B,MAAI,SAAS,WAAW,EACtB,OAAM,IAAI,MAAM,eAAe,MAAM,oBAAoB;AAE3D,MAAI,CAAC,KAAK,WAAW,KAAK,CACxB,OAAM,IAAI,MACR,uBAAuB,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,IAAI,OACxD;AAEH,QAAM;GAAE;GAAO;GAAU,SAAS;GAAyB;AAC3D;;AAEF,KAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAA4B,CACpE,QAAO,UAAU,OAAO,CAAC,GAAG,UAAU,IAAI,EAAE,MAAM;AAEpD;;AAEF,OAAM,IAAI,MACR,8BAA8B,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,IAAI,KAAK,UAAU,KAAK,GACnF;;AAGH,MAAa,eAAe,SAAiB,SAC3C,KAAK,KAAK,SAAS,KAAK,OAAO,GAAG,KAAK,SAAS,GAAG;AAErD,MAAa,WAAW,SAAiB,SAAmC;CAC1E,MAAM,OAAO,YAAY,SAAS,KAAK;AACvC,KAAI,CAAC,GAAG,WAAW,KAAK,CAAE,QAAO;CACjC,MAAM,MAAM,GAAG,aAAa,MAAM,OAAO;CACzC,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,KAAI,CAAC,MAAM,QAAQ,OAAO,CACxB,OAAM,IAAI,MAAM,UAAU,KAAK,sBAAsB;AAEvD,QAAO;;AAGT,MAAa,YACX,SACA,MACA,QACS;CACT,MAAM,OAAO,YAAY,SAAS,KAAK;AACvC,IAAG,UAAU,KAAK,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AACrD,IAAG,cAAc,MAAM,KAAK,UAAU,KAAK,MAAM,EAAE,GAAG,MAAM,OAAO;;;;ACrErE,MAAa,YAAY;CACvB,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,MAAM;CACN,SAAS;CACT,KAAK;CACL,MAAM;CACN,OAAO;CACP,WAAW;CACX,UAAU;CACV,YAAY;CACZ,KAAK;CACL,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,OAAO;CACP,KAAK;CACL,UAAU;CACV,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACV;AAID,MAAa,cAAc,WAA2B;CACpD,MAAM,KAAM,UAAqC;AACjD,KAAI,OAAO,KAAA,EACT,OAAM,IAAI,MAAM,yBAAyB,SAAS;AAEpD,QAAO"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
// Fallback ambient
|
|
2
|
-
// When `pull-org`
|
|
3
|
-
//
|
|
1
|
+
// Fallback ambient declarations for the .zodiac-os codegen module and for
|
|
2
|
+
// the AllowKit interface. When `pull-org` / `pull-contracts` have been run,
|
|
3
|
+
// the files under node_modules/.zodiac-os/ provide narrow types that take
|
|
4
|
+
// precedence over these empty fallbacks.
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
// Augmented by node_modules/.zodiac-os/allow.d.ts when `pull-contracts` runs.
|
|
8
|
+
interface AllowKit {}
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
declare module '.zodiac-os' {
|
|
5
12
|
import { Address, ChainId } from '@zodiac-os/api-types'
|
|
6
13
|
import { UUID } from 'crypto'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zodiac-os/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"author": "Gnosis Guild",
|
|
5
5
|
"license": "LGPL-3.0-only",
|
|
6
6
|
"homepage": "https://github.com/gnosisguild/zodiac-os-sdk",
|
|
@@ -12,8 +12,18 @@
|
|
|
12
12
|
"module": "dist/index.mjs",
|
|
13
13
|
"types": "dist/index.d.mts",
|
|
14
14
|
"exports": {
|
|
15
|
-
"
|
|
16
|
-
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
17
|
+
"import": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./allow": {
|
|
20
|
+
"types": "./dist/allow/index.d.mts",
|
|
21
|
+
"import": "./dist/allow/index.mjs"
|
|
22
|
+
},
|
|
23
|
+
"./cli/config": {
|
|
24
|
+
"types": "./dist/cli/config.d.mts",
|
|
25
|
+
"import": "./dist/cli/config.mjs"
|
|
26
|
+
}
|
|
17
27
|
},
|
|
18
28
|
"files": [
|
|
19
29
|
"dist",
|
|
@@ -34,20 +44,23 @@
|
|
|
34
44
|
"test": "bun test",
|
|
35
45
|
"install-local-api-types": "cd ../zodiac-os/public-packages/api-types && pnpm pack-tmp && cd ../../../zodiac-os-sdk && tar -xzf /tmp/zodiac-os-api-types.tgz --strip-components=1 -C node_modules/@zodiac-os/api-types"
|
|
36
46
|
},
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"@types/bun": "^1.2.21",
|
|
39
|
-
"prettier": "^3.6.2",
|
|
40
|
-
"rimraf": "^6.1.0",
|
|
41
|
-
"tsdown": "^0.21.4",
|
|
42
|
-
"typescript": "^5.9.2"
|
|
43
|
-
},
|
|
44
47
|
"dependencies": {
|
|
45
48
|
"@epic-web/invariant": "1.0.0",
|
|
46
|
-
"@gnosis-guild/eth-sdk": "^0.4.0",
|
|
47
|
-
"@gnosis-guild/eth-sdk-client": "^0.1.6",
|
|
48
49
|
"@zodiac-os/api-types": "1.5.1",
|
|
49
50
|
"commander": "^14.0.3",
|
|
50
51
|
"ts-morph": "^27.0.2",
|
|
52
|
+
"zodiac-roles-deployments": "^3.2.0",
|
|
51
53
|
"zodiac-roles-sdk": "^3.4.7"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"ethers": "^6.13.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/bun": "^1.2.21",
|
|
60
|
+
"ethers": "^6.13.0",
|
|
61
|
+
"prettier": "^3.6.2",
|
|
62
|
+
"rimraf": "^6.1.0",
|
|
63
|
+
"tsdown": "^0.21.4",
|
|
64
|
+
"typescript": "^5.9.2"
|
|
52
65
|
}
|
|
53
66
|
}
|