@zodiaceco/sdk 2.2.0 → 2.4.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.
Files changed (34) hide show
  1. package/README.md +97 -6
  2. package/dist/{actions-tA_Oe_kC.d.mts → actions-Cri7InV-.d.mts} +57 -4
  3. package/dist/actions.d.mts +1 -1
  4. package/dist/actions.mjs +18 -2
  5. package/dist/actions.mjs.map +1 -1
  6. package/dist/allow/index.d.mts +1 -1
  7. package/dist/allow/index.mjs +2 -2
  8. package/dist/{allow-CrDzDtxx.mjs → allow-DX3iyizB.mjs} +2 -2
  9. package/dist/{allow-CrDzDtxx.mjs.map → allow-DX3iyizB.mjs.map} +1 -1
  10. package/dist/cli/config.mjs +1 -1
  11. package/dist/cli/push.d.mts +24 -0
  12. package/dist/cli/push.mjs +36 -0
  13. package/dist/cli/push.mjs.map +1 -0
  14. package/dist/cli.mjs +15 -6
  15. package/dist/cli.mjs.map +1 -1
  16. package/dist/{config-D0FqysME.mjs → config-BaChz2bq.mjs} +10 -5
  17. package/dist/config-BaChz2bq.mjs.map +1 -0
  18. package/dist/{index-CnH26Z_A.d.mts → index-D49x1ozw.d.mts} +1 -1
  19. package/dist/index.d.mts +34 -5
  20. package/dist/index.mjs +8 -180
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{networks-B0H4Uoyh.mjs → networks-UKtJ8QDG.mjs} +1 -1
  23. package/dist/{networks-B0H4Uoyh.mjs.map → networks-UKtJ8QDG.mjs.map} +1 -1
  24. package/dist/paths-LsD6ULk0.mjs +34 -0
  25. package/dist/paths-LsD6ULk0.mjs.map +1 -0
  26. package/dist/{permissionEntries-D3fSVhwO.mjs → permissionEntries-C4dAwZBb.mjs} +3 -1
  27. package/dist/permissionEntries-C4dAwZBb.mjs.map +1 -0
  28. package/dist/push-DdnHsLpO.mjs +356 -0
  29. package/dist/push-DdnHsLpO.mjs.map +1 -0
  30. package/package.json +7 -3
  31. package/dist/api-kKgzHQlu.mjs +0 -158
  32. package/dist/api-kKgzHQlu.mjs.map +0 -1
  33. package/dist/config-D0FqysME.mjs.map +0 -1
  34. package/dist/permissionEntries-D3fSVhwO.mjs.map +0 -1
package/README.md CHANGED
@@ -65,7 +65,7 @@ your code ◀──pull── Zodiac OS ◀───────────
65
65
 
66
66
  ## Constellation API
67
67
 
68
- The `constellation()` function is the main SDK entry point. It returns an API for declaring account constellations — the set of Safes, Roles mods, and users that make up your on-chain setup.
68
+ The `constellation()` function is the main SDK entry point. It returns an API for declaring account constellations — the set of Safes, Roles mods, Delay mods, and users that make up your on-chain setup.
69
69
 
70
70
  ```ts
71
71
  import { constellation } from '@zodiaceco/sdk'
@@ -85,7 +85,7 @@ const eth = constellation({
85
85
 
86
86
  ### Referencing existing accounts
87
87
 
88
- Bracket access gives you existing Safes and Roles mods from the selected workspace and chain — both **vault accounts** (manually-promoted entries surfaced in the workspace UI) and any **constellation accounts** previously created by a `push()`. The codegen records them under the same `accounts` map, marked with a `vault` flag for the subset that are also workspace vaults. Names auto-complete from the codegen output.
88
+ Bracket access gives you existing Safes, Roles mods and Delay mods from the selected workspace and chain — both **vault accounts** (manually-promoted entries surfaced in the workspace UI) and any **constellation accounts** previously created by a `push()`. The codegen records them under the same `accounts` map, marked with a `vault` flag for the subset that are also workspace vaults. Names auto-complete from the codegen output.
89
89
 
90
90
  A label only ever names an account on the constellation's own chain. The same name on another chain is a different account whose address means nothing here, so it reads as a new node rather than as a reference — two workspaces can both have a `Treasury` on mainnet and on Gnosis without either having to be addressed by address.
91
91
 
@@ -96,6 +96,9 @@ const ggDao = eth.safe['GG DAO']
96
96
  // Reference an existing Roles mod
97
97
  const ggDaoRoles = eth.roles['GG DAO Roles']
98
98
 
99
+ // Reference an existing Delay mod
100
+ const ggDaoDelay = eth.delay['GG DAO Timelock']
101
+
99
102
  // Optionally invoke with overrides
100
103
  const ggDaoOverridden = eth.safe['GG DAO']({ threshold: 5 })
101
104
  ```
@@ -104,7 +107,7 @@ const ggDaoOverridden = eth.safe['GG DAO']({ threshold: 5 })
104
107
 
105
108
  ### Creating new accounts
106
109
 
107
- Use bracket access with a new label to create new nodes. Every mandatory field (`nonce`, `threshold`, `owners` for Safes; `nonce` for Roles mods) must be supplied explicitly — the SDK does not inject any runtime defaults. The type system surfaces a missing field as a compile-time error so you can't ship an incomplete spec.
110
+ Use bracket access with a new label to create new nodes. Every mandatory field (`nonce`, `threshold`, `owners` for Safes; `nonce` for Roles mods; `nonce`, `cooldown`, `expiration` for Delay mods) must be supplied explicitly — the SDK does not inject any runtime defaults. The type system surfaces a missing field as a compile-time error so you can't ship an incomplete spec.
108
111
 
109
112
  ```ts
110
113
  // New Safe — nonce, threshold, owners are required
@@ -123,6 +126,42 @@ const newRoles = eth.roles['New Roles']({
123
126
  nonce: 0n,
124
127
  target: ggDao,
125
128
  })
129
+
130
+ // New Delay mod targeting an existing Safe
131
+ const newDelay = eth.delay['New Timelock']({
132
+ nonce: 0n,
133
+ target: ggDao,
134
+ cooldown: 86400n, // a day before a queued transaction may execute
135
+ expiration: 604800n, // a week to execute it in, `0n` to never expire
136
+ })
137
+ ```
138
+
139
+ ### Delay mods
140
+
141
+ A Delay mod holds every transaction sent through it for `cooldown` seconds before it can be executed, and drops it again after `expiration` seconds have passed (`0n` means it never expires). Both are declared in seconds.
142
+
143
+ The accounts allowed to queue a transaction through the delay are its `modules` — a complete array replaces the enabled set:
144
+
145
+ ```ts
146
+ const timelock = eth.delay['Treasury Timelock']({
147
+ nonce: 0n,
148
+ target: ggDao,
149
+ cooldown: 172800n,
150
+ expiration: 0n,
151
+ modules: [ggDaoRoles],
152
+ })
153
+
154
+ // The safe executes what has been through the delay
155
+ const treasury = eth.safe['Treasury']({ modules: [timelock] })
156
+ ```
157
+
158
+ To reconfigure a Delay mod that is already on chain but not in your workspace, bind it by `address` instead of declaring a `nonce` — the same either/or that applies to Roles mods:
159
+
160
+ ```ts
161
+ const existing = eth.delay['Existing timelock']({
162
+ address: '0x88A51CcB262d04B334065Ad425928dF79c4CB7d7',
163
+ cooldown: 3600n,
164
+ })
126
165
  ```
127
166
 
128
167
  When a bracket label matches an existing account from your codegen, all overrides become optional — you pass only the fields you want to change against the live configuration.
@@ -217,7 +256,12 @@ Each helper covers a different kind of action:
217
256
  name one with `chain` when the recipients are plain addresses. Tokens without
218
257
  an Across route to a target are skipped, the same way the app skips them —
219
258
  routes change between writing a spec and deploying it — but a target nothing
