@zodiaceco/sdk 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/{actions-Cri7InV-.d.mts → actions-PPtr3RTo.d.mts} +12 -2
- package/dist/actions.d.mts +1 -1
- package/dist/actions.mjs +4 -2
- package/dist/actions.mjs.map +1 -1
- package/dist/allow/index.d.mts +1 -1
- package/dist/allow/index.mjs +1 -1
- package/dist/{allow-DX3iyizB.mjs → allow-C2ss5D6B.mjs} +4 -4
- package/dist/allow-C2ss5D6B.mjs.map +1 -0
- package/dist/cli/push.mjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{index-D49x1ozw.d.mts → index-C2IDsf5U.d.mts} +4 -4
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/permissionEntries-C4dAwZBb.mjs.map +1 -1
- package/dist/{push-DdnHsLpO.mjs → push-B7K4sB4m.mjs} +55 -8
- package/dist/push-B7K4sB4m.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/allow-DX3iyizB.mjs.map +0 -1
- package/dist/push-DdnHsLpO.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -247,7 +247,10 @@ const treasuryRoles = eth.roles['GG Treasury Roles']({
|
|
|
247
247
|
|
|
248
248
|
Each helper covers a different kind of action:
|
|
249
249
|
|
|
250
|
-
- `swap()` allows signing CoW orders between the tokens it names
|
|
250
|
+
- `swap()` allows signing CoW orders between the tokens it names, optionally
|
|
251
|
+
capped by allowances declared on the same Roles mod: `sellAllowance` caps
|
|
252
|
+
what may be sold across every token in `sell`, `buyAllowance` what may be
|
|
253
|
+
bought across every token in `buy`. Either one can be set on its own.
|
|
251
254
|
- `transfer()` allows sending tokens to the addresses it names, optionally
|
|
252
255
|
capped by an allowance declared on the same Roles mod. Pass the zero address
|
|
253
256
|
to allow sending the native token. `bridge` names destinations on other
|
|
@@ -277,8 +280,11 @@ it has nowhere to appear in the app beyond the targets it allows.
|
|
|
277
280
|
Allowance keys are plain labels — `key: 'usdc_payouts'` on the declaration, and
|
|
278
281
|
`allowance: usdc_payouts` on the transfer, which reads the key off it. They are
|
|
279
282
|
encoded to bytes32 when the constellation is deployed, so nothing calls
|
|
280
|
-
`encodeKey` by hand.
|
|
281
|
-
|
|
283
|
+
`encodeKey` by hand. The same holds where an `allow`-kit permission draws on an
|
|
284
|
+
allowance: `c.withinAllowance('usdc_payouts')` on a parameter, or
|
|
285
|
+
`{ send: true, etherWithinAllowance: 'eth_budget' }` and
|
|
286
|
+
`{ callWithinAllowance: 'daily_calls' }` in its options. Allowance and role
|
|
287
|
+
keys consist of 1 to 31 letters, digits, underscores or hyphens.
|
|
282
288
|
|
|
283
289
|
Tokens are named by address, not by symbol. A `transfer()` recipient may also
|
|
284
290
|
be a node — an account from your codegen, or one bound by address — which
|
|
@@ -284,6 +284,8 @@ type SwapEntry = {
|
|
|
284
284
|
type: 'swap';
|
|
285
285
|
sell: Address[];
|
|
286
286
|
buy: Address[];
|
|
287
|
+
sellAllowance?: AllowanceKey;
|
|
288
|
+
buyAllowance?: AllowanceKey;
|
|
287
289
|
};
|
|
288
290
|
};
|
|
289
291
|
type TransferEntry = {
|
|
@@ -353,7 +355,9 @@ type Permissions = readonly PermissionEntry[];
|
|
|
353
355
|
declare const swap: ({
|
|
354
356
|
label,
|
|
355
357
|
sell,
|
|
356
|
-
buy
|
|
358
|
+
buy,
|
|
359
|
+
sellAllowance,
|
|
360
|
+
buyAllowance
|
|
357
361
|
}: SwapParams) => SwapEntry;
|
|
358
362
|
/**
|
|
359
363
|
* Allows transferring any of `tokens` to any of `to` on the role's own chain,
|
|
@@ -390,6 +394,12 @@ type SwapParams = {
|
|
|
390
394
|
/** Names the action in the app. Never reaches the chain. */label: string; /** Tokens the role may sell. */
|
|
391
395
|
sell: readonly Address[]; /** Tokens the role may buy. */
|
|
392
396
|
buy: readonly Address[];
|
|
397
|
+
/** Allowance capping what may be sold, across every token in `sell`. Pass
|
|
398
|
+
* the allowance declared on the roles modifier, or its key. */
|
|
399
|
+
sellAllowance?: AllowanceSpec | AllowanceKey;
|
|
400
|
+
/** Allowance capping what may be bought, across every token in `buy`. Pass
|
|
401
|
+
* the allowance declared on the roles modifier, or its key. */
|
|
402
|
+
buyAllowance?: AllowanceSpec | AllowanceKey;
|
|
393
403
|
};
|
|
394
404
|
type TransferParams = {
|
|
395
405
|
/** Names the action in the app. Never reaches the chain. */label: string; /** Tokens the role may transfer. Use the zero address for the native token. */
|
|
@@ -426,4 +436,4 @@ type DeFiKitVerb<Verb> = Verb extends ((params: infer Params) => unknown) ? (par
|
|
|
426
436
|
}) => DeFiKitEntry;
|
|
427
437
|
//#endregion
|
|
428
438
|
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 };
|
|
429
|
-
//# sourceMappingURL=actions-
|
|
439
|
+
//# sourceMappingURL=actions-PPtr3RTo.d.mts.map
|
package/dist/actions.d.mts
CHANGED
|
@@ -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-
|
|
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-PPtr3RTo.mjs";
|
|
2
2
|
export { PermissionEntry, PermissionTarget, Permissions, custom, defikit, swap, transfer };
|
package/dist/actions.mjs
CHANGED
|
@@ -6,12 +6,14 @@ import { r as recipientChains } from "./permissionEntries-C4dAwZBb.mjs";
|
|
|
6
6
|
* never here — a stored revision that carried its own copy would go stale as
|
|
7
7
|
* soon as the compiler changed.
|
|
8
8
|
*/
|
|
9
|
-
const swap = ({ label, sell, buy }) => ({
|
|
9
|
+
const swap = ({ label, sell, buy, sellAllowance, buyAllowance }) => ({
|
|
10
10
|
label,
|
|
11
11
|
action: {
|
|
12
12
|
type: "swap",
|
|
13
13
|
sell: [...sell],
|
|
14
|
-
buy: [...buy]
|
|
14
|
+
buy: [...buy],
|
|
15
|
+
...sellAllowance != null && { sellAllowance: allowanceKey(sellAllowance) },
|
|
16
|
+
...buyAllowance != null && { buyAllowance: allowanceKey(buyAllowance) }
|
|
15
17
|
}
|
|
16
18
|
});
|
|
17
19
|
/**
|
package/dist/actions.mjs.map
CHANGED
|
@@ -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 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
|
+
{"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 = ({\n label,\n sell,\n buy,\n sellAllowance,\n buyAllowance,\n}: SwapParams): SwapEntry => ({\n label,\n action: {\n type: 'swap',\n sell: [...sell],\n buy: [...buy],\n ...(sellAllowance != null && {\n sellAllowance: allowanceKey(sellAllowance),\n }),\n ...(buyAllowance != null && { buyAllowance: allowanceKey(buyAllowance) }),\n },\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 /** Allowance capping what may be sold, across every token in `sell`. Pass\n * the allowance declared on the roles modifier, or its key. */\n sellAllowance?: AllowanceSpec | AllowanceKey\n /** Allowance capping what may be bought, across every token in `buy`. Pass\n * the allowance declared on the roles modifier, or its key. */\n buyAllowance?: AllowanceSpec | AllowanceKey\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,EACnB,OACA,MACA,KACA,eACA,oBAC4B;CAC5B;CACA,QAAQ;EACN,MAAM;EACN,MAAM,CAAC,GAAG,KAAK;EACf,KAAK,CAAC,GAAG,IAAI;EACb,GAAI,iBAAiB,QAAQ,EAC3B,eAAe,aAAa,cAAc,EAC3C;EACD,GAAI,gBAAgB,QAAQ,EAAE,cAAc,aAAa,aAAa,EAAE;EACzE;CACF;;;;;;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"}
|
package/dist/allow/index.d.mts
CHANGED
|
@@ -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-
|
|
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-C2IDsf5U.mjs";
|
|
2
2
|
export { CHAIN_IDS, ChainPrefix, ConditionFunction, EVERYTHING, FunctionPermission, Options, Scoping, TargetPermission, buildAllowKit, chainIdFor };
|
package/dist/allow/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { n as chainIdFor, t as CHAIN_IDS } from "../networks-UKtJ8QDG.mjs";
|
|
2
|
-
import { n as EVERYTHING, t as buildAllowKit } from "../allow-
|
|
2
|
+
import { n as EVERYTHING, t as buildAllowKit } from "../allow-C2ss5D6B.mjs";
|
|
3
3
|
export { CHAIN_IDS, EVERYTHING, buildAllowKit, chainIdFor };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as walkContracts, i as readAbi } from "./networks-UKtJ8QDG.mjs";
|
|
2
|
-
import { Operator, ParameterType, c, coercePermission } from "zodiac-roles-sdk";
|
|
2
|
+
import { Operator, ParameterType, c, coercePermission, encodeKey } from "zodiac-roles-sdk";
|
|
3
3
|
import { Interface, isError } from "ethers";
|
|
4
4
|
//#region src/allow/types.ts
|
|
5
5
|
const EVERYTHING = Symbol.for("@zodiaceco/allow-kit/EVERYTHING");
|
|
@@ -111,13 +111,13 @@ const applyOptions = (permission, options) => {
|
|
|
111
111
|
condition = applyGlobalAllowance(condition, {
|
|
112
112
|
paramType: ParameterType.None,
|
|
113
113
|
operator: Operator.EtherWithinAllowance,
|
|
114
|
-
compValue: options.etherWithinAllowance
|
|
114
|
+
compValue: encodeKey(options.etherWithinAllowance)
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
117
|
if (options.callWithinAllowance) condition = applyGlobalAllowance(condition, {
|
|
118
118
|
paramType: ParameterType.None,
|
|
119
119
|
operator: Operator.CallWithinAllowance,
|
|
120
|
-
compValue: options.callWithinAllowance
|
|
120
|
+
compValue: encodeKey(options.callWithinAllowance)
|
|
121
121
|
});
|
|
122
122
|
return {
|
|
123
123
|
...permission,
|
|
@@ -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-
|
|
132
|
+
//# sourceMappingURL=allow-C2ss5D6B.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allow-C2ss5D6B.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 /** Meters the Ether sent. The allowance's key, as a label or already encoded. */\n etherWithinAllowance?: string\n /** Meters the number of calls. The allowance's key, as a label or already encoded. */\n callWithinAllowance?: 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 {\n c,\n coercePermission,\n encodeKey,\n ParameterType,\n Operator,\n} 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: encodeKey(options.etherWithinAllowance),\n })\n }\n if (options.callWithinAllowance) {\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.CallWithinAllowance,\n compValue: encodeKey(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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"}
|
package/dist/cli/push.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as push, t as ConstellationRejectedError } from "../push-
|
|
1
|
+
import { n as push, t as ConstellationRejectedError } from "../push-B7K4sB4m.mjs";
|
|
2
2
|
import { i as loadConfig } from "../config-BaChz2bq.mjs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as resolveZodiacDir } from "./paths-LsD6ULk0.mjs";
|
|
3
|
-
import { r as ApiClient } from "./push-
|
|
3
|
+
import { r as ApiClient } from "./push-B7K4sB4m.mjs";
|
|
4
4
|
import { a as walkContracts, i as readAbi, n as chainIdFor, o as writeAbi, r as abiFilePath } from "./networks-UKtJ8QDG.mjs";
|
|
5
5
|
import { a as resolveAbisDir, i as loadConfig, o as findProjectRoot, r as ensureConfigStub } from "./config-BaChz2bq.mjs";
|
|
6
6
|
import { pushEntrypoint } from "./cli/push.mjs";
|
|
@@ -7,9 +7,9 @@ declare function buildAllowKit(abisDir: string, contractsConfig: Record<string,
|
|
|
7
7
|
//#region src/allow/types.d.ts
|
|
8
8
|
type Options = {
|
|
9
9
|
send?: boolean;
|
|
10
|
-
delegatecall?: boolean;
|
|
11
|
-
etherWithinAllowance?:
|
|
12
|
-
callWithinAllowance?:
|
|
10
|
+
delegatecall?: boolean; /** Meters the Ether sent. The allowance's key, as a label or already encoded. */
|
|
11
|
+
etherWithinAllowance?: string; /** Meters the number of calls. The allowance's key, as a label or already encoded. */
|
|
12
|
+
callWithinAllowance?: string;
|
|
13
13
|
};
|
|
14
14
|
type PrimitiveValue = BigNumberish | BytesLike | string | boolean;
|
|
15
15
|
type ConditionFunction<T = unknown> = (abiType: ParamType, _?: T) => Condition;
|
|
@@ -60,4 +60,4 @@ type ChainPrefix = keyof typeof CHAIN_IDS;
|
|
|
60
60
|
declare const chainIdFor: (prefix: string) => number;
|
|
61
61
|
//#endregion
|
|
62
62
|
export { EVERYTHING as a, Scoping as c, ConditionFunction as i, TargetPermission as l, ChainPrefix as n, FunctionPermission as o, chainIdFor as r, Options as s, CHAIN_IDS as t, buildAllowKit as u };
|
|
63
|
-
//# sourceMappingURL=index-
|
|
63
|
+
//# sourceMappingURL=index-C2IDsf5U.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference path="./zodiac-os-codegen.d.ts" />
|
|
2
|
-
import { a as PermissionEntry, c as ConstellationNode, l as constellation, s as Permissions } from "./actions-
|
|
3
|
-
import { a as EVERYTHING, c as Scoping, i as ConditionFunction, l as TargetPermission, n as ChainPrefix, o as FunctionPermission, s as Options, u as buildAllowKit } from "./index-
|
|
2
|
+
import { a as PermissionEntry, c as ConstellationNode, l as constellation, s as Permissions } from "./actions-PPtr3RTo.mjs";
|
|
3
|
+
import { a as EVERYTHING, c as Scoping, i as ConditionFunction, l as TargetPermission, n as ChainPrefix, o as FunctionPermission, s as Options, u as buildAllowKit } from "./index-C2IDsf5U.mjs";
|
|
4
4
|
import { Annotation, Permission, PermissionSet, c, encodeKey, forAll } from "zodiac-roles-sdk";
|
|
5
5
|
import { ApiErrorIssue, ApplyConstellationPayload, ApplyConstellationResult, ListAccountsResult, ListUsersResult, ResolveConstellationPayload, ResolveConstellationResult } from "@zodiaceco/api-types";
|
|
6
6
|
import { UUID } from "crypto";
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as resolveZodiacDir } from "./paths-LsD6ULk0.mjs";
|
|
2
|
-
import { n as push, t as ConstellationRejectedError } from "./push-
|
|
3
|
-
import { n as EVERYTHING, t as buildAllowKit } from "./allow-
|
|
2
|
+
import { n as push, t as ConstellationRejectedError } from "./push-B7K4sB4m.mjs";
|
|
3
|
+
import { n as EVERYTHING, t as buildAllowKit } from "./allow-C2ss5D6B.mjs";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
5
|
import { c, encodeKey, forAll } from "zodiac-roles-sdk";
|
|
6
6
|
//#region src/constellation.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissionEntries-C4dAwZBb.mjs","names":[],"sources":["../src/permissionEntries.ts"],"sourcesContent":["import type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { Permission } from 'zodiac-roles-sdk'\nimport type { NodeRef } from './constellation'\n\n/**\n * The shapes a role's `permissions` travel to the API in, and the one piece of\n * rendering `push()` still does. Internal: the helpers that build these live in\n * `./actions`, and nothing here is part of the published interface beyond the\n * `PermissionEntry` type that module re-exports.\n */\n\n/**\n * The base url of the DeFi Kit API. Annotations point back at it so that a role\n * can be read as presets again, and so that a DeFi Kit entry written in code is\n * indistinguishable from one configured in the app.\n */\nconst DEFI_KIT_BASE_URL = 'https://kit.kpk.io/api/v1'\n\n/** The chains DeFi Kit serves, by the prefix it addresses them with. */\nconst DEFI_KIT_CHAIN_PREFIXES: Record<number, string> = {\n 1: 'eth',\n 10: 'oeth',\n 100: 'gno',\n 8453: 'base',\n 42161: 'arb1',\n}\n\n/** Renders a DeFi Kit entry into the annotation it stands for on `chain`. */\nexport const toAnnotation = (\n { defiKit: { protocol, verb, params } }: DeFiKitEntry,\n chain: ChainId\n) => {\n const prefix = DEFI_KIT_CHAIN_PREFIXES[chain]\n\n if (prefix == null) {\n throw new Error(`DeFi Kit does not serve chain \"${chain}\"`)\n }\n\n return {\n uri: `${DEFI_KIT_BASE_URL}/permissions/${prefix}/${protocol}/${verb}?${searchParams(params)}`,\n schema: `${DEFI_KIT_BASE_URL}/openapi.json`,\n }\n}\n\nexport const isDeFiKitEntry = (entry: PermissionEntry): entry is DeFiKitEntry =>\n typeof entry === 'object' && entry !== null && 'defiKit' in entry\n\n/**\n * The chains of the node recipients a `transfer()`'s same-chain `to` named.\n * Symbol-keyed so it never serializes; `push()` checks it against the chain of\n * the roles node the entry ends up on. A transfer to a node on another chain\n * would permit sending funds to an address that may not exist there.\n */\nexport const recipientChains = Symbol('recipientChains')\n\nexport const assertRecipientChains = (\n entry: PermissionEntry,\n chain: ChainId\n): void => {\n const chains = (entry as { [recipientChains]?: readonly ChainId[] })[\n recipientChains\n ]\n\n for (const recipientChain of chains ?? []) {\n if (recipientChain !== chain) {\n const label = 'label' in entry ? `\"${entry.label}\"` : 'a transfer'\n throw new Error(\n `A recipient of ${label} lives on chain \"${recipientChain}\", but the role is on chain \"${chain}\". Use \\`bridge\\` for cross-chain recipients.`\n )\n }\n }\n\n if (!('action' in entry) || entry.action.type !== 'transfer') {\n return\n }\n\n for (const target of entry.action.bridge ?? []) {\n if (target.chain === chain) {\n throw new Error(\n `\"${entry.label}\" bridges to chain \"${chain}\", the role's own chain. Name those recipients in \\`to\\` instead.`\n )\n }\n }\n}\n\n/**\n * Search params are appended one value at a time, the form annotation uris are\n * normalized to before they are matched back against a preset.\n */\nconst searchParams = (params: Record<string, DeFiKitParamValue>) => {\n const searchParams = new URLSearchParams()\n\n for (const [name, value] of Object.entries(params)) {\n if (value == null) {\n continue\n }\n\n for (const entry of Array.isArray(value) ? value : [value]) {\n searchParams.append(name, String(entry))\n }\n }\n\n return searchParams\n}\n\n/**\n * An allowance key. Authored as a plain label, the way a role key is, and\n * encoded to bytes32 when the constellation is deployed. An already-encoded\n * key still works — `encodeKey` passes 32-byte hex through unchanged.\n */\nexport type AllowanceKey = string\n\nexport type SwapEntry = {\n label: string\n action: {
|
|
1
|
+
{"version":3,"file":"permissionEntries-C4dAwZBb.mjs","names":[],"sources":["../src/permissionEntries.ts"],"sourcesContent":["import type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { Permission } from 'zodiac-roles-sdk'\nimport type { NodeRef } from './constellation'\n\n/**\n * The shapes a role's `permissions` travel to the API in, and the one piece of\n * rendering `push()` still does. Internal: the helpers that build these live in\n * `./actions`, and nothing here is part of the published interface beyond the\n * `PermissionEntry` type that module re-exports.\n */\n\n/**\n * The base url of the DeFi Kit API. Annotations point back at it so that a role\n * can be read as presets again, and so that a DeFi Kit entry written in code is\n * indistinguishable from one configured in the app.\n */\nconst DEFI_KIT_BASE_URL = 'https://kit.kpk.io/api/v1'\n\n/** The chains DeFi Kit serves, by the prefix it addresses them with. */\nconst DEFI_KIT_CHAIN_PREFIXES: Record<number, string> = {\n 1: 'eth',\n 10: 'oeth',\n 100: 'gno',\n 8453: 'base',\n 42161: 'arb1',\n}\n\n/** Renders a DeFi Kit entry into the annotation it stands for on `chain`. */\nexport const toAnnotation = (\n { defiKit: { protocol, verb, params } }: DeFiKitEntry,\n chain: ChainId\n) => {\n const prefix = DEFI_KIT_CHAIN_PREFIXES[chain]\n\n if (prefix == null) {\n throw new Error(`DeFi Kit does not serve chain \"${chain}\"`)\n }\n\n return {\n uri: `${DEFI_KIT_BASE_URL}/permissions/${prefix}/${protocol}/${verb}?${searchParams(params)}`,\n schema: `${DEFI_KIT_BASE_URL}/openapi.json`,\n }\n}\n\nexport const isDeFiKitEntry = (entry: PermissionEntry): entry is DeFiKitEntry =>\n typeof entry === 'object' && entry !== null && 'defiKit' in entry\n\n/**\n * The chains of the node recipients a `transfer()`'s same-chain `to` named.\n * Symbol-keyed so it never serializes; `push()` checks it against the chain of\n * the roles node the entry ends up on. A transfer to a node on another chain\n * would permit sending funds to an address that may not exist there.\n */\nexport const recipientChains = Symbol('recipientChains')\n\nexport const assertRecipientChains = (\n entry: PermissionEntry,\n chain: ChainId\n): void => {\n const chains = (entry as { [recipientChains]?: readonly ChainId[] })[\n recipientChains\n ]\n\n for (const recipientChain of chains ?? []) {\n if (recipientChain !== chain) {\n const label = 'label' in entry ? `\"${entry.label}\"` : 'a transfer'\n throw new Error(\n `A recipient of ${label} lives on chain \"${recipientChain}\", but the role is on chain \"${chain}\". Use \\`bridge\\` for cross-chain recipients.`\n )\n }\n }\n\n if (!('action' in entry) || entry.action.type !== 'transfer') {\n return\n }\n\n for (const target of entry.action.bridge ?? []) {\n if (target.chain === chain) {\n throw new Error(\n `\"${entry.label}\" bridges to chain \"${chain}\", the role's own chain. Name those recipients in \\`to\\` instead.`\n )\n }\n }\n}\n\n/**\n * Search params are appended one value at a time, the form annotation uris are\n * normalized to before they are matched back against a preset.\n */\nconst searchParams = (params: Record<string, DeFiKitParamValue>) => {\n const searchParams = new URLSearchParams()\n\n for (const [name, value] of Object.entries(params)) {\n if (value == null) {\n continue\n }\n\n for (const entry of Array.isArray(value) ? value : [value]) {\n searchParams.append(name, String(entry))\n }\n }\n\n return searchParams\n}\n\n/**\n * An allowance key. Authored as a plain label, the way a role key is, and\n * encoded to bytes32 when the constellation is deployed. An already-encoded\n * key still works — `encodeKey` passes 32-byte hex through unchanged.\n */\nexport type AllowanceKey = string\n\nexport type SwapEntry = {\n label: string\n action: {\n type: 'swap'\n sell: Address[]\n buy: Address[]\n sellAllowance?: AllowanceKey\n buyAllowance?: AllowanceKey\n }\n}\n\nexport type TransferEntry = {\n label: string\n action: {\n type: 'transfer'\n tokens: Address[]\n to: Address[]\n bridge?: { chain: ChainId; to: Address[]; receive: Address[] }[]\n allowance?: AllowanceKey\n }\n}\n\n/**\n * A permission's target, written either as an address or as another node in the\n * same constellation.\n *\n * A policy is usually pushed alongside the accounts it governs, so the address\n * a permission points at often does not exist yet. Naming the node instead\n * sends a reference, and the address is substituted once it is derived at\n * deploy — the same way a node is named as an owner, a module or an avatar.\n *\n * `NodeRef` for the same reason those fields use it: an uninvoked accessor is\n * a forward reference to a node, and is how two nodes that need each other are\n * written. It carries the label a reference resolves by without being a\n * complete node.\n */\nexport type PermissionTarget = `0x${string}` | NodeRef\n\ntype WithNodeTarget<P> = P extends { targetAddress: `0x${string}` }\n ? Omit<P, 'targetAddress'> & { targetAddress: PermissionTarget }\n : P\n\n/** A `zodiac-roles-sdk` permission that may name a node as its target. */\nexport type ConstellationPermission = WithNodeTarget<Permission>\n\nexport type LabelledPermissions = {\n label: string\n permissions: ConstellationPermission[]\n}\n\nexport type DeFiKitEntry = {\n label: string\n defiKit: {\n protocol: string\n verb: string\n params: Record<string, DeFiKitParamValue>\n }\n}\n\nexport type DeFiKitParamValue =\n | string\n | number\n | boolean\n | readonly (string | number | boolean)[]\n | null\n | undefined\n\n/**\n * One entry of a role's `permissions`. A bare permission stays valid, but every\n * labelled form names something the app can show as a single card.\n */\nexport type PermissionEntry =\n | ConstellationPermission\n | LabelledPermissions\n | SwapEntry\n | TransferEntry\n | DeFiKitEntry\n\n/** A role's full permission list — the type `permissions.ts` files satisfy. */\nexport type Permissions = readonly PermissionEntry[]\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,oBAAoB;;AAG1B,MAAM,0BAAkD;CACtD,GAAG;CACH,IAAI;CACJ,KAAK;CACL,MAAM;CACN,OAAO;CACR;;AAGD,MAAa,gBACX,EAAE,SAAS,EAAE,UAAU,MAAM,YAC7B,UACG;CACH,MAAM,SAAS,wBAAwB;AAEvC,KAAI,UAAU,KACZ,OAAM,IAAI,MAAM,kCAAkC,MAAM,GAAG;AAG7D,QAAO;EACL,KAAK,GAAG,kBAAkB,eAAe,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,aAAa,OAAO;EAC3F,QAAQ,GAAG,kBAAkB;EAC9B;;AAGH,MAAa,kBAAkB,UAC7B,OAAO,UAAU,YAAY,UAAU,QAAQ,aAAa;;;;;;;AAQ9D,MAAa,kBAAkB,OAAO,kBAAkB;AAExD,MAAa,yBACX,OACA,UACS;CACT,MAAM,SAAU,MACd;AAGF,MAAK,MAAM,kBAAkB,UAAU,EAAE,CACvC,KAAI,mBAAmB,OAAO;EAC5B,MAAM,QAAQ,WAAW,QAAQ,IAAI,MAAM,MAAM,KAAK;AACtD,QAAM,IAAI,MACR,kBAAkB,MAAM,mBAAmB,eAAe,+BAA+B,MAAM,+CAChG;;AAIL,KAAI,EAAE,YAAY,UAAU,MAAM,OAAO,SAAS,WAChD;AAGF,MAAK,MAAM,UAAU,MAAM,OAAO,UAAU,EAAE,CAC5C,KAAI,OAAO,UAAU,MACnB,OAAM,IAAI,MACR,IAAI,MAAM,MAAM,sBAAsB,MAAM,mEAC7C;;;;;;AASP,MAAM,gBAAgB,WAA8C;CAClE,MAAM,eAAe,IAAI,iBAAiB;AAE1C,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,EAAE;AAClD,MAAI,SAAS,KACX;AAGF,OAAK,MAAM,SAAS,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM,CACxD,cAAa,OAAO,MAAM,OAAO,MAAM,CAAC;;AAI5C,QAAO"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { i as toAnnotation, n as isDeFiKitEntry, t as assertRecipientChains } from "./permissionEntries-C4dAwZBb.mjs";
|
|
2
2
|
import { invariant } from "@epic-web/invariant";
|
|
3
|
+
import { coercePermission } from "zodiac-roles-sdk";
|
|
3
4
|
import assert from "assert";
|
|
4
5
|
//#region src/api.ts
|
|
5
6
|
const DEFAULT_API_URL = "https://app.zodiac.eco/api/v1";
|
|
@@ -296,9 +297,10 @@ function nodeToSpec(node, refs) {
|
|
|
296
297
|
}
|
|
297
298
|
/**
|
|
298
299
|
* Entries travel to the API as they were written — parameters and labels — and
|
|
299
|
-
* are compiled when the constellation is deployed.
|
|
300
|
-
*
|
|
301
|
-
* for; the permissions behind it are fetched from that uri at
|
|
300
|
+
* are compiled when the constellation is deployed. Two kinds are rendered
|
|
301
|
+
* here. A DeFi Kit entry becomes the annotation uri that names the chain it
|
|
302
|
+
* was written for; the permissions behind it are fetched from that uri at
|
|
303
|
+
* deploy. A hand-written permission is settled into what JSON can carry.
|
|
302
304
|
*/
|
|
303
305
|
function describeRoles(roles, chain, refs) {
|
|
304
306
|
return Object.fromEntries(Object.entries(roles).map(([key, def]) => {
|
|
@@ -310,14 +312,59 @@ function describeRoles(roles, chain, refs) {
|
|
|
310
312
|
...def.description != null && { description: def.description },
|
|
311
313
|
permissions: def.permissions.map((entry) => {
|
|
312
314
|
assertRecipientChains(entry, chain);
|
|
313
|
-
return resolveRefs(
|
|
314
|
-
label: entry.label,
|
|
315
|
-
annotation: toAnnotation(entry, chain)
|
|
316
|
-
} : entry, refs);
|
|
315
|
+
return resolveRefs(describeEntry(entry, key, chain), refs);
|
|
317
316
|
})
|
|
318
317
|
}];
|
|
319
318
|
}));
|
|
320
319
|
}
|
|
320
|
+
function describeEntry(entry, role, chain) {
|
|
321
|
+
if (isDeFiKitEntry(entry)) return {
|
|
322
|
+
label: entry.label,
|
|
323
|
+
annotation: toAnnotation(entry, chain)
|
|
324
|
+
};
|
|
325
|
+
if ("permissions" in entry) return {
|
|
326
|
+
...entry,
|
|
327
|
+
permissions: entry.permissions.map((permission) => settlePermission(permission, role))
|
|
328
|
+
};
|
|
329
|
+
return "targetAddress" in entry ? settlePermission(entry, role) : entry;
|
|
330
|
+
}
|
|
331
|
+
const PERMISSION_KEYS = new Set([
|
|
332
|
+
"targetAddress",
|
|
333
|
+
"selector",
|
|
334
|
+
"signature",
|
|
335
|
+
"condition",
|
|
336
|
+
"send",
|
|
337
|
+
"delegatecall"
|
|
338
|
+
]);
|
|
339
|
+
/**
|
|
340
|
+
* A permission as JSON can carry it. A condition may still be a function,
|
|
341
|
+
* which only this side can evaluate and which would be dropped on the way,
|
|
342
|
+
* leaving the function it scopes open to any parameters. A signature becomes
|
|
343
|
+
* the selector it names, so one that names nothing fails here rather than at
|
|
344
|
+
* the API.
|
|
345
|
+
*
|
|
346
|
+
* A key no permission has is refused for the same reason a dropped condition
|
|
347
|
+
* is: `conditon` would vanish and take the scoping with it.
|
|
348
|
+
*/
|
|
349
|
+
function settlePermission(permission, role) {
|
|
350
|
+
const unknown = Object.keys(permission).find((key) => !PERMISSION_KEYS.has(key));
|
|
351
|
+
invariant(unknown == null, `A permission of role "${role}" has an unknown key "${unknown}"`);
|
|
352
|
+
invariant(!("selector" in permission && "signature" in permission), `A permission of role "${role}" names its function by both \`selector\` and \`signature\``);
|
|
353
|
+
const { targetAddress, ...rest } = permission;
|
|
354
|
+
try {
|
|
355
|
+
return {
|
|
356
|
+
...coercePermission({
|
|
357
|
+
...rest,
|
|
358
|
+
targetAddress: PLACEHOLDER_TARGET
|
|
359
|
+
}),
|
|
360
|
+
targetAddress
|
|
361
|
+
};
|
|
362
|
+
} catch (error) {
|
|
363
|
+
if ("signature" in permission) throw new Error(`A permission of role "${role}" names the function "${permission.signature}", which is not a function signature`, { cause: error });
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const PLACEHOLDER_TARGET = "0x0000000000000000000000000000000000000000";
|
|
321
368
|
function resolveRefs(value, refs) {
|
|
322
369
|
if (isConstellationNode(value)) {
|
|
323
370
|
const ref = refs.byIdentity.get(value) ?? refs.byLabel.get(labelKey(value));
|
|
@@ -353,4 +400,4 @@ function isConstellationNode(value) {
|
|
|
353
400
|
//#endregion
|
|
354
401
|
export { push as n, ApiClient as r, ConstellationRejectedError as t };
|
|
355
402
|
|
|
356
|
-
//# sourceMappingURL=push-
|
|
403
|
+
//# sourceMappingURL=push-B7K4sB4m.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-B7K4sB4m.mjs","names":[],"sources":["../src/api.ts","../src/push.ts"],"sourcesContent":["import type {\n ApplyConstellationPayload,\n ApplyConstellationResult,\n ResolveConstellationPayload,\n ResolveConstellationResult,\n ApiError as ApiErrorResponse,\n ApiErrorIssue,\n ListAccountsResult,\n ListUsersResult,\n} from '@zodiaceco/api-types'\nimport assert from 'assert'\nimport { UUID } from 'crypto'\n\nexport type Options = {\n workspace?: string\n apiKey?: string\n baseUrl?: string\n fetch?: typeof globalThis.fetch\n headers?: Record<string, string>\n}\n\nconst DEFAULT_API_URL = 'https://app.zodiac.eco/api/v1'\n\nexport class ApiClient {\n private apiKey: string\n private baseUrl: string\n private _fetch: typeof fetch\n private headers: Record<string, string>\n\n constructor({\n // Read `ZODIAC_API_URL`/`ZODIAC_API_KEY` lazily at construction time\n // rather than at module load: the CLI loads `.env` (and `init` writes a\n // fresh one) before the first client is created, so capturing them at\n // import time would bake in the default and ignore the user's config.\n baseUrl = process.env.ZODIAC_API_URL ?? DEFAULT_API_URL,\n fetch: customFetch = fetch,\n headers = {},\n apiKey = process.env.ZODIAC_API_KEY,\n }: Options = {}) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this._fetch = customFetch\n this.headers = headers\n\n assert(\n apiKey,\n 'No API key provided to the API client. Either pass it as the \"apiKey\" option or set the ZODIAC_API_KEY environment variable.'\n )\n\n this.apiKey = apiKey\n }\n\n protected async postJson(endpoint: string, payload: unknown) {\n const res = await this._fetch(`${this.baseUrl}/${endpoint}`, {\n method: 'POST',\n headers: {\n ...this.headers,\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n },\n body: jsonStringify(payload),\n })\n if (!res.ok) {\n await handleApiError(res)\n }\n\n return res.json()\n }\n\n protected async get(endpoint: string) {\n const res = await this._fetch(`${this.baseUrl}/${endpoint}`, {\n headers: { ...this.headers, authorization: `Bearer ${this.apiKey}` },\n })\n\n if (!res.ok) {\n await handleApiError(res)\n }\n\n return res.json()\n }\n\n /**\n * Every account the workspaces of this org hold, grouped by workspace.\n *\n * Only accounts that exist on chain are listed, so every entry carries an\n * address something was deployed to.\n */\n listAccounts(): Promise<ListAccountsResult> {\n return this.get('accounts')\n }\n\n /** Every member of this org, with the personal safes they have activated. */\n listUsers(): Promise<ListUsersResult> {\n return this.get('users')\n }\n\n /**\n * Stores a specification as the constellation's next revision, and answers\n * with a url to review and deploy it. Applying does not deploy anything, and\n * re-applying an undeployed revision replaces it rather than stacking.\n */\n applyConstellation(\n workspaceId: UUID,\n payload: ApplyConstellationPayload\n ): Promise<ApplyConstellationResult> {\n return this.postJson(\n `workspace/${workspaceId}/constellation/apply`,\n payload\n )\n }\n\n /**\n * Reports what the accounts a specification names look like on chain.\n *\n * Declarations are merged over what is read, and a node with no address is\n * resolved against the setup safe of whoever's API key asks — describing the\n * account *that* caller would deploy. Name accounts by address and declare\n * nothing to be told only what is there, the same for every key.\n */\n resolveConstellation(\n workspaceId: UUID,\n payload: ResolveConstellationPayload\n ): Promise<ResolveConstellationResult> {\n return this.postJson(\n `workspace/${workspaceId}/constellation/resolve`,\n payload\n )\n }\n}\n\nexport class ApiRequestError extends Error {\n public readonly status: number\n public readonly statusText: string\n public readonly details?: unknown\n /** What the API refused, when it could name each problem. */\n public readonly issues: ApiErrorIssue[]\n /** The API's own summary of the problem. */\n public readonly reason: string\n\n constructor(\n message: string,\n opts: {\n status: number\n statusText: string\n details?: unknown\n cause?: unknown\n }\n ) {\n const issues = readIssues(opts.details)\n\n super(\n issues.length > 0\n ? formatIssues(message, issues)\n : ApiRequestError.composeMessage(message, opts.details)\n )\n this.name = 'ApiRequestError'\n this.status = opts.status\n this.statusText = opts.statusText\n this.details = opts.details\n this.issues = issues\n this.reason = message\n if (opts.cause !== undefined) {\n ;(this as any).cause = opts.cause\n }\n }\n\n private static composeMessage(message: string, details?: unknown) {\n if (details == null) return message\n let detailsString: string\n try {\n detailsString =\n typeof details === 'string' ? details : jsonStringify(details, 2)\n } catch (_err) {\n detailsString = String(details)\n }\n return `${message}\\nDetails: ${detailsString}`\n }\n\n toString() {\n return `${this.name}: ${this.message}`\n }\n}\n\nasync function handleApiError(response: Response): Promise<never> {\n const contentType = response.headers.get('content-type')\n if (contentType?.includes('application/json')) {\n const errorData = (await response.json()) as ApiErrorResponse\n let error: ApiRequestError\n try {\n error = new ApiRequestError(errorData.error.message, {\n status: response.status,\n statusText: response.statusText,\n details: errorData.error.details,\n })\n } catch (jsonShapeError) {\n error = new ApiRequestError(\n `Failed parsing error response: ${jsonShapeError}`,\n {\n status: response.status,\n statusText: response.statusText,\n details: errorData,\n }\n )\n }\n throw error\n } else {\n throw new ApiRequestError(\n `${response.status} ${response.statusText}: ${response.url}`,\n {\n status: response.status,\n statusText: response.statusText,\n }\n )\n }\n}\n\n/**\n * The issues of an error response, each naming where in the request it is.\n * Anything that does not look like one is left to the raw details.\n */\nconst readIssues = (details: unknown): ApiErrorIssue[] => {\n if (\n typeof details !== 'object' ||\n details == null ||\n !('issues' in details)\n ) {\n return []\n }\n\n const { issues } = details\n\n if (!Array.isArray(issues)) {\n return []\n }\n\n return issues.flatMap((issue): ApiErrorIssue[] =>\n typeof issue === 'object' &&\n issue != null &&\n Array.isArray(issue.path) &&\n typeof issue.message === 'string'\n ? [{ path: issue.path, message: issue.message }]\n : []\n )\n}\n\n/** A headline and one line per issue, the way a terminal reads best. */\nexport const formatIssues = (\n headline: string,\n issues: { path: string; message: string }[] | ApiErrorIssue[]\n) =>\n [\n `${headline}:`,\n ...issues.map(({ path, message }) => {\n const where = Array.isArray(path) ? path.join(' › ') : path\n\n return where === '' ? ` • ${message}` : ` • ${where}: ${message}`\n }),\n ].join('\\n')\n\n/** JSON.stringify with bigint support */\nconst jsonStringify = (value: unknown, indent?: number) =>\n JSON.stringify(\n value,\n (_, value) => {\n if (typeof value === 'bigint') {\n return value.toString()\n }\n\n return value\n },\n indent\n )\n","import type {\n ApplyConstellationPayload,\n ApplyConstellationResult,\n ChainId,\n} from '@zodiaceco/api-types'\nimport { invariant } from '@epic-web/invariant'\nimport { coercePermission } from 'zodiac-roles-sdk'\nimport {\n assertRecipientChains,\n isDeFiKitEntry,\n toAnnotation,\n type ConstellationPermission,\n type PermissionEntry,\n} from './permissionEntries'\nimport { ApiClient, ApiRequestError, formatIssues } from './api'\nimport type {\n ConstellationMeta,\n ConstellationNode,\n ConstellationNodeInternal,\n RoleDef,\n} from './constellation'\n\ntype PushOpts = {\n /** API client instance. Defaults to a client configured from environment variables. */\n api?: ApiClient\n /** API key for the default client, instead of `ZODIAC_API_KEY`. */\n apiKey?: string\n}\n\n/**\n * Applies the given nodes as the next revision of the constellations they\n * belong to, and answers with a url per constellation to review and deploy.\n *\n * Each node travels with what it was actually given, never with the onchain\n * state a `pull` filled it with for reading — so a node passed as a bare\n * reference declares nothing and leaves the account it names alone. Nodes\n * naming each other travel as references and are substituted for addresses\n * once those are derived at deploy.\n *\n * Passing an object names each node by its key; passing an array names them by\n * position. Either way the name is the node's `ref`, which is how the app\n * follows a node from one revision to the next.\n *\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n * const dao = eth.safe['GG DAO']\n * const roles = eth.roles['New Roles']({ nonce: 0n, target: dao, owner: dao, avatar: dao })\n *\n * await push({ dao, roles })\n * ```\n */\nexport async function push(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode },\n opts?: PushOpts\n): Promise<ApplyConstellationResult[]> {\n const api = opts?.api ?? new ApiClient({ apiKey: opts?.apiKey })\n const refs = deriveRefs(nodes)\n\n // Group nodes by constellation (multiple constellations can be applied with a single call)\n const groups = new Map<\n string,\n { meta: ConstellationMeta; nodes: ConstellationNodeInternal[] }\n >()\n\n for (const node of refs.byIdentity.keys()) {\n const meta = node._constellation\n invariant(\n meta,\n `Node \"${node.label}\" is not associated with a constellation`\n )\n const key = `${meta.workspaceId}:${meta.chain}:${meta.label}`\n let group = groups.get(key)\n if (!group) {\n group = { meta, nodes: [] }\n groups.set(key, group)\n }\n group.nodes.push(node)\n }\n\n const results: ApplyConstellationResult[] = []\n for (const { meta, nodes: groupNodes } of groups.values()) {\n const specification = groupNodes.map((n) => nodeToSpec(n, refs))\n const result = await api\n .applyConstellation(meta.workspaceId, {\n label: meta.label,\n chain: meta.chain,\n specification,\n })\n .catch((error: unknown) => {\n if (error instanceof ApiRequestError && error.issues.length > 0) {\n throw new ConstellationRejectedError(meta.label, error, (index) => {\n const node = groupNodes[index]\n\n return node == null\n ? `node ${index}`\n : `${refs.byIdentity.get(node)} (\"${node.label}\")`\n })\n }\n\n throw error\n })\n results.push(result)\n }\n\n return results\n}\n\n/**\n * Zodiac refused a constellation as it was pushed. Each issue names the node\n * it concerns by the name it was pushed under, followed by the path within it.\n */\nexport class ConstellationRejectedError extends Error {\n readonly issues: { path: string; message: string }[]\n\n constructor(\n label: string,\n error: ApiRequestError,\n nameNode: (index: number) => string\n ) {\n const issues = error.issues.map(({ path, message }) => {\n const [root, index, ...rest] = path\n\n return {\n path:\n root === 'specification' && typeof index === 'number'\n ? [nameNode(index), ...rest].join(' › ')\n : path.join(' › '),\n message,\n }\n })\n\n super(\n formatIssues(\n `Zodiac refused the constellation \"${label}\". ${error.reason}`,\n issues\n )\n )\n\n this.name = 'ConstellationRejectedError'\n this.issues = issues\n }\n}\n\n/**\n * What a ref may be spelled like. Refs come from export names and object keys,\n * so any identifier a node can be declared under has to survive the push —\n * except one opening with `$`, which is how a node references another. A list\n * of nodes refs them by position, so a plain index counts too.\n */\nconst IDENTIFIER = /^(?:[a-zA-Z_][a-zA-Z0-9_$]*|\\d+)$/\n\ntype RefsIndex = {\n byIdentity: Map<ConstellationNodeInternal, string>\n byLabel: Map<string, string>\n}\n\nfunction labelKey(node: ConstellationNodeInternal): string {\n return `${node._constellation.workspaceId}:${node.type}:${node.label}`\n}\n\nfunction deriveRefs(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode }\n): RefsIndex {\n const byIdentity = new Map<ConstellationNodeInternal, string>()\n const byLabel = new Map<string, string>()\n\n const register = (node: ConstellationNodeInternal, ref: string) => {\n invariant(\n IDENTIFIER.test(ref),\n `Invalid ref \"${ref}\": a ref is a JavaScript identifier, and cannot start with \"$\"`\n )\n byIdentity.set(node, ref)\n byLabel.set(labelKey(node), ref)\n }\n\n if (Array.isArray(nodes)) {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n invariant(\n isConstellationNode(node),\n `unexpected node input at index: ${i}`\n )\n register(node, `${i}`)\n }\n } else {\n for (const [key, node] of Object.entries(nodes)) {\n invariant(\n isConstellationNode(node),\n `unexpected node input under key: ${key}`\n )\n register(node, key)\n }\n }\n\n return { byIdentity, byLabel }\n}\n\n/**\n * What situates a node rather than configures it: which account it is, and how\n * the workspace holds it. Always sent, even for a reference that declares\n * nothing — none of it reaches the onchain diff, and leaving it out would lose\n * the label and the vault the node belongs to.\n *\n * `address` above all. It is what the pull resolved the label to, and the only\n * thing that says which account this is: a node without one is a creation, and\n * its address is derived from whoever deploys. Drop it and a second deployer\n * provisions a duplicate under the same label rather than reconfiguring the\n * account that is already there.\n */\nconst IDENTITY_FIELDS = new Set(['type', 'chain', 'address', 'label', 'vault'])\n\nfunction nodeToSpec(\n node: ConstellationNodeInternal,\n refs: RefsIndex\n): ApplyConstellationPayload['specification'][number] {\n const { id, _constellation, _declared, ...rest } = node as Record<string, any>\n // A node built by hand rather than through an accessor says nothing about\n // what it declares, so there is nothing to go on and everything travels.\n const declared = Array.isArray(_declared) ? new Set<string>(_declared) : null\n const spec: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(rest)) {\n // A referenced node carries the state `pull` read from chain. Sending it\n // back would declare it: an owner added since the pull would be removed\n // again by the deployment, without anyone having written that down. So\n // state travels only when this node actually declares it.\n if (declared != null && !IDENTITY_FIELDS.has(key) && !declared.has(key)) {\n continue\n }\n\n if (node.type === 'ROLES' && key === 'roles' && value != null) {\n spec.roles = describeRoles(\n value as Record<string, RoleDef | null>,\n node.chain,\n refs\n )\n continue\n }\n spec[key] = resolveRefs(value, refs)\n }\n\n spec.ref = refs.byIdentity.get(node)\n invariant(spec.ref != null, 'ref not found')\n\n return stringifyBigints(\n spec\n ) as ApplyConstellationPayload['specification'][number]\n}\n\n/**\n * Entries travel to the API as they were written — parameters and labels — and\n * are compiled when the constellation is deployed. Two kinds are rendered\n * here. A DeFi Kit entry becomes the annotation uri that names the chain it\n * was written for; the permissions behind it are fetched from that uri at\n * deploy. A hand-written permission is settled into what JSON can carry.\n */\nfunction describeRoles(\n roles: Record<string, RoleDef | null>,\n chain: ChainId,\n refs: RefsIndex\n): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(roles).map(([key, def]) => {\n if (def == null) {\n return [key, null] as const\n }\n\n return [\n key,\n {\n key,\n members: resolveRefs(def.members, refs),\n ...(def.policy != null && { policy: def.policy }),\n ...(def.description != null && { description: def.description }),\n permissions: def.permissions.map((entry) => {\n assertRecipientChains(entry, chain)\n\n return resolveRefs(describeEntry(entry, key, chain), refs)\n }),\n },\n ] as const\n })\n )\n}\n\nfunction describeEntry(entry: PermissionEntry, role: string, chain: ChainId) {\n if (isDeFiKitEntry(entry)) {\n return { label: entry.label, annotation: toAnnotation(entry, chain) }\n }\n\n if ('permissions' in entry) {\n return {\n ...entry,\n permissions: entry.permissions.map((permission) =>\n settlePermission(permission, role)\n ),\n }\n }\n\n return 'targetAddress' in entry ? settlePermission(entry, role) : entry\n}\n\nconst PERMISSION_KEYS = new Set([\n 'targetAddress',\n 'selector',\n 'signature',\n 'condition',\n 'send',\n 'delegatecall',\n])\n\n/**\n * A permission as JSON can carry it. A condition may still be a function,\n * which only this side can evaluate and which would be dropped on the way,\n * leaving the function it scopes open to any parameters. A signature becomes\n * the selector it names, so one that names nothing fails here rather than at\n * the API.\n *\n * A key no permission has is refused for the same reason a dropped condition\n * is: `conditon` would vanish and take the scoping with it.\n */\nfunction settlePermission(permission: ConstellationPermission, role: string) {\n const unknown = Object.keys(permission).find(\n (key) => !PERMISSION_KEYS.has(key)\n )\n\n invariant(\n unknown == null,\n `A permission of role \"${role}\" has an unknown key \"${unknown}\"`\n )\n invariant(\n !('selector' in permission && 'signature' in permission),\n `A permission of role \"${role}\" names its function by both \\`selector\\` and \\`signature\\``\n )\n\n // The target may be a node, which is resolved to a reference afterwards and\n // plays no part in what is settled here.\n const { targetAddress, ...rest } = permission\n\n try {\n return {\n ...coercePermission({ ...rest, targetAddress: PLACEHOLDER_TARGET }),\n targetAddress,\n }\n } catch (error) {\n if ('signature' in permission) {\n throw new Error(\n `A permission of role \"${role}\" names the function \"${permission.signature}\", which is not a function signature`,\n { cause: error }\n )\n }\n\n throw error\n }\n}\n\nconst PLACEHOLDER_TARGET = '0x0000000000000000000000000000000000000000'\n\nfunction resolveRefs(value: unknown, refs: RefsIndex): unknown {\n if (isConstellationNode(value)) {\n const ref = refs.byIdentity.get(value) ?? refs.byLabel.get(labelKey(value))\n invariant(\n ref != null,\n `Node \"${value.label}\" is referenced but not included in the push() call`\n )\n return `$${ref}`\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => resolveRefs(v, refs))\n }\n\n if (typeof value === 'object' && value !== null) {\n const resolved: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n resolved[k] = resolveRefs(v, refs)\n }\n return resolved\n }\n\n // API expects lowercase addresses\n if (typeof value === 'string' && /^0x[0-9a-fA-F]{40}$/.test(value)) {\n return value.toLowerCase()\n }\n\n return value\n}\n\nfunction stringifyBigints(value: unknown): unknown {\n if (typeof value === 'bigint') {\n return value.toString()\n }\n\n if (Array.isArray(value)) {\n return value.map(stringifyBigints)\n }\n\n if (typeof value === 'object' && value !== null) {\n const result: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n result[k] = stringifyBigints(v)\n }\n return result\n }\n\n return value\n}\n\nfunction isConstellationNode(\n value: unknown\n): value is ConstellationNodeInternal {\n if (typeof value === 'function' || typeof value === 'object') {\n const obj = value as any\n return (\n obj != null &&\n typeof obj.type === 'string' &&\n typeof obj.chain === 'number' &&\n typeof obj._constellation === 'object'\n )\n }\n return false\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zodiaceco/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"author": "Zodiac Labs GmbH",
|
|
5
5
|
"license": "LGPL-3.0-only",
|
|
6
6
|
"homepage": "https://github.com/gnosisguild/zodiac-sdk",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@epic-web/invariant": "1.0.0",
|
|
58
|
-
"@zodiaceco/api-types": "^1.
|
|
58
|
+
"@zodiaceco/api-types": "^1.12.0",
|
|
59
59
|
"commander": "^14.0.3",
|
|
60
60
|
"defi-kit": "^2.26.12",
|
|
61
61
|
"dotenv": "^17.4.2",
|
|
@@ -1 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"push-DdnHsLpO.mjs","names":[],"sources":["../src/api.ts","../src/push.ts"],"sourcesContent":["import type {\n ApplyConstellationPayload,\n ApplyConstellationResult,\n ResolveConstellationPayload,\n ResolveConstellationResult,\n ApiError as ApiErrorResponse,\n ApiErrorIssue,\n ListAccountsResult,\n ListUsersResult,\n} from '@zodiaceco/api-types'\nimport assert from 'assert'\nimport { UUID } from 'crypto'\n\nexport type Options = {\n workspace?: string\n apiKey?: string\n baseUrl?: string\n fetch?: typeof globalThis.fetch\n headers?: Record<string, string>\n}\n\nconst DEFAULT_API_URL = 'https://app.zodiac.eco/api/v1'\n\nexport class ApiClient {\n private apiKey: string\n private baseUrl: string\n private _fetch: typeof fetch\n private headers: Record<string, string>\n\n constructor({\n // Read `ZODIAC_API_URL`/`ZODIAC_API_KEY` lazily at construction time\n // rather than at module load: the CLI loads `.env` (and `init` writes a\n // fresh one) before the first client is created, so capturing them at\n // import time would bake in the default and ignore the user's config.\n baseUrl = process.env.ZODIAC_API_URL ?? DEFAULT_API_URL,\n fetch: customFetch = fetch,\n headers = {},\n apiKey = process.env.ZODIAC_API_KEY,\n }: Options = {}) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this._fetch = customFetch\n this.headers = headers\n\n assert(\n apiKey,\n 'No API key provided to the API client. Either pass it as the \"apiKey\" option or set the ZODIAC_API_KEY environment variable.'\n )\n\n this.apiKey = apiKey\n }\n\n protected async postJson(endpoint: string, payload: unknown) {\n const res = await this._fetch(`${this.baseUrl}/${endpoint}`, {\n method: 'POST',\n headers: {\n ...this.headers,\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n },\n body: jsonStringify(payload),\n })\n if (!res.ok) {\n await handleApiError(res)\n }\n\n return res.json()\n }\n\n protected async get(endpoint: string) {\n const res = await this._fetch(`${this.baseUrl}/${endpoint}`, {\n headers: { ...this.headers, authorization: `Bearer ${this.apiKey}` },\n })\n\n if (!res.ok) {\n await handleApiError(res)\n }\n\n return res.json()\n }\n\n /**\n * Every account the workspaces of this org hold, grouped by workspace.\n *\n * Only accounts that exist on chain are listed, so every entry carries an\n * address something was deployed to.\n */\n listAccounts(): Promise<ListAccountsResult> {\n return this.get('accounts')\n }\n\n /** Every member of this org, with the personal safes they have activated. */\n listUsers(): Promise<ListUsersResult> {\n return this.get('users')\n }\n\n /**\n * Stores a specification as the constellation's next revision, and answers\n * with a url to review and deploy it. Applying does not deploy anything, and\n * re-applying an undeployed revision replaces it rather than stacking.\n */\n applyConstellation(\n workspaceId: UUID,\n payload: ApplyConstellationPayload\n ): Promise<ApplyConstellationResult> {\n return this.postJson(\n `workspace/${workspaceId}/constellation/apply`,\n payload\n )\n }\n\n /**\n * Reports what the accounts a specification names look like on chain.\n *\n * Declarations are merged over what is read, and a node with no address is\n * resolved against the setup safe of whoever's API key asks — describing the\n * account *that* caller would deploy. Name accounts by address and declare\n * nothing to be told only what is there, the same for every key.\n */\n resolveConstellation(\n workspaceId: UUID,\n payload: ResolveConstellationPayload\n ): Promise<ResolveConstellationResult> {\n return this.postJson(\n `workspace/${workspaceId}/constellation/resolve`,\n payload\n )\n }\n}\n\nexport class ApiRequestError extends Error {\n public readonly status: number\n public readonly statusText: string\n public readonly details?: unknown\n /** What the API refused, when it could name each problem. */\n public readonly issues: ApiErrorIssue[]\n /** The API's own summary of the problem. */\n public readonly reason: string\n\n constructor(\n message: string,\n opts: {\n status: number\n statusText: string\n details?: unknown\n cause?: unknown\n }\n ) {\n const issues = readIssues(opts.details)\n\n super(\n issues.length > 0\n ? formatIssues(message, issues)\n : ApiRequestError.composeMessage(message, opts.details)\n )\n this.name = 'ApiRequestError'\n this.status = opts.status\n this.statusText = opts.statusText\n this.details = opts.details\n this.issues = issues\n this.reason = message\n if (opts.cause !== undefined) {\n ;(this as any).cause = opts.cause\n }\n }\n\n private static composeMessage(message: string, details?: unknown) {\n if (details == null) return message\n let detailsString: string\n try {\n detailsString =\n typeof details === 'string' ? details : jsonStringify(details, 2)\n } catch (_err) {\n detailsString = String(details)\n }\n return `${message}\\nDetails: ${detailsString}`\n }\n\n toString() {\n return `${this.name}: ${this.message}`\n }\n}\n\nasync function handleApiError(response: Response): Promise<never> {\n const contentType = response.headers.get('content-type')\n if (contentType?.includes('application/json')) {\n const errorData = (await response.json()) as ApiErrorResponse\n let error: ApiRequestError\n try {\n error = new ApiRequestError(errorData.error.message, {\n status: response.status,\n statusText: response.statusText,\n details: errorData.error.details,\n })\n } catch (jsonShapeError) {\n error = new ApiRequestError(\n `Failed parsing error response: ${jsonShapeError}`,\n {\n status: response.status,\n statusText: response.statusText,\n details: errorData,\n }\n )\n }\n throw error\n } else {\n throw new ApiRequestError(\n `${response.status} ${response.statusText}: ${response.url}`,\n {\n status: response.status,\n statusText: response.statusText,\n }\n )\n }\n}\n\n/**\n * The issues of an error response, each naming where in the request it is.\n * Anything that does not look like one is left to the raw details.\n */\nconst readIssues = (details: unknown): ApiErrorIssue[] => {\n if (\n typeof details !== 'object' ||\n details == null ||\n !('issues' in details)\n ) {\n return []\n }\n\n const { issues } = details\n\n if (!Array.isArray(issues)) {\n return []\n }\n\n return issues.flatMap((issue): ApiErrorIssue[] =>\n typeof issue === 'object' &&\n issue != null &&\n Array.isArray(issue.path) &&\n typeof issue.message === 'string'\n ? [{ path: issue.path, message: issue.message }]\n : []\n )\n}\n\n/** A headline and one line per issue, the way a terminal reads best. */\nexport const formatIssues = (\n headline: string,\n issues: { path: string; message: string }[] | ApiErrorIssue[]\n) =>\n [\n `${headline}:`,\n ...issues.map(({ path, message }) => {\n const where = Array.isArray(path) ? path.join(' › ') : path\n\n return where === '' ? ` • ${message}` : ` • ${where}: ${message}`\n }),\n ].join('\\n')\n\n/** JSON.stringify with bigint support */\nconst jsonStringify = (value: unknown, indent?: number) =>\n JSON.stringify(\n value,\n (_, value) => {\n if (typeof value === 'bigint') {\n return value.toString()\n }\n\n return value\n },\n indent\n )\n","import type {\n ApplyConstellationPayload,\n ApplyConstellationResult,\n ChainId,\n} from '@zodiaceco/api-types'\nimport { invariant } from '@epic-web/invariant'\nimport {\n assertRecipientChains,\n isDeFiKitEntry,\n toAnnotation,\n} from './permissionEntries'\nimport { ApiClient, ApiRequestError, formatIssues } from './api'\nimport type {\n ConstellationMeta,\n ConstellationNode,\n ConstellationNodeInternal,\n RoleDef,\n} from './constellation'\n\ntype PushOpts = {\n /** API client instance. Defaults to a client configured from environment variables. */\n api?: ApiClient\n /** API key for the default client, instead of `ZODIAC_API_KEY`. */\n apiKey?: string\n}\n\n/**\n * Applies the given nodes as the next revision of the constellations they\n * belong to, and answers with a url per constellation to review and deploy.\n *\n * Each node travels with what it was actually given, never with the onchain\n * state a `pull` filled it with for reading — so a node passed as a bare\n * reference declares nothing and leaves the account it names alone. Nodes\n * naming each other travel as references and are substituted for addresses\n * once those are derived at deploy.\n *\n * Passing an object names each node by its key; passing an array names them by\n * position. Either way the name is the node's `ref`, which is how the app\n * follows a node from one revision to the next.\n *\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n * const dao = eth.safe['GG DAO']\n * const roles = eth.roles['New Roles']({ nonce: 0n, target: dao, owner: dao, avatar: dao })\n *\n * await push({ dao, roles })\n * ```\n */\nexport async function push(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode },\n opts?: PushOpts\n): Promise<ApplyConstellationResult[]> {\n const api = opts?.api ?? new ApiClient({ apiKey: opts?.apiKey })\n const refs = deriveRefs(nodes)\n\n // Group nodes by constellation (multiple constellations can be applied with a single call)\n const groups = new Map<\n string,\n { meta: ConstellationMeta; nodes: ConstellationNodeInternal[] }\n >()\n\n for (const node of refs.byIdentity.keys()) {\n const meta = node._constellation\n invariant(\n meta,\n `Node \"${node.label}\" is not associated with a constellation`\n )\n const key = `${meta.workspaceId}:${meta.chain}:${meta.label}`\n let group = groups.get(key)\n if (!group) {\n group = { meta, nodes: [] }\n groups.set(key, group)\n }\n group.nodes.push(node)\n }\n\n const results: ApplyConstellationResult[] = []\n for (const { meta, nodes: groupNodes } of groups.values()) {\n const specification = groupNodes.map((n) => nodeToSpec(n, refs))\n const result = await api\n .applyConstellation(meta.workspaceId, {\n label: meta.label,\n chain: meta.chain,\n specification,\n })\n .catch((error: unknown) => {\n if (error instanceof ApiRequestError && error.issues.length > 0) {\n throw new ConstellationRejectedError(meta.label, error, (index) => {\n const node = groupNodes[index]\n\n return node == null\n ? `node ${index}`\n : `${refs.byIdentity.get(node)} (\"${node.label}\")`\n })\n }\n\n throw error\n })\n results.push(result)\n }\n\n return results\n}\n\n/**\n * Zodiac refused a constellation as it was pushed. Each issue names the node\n * it concerns by the name it was pushed under, followed by the path within it.\n */\nexport class ConstellationRejectedError extends Error {\n readonly issues: { path: string; message: string }[]\n\n constructor(\n label: string,\n error: ApiRequestError,\n nameNode: (index: number) => string\n ) {\n const issues = error.issues.map(({ path, message }) => {\n const [root, index, ...rest] = path\n\n return {\n path:\n root === 'specification' && typeof index === 'number'\n ? [nameNode(index), ...rest].join(' › ')\n : path.join(' › '),\n message,\n }\n })\n\n super(\n formatIssues(\n `Zodiac refused the constellation \"${label}\". ${error.reason}`,\n issues\n )\n )\n\n this.name = 'ConstellationRejectedError'\n this.issues = issues\n }\n}\n\n/**\n * What a ref may be spelled like. Refs come from export names and object keys,\n * so any identifier a node can be declared under has to survive the push —\n * except one opening with `$`, which is how a node references another. A list\n * of nodes refs them by position, so a plain index counts too.\n */\nconst IDENTIFIER = /^(?:[a-zA-Z_][a-zA-Z0-9_$]*|\\d+)$/\n\ntype RefsIndex = {\n byIdentity: Map<ConstellationNodeInternal, string>\n byLabel: Map<string, string>\n}\n\nfunction labelKey(node: ConstellationNodeInternal): string {\n return `${node._constellation.workspaceId}:${node.type}:${node.label}`\n}\n\nfunction deriveRefs(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode }\n): RefsIndex {\n const byIdentity = new Map<ConstellationNodeInternal, string>()\n const byLabel = new Map<string, string>()\n\n const register = (node: ConstellationNodeInternal, ref: string) => {\n invariant(\n IDENTIFIER.test(ref),\n `Invalid ref \"${ref}\": a ref is a JavaScript identifier, and cannot start with \"$\"`\n )\n byIdentity.set(node, ref)\n byLabel.set(labelKey(node), ref)\n }\n\n if (Array.isArray(nodes)) {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n invariant(\n isConstellationNode(node),\n `unexpected node input at index: ${i}`\n )\n register(node, `${i}`)\n }\n } else {\n for (const [key, node] of Object.entries(nodes)) {\n invariant(\n isConstellationNode(node),\n `unexpected node input under key: ${key}`\n )\n register(node, key)\n }\n }\n\n return { byIdentity, byLabel }\n}\n\n/**\n * What situates a node rather than configures it: which account it is, and how\n * the workspace holds it. Always sent, even for a reference that declares\n * nothing — none of it reaches the onchain diff, and leaving it out would lose\n * the label and the vault the node belongs to.\n *\n * `address` above all. It is what the pull resolved the label to, and the only\n * thing that says which account this is: a node without one is a creation, and\n * its address is derived from whoever deploys. Drop it and a second deployer\n * provisions a duplicate under the same label rather than reconfiguring the\n * account that is already there.\n */\nconst IDENTITY_FIELDS = new Set(['type', 'chain', 'address', 'label', 'vault'])\n\nfunction nodeToSpec(\n node: ConstellationNodeInternal,\n refs: RefsIndex\n): ApplyConstellationPayload['specification'][number] {\n const { id, _constellation, _declared, ...rest } = node as Record<string, any>\n // A node built by hand rather than through an accessor says nothing about\n // what it declares, so there is nothing to go on and everything travels.\n const declared = Array.isArray(_declared) ? new Set<string>(_declared) : null\n const spec: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(rest)) {\n // A referenced node carries the state `pull` read from chain. Sending it\n // back would declare it: an owner added since the pull would be removed\n // again by the deployment, without anyone having written that down. So\n // state travels only when this node actually declares it.\n if (declared != null && !IDENTITY_FIELDS.has(key) && !declared.has(key)) {\n continue\n }\n\n if (node.type === 'ROLES' && key === 'roles' && value != null) {\n spec.roles = describeRoles(\n value as Record<string, RoleDef | null>,\n node.chain,\n refs\n )\n continue\n }\n spec[key] = resolveRefs(value, refs)\n }\n\n spec.ref = refs.byIdentity.get(node)\n invariant(spec.ref != null, 'ref not found')\n\n return stringifyBigints(\n spec\n ) as ApplyConstellationPayload['specification'][number]\n}\n\n/**\n * Entries travel to the API as they were written — parameters and labels — and\n * are compiled when the constellation is deployed. Only a DeFi Kit entry is\n * rendered here, into the annotation uri that names the chain it was written\n * for; the permissions behind it are fetched from that uri at deploy.\n */\nfunction describeRoles(\n roles: Record<string, RoleDef | null>,\n chain: ChainId,\n refs: RefsIndex\n): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(roles).map(([key, def]) => {\n if (def == null) {\n return [key, null] as const\n }\n\n return [\n key,\n {\n key,\n members: resolveRefs(def.members, refs),\n ...(def.policy != null && { policy: def.policy }),\n ...(def.description != null && { description: def.description }),\n permissions: def.permissions.map((entry) => {\n assertRecipientChains(entry, chain)\n\n return resolveRefs(\n isDeFiKitEntry(entry)\n ? { label: entry.label, annotation: toAnnotation(entry, chain) }\n : entry,\n refs\n )\n }),\n },\n ] as const\n })\n )\n}\n\nfunction resolveRefs(value: unknown, refs: RefsIndex): unknown {\n if (isConstellationNode(value)) {\n const ref = refs.byIdentity.get(value) ?? refs.byLabel.get(labelKey(value))\n invariant(\n ref != null,\n `Node \"${value.label}\" is referenced but not included in the push() call`\n )\n return `$${ref}`\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => resolveRefs(v, refs))\n }\n\n if (typeof value === 'object' && value !== null) {\n const resolved: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n resolved[k] = resolveRefs(v, refs)\n }\n return resolved\n }\n\n // API expects lowercase addresses\n if (typeof value === 'string' && /^0x[0-9a-fA-F]{40}$/.test(value)) {\n return value.toLowerCase()\n }\n\n return value\n}\n\nfunction stringifyBigints(value: unknown): unknown {\n if (typeof value === 'bigint') {\n return value.toString()\n }\n\n if (Array.isArray(value)) {\n return value.map(stringifyBigints)\n }\n\n if (typeof value === 'object' && value !== null) {\n const result: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n result[k] = stringifyBigints(v)\n }\n return result\n }\n\n return value\n}\n\nfunction isConstellationNode(\n value: unknown\n): value is ConstellationNodeInternal {\n if (typeof value === 'function' || typeof value === 'object') {\n const obj = value as any\n return (\n obj != null &&\n typeof obj.type === 'string' &&\n typeof obj.chain === 'number' &&\n typeof obj._constellation === 'object'\n )\n }\n return false\n}\n"],"mappings":"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"}
|