@zodiaceco/sdk 2.3.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 +55 -3
  2. package/dist/{actions-DUOQItr6.d.mts → actions-Cri7InV-.d.mts} +14 -2
  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 +4 -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
@@ -256,7 +256,12 @@ Each helper covers a different kind of action:
256
256
  name one with `chain` when the recipients are plain addresses. Tokens without
257
257
  an Across route to a target are skipped, the same way the app skips them —
258
258
  routes change between writing a spec and deploying it — but a target nothing
259
- 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.
260
265
  - `defikit` mirrors the DeFi Kit allow kit — same protocols, verbs and
261
266
  parameters, plus a `label`. A DeFi Kit entry is nothing but its annotation;
262
267
  the permissions behind it are fetched from the annotation's uri at deploy, so
@@ -272,13 +277,36 @@ it has nowhere to appear in the app beyond the targets it allows.
272
277
  Allowance keys are plain labels — `key: 'usdc_payouts'` on the declaration, and
273
278
  `allowance: usdc_payouts` on the transfer, which reads the key off it. They are
274
279
  encoded to bytes32 when the constellation is deployed, so nothing calls
275
- `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.
276
282
 
277
283
  Tokens are named by address, not by symbol. A `transfer()` recipient may also
278
284
  be a node — an account from your codegen, or one bound by address — which
279
285
  stands for the address it lives at. A node whose address is only known once the
280
286
  constellation is deployed is rejected at compile time.
281
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
+
282
310
  ### Pushing the constellation
283
311
 
284
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:
@@ -297,12 +325,22 @@ By default, `push()` creates an API client from the `ZODIAC_API_KEY` environment
297
325
  await push({ ggDao, newRoles }, { api: new ApiClient({ apiKey: '...' }) })
298
326
  ```
299
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
+
300
338
  ## CLI reference
301
339
 
302
340
  ```
303
341
  Usage: zodiac [options] [command]
304
342
 
305
- Zodiac SDK CLI – pull org data and contract ABIs
343
+ Zodiac SDK CLI – pull org data and contract ABIs, push constellations
306
344
 
307
345
  Options:
308
346
  -V, --version output the version number
@@ -314,5 +352,19 @@ Commands:
314
352
  pull-org Fetch Zodiac users and accounts, generate TypeScript types
315
353
  pull-contracts Fetch contract ABIs, generate typed permissions kit
316
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)
317
356
  help [command] display help for command
318
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;
@@ -414,4 +426,4 @@ type DeFiKitVerb<Verb> = Verb extends ((params: infer Params) => unknown) ? (par
414
426
  }) => DeFiKitEntry;
415
427
  //#endregion
416
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 };
417
- //# sourceMappingURL=actions-DUOQItr6.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-DUOQItr6.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);