220
- can reach at all is refused at deploy rather than deployed half-working.
259
+ can reach at all is refused at deploy rather than deployed half-working. A
260
+ transfer bridges to each chain once: every recipient of a target may receive
261
+ every token it names, so recipients that receive other tokens on the same
262
+ chain get a transfer of their own. `transfer()` throws on a second target for
263
+ a chain, and on `to` recipients that live on different chains; `push()`
264
+ throws on `to` recipients off the role's chain, and on a bridge to it.
221
265
  - `defikit` mirrors the DeFi Kit allow kit — same protocols, verbs and
222
266
  parameters, plus a `label`. A DeFi Kit entry is nothing but its annotation;
223
267
  the permissions behind it are fetched from the annotation's uri at deploy, so
@@ -233,13 +277,36 @@ it has nowhere to appear in the app beyond the targets it allows.
233
277
  Allowance keys are plain labels — `key: 'usdc_payouts'` on the declaration, and
234
278
  `allowance: usdc_payouts` on the transfer, which reads the key off it. They are
235
279
  encoded to bytes32 when the constellation is deployed, so nothing calls
236
- `encodeKey` by hand. A label has to fit in 32 bytes.
280
+ `encodeKey` by hand. Allowance and role keys consist of 1 to 31 letters,
281
+ digits, underscores or hyphens.
237
282
 
238
283
  Tokens are named by address, not by symbol. A `transfer()` recipient may also
239
284
  be a node — an account from your codegen, or one bound by address — which
240
285
  stands for the address it lives at. A node whose address is only known once the
241
286
  constellation is deployed is rejected at compile time.
242
287
 
288
+ ### Showing accounts and roles in the Zodiac app
289
+
290
+ A constellation deploys whatever it describes, but only what you mark shows up
291
+ in the Zodiac app for the rest of your org:
292
+
293
+ - **`vault: true`** on a Safe lists it under **Vaults**.
294
+ - **`policy`** on a role lists it under **Policies**, labelled as given, so
295
+ everyone in your org can see what it permits right in the app. A
296
+ `description` is optional. In a template project, export both from the
297
+ role's folder:
298
+
299
+ ```ts
300
+ // constellation/roles/treasury_ops/index.ts
301
+ export const policy = 'Treasury Ops'
302
+ export const description = 'Day-to-day treasury operations'
303
+ ```
304
+
305
+ The role key (`treasury_ops`) is the policy's identity and its on-chain role
306
+ key, so renaming the label changes nothing on chain. Every Roles mod carrying
307
+ the key has to grant it the same way, and no two of them may act for the
308
+ same Safe.
309
+
243
310
  ### Pushing the constellation
244
311
 
245
312
  The `push()` function takes all nodes and sends them to the Zodiac OS API. Pass either a named object (keys become refs) or an array:
@@ -258,12 +325,22 @@ By default, `push()` creates an API client from the `ZODIAC_API_KEY` environment
258
325
  await push({ ggDao, newRoles }, { api: new ApiClient({ apiKey: '...' }) })
259
326
  ```
260
327
 
328
+ When Zodiac refuses a constellation as it stands — a policy it could not hold,
329
+ a key it cannot encode — `push()` rejects with a `ConstellationRejectedError`
330
+ that lists every issue, starting at the node you pushed. `zodiac push` prints
331
+ it like this:
332
+
333
+ ```
334
+ Zodiac refused the constellation "Production". Some policies of this constellation cannot be held in Zodiac:
335
+ • opsRoles ("Ops Roles") › roles › treasury_ops › policy: "Treasury Ops" is a policy on another Roles modifier. Mark this role as that policy too.
336
+ ```
337
+
261
338
  ## CLI reference
262
339
 
263
340
  ```
264
341
  Usage: zodiac [options] [command]
265
342
 
266
- Zodiac SDK CLI – pull org data and contract ABIs
343
+ Zodiac SDK CLI – pull org data and contract ABIs, push constellations
267
344
 
268
345
  Options:
269
346
  -V, --version output the version number
@@ -275,5 +352,19 @@ Commands:
275
352
  pull-org Fetch Zodiac users and accounts, generate TypeScript types
276
353
  pull-contracts Fetch contract ABIs, generate typed permissions kit
277
354
  pull Fetch Zodiac org and contracts ABI, generate SDK functions
355
+ push [entrypoint] Push the nodes an entrypoint exports and open them for review (--no-open to skip the browser)
278
356
  help [command] display help for command
279
357
  ```
358
+
359
+ `zodiac push` imports the entrypoint (`constellation/index.ts` by default),
360
+ pushes every named export as a node, and prints where each constellation can be
361
+ reviewed. When Zodiac refuses a constellation, it prints what was refused and
362
+ exits with code 1. A project whose entrypoint relies on globals it sets up
363
+ itself runs that setup first and then calls the same command:
364
+
365
+ ```ts
366
+ import './globals'
367
+ import { pushEntrypoint } from '@zodiaceco/sdk/cli/push'
368
+
369
+ await pushEntrypoint({ entrypoint: process.argv[2] })
370
+ ```
@@ -23,11 +23,23 @@ type AllowanceSpec = RealBigints<ApiAllowanceSpec>;
23
23
  * parameters and labels, never compiled permissions. They are expanded into
24
24
  * `{ targets, annotations }` when the constellation is deployed, so that a
25
25
  * stored revision always compiles through the current compilers.
26
+ *
27
+ * A role carrying `policy` is a policy in Zodiac. Its key is the policy's
28
+ * identity, so the same key on several Roles mods is one policy, active on
29
+ * each of their vaults. Only a policy has a `description`.
26
30
  */
27
31
  type RoleDef = {
28
32
  members: readonly AddressOrRef[];
29
33
  permissions: readonly PermissionEntry[];
30
- };
34
+ } & ({
35
+ /** Marks the role as a policy, labelled with this value. Never reaches
36
+ * the chain. */
37
+ policy: string; /** Describes the policy in Zodiac. Never reaches the chain. */
38
+ description?: string;
39
+ } | {
40
+ policy?: undefined;
41
+ description?: undefined;
42
+ });
31
43
  type User = {
32
44
  id: UUID;
33
45
  fullName: string;
@@ -84,6 +96,7 @@ type Prettify<T> = { readonly [K in keyof T]: T[K] } & {};
84
96
  type ChainEntries<A, Ch extends ChainId> = Ch extends keyof A ? NonNullable<A[Ch]> : {};
85
97
  type SafeEntries<C extends CodegenData, W extends keyof C['accounts'], Ch extends ChainId> = ChainEntries<C['accounts'][W]['safes'], Ch>;
86
98
  type RolesEntries<C extends CodegenData, W extends keyof C['accounts'], Ch extends ChainId> = ChainEntries<C['accounts'][W]['rolesMods'], Ch>;
99
+ type DelayEntries<C extends CodegenData, W extends keyof C['accounts'], Ch extends ChainId> = ChainEntries<C['accounts'][W]['delays'], Ch>;
87
100
  type NodeType = 'SAFE' | 'ROLES' | 'DELAY';
88
101
  /** A reference to a node used in `owners`, `modules`, `target`, etc. */
89
102
  type NodeRef = Readonly<{
@@ -128,8 +141,19 @@ type RolesNode = NodeBase & Readonly<{
128
141
  * left untouched. */
129
142
  allowances?: Record<string, AllowanceSpec | null>;
130
143
  }>;
144
+ /** A delay modifier node — a reference to one the workspace already has, or a
145
+ * new one with the config it is deployed with. */
146
+ type DelayNode = NodeBase & Readonly<{
147
+ /** Discriminator identifying this node as a Delay modifier. */type: 'DELAY'; /** The safe that this delay modifier controls. */
148
+ target?: AddressOrRef; /** The account that is allowed to update the configuration of the Delay mod. */
149
+ owner?: AddressOrRef; /** The account that calls will be executed from. */
150
+ avatar?: AddressOrRef; /** Seconds a queued transaction waits before it becomes executable. */
151
+ cooldown?: bigint; /** Seconds a transaction stays executable once its cooldown has passed. */
152
+ expiration?: bigint; /** Module addresses or node references enabled on the delay. */
153
+ modules?: readonly (string | NodeRef)[];
154
+ }>;
131
155
  /** Any complete node that can be passed to `push()`. */
132
- type ConstellationNode = SafeNode | RolesNode;
156
+ type ConstellationNode = SafeNode | RolesNode | DelayNode;
133
157
  type NewSafeProps = {
134
158
  /** Deployment nonce for CREATE2 address derivation. */nonce: bigint; /** Number of owner signatures required to execute a transaction. */
135
159
  threshold: number; /** Safe owner addresses or node references. */
@@ -162,6 +186,31 @@ type ExistingRolesByAddress = RolesConfig & {
162
186
  /** Address of the existing Roles mod to bind to and reconfigure. */address: Address;
163
187
  };
164
188
  type NewRolesProps = NewRolesByNonce | ExistingRolesByAddress;
189
+ /** Configuration shared by both ways of declaring a delay modifier. */
190
+ type DelayConfig = {
191
+ /** The safe that this delay modifier controls. Defaults to the new safe with the same label, when one exists. */target?: AddressOrRef; /** The account that calls will be executed from. Defaults to `target` value */
192
+ avatar?: AddressOrRef; /** The account that is allowed to update the configuration of the Delay Mod. Defaults to `target` value */
193
+ owner?: AddressOrRef;
194
+ /** Module addresses or node references allowed to queue transactions through
195
+ * this delay. A complete array replaces the enabled modules. */
196
+ modules?: readonly AddressOrRef[];
197
+ };
198
+ /** Declare a brand-new delay modifier (address derived via CREATE2 from `nonce`). */
199
+ type NewDelayByNonce = DelayConfig & {
200
+ /** Deployment nonce for CREATE2 address derivation. */nonce: bigint; /** Seconds a queued transaction waits before it becomes executable. */
201
+ cooldown: bigint; /** Seconds a transaction stays executable once its cooldown has passed. */
202
+ expiration: bigint;
203
+ };
204
+ /** Bind to a delay modifier already deployed on-chain at a known address, to
205
+ * reconfigure its cooldown, expiration or modules. Use this for mods not (yet)
206
+ * imported into the workspace — pass the address instead of a deployment
207
+ * `nonce`. */
208
+ type ExistingDelayByAddress = DelayConfig & {
209
+ /** Address of the existing Delay mod to bind to and reconfigure. */address: Address; /** Seconds a queued transaction waits before it becomes executable. */
210
+ cooldown?: bigint; /** Seconds a transaction stays executable once its cooldown has passed. */
211
+ expiration?: bigint;
212
+ };
213
+ type NewDelayProps = NewDelayByNonce | ExistingDelayByAddress;
165
214
  type ExistingNodeAccessor<Type extends string, K extends string, E, Ch extends ChainId, NP extends Record<string, any>> = Readonly<Prettify<E & {
166
215
  type: Type;
167
216
  label: K;
@@ -190,7 +239,10 @@ type ConstellationResult<C extends CodegenData, W extends keyof C['accounts'] =
190
239
  safe: EntityAccessor<'SAFE', SafeEntries<C, W, Ch>, Ch, NewSafeProps>;
191
240
  /** Access existing roles modifiers by label or create new ones with a
192
241
  * new label. Only ROLES-typed accounts are suggested in IntelliSense. */
193
- roles: EntityAccessor<'ROLES', RolesEntries<C, W, Ch>, Ch, NewRolesProps>; /** Resolve a user's personal safe address on the constellation's chain. */
242
+ roles: EntityAccessor<'ROLES', RolesEntries<C, W, Ch>, Ch, NewRolesProps>;
243
+ /** Access existing delay modifiers by label or create new ones with a
244
+ * new label. Only DELAY-typed accounts are suggested in IntelliSense. */
245
+ delay: EntityAccessor<'DELAY', DelayEntries<C, W, Ch>, Ch, NewDelayProps>; /** Resolve a user's personal safe address on the constellation's chain. */
194
246
  user: UserAccessor<C, Ch>;
195
247
  };
196
248
  /** @internal */
@@ -208,6 +260,7 @@ type ConstellationResult<C extends CodegenData, W extends keyof C['accounts'] =
208
260
  * const dao = eth.safe['GG DAO'] // the safe of that name on chain 1
209
261
  * const roles = eth.roles['GG DAO'] // the roles mod of that name
210
262
  * const newSafe = eth.safe['New Safe']({ nonce: 0n, threshold: 2, owners: [...], modules: [...] })
263
+ * const timelock = eth.delay['Timelock']({ nonce: 0n, target: dao, cooldown: 86400n, expiration: 0n })
211
264
  * ```
212
265
  *
213
266
  * Names come from the last `pull-org`, so an account deployed since then is
@@ -373,4 +426,4 @@ type DeFiKitVerb<Verb> = Verb extends ((params: infer Params) => unknown) ? (par
373
426
  }) => DeFiKitEntry;
374
427
  //#endregion
375
428
  export { PermissionEntry as a, ConstellationNode as c, transfer as i, constellation as l, defikit as n, PermissionTarget as o, swap as r, Permissions as s, custom as t };
376
- //# sourceMappingURL=actions-tA_Oe_kC.d.mts.map
429
+ //# sourceMappingURL=actions-Cri7InV-.d.mts.map
@@ -1,2 +1,2 @@
1
- import { a as PermissionEntry, i as transfer, n as defikit, o as PermissionTarget, r as swap, s as Permissions, t as custom } from "./actions-tA_Oe_kC.mjs";
1
+ import { a as PermissionEntry, i as transfer, n as defikit, o as PermissionTarget, r as swap, s as Permissions, t as custom } from "./actions-Cri7InV-.mjs";
2
2
  export { PermissionEntry, PermissionTarget, Permissions, custom, defikit, swap, transfer };
package/dist/actions.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { r as recipientChains } from "./permissionEntries-D3fSVhwO.mjs";
1
+ import { r as recipientChains } from "./permissionEntries-C4dAwZBb.mjs";
2
2
  //#region src/actions.ts
3
3
  /**
4
4
  * Allows signing CoW orders selling any of `sell` for any of `buy`. The
@@ -20,17 +20,22 @@ const swap = ({ label, sell, buy }) => ({
20
20
  * optional on their own; a transfer names at least one recipient somewhere.
21
21
  */
22
22
  const transfer = ({ label, tokens, to = [], bridge = [], allowance }) => {
23
+ const bridges = bridge.map(toBridgeTarget);
24
+ assertOneTargetPerChain(bridges);
23
25
  const entry = {
24
26
  label,
25
27
  action: {
26
28
  type: "transfer",
27
29
  tokens: [...tokens],
28
30
  to: to.map(toAddress),
29
- ...bridge.length > 0 && { bridge: bridge.map(toBridgeTarget) },
31
+ ...bridges.length > 0 && { bridge: bridges },
30
32
  ...allowance != null && { allowance: allowanceKey(allowance) }
31
33
  }
32
34
  };
33
35
  const chains = to.flatMap((recipient) => typeof recipient === "string" || recipient.chain == null ? [] : [recipient.chain]);
36
+ const [chain] = chains;
37
+ const otherChain = chains.find((other) => other !== chain);
38
+ if (otherChain != null) throw new Error(`The recipients of "${label}" span chains "${chain}" and "${otherChain}". \`to\` names recipients on the role's own chain; use \`bridge\` for the others.`);
34
39
  if (chains.length > 0) Object.defineProperty(entry, recipientChains, {
35
40
  value: chains,
36
41
  enumerable: false
@@ -76,6 +81,17 @@ const toBridgeTarget = ({ chain, to, receive }) => ({
76
81
  to: to.map(toAddress),
77
82
  receive: [...receive]
78
83
  });
84
+ /**
85
+ * Every recipient of a bridge target may receive every token it names, so two
86
+ * targets on one chain would merge into one that allows more than either.
87
+ */
88
+ const assertOneTargetPerChain = (bridges) => {
89
+ const chains = /* @__PURE__ */ new Set();
90
+ for (const { chain } of bridges) {
91
+ if (chains.has(chain)) throw new Error(`A transfer bridges to each chain once, but this one bridges to chain "${chain}" twice. Write a separate transfer for recipients that receive other tokens on the same chain.`);
92
+ chains.add(chain);
93
+ }
94
+ };
79
95
  const destinationChain = (chain, recipients) => {
80
96
  let destination = chain;
81
97
  for (const recipient of recipients) {
@@ -1 +1 @@
1
- {"version":3,"file":"actions.mjs","names":[],"sources":["../src/actions.ts"],"sourcesContent":["import type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { allow as ethereumKit } from 'defi-kit/eth'\nimport { recipientChains } from './permissionEntries'\nimport type {\n AllowanceKey,\n ConstellationPermission,\n DeFiKitEntry,\n LabelledPermissions,\n PermissionEntry,\n PermissionTarget,\n SwapEntry,\n TransferEntry,\n} from './permissionEntries'\nimport type { AllowanceSpec } from './types'\n\nexport type { PermissionEntry, PermissionTarget }\nexport type { Permissions } from './permissionEntries'\n\n/**\n * Allows signing CoW orders selling any of `sell` for any of `buy`. The\n * parameters are compiled into permissions when the constellation is deployed,\n * never here — a stored revision that carried its own copy would go stale as\n * soon as the compiler changed.\n */\nexport const swap = ({ label, sell, buy }: SwapParams): SwapEntry => ({\n label,\n action: { type: 'swap', sell: [...sell], buy: [...buy] },\n})\n\n/**\n * Allows transferring any of `tokens` to any of `to` on the role's own chain,\n * and — through `bridge` — to recipients on other chains over Across. Both are\n * optional on their own; a transfer names at least one recipient somewhere.\n */\nexport const transfer = ({\n label,\n tokens,\n to = [],\n bridge = [],\n allowance,\n}: TransferParams): TransferEntry => {\n const entry: TransferEntry = {\n label,\n action: {\n type: 'transfer',\n tokens: [...tokens],\n to: to.map(toAddress),\n ...(bridge.length > 0 && { bridge: bridge.map(toBridgeTarget) }),\n ...(allowance != null && { allowance: allowanceKey(allowance) }),\n },\n }\n\n // Remember which chains the `to` nodes live on so push() can refuse a\n // recipient from another chain. Symbol-keyed: never serialized.\n const chains = to.flatMap((recipient) =>\n typeof recipient === 'string' || recipient.chain == null\n ? []\n : [recipient.chain]\n )\n\n if (chains.length > 0) {\n Object.defineProperty(entry, recipientChains, {\n value: chains,\n enumerable: false,\n })\n }\n\n return entry\n}\n\n/**\n * A labelled bag of `allow`-kit permissions — the code-side counterpart of the\n * app's Custom action, for everything the other action types don't cover. It\n * takes plain permissions, not other actions.\n */\nexport const custom = ({\n label,\n permissions,\n}: CustomParams): LabelledPermissions => ({\n label,\n permissions: [...permissions],\n})\n\n/**\n * The DeFi Kit allow kit — same protocol, verb and parameter surface, plus a\n * `label`. A DeFi Kit entry is nothing but its annotation: the permissions are\n * fetched from the annotation's uri at deploy, so nothing is fetched here.\n *\n * Protocols and parameters are typed against the Ethereum kit, the widest of\n * the chains DeFi Kit serves. Values that only exist on another chain still\n * type-check; the API validates them when the annotation is resolved.\n */\nexport const defikit: DeFiKit = new Proxy({} as DeFiKit, {\n get: (_, protocol: string) =>\n new Proxy(\n {},\n {\n get:\n (_, verb: string) =>\n ({ label, ...params }: { label: string }) => ({\n label,\n defiKit: { protocol, verb, params },\n }),\n }\n ),\n})\n\nconst allowanceKey = (allowance: AllowanceSpec | AllowanceKey) =>\n typeof allowance === 'string' ? allowance : allowance.key\n\nconst toAddress = (recipient: Recipient): Address =>\n typeof recipient === 'string' ? recipient : recipient.address\n\n/**\n * A node knows the chain it lives on, so a bridge target written with nodes\n * needs no `chain` of its own. Naming one anyway is allowed — a plain address\n * carries no chain — but a node that disagrees with it is a mistake worth\n * catching here rather than at deploy.\n */\nconst toBridgeTarget = ({ chain, to, receive }: BridgeTarget) => ({\n chain: destinationChain(chain, to),\n to: to.map(toAddress),\n receive: [...receive],\n})\n\nconst destinationChain = (\n chain: ChainId | undefined,\n recipients: readonly Recipient[]\n): ChainId => {\n let destination = chain\n\n for (const recipient of recipients) {\n if (typeof recipient === 'string' || recipient.chain == null) {\n continue\n }\n\n if (destination != null && destination !== recipient.chain) {\n throw new Error(\n `A bridge target has one destination, but this one spans chains \"${destination}\" and \"${recipient.chain}\"`\n )\n }\n\n destination = recipient.chain\n }\n\n if (destination == null) {\n throw new Error(\n 'A bridge target needs a `chain` unless its recipients are nodes that know one'\n )\n }\n\n return destination\n}\n\ntype SwapParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n /** Tokens the role may sell. */\n sell: readonly Address[]\n /** Tokens the role may buy. */\n buy: readonly Address[]\n}\n\ntype TransferParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n /** Tokens the role may transfer. Use the zero address for the native token. */\n tokens: readonly Address[]\n /** Addresses the role may transfer to on the role's own chain. A node stands\n * for its address, so only nodes that already have one — an account from your\n * codegen, or one bound by address — can be named here. */\n to?: readonly Recipient[]\n /** Destinations on other chains, bridged over Across. */\n bridge?: readonly BridgeTarget[]\n /** Allowance capping what may be sent. Pass the allowance declared on the\n * roles modifier, or its key. */\n allowance?: AllowanceSpec | AllowanceKey\n}\n\ntype CustomParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n permissions: readonly ConstellationPermission[]\n}\n\n/** An address, or a node that already knows the address it lives at. */\ntype Recipient =\n | Address\n | { readonly address: Address; readonly chain?: ChainId }\n\ntype BridgeTarget = {\n /** The destination chain. Inferred from the recipients when they are nodes. */\n chain?: ChainId\n /** Addresses the role may bridge to on the destination chain. */\n to: readonly Recipient[]\n /** Tokens the role may receive on the destination chain. */\n receive: readonly Address[]\n}\n\ntype EthereumKit = typeof ethereumKit\n\ntype DeFiKit = {\n readonly [Protocol in keyof EthereumKit]: {\n readonly [Verb in keyof EthereumKit[Protocol]]: DeFiKitVerb<\n EthereumKit[Protocol][Verb]\n >\n }\n}\n\ntype DeFiKitVerb<Verb> = Verb extends (params: infer Params) => unknown\n ? (params: Params & { label: string }) => DeFiKitEntry\n : (params: { label: string }) => DeFiKitEntry\n"],"mappings":";;;;;;;;AAwBA,MAAa,QAAQ,EAAE,OAAO,MAAM,WAAkC;CACpE;CACA,QAAQ;EAAE,MAAM;EAAQ,MAAM,CAAC,GAAG,KAAK;EAAE,KAAK,CAAC,GAAG,IAAI;EAAE;CACzD;;;;;;AAOD,MAAa,YAAY,EACvB,OACA,QACA,KAAK,EAAE,EACP,SAAS,EAAE,EACX,gBACmC;CACnC,MAAM,QAAuB;EAC3B;EACA,QAAQ;GACN,MAAM;GACN,QAAQ,CAAC,GAAG,OAAO;GACnB,IAAI,GAAG,IAAI,UAAU;GACrB,GAAI,OAAO,SAAS,KAAK,EAAE,QAAQ,OAAO,IAAI,eAAe,EAAE;GAC/D,GAAI,aAAa,QAAQ,EAAE,WAAW,aAAa,UAAU,EAAE;GAChE;EACF;CAID,MAAM,SAAS,GAAG,SAAS,cACzB,OAAO,cAAc,YAAY,UAAU,SAAS,OAChD,EAAE,GACF,CAAC,UAAU,MAAM,CACtB;AAED,KAAI,OAAO,SAAS,EAClB,QAAO,eAAe,OAAO,iBAAiB;EAC5C,OAAO;EACP,YAAY;EACb,CAAC;AAGJ,QAAO;;;;;;;AAQT,MAAa,UAAU,EACrB,OACA,mBACwC;CACxC;CACA,aAAa,CAAC,GAAG,YAAY;CAC9B;;;;;;;;;;AAWD,MAAa,UAAmB,IAAI,MAAM,EAAE,EAAa,EACvD,MAAM,GAAG,aACP,IAAI,MACF,EAAE,EACF,EACE,MACG,GAAG,UACH,EAAE,OAAO,GAAG,cAAiC;CAC5C;CACA,SAAS;EAAE;EAAU;EAAM;EAAQ;CACpC,GACJ,CACF,EACJ,CAAC;AAEF,MAAM,gBAAgB,cACpB,OAAO,cAAc,WAAW,YAAY,UAAU;AAExD,MAAM,aAAa,cACjB,OAAO,cAAc,WAAW,YAAY,UAAU;;;;;;;AAQxD,MAAM,kBAAkB,EAAE,OAAO,IAAI,eAA6B;CAChE,OAAO,iBAAiB,OAAO,GAAG;CAClC,IAAI,GAAG,IAAI,UAAU;CACrB,SAAS,CAAC,GAAG,QAAQ;CACtB;AAED,MAAM,oBACJ,OACA,eACY;CACZ,IAAI,cAAc;AAElB,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,OAAO,cAAc,YAAY,UAAU,SAAS,KACtD;AAGF,MAAI,eAAe,QAAQ,gBAAgB,UAAU,MACnD,OAAM,IAAI,MACR,mEAAmE,YAAY,SAAS,UAAU,MAAM,GACzG;AAGH,gBAAc,UAAU;;AAG1B,KAAI,eAAe,KACjB,OAAM,IAAI,MACR,gFACD;AAGH,QAAO"}
1
+ {"version":3,"file":"actions.mjs","names":[],"sources":["../src/actions.ts"],"sourcesContent":["import type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { allow as ethereumKit } from 'defi-kit/eth'\nimport { recipientChains } from './permissionEntries'\nimport type {\n AllowanceKey,\n ConstellationPermission,\n DeFiKitEntry,\n LabelledPermissions,\n PermissionEntry,\n PermissionTarget,\n SwapEntry,\n TransferEntry,\n} from './permissionEntries'\nimport type { AllowanceSpec } from './types'\n\nexport type { PermissionEntry, PermissionTarget }\nexport type { Permissions } from './permissionEntries'\n\n/**\n * Allows signing CoW orders selling any of `sell` for any of `buy`. The\n * parameters are compiled into permissions when the constellation is deployed,\n * never here — a stored revision that carried its own copy would go stale as\n * soon as the compiler changed.\n */\nexport const swap = ({ label, sell, buy }: SwapParams): SwapEntry => ({\n label,\n action: { type: 'swap', sell: [...sell], buy: [...buy] },\n})\n\n/**\n * Allows transferring any of `tokens` to any of `to` on the role's own chain,\n * and — through `bridge` — to recipients on other chains over Across. Both are\n * optional on their own; a transfer names at least one recipient somewhere.\n */\nexport const transfer = ({\n label,\n tokens,\n to = [],\n bridge = [],\n allowance,\n}: TransferParams): TransferEntry => {\n const bridges = bridge.map(toBridgeTarget)\n\n assertOneTargetPerChain(bridges)\n\n const entry: TransferEntry = {\n label,\n action: {\n type: 'transfer',\n tokens: [...tokens],\n to: to.map(toAddress),\n ...(bridges.length > 0 && { bridge: bridges }),\n ...(allowance != null && { allowance: allowanceKey(allowance) }),\n },\n }\n\n // Remember which chains the `to` nodes live on so push() can refuse a\n // recipient from another chain. Symbol-keyed: never serialized.\n const chains = to.flatMap((recipient) =>\n typeof recipient === 'string' || recipient.chain == null\n ? []\n : [recipient.chain]\n )\n\n const [chain] = chains\n const otherChain = chains.find((other) => other !== chain)\n\n if (otherChain != null) {\n throw new Error(\n `The recipients of \"${label}\" span chains \"${chain}\" and \"${otherChain}\". \\`to\\` names recipients on the role's own chain; use \\`bridge\\` for the others.`\n )\n }\n\n if (chains.length > 0) {\n Object.defineProperty(entry, recipientChains, {\n value: chains,\n enumerable: false,\n })\n }\n\n return entry\n}\n\n/**\n * A labelled bag of `allow`-kit permissions — the code-side counterpart of the\n * app's Custom action, for everything the other action types don't cover. It\n * takes plain permissions, not other actions.\n */\nexport const custom = ({\n label,\n permissions,\n}: CustomParams): LabelledPermissions => ({\n label,\n permissions: [...permissions],\n})\n\n/**\n * The DeFi Kit allow kit — same protocol, verb and parameter surface, plus a\n * `label`. A DeFi Kit entry is nothing but its annotation: the permissions are\n * fetched from the annotation's uri at deploy, so nothing is fetched here.\n *\n * Protocols and parameters are typed against the Ethereum kit, the widest of\n * the chains DeFi Kit serves. Values that only exist on another chain still\n * type-check; the API validates them when the annotation is resolved.\n */\nexport const defikit: DeFiKit = new Proxy({} as DeFiKit, {\n get: (_, protocol: string) =>\n new Proxy(\n {},\n {\n get:\n (_, verb: string) =>\n ({ label, ...params }: { label: string }) => ({\n label,\n defiKit: { protocol, verb, params },\n }),\n }\n ),\n})\n\nconst allowanceKey = (allowance: AllowanceSpec | AllowanceKey) =>\n typeof allowance === 'string' ? allowance : allowance.key\n\nconst toAddress = (recipient: Recipient): Address =>\n typeof recipient === 'string' ? recipient : recipient.address\n\n/**\n * A node knows the chain it lives on, so a bridge target written with nodes\n * needs no `chain` of its own. Naming one anyway is allowed — a plain address\n * carries no chain — but a node that disagrees with it is a mistake worth\n * catching here rather than at deploy.\n */\nconst toBridgeTarget = ({ chain, to, receive }: BridgeTarget) => ({\n chain: destinationChain(chain, to),\n to: to.map(toAddress),\n receive: [...receive],\n})\n\n/**\n * Every recipient of a bridge target may receive every token it names, so two\n * targets on one chain would merge into one that allows more than either.\n */\nconst assertOneTargetPerChain = (bridges: { chain: ChainId }[]) => {\n const chains = new Set<ChainId>()\n\n for (const { chain } of bridges) {\n if (chains.has(chain)) {\n throw new Error(\n `A transfer bridges to each chain once, but this one bridges to chain \"${chain}\" twice. Write a separate transfer for recipients that receive other tokens on the same chain.`\n )\n }\n\n chains.add(chain)\n }\n}\n\nconst destinationChain = (\n chain: ChainId | undefined,\n recipients: readonly Recipient[]\n): ChainId => {\n let destination = chain\n\n for (const recipient of recipients) {\n if (typeof recipient === 'string' || recipient.chain == null) {\n continue\n }\n\n if (destination != null && destination !== recipient.chain) {\n throw new Error(\n `A bridge target has one destination, but this one spans chains \"${destination}\" and \"${recipient.chain}\"`\n )\n }\n\n destination = recipient.chain\n }\n\n if (destination == null) {\n throw new Error(\n 'A bridge target needs a `chain` unless its recipients are nodes that know one'\n )\n }\n\n return destination\n}\n\ntype SwapParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n /** Tokens the role may sell. */\n sell: readonly Address[]\n /** Tokens the role may buy. */\n buy: readonly Address[]\n}\n\ntype TransferParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n /** Tokens the role may transfer. Use the zero address for the native token. */\n tokens: readonly Address[]\n /** Addresses the role may transfer to on the role's own chain. A node stands\n * for its address, so only nodes that already have one — an account from your\n * codegen, or one bound by address — can be named here. */\n to?: readonly Recipient[]\n /** Destinations on other chains, bridged over Across. */\n bridge?: readonly BridgeTarget[]\n /** Allowance capping what may be sent. Pass the allowance declared on the\n * roles modifier, or its key. */\n allowance?: AllowanceSpec | AllowanceKey\n}\n\ntype CustomParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n permissions: readonly ConstellationPermission[]\n}\n\n/** An address, or a node that already knows the address it lives at. */\ntype Recipient =\n | Address\n | { readonly address: Address; readonly chain?: ChainId }\n\ntype BridgeTarget = {\n /** The destination chain. Inferred from the recipients when they are nodes. */\n chain?: ChainId\n /** Addresses the role may bridge to on the destination chain. */\n to: readonly Recipient[]\n /** Tokens the role may receive on the destination chain. */\n receive: readonly Address[]\n}\n\ntype EthereumKit = typeof ethereumKit\n\ntype DeFiKit = {\n readonly [Protocol in keyof EthereumKit]: {\n readonly [Verb in keyof EthereumKit[Protocol]]: DeFiKitVerb<\n EthereumKit[Protocol][Verb]\n >\n }\n}\n\ntype DeFiKitVerb<Verb> = Verb extends (params: infer Params) => unknown\n ? (params: Params & { label: string }) => DeFiKitEntry\n : (params: { label: string }) => DeFiKitEntry\n"],"mappings":";;;;;;;;AAwBA,MAAa,QAAQ,EAAE,OAAO,MAAM,WAAkC;CACpE;CACA,QAAQ;EAAE,MAAM;EAAQ,MAAM,CAAC,GAAG,KAAK;EAAE,KAAK,CAAC,GAAG,IAAI;EAAE;CACzD;;;;;;AAOD,MAAa,YAAY,EACvB,OACA,QACA,KAAK,EAAE,EACP,SAAS,EAAE,EACX,gBACmC;CACnC,MAAM,UAAU,OAAO,IAAI,eAAe;AAE1C,yBAAwB,QAAQ;CAEhC,MAAM,QAAuB;EAC3B;EACA,QAAQ;GACN,MAAM;GACN,QAAQ,CAAC,GAAG,OAAO;GACnB,IAAI,GAAG,IAAI,UAAU;GACrB,GAAI,QAAQ,SAAS,KAAK,EAAE,QAAQ,SAAS;GAC7C,GAAI,aAAa,QAAQ,EAAE,WAAW,aAAa,UAAU,EAAE;GAChE;EACF;CAID,MAAM,SAAS,GAAG,SAAS,cACzB,OAAO,cAAc,YAAY,UAAU,SAAS,OAChD,EAAE,GACF,CAAC,UAAU,MAAM,CACtB;CAED,MAAM,CAAC,SAAS;CAChB,MAAM,aAAa,OAAO,MAAM,UAAU,UAAU,MAAM;AAE1D,KAAI,cAAc,KAChB,OAAM,IAAI,MACR,sBAAsB,MAAM,iBAAiB,MAAM,SAAS,WAAW,oFACxE;AAGH,KAAI,OAAO,SAAS,EAClB,QAAO,eAAe,OAAO,iBAAiB;EAC5C,OAAO;EACP,YAAY;EACb,CAAC;AAGJ,QAAO;;;;;;;AAQT,MAAa,UAAU,EACrB,OACA,mBACwC;CACxC;CACA,aAAa,CAAC,GAAG,YAAY;CAC9B;;;;;;;;;;AAWD,MAAa,UAAmB,IAAI,MAAM,EAAE,EAAa,EACvD,MAAM,GAAG,aACP,IAAI,MACF,EAAE,EACF,EACE,MACG,GAAG,UACH,EAAE,OAAO,GAAG,cAAiC;CAC5C;CACA,SAAS;EAAE;EAAU;EAAM;EAAQ;CACpC,GACJ,CACF,EACJ,CAAC;AAEF,MAAM,gBAAgB,cACpB,OAAO,cAAc,WAAW,YAAY,UAAU;AAExD,MAAM,aAAa,cACjB,OAAO,cAAc,WAAW,YAAY,UAAU;;;;;;;AAQxD,MAAM,kBAAkB,EAAE,OAAO,IAAI,eAA6B;CAChE,OAAO,iBAAiB,OAAO,GAAG;CAClC,IAAI,GAAG,IAAI,UAAU;CACrB,SAAS,CAAC,GAAG,QAAQ;CACtB;;;;;AAMD,MAAM,2BAA2B,YAAkC;CACjE,MAAM,yBAAS,IAAI,KAAc;AAEjC,MAAK,MAAM,EAAE,WAAW,SAAS;AAC/B,MAAI,OAAO,IAAI,MAAM,CACnB,OAAM,IAAI,MACR,yEAAyE,MAAM,gGAChF;AAGH,SAAO,IAAI,MAAM;;;AAIrB,MAAM,oBACJ,OACA,eACY;CACZ,IAAI,cAAc;AAElB,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,OAAO,cAAc,YAAY,UAAU,SAAS,KACtD;AAGF,MAAI,eAAe,QAAQ,gBAAgB,UAAU,MACnD,OAAM,IAAI,MACR,mEAAmE,YAAY,SAAS,UAAU,MAAM,GACzG;AAGH,gBAAc,UAAU;;AAG1B,KAAI,eAAe,KACjB,OAAM,IAAI,MACR,gFACD;AAGH,QAAO"}
@@ -1,2 +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-CnH26Z_A.mjs";
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-D49x1ozw.mjs";
2
2
  export { CHAIN_IDS, ChainPrefix, ConditionFunction, EVERYTHING, FunctionPermission, Options, Scoping, TargetPermission, buildAllowKit, chainIdFor };
@@ -1,3 +1,3 @@
1
- import { n as chainIdFor, t as CHAIN_IDS } from "../networks-B0H4Uoyh.mjs";
2
- import { n as EVERYTHING, t as buildAllowKit } from "../allow-CrDzDtxx.mjs";
1
+ import { n as chainIdFor, t as CHAIN_IDS } from "../networks-UKtJ8QDG.mjs";
2
+ import { n as EVERYTHING, t as buildAllowKit } from "../allow-DX3iyizB.mjs";
3
3
  export { CHAIN_IDS, EVERYTHING, buildAllowKit, chainIdFor };
@@ -1,4 +1,4 @@
1
- import { a as walkContracts, i as readAbi } from "./networks-B0H4Uoyh.mjs";
1
+ import { a as walkContracts, i as readAbi } from "./networks-UKtJ8QDG.mjs";
2
2
  import { Operator, ParameterType, c, coercePermission } from "zodiac-roles-sdk";
3
3
  import { Interface, isError } from "ethers";
4
4
  //#region src/allow/types.ts
@@ -129,4 +129,4 @@ const applyOptions = (permission, options) => {
129
129
  //#endregion
130
130
  export { EVERYTHING as n, buildAllowKit as t };
131
131
 
132
- //# sourceMappingURL=allow-CrDzDtxx.mjs.map
132
+ //# sourceMappingURL=allow-DX3iyizB.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"allow-CrDzDtxx.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('@zodiaceco/allow-kit/EVERYTHING')\nexport type EVERYTHING = typeof EVERYTHING\n","import { Interface, FunctionFragment, isError, type InterfaceAbi } from 'ethers'\nimport { c, coercePermission, ParameterType, Operator } from 'zodiac-roles-sdk'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\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(\n node.address,\n abi as InterfaceAbi,\n [node.chain, ...node.segments].join('.')\n )\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 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 path: string\n): Record<string | symbol, any> {\n const iface = Interface.from(abi)\n const lowerAddr = address.toLowerCase() as `0x${string}`\n\n // Permissionable functions grouped by bare name, so an ambiguous one can be\n // told apart from an unknown one. `Interface.getFunction` reports both as\n // INVALID_ARGUMENT, and the two want opposite answers: an unknown name is\n // simply not a member, an ambiguous one is a member the caller has not\n // finished naming.\n const overloads = new Map<string, FunctionFragment[]>()\n iface.forEachFunction((fn) => {\n if (fn.stateMutability === 'view' || fn.stateMutability === 'pure') return\n overloads.set(fn.name, [...(overloads.get(fn.name) ?? []), fn])\n })\n\n const ambiguous = (prop: string) => (overloads.get(prop)?.length ?? 0) > 1\n\n const explainAmbiguity = (prop: string): never => {\n const signatures = overloads.get(prop)!.map((fn) => fn.format('sighash'))\n throw new Error(\n `\\`allow.${path}.${prop}\\` is ambiguous: ${address} has ` +\n `${signatures.length} functions named \"${prop}\". Name the overload ` +\n `you mean by its full signature — ` +\n signatures\n .map((sig) => `allow.${path}[${JSON.stringify(sig)}]`)\n .join(' or ') +\n `.`\n )\n }\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 (ambiguous(prop)) explainAmbiguity(prop)\n if (!has(prop)) return undefined\n const fn = iface.getFunction(prop)!\n return makeAllowFunction(fn, lowerAddr)\n },\n // An ambiguous bare name is not a member — only the full signatures are.\n // `in` answers that question; reaching for the property explains it.\n has: (_target, prop) => {\n if (prop === EVERYTHING) return true\n return typeof prop === 'string' && !ambiguous(prop) && 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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"}
1
+ {"version":3,"file":"allow-DX3iyizB.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('@zodiaceco/allow-kit/EVERYTHING')\nexport type EVERYTHING = typeof EVERYTHING\n","import { Interface, FunctionFragment, isError, type InterfaceAbi } from 'ethers'\nimport { c, coercePermission, ParameterType, Operator } from 'zodiac-roles-sdk'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\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(\n node.address,\n abi as InterfaceAbi,\n [node.chain, ...node.segments].join('.')\n )\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 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 path: string\n): Record<string | symbol, any> {\n const iface = Interface.from(abi)\n const lowerAddr = address.toLowerCase() as `0x${string}`\n\n // Permissionable functions grouped by bare name, so an ambiguous one can be\n // told apart from an unknown one. `Interface.getFunction` reports both as\n // INVALID_ARGUMENT, and the two want opposite answers: an unknown name is\n // simply not a member, an ambiguous one is a member the caller has not\n // finished naming.\n const overloads = new Map<string, FunctionFragment[]>()\n iface.forEachFunction((fn) => {\n if (fn.stateMutability === 'view' || fn.stateMutability === 'pure') return\n overloads.set(fn.name, [...(overloads.get(fn.name) ?? []), fn])\n })\n\n const ambiguous = (prop: string) => (overloads.get(prop)?.length ?? 0) > 1\n\n const explainAmbiguity = (prop: string): never => {\n const signatures = overloads.get(prop)!.map((fn) => fn.format('sighash'))\n throw new Error(\n `\\`allow.${path}.${prop}\\` is ambiguous: ${address} has ` +\n `${signatures.length} functions named \"${prop}\". Name the overload ` +\n `you mean by its full signature — ` +\n signatures\n .map((sig) => `allow.${path}[${JSON.stringify(sig)}]`)\n .join(' or ') +\n `.`\n )\n }\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 (ambiguous(prop)) explainAmbiguity(prop)\n if (!has(prop)) return undefined\n const fn = iface.getFunction(prop)!\n return makeAllowFunction(fn, lowerAddr)\n },\n // An ambiguous bare name is not a member — only the full signatures are.\n // `in` answers that question; reaching for the property explains it.\n has: (_target, prop) => {\n if (prop === EVERYTHING) return true\n return typeof prop === 'string' && !ambiguous(prop) && 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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"}
@@ -1,2 +1,2 @@
1
- import { a as resolveAbisDir, i as loadConfig, n as defineConfig, r as ensureConfigStub, t as DEFAULT_ABIS_DIR } from "../config-D0FqysME.mjs";
1
+ import { a as resolveAbisDir, i as loadConfig, n as defineConfig, r as ensureConfigStub, t as DEFAULT_ABIS_DIR } from "../config-BaChz2bq.mjs";
2
2
  export { DEFAULT_ABIS_DIR, defineConfig, ensureConfigStub, loadConfig, resolveAbisDir };
@@ -0,0 +1,24 @@
1
+ //#region src/cli/commands/push.d.ts
2
+ type PushEntrypointOptions = {
3
+ /** The module whose named exports are the nodes to push, relative to the
4
+ * working directory. @default constellation/index.ts in the project root */
5
+ entrypoint?: string; /** Open every pushed constellation's review page. @default true */
6
+ openInBrowser?: boolean; /** The config file whose API key to push with, as for `zodiac pull`. */
7
+ config?: string;
8
+ };
9
+ /**
10
+ * Pushes every node an entrypoint exports — each export name becomes the
11
+ * node's ref — and prints where each constellation can be reviewed.
12
+ *
13
+ * A constellation Zodiac refuses is printed as the list of what it refused,
14
+ * and the process exits with code 1. Projects that set up globals the
15
+ * entrypoint relies on run their setup first and call this afterwards.
16
+ */
17
+ declare const pushEntrypoint: ({
18
+ entrypoint,
19
+ openInBrowser,
20
+ config: configPath
21
+ }?: PushEntrypointOptions) => Promise<void>;
22
+ //#endregion
23
+ export { pushEntrypoint };
24
+ //# sourceMappingURL=push.d.mts.map
@@ -0,0 +1,36 @@
1
+ import { n as push, t as ConstellationRejectedError } from "../push-DdnHsLpO.mjs";
2
+ import { i as loadConfig } from "../config-BaChz2bq.mjs";
3
+ import { resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import open from "open";
6
+ //#region src/cli/commands/push.ts
7
+ const DEFAULT_ENTRYPOINT = "constellation/index.ts";
8
+ /**
9
+ * Pushes every node an entrypoint exports — each export name becomes the
10
+ * node's ref — and prints where each constellation can be reviewed.
11
+ *
12
+ * A constellation Zodiac refuses is printed as the list of what it refused,
13
+ * and the process exits with code 1. Projects that set up globals the
14
+ * entrypoint relies on run their setup first and call this afterwards.
15
+ */
16
+ const pushEntrypoint = async ({ entrypoint, openInBrowser = true, config: configPath } = {}) => {
17
+ const { apiKey, rootDir } = await loadConfig(configPath);
18
+ const entrypointPath = entrypoint == null ? resolve(rootDir, DEFAULT_ENTRYPOINT) : resolve(process.cwd(), entrypoint);
19
+ const { default: defaultExport, ...nodes } = await import(pathToFileURL(entrypointPath).href);
20
+ if (defaultExport !== void 0) console.warn(`warning: ${entrypointPath} has a default export which will be ignored. Use named exports — each export becomes a ref in the pushed spec.`);
21
+ try {
22
+ const results = await push(nodes, { apiKey });
23
+ for (const { url } of results) {
24
+ console.log(`Pushed constellation to: ${url}`);
25
+ if (openInBrowser) await open(url);
26
+ }
27
+ } catch (error) {
28
+ if (!(error instanceof ConstellationRejectedError)) throw error;
29
+ console.error(error.message);
30
+ process.exitCode = 1;
31
+ }
32
+ };
33
+ //#endregion
34
+ export { pushEntrypoint };
35
+
36
+ //# sourceMappingURL=push.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.mjs","names":[],"sources":["../../src/cli/commands/push.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport open from 'open'\nimport { ConstellationRejectedError, push } from '../../push'\nimport { loadConfig } from '../config'\n\nconst DEFAULT_ENTRYPOINT = 'constellation/index.ts'\n\ntype PushEntrypointOptions = {\n /** The module whose named exports are the nodes to push, relative to the\n * working directory. @default constellation/index.ts in the project root */\n entrypoint?: string\n /** Open every pushed constellation's review page. @default true */\n openInBrowser?: boolean\n /** The config file whose API key to push with, as for `zodiac pull`. */\n config?: string\n}\n\n/**\n * Pushes every node an entrypoint exports — each export name becomes the\n * node's ref — and prints where each constellation can be reviewed.\n *\n * A constellation Zodiac refuses is printed as the list of what it refused,\n * and the process exits with code 1. Projects that set up globals the\n * entrypoint relies on run their setup first and call this afterwards.\n */\nexport const pushEntrypoint = async ({\n entrypoint,\n openInBrowser = true,\n config: configPath,\n}: PushEntrypointOptions = {}) => {\n const { apiKey, rootDir } = await loadConfig(configPath)\n const entrypointPath =\n entrypoint == null\n ? resolve(rootDir, DEFAULT_ENTRYPOINT)\n : resolve(process.cwd(), entrypoint)\n const { default: defaultExport, ...nodes } = await import(\n pathToFileURL(entrypointPath).href\n )\n\n if (defaultExport !== undefined) {\n console.warn(\n `warning: ${entrypointPath} has a default export which will be ignored. ` +\n `Use named exports — each export becomes a ref in the pushed spec.`\n )\n }\n\n try {\n const results = await push(nodes, { apiKey })\n\n for (const { url } of results) {\n console.log(`Pushed constellation to: ${url}`)\n\n if (openInBrowser) {\n await open(url)\n }\n }\n } catch (error) {\n if (!(error instanceof ConstellationRejectedError)) {\n throw error\n }\n\n console.error(error.message)\n process.exitCode = 1\n }\n}\n"],"mappings":";;;;;;AAMA,MAAM,qBAAqB;;;;;;;;;AAoB3B,MAAa,iBAAiB,OAAO,EACnC,YACA,gBAAgB,MAChB,QAAQ,eACiB,EAAE,KAAK;CAChC,MAAM,EAAE,QAAQ,YAAY,MAAM,WAAW,WAAW;CACxD,MAAM,iBACJ,cAAc,OACV,QAAQ,SAAS,mBAAmB,GACpC,QAAQ,QAAQ,KAAK,EAAE,WAAW;CACxC,MAAM,EAAE,SAAS,eAAe,GAAG,UAAU,MAAM,OACjD,cAAc,eAAe,CAAC;AAGhC,KAAI,kBAAkB,KAAA,EACpB,SAAQ,KACN,YAAY,eAAe,gHAE5B;AAGH,KAAI;EACF,MAAM,UAAU,MAAM,KAAK,OAAO,EAAE,QAAQ,CAAC;AAE7C,OAAK,MAAM,EAAE,SAAS,SAAS;AAC7B,WAAQ,IAAI,4BAA4B,MAAM;AAE9C,OAAI,cACF,OAAM,KAAK,IAAI;;UAGZ,OAAO;AACd,MAAI,EAAE,iBAAiB,4BACrB,OAAM;AAGR,UAAQ,MAAM,MAAM,QAAQ;AAC5B,UAAQ,WAAW"}
package/dist/cli.mjs CHANGED
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { n as resolveZodiacDir, t as ApiClient } from "./api-kKgzHQlu.mjs";
3
- import { a as walkContracts, i as readAbi, n as chainIdFor, o as writeAbi, r as abiFilePath } from "./networks-B0H4Uoyh.mjs";
4
- import { a as resolveAbisDir, i as loadConfig, o as findProjectRoot, r as ensureConfigStub } from "./config-D0FqysME.mjs";
2
+ import { t as resolveZodiacDir } from "./paths-LsD6ULk0.mjs";
3
+ import { r as ApiClient } from "./push-DdnHsLpO.mjs";
4
+ import { a as walkContracts, i as readAbi, n as chainIdFor, o as writeAbi, r as abiFilePath } from "./networks-UKtJ8QDG.mjs";
5
+ import { a as resolveAbisDir, i as loadConfig, o as findProjectRoot, r as ensureConfigStub } from "./config-BaChz2bq.mjs";
6
+ import { pushEntrypoint } from "./cli/push.mjs";
5
7
  import fs, { existsSync, readFileSync, writeFileSync } from "node:fs";
6
8
  import path, { basename, dirname, join, resolve } from "node:path";
7
9
  import { getAddress } from "ethers";
@@ -703,14 +705,14 @@ const loadConfigOrInit = (configPath) => loadConfig(configPath, {
703
705
  });
704
706
  const run = async (argv = process.argv) => {
705
707
  const program = new Command();
706
- program.name("zodiac").description("Zodiac SDK CLI – pull org data and contract ABIs").version(ownVersion()).option("-c, --config <path>", "path to the config file", "zodiac.config.ts");
708
+ program.name("zodiac").description("Zodiac SDK CLI – pull org data and contract ABIs, push constellations").version(ownVersion()).option("-c, --config <path>", "path to the config file", "zodiac.config.ts");
707
709
  let updateNotice = Promise.resolve(null);
708
710
  program.hook("preAction", () => {
709
711
  updateNotice = checkForUpdate();
710
712
  });
711
713
  program.hook("postAction", async () => {
712
714
  const notice = await updateNotice;
713
- if (notice != null) console.warn(`\n${notice}`);
715
+ if (notice != null && !process.exitCode) console.warn(`\n${notice}`);
714
716
  });
715
717
  program.command("pull").description("Fetch Zodiac org data and contract ABIs, generating typed SDK functions. Authorizes this directory automatically on first run.").action(async (_opts, cmd) => {
716
718
  const config = await loadConfigOrInit(cmd.optsWithGlobals().config);
@@ -722,6 +724,13 @@ const run = async (argv = process.argv) => {
722
724
  program.command("pull-contracts").description("Fetch contract ABIs, generate typed permissions kit").action(async (_opts, cmd) => {
723
725
  await pullContracts(await loadConfigOrInit(cmd.optsWithGlobals().config));
724
726
  });
727
+ program.command("push").description("Push the nodes an entrypoint exports to Zodiac and open them for review. Projects that set up globals for their entrypoint call `pushEntrypoint` from `@zodiaceco/sdk/cli/push` after that setup instead.").argument("[entrypoint]", "module exporting the nodes (default: constellation/index.ts in the project root)").option("--no-open", "do not open the review pages in a browser").action(async (entrypoint, opts, cmd) => {
728
+ await pushEntrypoint({
729
+ entrypoint,
730
+ openInBrowser: opts.open,
731
+ config: cmd.optsWithGlobals().config
732
+ });
733
+ });
725
734
  program.command("init").description("Re-authorize this directory / mint a new API key (run automatically by `pull` on first use).").option("--app-url <url>", "Override the Zodiac app URL (defaults to ZODIAC_APP_URL or app.zodiac.eco)").action(async (opts) => {
726
735
  await init({ appUrl: opts.appUrl });
727
736
  });
@@ -730,7 +739,7 @@ const run = async (argv = process.argv) => {
730
739
  //#endregion
731
740
  //#region src/cli/index.ts
732
741
  run().then(() => {
733
- process.exit(0);
742
+ process.exit(process.exitCode ?? 0);
734
743
  }, (error) => {
735
744
  if (error) console.error(error);
736
745
  process.exit(1);