@zodiaceco/sdk 1.13.2 → 2.1.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 +85 -1
- package/dist/actions-Dn5NC3oq.d.mts +352 -0
- package/dist/actions.d.mts +2 -0
- package/dist/actions.mjs +92 -0
- package/dist/actions.mjs.map +1 -0
- package/dist/allow/index.d.mts +1 -1
- package/dist/allow/index.mjs +2 -2
- package/dist/{allow-CzPpzclq.mjs → allow-CrDzDtxx.mjs} +16 -5
- package/dist/allow-CrDzDtxx.mjs.map +1 -0
- package/dist/cli/config.mjs +1 -1
- package/dist/cli.mjs +44 -12
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-D5ArAp07.mjs → config-D0FqysME.mjs} +1 -1
- package/dist/{config-D5ArAp07.mjs.map → config-D0FqysME.mjs.map} +1 -1
- package/dist/{index-DoZbhpS6.d.mts → index-CnH26Z_A.d.mts} +1 -1
- package/dist/index.d.mts +5 -199
- package/dist/index.mjs +21 -11
- package/dist/index.mjs.map +1 -1
- package/dist/{networks-BTW1qAAa.mjs → networks-B0H4Uoyh.mjs} +1 -1
- package/dist/{networks-BTW1qAAa.mjs.map → networks-B0H4Uoyh.mjs.map} +1 -1
- package/dist/permissionEntries-D3fSVhwO.mjs +61 -0
- package/dist/permissionEntries-D3fSVhwO.mjs.map +1 -0
- package/package.json +7 -2
- package/dist/allow-CzPpzclq.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { n as resolveZodiacDir, t as ApiClient } from "./api-C49DoaPC.mjs";
|
|
2
|
-
import { n as
|
|
2
|
+
import { i as toAnnotation, n as isDeFiKitEntry, t as assertRecipientChains } from "./permissionEntries-D3fSVhwO.mjs";
|
|
3
|
+
import { n as EVERYTHING, t as buildAllowKit } from "./allow-CrDzDtxx.mjs";
|
|
3
4
|
import { createRequire } from "module";
|
|
4
5
|
import { invariant } from "@epic-web/invariant";
|
|
5
|
-
import { c, encodeKey, forAll
|
|
6
|
+
import { c, encodeKey, forAll } from "zodiac-roles-sdk";
|
|
6
7
|
//#region src/constellation.ts
|
|
7
8
|
function loadCodegen() {
|
|
8
9
|
return createRequire(import.meta.url)(resolveZodiacDir());
|
|
@@ -115,7 +116,7 @@ async function push(nodes, opts) {
|
|
|
115
116
|
}
|
|
116
117
|
const results = [];
|
|
117
118
|
for (const { meta, nodes: groupNodes } of groups.values()) {
|
|
118
|
-
const specification =
|
|
119
|
+
const specification = groupNodes.map((n) => nodeToSpec(n, refs));
|
|
119
120
|
const result = await api.applyConstellation(meta.workspaceId, {
|
|
120
121
|
label: meta.label,
|
|
121
122
|
chain: meta.chain,
|
|
@@ -149,12 +150,12 @@ function deriveRefs(nodes) {
|
|
|
149
150
|
byLabel
|
|
150
151
|
};
|
|
151
152
|
}
|
|
152
|
-
|
|
153
|
+
function nodeToSpec(node, refs) {
|
|
153
154
|
const { id, _constellation, ...rest } = node;
|
|
154
155
|
const spec = {};
|
|
155
156
|
for (const [key, value] of Object.entries(rest)) {
|
|
156
157
|
if (node.type === "ROLES" && key === "roles" && value != null) {
|
|
157
|
-
spec.roles =
|
|
158
|
+
spec.roles = describeRoles(value, node.chain, refs);
|
|
158
159
|
continue;
|
|
159
160
|
}
|
|
160
161
|
spec[key] = resolveRefs(value, refs);
|
|
@@ -163,18 +164,27 @@ async function nodeToSpec(node, refs) {
|
|
|
163
164
|
invariant(spec.ref != null, "ref not found");
|
|
164
165
|
return stringifyBigints(spec);
|
|
165
166
|
}
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Entries travel to the API as they were written — parameters and labels — and
|
|
169
|
+
* are compiled when the constellation is deployed. Only a DeFi Kit entry is
|
|
170
|
+
* rendered here, into the annotation uri that names the chain it was written
|
|
171
|
+
* for; the permissions behind it are fetched from that uri at deploy.
|
|
172
|
+
*/
|
|
173
|
+
function describeRoles(roles, chain, refs) {
|
|
174
|
+
return Object.fromEntries(Object.entries(roles).map(([key, def]) => {
|
|
168
175
|
if (def == null) return [key, null];
|
|
169
|
-
const { targets, annotations } = processPermissions(await Promise.all(def.permissions.map((p) => Promise.resolve(p))));
|
|
170
176
|
return [key, {
|
|
171
177
|
key,
|
|
172
178
|
members: resolveRefs(def.members, refs),
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
permissions: def.permissions.map((entry) => {
|
|
180
|
+
assertRecipientChains(entry, chain);
|
|
181
|
+
return resolveRefs(isDeFiKitEntry(entry) ? {
|
|
182
|
+
label: entry.label,
|
|
183
|
+
annotation: toAnnotation(entry, chain)
|
|
184
|
+
} : entry, refs);
|
|
185
|
+
})
|
|
175
186
|
}];
|
|
176
187
|
}));
|
|
177
|
-
return Object.fromEntries(entries);
|
|
178
188
|
}
|
|
179
189
|
function resolveRefs(value, refs) {
|
|
180
190
|
if (isConstellationNode(value)) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/constellation.ts","../src/push.ts"],"sourcesContent":["/// <reference path=\"./zodiac-os-codegen.d.ts\" />\nimport type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { AllowanceSpec } from './types'\nimport type { Annotation, Permission, PermissionSet } from 'zodiac-roles-sdk'\nimport { createRequire } from 'module'\nimport { resolveZodiacDir } from './paths'\nimport { UUID } from 'crypto'\n\n/**\n * A role definition keyed by role name. Permissions are expanded into\n * `{ targets, annotations }` via `processPermissions` at `push()` time.\n */\nexport type RoleDef = {\n members: readonly AddressOrRef[]\n permissions: readonly (Permission | PermissionSet | Promise<PermissionSet>)[]\n annotations?: readonly Annotation[]\n}\n\ntype User = {\n id: UUID\n fullName: string\n personalSafes: Record<\n number,\n { address: Lowercase<Address>; active: boolean }\n >\n}\n\ntype Account = {\n id: UUID\n label: string\n address: Lowercase<Address>\n chain: ChainId\n /** True for accounts promoted to a workspace vault. */\n vault: boolean\n}\n\n/**\n * Accounts grouped by node type within a workspace. Per-type maps keep\n * bracket-accessor namespaces separate: a SAFE and a ROLES mod sharing a\n * label don't collide, and `eth.safe[...]` IntelliSense doesn't suggest\n * ROLES mod labels (and vice versa).\n */\ntype WorkspaceAccounts = {\n workspaceId: UUID\n workspaceName: string\n safes: Readonly<Record<string, Account>>\n rolesMods: Readonly<Record<string, Account>>\n delays: Readonly<Record<string, Account>>\n}\n\n/** Shape of the codegen data produced by `zodiac pull-org`. */\nexport type CodegenData = {\n users: Readonly<Record<string, User>>\n accounts: Readonly<Record<string, WorkspaceAccounts>>\n}\n\n// If `pull-org` has been run, the consumer's `.zodiac/index.d.ts` augments\n// `ZodiacGeneratedCodegen` with literal `users`/`accounts` shapes. Otherwise\n// the interface is empty and we fall back to the wide `CodegenData`.\ntype GeneratedCodegen = ZodiacGeneratedCodegen extends CodegenData\n ? ZodiacGeneratedCodegen\n : CodegenData\n\ntype ConstellationOpts<C extends CodegenData> = {\n /** Workspace to scope accounts and roles to. */\n workspace: keyof C['accounts'] & string\n /** Human-readable label for this constellation. */\n label: string\n /** Target chain for all nodes in this constellation. */\n chain: ChainId\n}\n\ntype ConstellationInternalOpts<C extends CodegenData> = {\n /** Injected codegen data (used for testing). */\n codegen?: C\n}\n\ntype Prettify<T> = { readonly [K in keyof T]: T[K] } & {}\n\ntype SafeEntries<\n C extends CodegenData,\n W extends keyof C['accounts'],\n> = C['accounts'][W]['safes']\n\ntype RolesEntries<\n C extends CodegenData,\n W extends keyof C['accounts'],\n> = C['accounts'][W]['rolesMods']\n\ntype NodeType = 'SAFE' | 'ROLES' | 'DELAY'\n\n/** A reference to a node used in `owners`, `modules`, `target`, etc. */\ntype NodeRef = Readonly<{ type: NodeType; label: string; chain: ChainId }>\n\n/** A blockchain address (checksummed or lowercase) or a reference to another\n * node in the constellation. Values are normalized to lowercase before being\n * sent to the API. */\ntype AddressOrRef = Address | NodeRef\n\ntype NodeBase = Readonly<{\n /** Human-readable identifier, unique within the constellation. */\n label: string\n /** Chain the node is deployed on. */\n chain: ChainId\n /** Set for existing nodes (from codegen or bound by address), absent for new\n * nodes. Accepts checksummed or lowercase; normalized to lowercase on push. */\n address?: Address\n /** Deployment nonce — required for new nodes, optional for existing. */\n nonce?: bigint\n}>\n\n/** A safe node spec — existing vault ref or new safe with required config. */\nexport type SafeNode = NodeBase &\n Readonly<{\n /** Discriminator identifying this node as a Safe. */\n type: 'SAFE'\n /** Number of owner signatures required to execute a transaction. */\n threshold: number\n /** Safe owner addresses or node references. */\n owners: readonly (string | NodeRef)[]\n /** Module addresses or node references enabled on the safe. */\n modules?: readonly (string | NodeRef)[]\n /** Whether this safe shall appear as a vault in the workspace. @default false */\n vault?: boolean\n }>\n\n/** A roles modifier node spec — existing vault ref or new roles with modifier config. */\nexport type RolesNode = NodeBase &\n Readonly<{\n /** Discriminator identifying this node as a Roles modifier. */\n type: 'ROLES'\n /** The safe that this roles modifier controls. */\n target?: AddressOrRef\n /** The account that is allowed to update the configuration of the Roles mod. */\n owner?: AddressOrRef\n /** The account that calls will be executed from. */\n avatar?: AddressOrRef\n /** MultiSend contract addresses for batched transactions. */\n multisend?: readonly Address[]\n /** Role definitions configured on this modifier. Pass `null` for a key to\n * clear that role; unmentioned roles are left untouched. */\n roles?: Record<string, RoleDef | null>\n /** Spending allowances configured on this modifier, keyed by name. Pass\n * `null` for a key to clear that allowance; unmentioned allowances are\n * left untouched. */\n allowances?: Record<string, AllowanceSpec | null>\n }>\n\n/** Any complete node that can be passed to `push()`. */\nexport type ConstellationNode = SafeNode | RolesNode\nexport type ConstellationNodeInternal = ConstellationNode & {\n _constellation: ConstellationMeta\n}\n\ntype NewSafeProps = {\n /** Deployment nonce for CREATE2 address derivation. */\n nonce: bigint\n /** Number of owner signatures required to execute a transaction. */\n threshold: number\n /** Safe owner addresses or node references. */\n owners: readonly AddressOrRef[]\n /** Module addresses or node references to enable on the safe. */\n modules?: readonly AddressOrRef[]\n /** Whether this safe is a workspace vault. @default false */\n vault?: boolean\n}\n\n/** Configuration shared by both ways of declaring a roles modifier. */\ntype RolesConfig = {\n /** The safe that this roles modifier controls. Defaults to the new safe with the same label, when one exists. */\n target?: AddressOrRef\n /** The account that calls will be executed from. Defaults to `target` value */\n avatar?: AddressOrRef\n /** The account that is allowed to update the configuration of the Roles Mod. Defaults to `target` value */\n owner?: AddressOrRef\n /** MultiSend contract addresses for batched transactions. Defaults to `['0x38869bf66a61cf6bdb996a6ae40d5853fd43b526', '0x9641d764fc13c8b624c04430c7356c1c7c8102e2']` */\n multisend?: readonly Address[]\n /** Role definitions to configure on this modifier. Pass `null` for a key to\n * clear that role; unmentioned roles are left untouched. */\n roles?: Record<string, RoleDef | null>\n /** Spending allowances to configure on this modifier, keyed by name. Pass\n * `null` for a key to clear that allowance; unmentioned allowances are\n * left untouched. */\n allowances?: Record<string, AllowanceSpec | null>\n}\n\n/** Declare a brand-new roles modifier (address derived via CREATE2 from `nonce`). */\ntype NewRolesByNonce = RolesConfig & {\n /** Deployment nonce for CREATE2 address derivation. */\n nonce: bigint\n}\n\n/** Bind to a roles modifier already deployed on-chain at a known address, to\n * reconfigure its roles/allowances. Use this for mods not (yet) imported into\n * the workspace — pass the address instead of a deployment `nonce`. */\ntype ExistingRolesByAddress = RolesConfig & {\n /** Address of the existing Roles mod to bind to and reconfigure. */\n address: Address\n}\n\ntype NewRolesProps = NewRolesByNonce | ExistingRolesByAddress\n\ntype ExistingNodeAccessor<\n Type extends string,\n K extends string,\n E,\n Ch extends ChainId,\n NP extends Record<string, any>,\n> = Readonly<Prettify<E & { type: Type; label: K; chain: Ch }>> &\n (<\n O extends {\n [P in Exclude<keyof E & string, 'id' | 'label'>]?: any\n } & Partial<NP> = {},\n >(\n overrides?: {\n [P in Exclude<keyof E & string, 'id' | 'label'>]?: any\n } & Partial<NP> &\n O\n ) => Readonly<\n Prettify<\n Omit<E, keyof O> & O & Partial<NP> & { type: Type; label: K; chain: Ch }\n >\n >)\n\ntype NewNodeAccessor<\n Type extends string,\n Ch extends ChainId,\n NP extends Record<string, any>,\n> = Readonly<Prettify<{ type: Type; label: string; chain: Ch }>> &\n (<P extends NP>(\n props: P\n ) => Readonly<Prettify<P & { type: Type; label: string; chain: Ch }>>)\n\ntype EntityAccessor<\n Type extends string,\n Entries extends Record<string, any>,\n Ch extends ChainId = ChainId,\n NP extends Record<string, any> = Record<string, any>,\n> = {\n readonly [K in keyof Entries & string]: ExistingNodeAccessor<\n Type,\n K,\n Entries[K],\n Ch,\n NP\n >\n} & {\n readonly [key: string]: NewNodeAccessor<Type, Ch, NP>\n}\n\ntype UserAccessor<C extends CodegenData, Ch extends number> = {\n readonly [K in keyof C['users'] &\n string]: C['users'][K]['personalSafes'][Ch]['address']\n}\n\ntype ConstellationResult<\n C extends CodegenData,\n W extends keyof C['accounts'] = keyof C['accounts'],\n Ch extends ChainId = ChainId,\n> = {\n /** Access existing safes by label or create new ones with a new label.\n * Only SAFE-typed accounts are suggested in IntelliSense. */\n safe: EntityAccessor<'SAFE', SafeEntries<C, W>, Ch, NewSafeProps>\n /** Access existing roles modifiers by label or create new ones with a\n * new label. Only ROLES-typed accounts are suggested in IntelliSense. */\n roles: EntityAccessor<'ROLES', RolesEntries<C, W>, Ch, NewRolesProps>\n /** Resolve a user's personal safe address on the constellation's chain. */\n user: UserAccessor<C, Ch>\n}\n\n/** @internal */\nexport type ConstellationMeta = {\n label: string\n chain: ChainId\n workspaceId: UUID\n}\n\nfunction loadCodegen(): CodegenData {\n const require = createRequire(import.meta.url)\n return require(resolveZodiacDir()) as CodegenData\n}\n\n/**\n * Creates a constellation scoped to a workspace and chain.\n *\n * Use bracket access to reference existing accounts (vaults and other\n * applied constellation nodes) or define new ones:\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n *\n * const dao = eth.safe['GG DAO'] // existing account ref\n * const roles = eth.roles['GG DAO'] // existing roles ref\n * const newSafe = eth.safe['New Safe']({ nonce: 0n, threshold: 2, owners: [...], modules: [...] })\n * ```\n */\nexport function constellation<\n const C extends CodegenData = GeneratedCodegen,\n const W extends keyof C['accounts'] & string = keyof C['accounts'] & string,\n const Ch extends ChainId = ChainId,\n>(\n opts: ConstellationOpts<C> & { workspace: W; chain: Ch },\n internal?: ConstellationInternalOpts<C>\n): ConstellationResult<C, W, Ch> {\n const codegen: CodegenData = internal?.codegen ?? loadCodegen()\n\n const ws = codegen.accounts[opts.workspace]\n const safesByLabel: Record<string, Account> = {}\n const rolesByLabel: Record<string, Account> = {}\n if (ws) {\n for (const [label, account] of Object.entries(ws.safes)) {\n safesByLabel[label] = account\n }\n for (const [label, account] of Object.entries(ws.rolesMods)) {\n rolesByLabel[label] = account\n }\n }\n\n const meta: ConstellationMeta = {\n label: opts.label,\n chain: opts.chain,\n workspaceId: (ws?.workspaceId ?? '') as UUID,\n }\n\n function makeNodeRef(\n data: Record<string, any>\n ): Readonly<Record<string, any>> {\n return Object.freeze({\n ...data,\n chain: opts.chain,\n _constellation: meta,\n })\n }\n\n function entityAccessor(\n registry: Record<string, Record<string, any>>,\n type: string\n ) {\n const cache = new Map<string, Record<string, any>>()\n return new Proxy({} as Record<string, any>, {\n get(_target: any, name: string) {\n if (typeof name !== 'string') return undefined\n const cached = cache.get(name)\n if (cached) return cached\n const existing = registry[name]\n // Bracket-access keys in the generated codegen carry a\n // ` (0xChecksummed…)` suffix when multiple workspace accounts share\n // a label. The label sent in the push spec should be the clean\n // original, so prefer `existing.label` when it's available.\n const specLabel: string = existing?.label ?? name\n const fn = (overrides?: Record<string, any>) =>\n makeNodeRef({\n type,\n ...(existing || {}),\n ...overrides,\n label: specLabel,\n })\n Object.assign(fn, {\n type,\n ...(existing || {}),\n label: specLabel,\n chain: opts.chain,\n _constellation: meta,\n })\n cache.set(name, fn)\n return fn\n },\n })\n }\n\n function userAccessor() {\n return new Proxy(\n {},\n {\n get(_target: any, name: string) {\n const user = codegen.users[name]\n if (!user) throw new Error(`Unknown user: ${name}`)\n const personalSafe = user.personalSafes[opts.chain]\n if (!personalSafe) {\n throw new Error(\n `User ${name} has no personal safe on chain ${opts.chain}`\n )\n }\n return personalSafe.address\n },\n }\n )\n }\n\n const safe = entityAccessor(safesByLabel, 'SAFE')\n const roles = entityAccessor(rolesByLabel, 'ROLES')\n\n return {\n safe,\n roles,\n user: userAccessor(),\n } as ConstellationResult<C, W, Ch>\n}\n","import type {\n ApplyConstellationPayload,\n ApplyConstellationResult,\n} from '@zodiaceco/api-types'\nimport { invariant } from '@epic-web/invariant'\nimport { processPermissions } from 'zodiac-roles-sdk'\nimport type { PermissionSet } from 'zodiac-roles-sdk'\nimport { ApiClient } 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}\n\n/**\n * Resolves node references and pushes the constellation specification to the API.\n *\n *\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n * const dao = eth.safe['GG DAO']\n * const roles = eth.roles['New Roles']({ nonce: 0n, target: dao, owner: dao, avatar: dao })\n *\n * await 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()\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 = await Promise.all(\n groupNodes.map((n) => nodeToSpec(n, refs))\n )\n const result = await api.applyConstellation(meta.workspaceId, {\n label: meta.label,\n chain: meta.chain,\n specification,\n })\n results.push(result)\n }\n\n return results\n}\n\ntype RefsIndex = {\n byIdentity: Map<ConstellationNodeInternal, string>\n byLabel: Map<string, string>\n}\n\nfunction labelKey(node: ConstellationNodeInternal): string {\n return `${node._constellation.workspaceId}:${node.type}:${node.label}`\n}\n\nfunction deriveRefs(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode }\n): RefsIndex {\n const byIdentity = new Map<ConstellationNodeInternal, string>()\n const byLabel = new Map<string, string>()\n\n const register = (node: ConstellationNodeInternal, ref: string) => {\n byIdentity.set(node, ref)\n byLabel.set(labelKey(node), ref)\n }\n\n if (Array.isArray(nodes)) {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n invariant(\n isConstellationNode(node),\n `unexpected node input at index: ${i}`\n )\n register(node, `${i}`)\n }\n } else {\n for (const [key, node] of Object.entries(nodes)) {\n invariant(\n isConstellationNode(node),\n `unexpected node input under key: ${key}`\n )\n register(node, key)\n }\n }\n\n return { byIdentity, byLabel }\n}\n\nasync function nodeToSpec(\n node: ConstellationNodeInternal,\n refs: RefsIndex\n): Promise<ApplyConstellationPayload['specification'][number]> {\n const { id, _constellation, ...rest } = node as Record<string, any>\n const spec: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(rest)) {\n if (node.type === 'ROLES' && key === 'roles' && value != null) {\n spec.roles = await expandRoles(\n value as Record<string, RoleDef | null>,\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\nasync function expandRoles(\n roles: Record<string, RoleDef | null>,\n refs: RefsIndex\n): Promise<Record<string, unknown>> {\n const entries = await Promise.all(\n Object.entries(roles).map(async ([key, def]) => {\n if (def == null) {\n return [key, null] as const\n }\n const resolvedPermissions = (await Promise.all(\n def.permissions.map((p) => Promise.resolve(p))\n )) as Parameters<typeof processPermissions>[0][number][]\n const { targets, annotations } = processPermissions(resolvedPermissions)\n return [\n key,\n {\n key,\n members: resolveRefs(def.members, refs),\n targets,\n annotations: [...(def.annotations ?? []), ...annotations],\n },\n ] as const\n })\n )\n return Object.fromEntries(entries)\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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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/constellation.ts","../src/push.ts"],"sourcesContent":["/// <reference path=\"./zodiac-os-codegen.d.ts\" />\nimport type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { PermissionEntry } from './actions'\nimport type { AllowanceSpec } from './types'\nimport { createRequire } from 'module'\nimport { resolveZodiacDir } from './paths'\nimport { UUID } from 'crypto'\n\n/**\n * A role definition keyed by role name. Entries describe what the role may do —\n * parameters and labels, never compiled permissions. They are expanded into\n * `{ targets, annotations }` when the constellation is deployed, so that a\n * stored revision always compiles through the current compilers.\n */\nexport type RoleDef = {\n members: readonly AddressOrRef[]\n permissions: readonly PermissionEntry[]\n}\n\ntype User = {\n id: UUID\n fullName: string\n personalSafes: Record<\n number,\n { address: Lowercase<Address>; active: boolean }\n >\n}\n\ntype Account = {\n id: UUID\n label: string\n address: Lowercase<Address>\n chain: ChainId\n /** True for accounts promoted to a workspace vault. */\n vault: boolean\n}\n\n/**\n * Accounts grouped by node type within a workspace. Per-type maps keep\n * bracket-accessor namespaces separate: a SAFE and a ROLES mod sharing a\n * label don't collide, and `eth.safe[...]` IntelliSense doesn't suggest\n * ROLES mod labels (and vice versa).\n */\ntype WorkspaceAccounts = {\n workspaceId: UUID\n workspaceName: string\n safes: Readonly<Record<string, Account>>\n rolesMods: Readonly<Record<string, Account>>\n delays: Readonly<Record<string, Account>>\n}\n\n/** Shape of the codegen data produced by `zodiac pull-org`. */\nexport type CodegenData = {\n users: Readonly<Record<string, User>>\n accounts: Readonly<Record<string, WorkspaceAccounts>>\n}\n\n// If `pull-org` has been run, the consumer's `.zodiac/index.d.ts` augments\n// `ZodiacGeneratedCodegen` with literal `users`/`accounts` shapes. Otherwise\n// the interface is empty and we fall back to the wide `CodegenData`.\ntype GeneratedCodegen = ZodiacGeneratedCodegen extends CodegenData\n ? ZodiacGeneratedCodegen\n : CodegenData\n\ntype ConstellationOpts<C extends CodegenData> = {\n /** Workspace to scope accounts and roles to. */\n workspace: keyof C['accounts'] & string\n /** Human-readable label for this constellation. */\n label: string\n /** Target chain for all nodes in this constellation. */\n chain: ChainId\n}\n\ntype ConstellationInternalOpts<C extends CodegenData> = {\n /** Injected codegen data (used for testing). */\n codegen?: C\n}\n\ntype Prettify<T> = { readonly [K in keyof T]: T[K] } & {}\n\ntype SafeEntries<\n C extends CodegenData,\n W extends keyof C['accounts'],\n> = C['accounts'][W]['safes']\n\ntype RolesEntries<\n C extends CodegenData,\n W extends keyof C['accounts'],\n> = C['accounts'][W]['rolesMods']\n\ntype NodeType = 'SAFE' | 'ROLES' | 'DELAY'\n\n/** A reference to a node used in `owners`, `modules`, `target`, etc. */\ntype NodeRef = Readonly<{ type: NodeType; label: string; chain: ChainId }>\n\n/** A blockchain address (checksummed or lowercase) or a reference to another\n * node in the constellation. Values are normalized to lowercase before being\n * sent to the API. */\ntype AddressOrRef = Address | NodeRef\n\ntype NodeBase = Readonly<{\n /** Human-readable identifier, unique within the constellation. */\n label: string\n /** Chain the node is deployed on. */\n chain: ChainId\n /** Set for existing nodes (from codegen or bound by address), absent for new\n * nodes. Accepts checksummed or lowercase; normalized to lowercase on push. */\n address?: Address\n /** Deployment nonce — required for new nodes, optional for existing. */\n nonce?: bigint\n}>\n\n/** A safe node spec — existing vault ref or new safe with required config. */\nexport type SafeNode = NodeBase &\n Readonly<{\n /** Discriminator identifying this node as a Safe. */\n type: 'SAFE'\n /** Number of owner signatures required to execute a transaction. */\n threshold: number\n /** Safe owner addresses or node references. */\n owners: readonly (string | NodeRef)[]\n /** Module addresses or node references enabled on the safe. */\n modules?: readonly (string | NodeRef)[]\n /** Whether this safe shall appear as a vault in the workspace. @default false */\n vault?: boolean\n }>\n\n/** A roles modifier node spec — existing vault ref or new roles with modifier config. */\nexport type RolesNode = NodeBase &\n Readonly<{\n /** Discriminator identifying this node as a Roles modifier. */\n type: 'ROLES'\n /** The safe that this roles modifier controls. */\n target?: AddressOrRef\n /** The account that is allowed to update the configuration of the Roles mod. */\n owner?: AddressOrRef\n /** The account that calls will be executed from. */\n avatar?: AddressOrRef\n /** MultiSend contract addresses for batched transactions. */\n multisend?: readonly Address[]\n /** Role definitions configured on this modifier. Pass `null` for a key to\n * clear that role; unmentioned roles are left untouched. */\n roles?: Record<string, RoleDef | null>\n /** Spending allowances configured on this modifier, keyed by name. Pass\n * `null` for a key to clear that allowance; unmentioned allowances are\n * left untouched. */\n allowances?: Record<string, AllowanceSpec | null>\n }>\n\n/** Any complete node that can be passed to `push()`. */\nexport type ConstellationNode = SafeNode | RolesNode\nexport type ConstellationNodeInternal = ConstellationNode & {\n _constellation: ConstellationMeta\n}\n\ntype NewSafeProps = {\n /** Deployment nonce for CREATE2 address derivation. */\n nonce: bigint\n /** Number of owner signatures required to execute a transaction. */\n threshold: number\n /** Safe owner addresses or node references. */\n owners: readonly AddressOrRef[]\n /** Module addresses or node references to enable on the safe. */\n modules?: readonly AddressOrRef[]\n /** Whether this safe is a workspace vault. @default false */\n vault?: boolean\n}\n\n/** Configuration shared by both ways of declaring a roles modifier. */\ntype RolesConfig = {\n /** The safe that this roles modifier controls. Defaults to the new safe with the same label, when one exists. */\n target?: AddressOrRef\n /** The account that calls will be executed from. Defaults to `target` value */\n avatar?: AddressOrRef\n /** The account that is allowed to update the configuration of the Roles Mod. Defaults to `target` value */\n owner?: AddressOrRef\n /** MultiSend contract addresses for batched transactions. Defaults to `['0x38869bf66a61cf6bdb996a6ae40d5853fd43b526', '0x9641d764fc13c8b624c04430c7356c1c7c8102e2']` */\n multisend?: readonly Address[]\n /** Role definitions to configure on this modifier. Pass `null` for a key to\n * clear that role; unmentioned roles are left untouched. */\n roles?: Record<string, RoleDef | null>\n /** Spending allowances to configure on this modifier, keyed by name. Pass\n * `null` for a key to clear that allowance; unmentioned allowances are\n * left untouched. */\n allowances?: Record<string, AllowanceSpec | null>\n}\n\n/** Declare a brand-new roles modifier (address derived via CREATE2 from `nonce`). */\ntype NewRolesByNonce = RolesConfig & {\n /** Deployment nonce for CREATE2 address derivation. */\n nonce: bigint\n}\n\n/** Bind to a roles modifier already deployed on-chain at a known address, to\n * reconfigure its roles/allowances. Use this for mods not (yet) imported into\n * the workspace — pass the address instead of a deployment `nonce`. */\ntype ExistingRolesByAddress = RolesConfig & {\n /** Address of the existing Roles mod to bind to and reconfigure. */\n address: Address\n}\n\ntype NewRolesProps = NewRolesByNonce | ExistingRolesByAddress\n\ntype ExistingNodeAccessor<\n Type extends string,\n K extends string,\n E,\n Ch extends ChainId,\n NP extends Record<string, any>,\n> = Readonly<Prettify<E & { type: Type; label: K; chain: Ch }>> &\n (<\n O extends {\n [P in Exclude<keyof E & string, 'id' | 'label'>]?: any\n } & Partial<NP> = {},\n >(\n overrides?: {\n [P in Exclude<keyof E & string, 'id' | 'label'>]?: any\n } & Partial<NP> &\n O\n ) => Readonly<\n Prettify<\n Omit<E, keyof O> & O & Partial<NP> & { type: Type; label: K; chain: Ch }\n >\n >)\n\ntype NewNodeAccessor<\n Type extends string,\n Ch extends ChainId,\n NP extends Record<string, any>,\n> = Readonly<Prettify<{ type: Type; label: string; chain: Ch }>> &\n (<P extends NP>(\n props: P\n ) => Readonly<Prettify<P & { type: Type; label: string; chain: Ch }>>)\n\ntype EntityAccessor<\n Type extends string,\n Entries extends Record<string, any>,\n Ch extends ChainId = ChainId,\n NP extends Record<string, any> = Record<string, any>,\n> = {\n readonly [K in keyof Entries & string]: ExistingNodeAccessor<\n Type,\n K,\n Entries[K],\n Ch,\n NP\n >\n} & {\n readonly [key: string]: NewNodeAccessor<Type, Ch, NP>\n}\n\ntype UserAccessor<C extends CodegenData, Ch extends number> = {\n readonly [K in keyof C['users'] &\n string]: C['users'][K]['personalSafes'][Ch]['address']\n}\n\ntype ConstellationResult<\n C extends CodegenData,\n W extends keyof C['accounts'] = keyof C['accounts'],\n Ch extends ChainId = ChainId,\n> = {\n /** Access existing safes by label or create new ones with a new label.\n * Only SAFE-typed accounts are suggested in IntelliSense. */\n safe: EntityAccessor<'SAFE', SafeEntries<C, W>, Ch, NewSafeProps>\n /** Access existing roles modifiers by label or create new ones with a\n * new label. Only ROLES-typed accounts are suggested in IntelliSense. */\n roles: EntityAccessor<'ROLES', RolesEntries<C, W>, Ch, NewRolesProps>\n /** Resolve a user's personal safe address on the constellation's chain. */\n user: UserAccessor<C, Ch>\n}\n\n/** @internal */\nexport type ConstellationMeta = {\n label: string\n chain: ChainId\n workspaceId: UUID\n}\n\nfunction loadCodegen(): CodegenData {\n const require = createRequire(import.meta.url)\n return require(resolveZodiacDir()) as CodegenData\n}\n\n/**\n * Creates a constellation scoped to a workspace and chain.\n *\n * Use bracket access to reference existing accounts (vaults and other\n * applied constellation nodes) or define new ones:\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n *\n * const dao = eth.safe['GG DAO'] // existing account ref\n * const roles = eth.roles['GG DAO'] // existing roles ref\n * const newSafe = eth.safe['New Safe']({ nonce: 0n, threshold: 2, owners: [...], modules: [...] })\n * ```\n */\nexport function constellation<\n const C extends CodegenData = GeneratedCodegen,\n const W extends keyof C['accounts'] & string = keyof C['accounts'] & string,\n const Ch extends ChainId = ChainId,\n>(\n opts: ConstellationOpts<C> & { workspace: W; chain: Ch },\n internal?: ConstellationInternalOpts<C>\n): ConstellationResult<C, W, Ch> {\n const codegen: CodegenData = internal?.codegen ?? loadCodegen()\n\n const ws = codegen.accounts[opts.workspace]\n const safesByLabel: Record<string, Account> = {}\n const rolesByLabel: Record<string, Account> = {}\n if (ws) {\n for (const [label, account] of Object.entries(ws.safes)) {\n safesByLabel[label] = account\n }\n for (const [label, account] of Object.entries(ws.rolesMods)) {\n rolesByLabel[label] = account\n }\n }\n\n const meta: ConstellationMeta = {\n label: opts.label,\n chain: opts.chain,\n workspaceId: (ws?.workspaceId ?? '') as UUID,\n }\n\n function makeNodeRef(\n data: Record<string, any>\n ): Readonly<Record<string, any>> {\n return Object.freeze({\n ...data,\n chain: opts.chain,\n _constellation: meta,\n })\n }\n\n function entityAccessor(\n registry: Record<string, Record<string, any>>,\n type: string\n ) {\n const cache = new Map<string, Record<string, any>>()\n return new Proxy({} as Record<string, any>, {\n get(_target: any, name: string) {\n if (typeof name !== 'string') return undefined\n const cached = cache.get(name)\n if (cached) return cached\n const existing = registry[name]\n // Bracket-access keys in the generated codegen carry a\n // ` (0xChecksummed…)` suffix when multiple workspace accounts share\n // a label. The label sent in the push spec should be the clean\n // original, so prefer `existing.label` when it's available.\n const specLabel: string = existing?.label ?? name\n const fn = (overrides?: Record<string, any>) =>\n makeNodeRef({\n type,\n ...(existing || {}),\n ...overrides,\n label: specLabel,\n })\n Object.assign(fn, {\n type,\n ...(existing || {}),\n label: specLabel,\n chain: opts.chain,\n _constellation: meta,\n })\n cache.set(name, fn)\n return fn\n },\n })\n }\n\n function userAccessor() {\n return new Proxy(\n {},\n {\n get(_target: any, name: string) {\n const user = codegen.users[name]\n if (!user) throw new Error(`Unknown user: ${name}`)\n const personalSafe = user.personalSafes[opts.chain]\n if (!personalSafe) {\n throw new Error(\n `User ${name} has no personal safe on chain ${opts.chain}`\n )\n }\n return personalSafe.address\n },\n }\n )\n }\n\n const safe = entityAccessor(safesByLabel, 'SAFE')\n const roles = entityAccessor(rolesByLabel, 'ROLES')\n\n return {\n safe,\n roles,\n user: userAccessor(),\n } as ConstellationResult<C, W, Ch>\n}\n","import type {\n ApplyConstellationPayload,\n ApplyConstellationResult,\n ChainId,\n} from '@zodiaceco/api-types'\nimport { invariant } from '@epic-web/invariant'\nimport {\n assertRecipientChains,\n isDeFiKitEntry,\n toAnnotation,\n} from './permissionEntries'\nimport { ApiClient } 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}\n\n/**\n * Resolves node references and pushes the constellation specification to the API.\n *\n *\n * ```ts\n * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })\n * const dao = eth.safe['GG DAO']\n * const roles = eth.roles['New Roles']({ nonce: 0n, target: dao, owner: dao, avatar: dao })\n *\n * await 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()\n const refs = deriveRefs(nodes)\n\n // Group nodes by constellation (multiple constellations can be applied with a single call)\n const groups = new Map<\n string,\n { meta: ConstellationMeta; nodes: ConstellationNodeInternal[] }\n >()\n\n for (const node of refs.byIdentity.keys()) {\n const meta = node._constellation\n invariant(\n meta,\n `Node \"${node.label}\" is not associated with a constellation`\n )\n const key = `${meta.workspaceId}:${meta.chain}:${meta.label}`\n let group = groups.get(key)\n if (!group) {\n group = { meta, nodes: [] }\n groups.set(key, group)\n }\n group.nodes.push(node)\n }\n\n const results: ApplyConstellationResult[] = []\n for (const { meta, nodes: groupNodes } of groups.values()) {\n const specification = groupNodes.map((n) => nodeToSpec(n, refs))\n const result = await api.applyConstellation(meta.workspaceId, {\n label: meta.label,\n chain: meta.chain,\n specification,\n })\n results.push(result)\n }\n\n return results\n}\n\ntype RefsIndex = {\n byIdentity: Map<ConstellationNodeInternal, string>\n byLabel: Map<string, string>\n}\n\nfunction labelKey(node: ConstellationNodeInternal): string {\n return `${node._constellation.workspaceId}:${node.type}:${node.label}`\n}\n\nfunction deriveRefs(\n nodes: ConstellationNode[] | { [key: string]: ConstellationNode }\n): RefsIndex {\n const byIdentity = new Map<ConstellationNodeInternal, string>()\n const byLabel = new Map<string, string>()\n\n const register = (node: ConstellationNodeInternal, ref: string) => {\n byIdentity.set(node, ref)\n byLabel.set(labelKey(node), ref)\n }\n\n if (Array.isArray(nodes)) {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]\n invariant(\n isConstellationNode(node),\n `unexpected node input at index: ${i}`\n )\n register(node, `${i}`)\n }\n } else {\n for (const [key, node] of Object.entries(nodes)) {\n invariant(\n isConstellationNode(node),\n `unexpected node input under key: ${key}`\n )\n register(node, key)\n }\n }\n\n return { byIdentity, byLabel }\n}\n\nfunction nodeToSpec(\n node: ConstellationNodeInternal,\n refs: RefsIndex\n): ApplyConstellationPayload['specification'][number] {\n const { id, _constellation, ...rest } = node as Record<string, any>\n const spec: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(rest)) {\n 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 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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"networks-
|
|
1
|
+
{"version":3,"file":"networks-B0H4Uoyh.mjs","names":[],"sources":["../src/allow/abi.ts","../src/allow/networks.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Abi } from './fetch'\n\nexport type ContractAddress = `0x${string}`\n\nexport type ContractNode = {\n chain: string\n segments: string[]\n address: ContractAddress\n}\n\nexport function* walkContracts(\n config: Record<string, any>\n): Generator<ContractNode> {\n for (const [chain, contracts] of Object.entries(config)) {\n yield* walkLevel(chain, [], contracts)\n }\n}\n\nfunction* walkLevel(\n chain: string,\n segments: string[],\n node: unknown\n): Generator<ContractNode> {\n if (typeof node === 'string') {\n if (segments.length === 0) {\n throw new Error(`Contract at ${chain} is missing a name`)\n }\n if (!node.startsWith('0x')) {\n throw new Error(\n `Invalid address for ${chain}.${segments.join('.')}: ${node}`\n )\n }\n yield { chain, segments, address: node as ContractAddress }\n return\n }\n if (node && typeof node === 'object') {\n for (const [key, value] of Object.entries(node as Record<string, any>)) {\n yield* walkLevel(chain, [...segments, key], value)\n }\n return\n }\n throw new Error(\n `Invalid contracts entry at ${chain}.${segments.join('.')}: ${JSON.stringify(node)}`\n )\n}\n\nexport const abiFilePath = (abisDir: string, node: ContractNode): string =>\n path.join(abisDir, node.chain, ...node.segments) + '.json'\n\nexport const readAbi = (abisDir: string, node: ContractNode): Abi | null => {\n const file = abiFilePath(abisDir, node)\n if (!fs.existsSync(file)) return null\n const raw = fs.readFileSync(file, 'utf8')\n const parsed = JSON.parse(raw)\n if (!Array.isArray(parsed)) {\n throw new Error(`ABI at ${file} is not a JSON array`)\n }\n return parsed as Abi\n}\n\nexport const writeAbi = (\n abisDir: string,\n node: ContractNode,\n abi: Abi\n): void => {\n const file = abiFilePath(abisDir, node)\n fs.mkdirSync(path.dirname(file), { recursive: true })\n fs.writeFileSync(file, JSON.stringify(abi, null, 2) + '\\n', 'utf8')\n}\n","export const CHAIN_IDS = {\n eth: 1,\n oeth: 10,\n gno: 100,\n sep: 11155111,\n matic: 137,\n zkevm: 1101,\n arb1: 42161,\n avax: 43114,\n base: 8453,\n basesep: 84532,\n bnb: 56,\n celo: 42220,\n sonic: 146,\n berachain: 80094,\n unichain: 130,\n worldchain: 480,\n bob: 60808,\n mantle: 5000,\n hemi: 43111,\n katana: 747474,\n linea: 59144,\n ink: 57073,\n hyperevm: 999,\n flare: 14,\n scroll: 534352,\n plasma: 9745,\n megaeth: 4326,\n} as const satisfies Record<string, number>\n\nexport type ChainPrefix = keyof typeof CHAIN_IDS\n\nexport const chainIdFor = (prefix: string): number => {\n const id = (CHAIN_IDS as Record<string, number>)[prefix]\n if (id === undefined) {\n throw new Error(`Unknown chain prefix: ${prefix}`)\n }\n return id\n}\n"],"mappings":";;;AAYA,UAAiB,cACf,QACyB;AACzB,MAAK,MAAM,CAAC,OAAO,cAAc,OAAO,QAAQ,OAAO,CACrD,QAAO,UAAU,OAAO,EAAE,EAAE,UAAU;;AAI1C,UAAU,UACR,OACA,UACA,MACyB;AACzB,KAAI,OAAO,SAAS,UAAU;AAC5B,MAAI,SAAS,WAAW,EACtB,OAAM,IAAI,MAAM,eAAe,MAAM,oBAAoB;AAE3D,MAAI,CAAC,KAAK,WAAW,KAAK,CACxB,OAAM,IAAI,MACR,uBAAuB,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,IAAI,OACxD;AAEH,QAAM;GAAE;GAAO;GAAU,SAAS;GAAyB;AAC3D;;AAEF,KAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAA4B,CACpE,QAAO,UAAU,OAAO,CAAC,GAAG,UAAU,IAAI,EAAE,MAAM;AAEpD;;AAEF,OAAM,IAAI,MACR,8BAA8B,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,IAAI,KAAK,UAAU,KAAK,GACnF;;AAGH,MAAa,eAAe,SAAiB,SAC3C,KAAK,KAAK,SAAS,KAAK,OAAO,GAAG,KAAK,SAAS,GAAG;AAErD,MAAa,WAAW,SAAiB,SAAmC;CAC1E,MAAM,OAAO,YAAY,SAAS,KAAK;AACvC,KAAI,CAAC,GAAG,WAAW,KAAK,CAAE,QAAO;CACjC,MAAM,MAAM,GAAG,aAAa,MAAM,OAAO;CACzC,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,KAAI,CAAC,MAAM,QAAQ,OAAO,CACxB,OAAM,IAAI,MAAM,UAAU,KAAK,sBAAsB;AAEvD,QAAO;;AAGT,MAAa,YACX,SACA,MACA,QACS;CACT,MAAM,OAAO,YAAY,SAAS,KAAK;AACvC,IAAG,UAAU,KAAK,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AACrD,IAAG,cAAc,MAAM,KAAK,UAAU,KAAK,MAAM,EAAE,GAAG,MAAM,OAAO;;;;ACrErE,MAAa,YAAY;CACvB,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,MAAM;CACN,SAAS;CACT,KAAK;CACL,MAAM;CACN,OAAO;CACP,WAAW;CACX,UAAU;CACV,YAAY;CACZ,KAAK;CACL,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,OAAO;CACP,KAAK;CACL,UAAU;CACV,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACV;AAID,MAAa,cAAc,WAA2B;CACpD,MAAM,KAAM,UAAqC;AACjD,KAAI,OAAO,KAAA,EACT,OAAM,IAAI,MAAM,yBAAyB,SAAS;AAEpD,QAAO"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//#region src/permissionEntries.ts
|
|
2
|
+
/**
|
|
3
|
+
* The shapes a role's `permissions` travel to the API in, and the one piece of
|
|
4
|
+
* rendering `push()` still does. Internal: the helpers that build these live in
|
|
5
|
+
* `./actions`, and nothing here is part of the published interface beyond the
|
|
6
|
+
* `PermissionEntry` type that module re-exports.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The base url of the DeFi Kit API. Annotations point back at it so that a role
|
|
10
|
+
* can be read as presets again, and so that a DeFi Kit entry written in code is
|
|
11
|
+
* indistinguishable from one configured in the app.
|
|
12
|
+
*/
|
|
13
|
+
const DEFI_KIT_BASE_URL = "https://kit.kpk.io/api/v1";
|
|
14
|
+
/** The chains DeFi Kit serves, by the prefix it addresses them with. */
|
|
15
|
+
const DEFI_KIT_CHAIN_PREFIXES = {
|
|
16
|
+
1: "eth",
|
|
17
|
+
10: "oeth",
|
|
18
|
+
100: "gno",
|
|
19
|
+
8453: "base",
|
|
20
|
+
42161: "arb1"
|
|
21
|
+
};
|
|
22
|
+
/** Renders a DeFi Kit entry into the annotation it stands for on `chain`. */
|
|
23
|
+
const toAnnotation = ({ defiKit: { protocol, verb, params } }, chain) => {
|
|
24
|
+
const prefix = DEFI_KIT_CHAIN_PREFIXES[chain];
|
|
25
|
+
if (prefix == null) throw new Error(`DeFi Kit does not serve chain "${chain}"`);
|
|
26
|
+
return {
|
|
27
|
+
uri: `${DEFI_KIT_BASE_URL}/permissions/${prefix}/${protocol}/${verb}?${searchParams(params)}`,
|
|
28
|
+
schema: `${DEFI_KIT_BASE_URL}/openapi.json`
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const isDeFiKitEntry = (entry) => typeof entry === "object" && entry !== null && "defiKit" in entry;
|
|
32
|
+
/**
|
|
33
|
+
* The chains of the node recipients a `transfer()`'s same-chain `to` named.
|
|
34
|
+
* Symbol-keyed so it never serializes; `push()` checks it against the chain of
|
|
35
|
+
* the roles node the entry ends up on. A transfer to a node on another chain
|
|
36
|
+
* would permit sending funds to an address that may not exist there.
|
|
37
|
+
*/
|
|
38
|
+
const recipientChains = Symbol("recipientChains");
|
|
39
|
+
const assertRecipientChains = (entry, chain) => {
|
|
40
|
+
const chains = entry[recipientChains];
|
|
41
|
+
for (const recipientChain of chains ?? []) if (recipientChain !== chain) {
|
|
42
|
+
const label = "label" in entry ? `"${entry.label}"` : "a transfer";
|
|
43
|
+
throw new Error(`A recipient of ${label} lives on chain "${recipientChain}", but the role is on chain "${chain}". Use \`bridge\` for cross-chain recipients.`);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Search params are appended one value at a time, the form annotation uris are
|
|
48
|
+
* normalized to before they are matched back against a preset.
|
|
49
|
+
*/
|
|
50
|
+
const searchParams = (params) => {
|
|
51
|
+
const searchParams = new URLSearchParams();
|
|
52
|
+
for (const [name, value] of Object.entries(params)) {
|
|
53
|
+
if (value == null) continue;
|
|
54
|
+
for (const entry of Array.isArray(value) ? value : [value]) searchParams.append(name, String(entry));
|
|
55
|
+
}
|
|
56
|
+
return searchParams;
|
|
57
|
+
};
|
|
58
|
+
//#endregion
|
|
59
|
+
export { toAnnotation as i, isDeFiKitEntry as n, recipientChains as r, assertRecipientChains as t };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=permissionEntries-D3fSVhwO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissionEntries-D3fSVhwO.mjs","names":[],"sources":["../src/permissionEntries.ts"],"sourcesContent":["import type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { Permission } from 'zodiac-roles-sdk'\nimport type { ConstellationNode } 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\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: { type: 'swap'; sell: Address[]; buy: Address[] }\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 */\nexport type PermissionTarget = `0x${string}` | ConstellationNode\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;;;;;;;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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zodiaceco/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"author": "Zodiac Labs GmbH",
|
|
5
5
|
"license": "LGPL-3.0-only",
|
|
6
6
|
"homepage": "https://github.com/gnosisguild/zodiac-sdk",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"types": "./dist/index.d.mts",
|
|
18
18
|
"import": "./dist/index.mjs"
|
|
19
19
|
},
|
|
20
|
+
"./actions": {
|
|
21
|
+
"types": "./dist/actions.d.mts",
|
|
22
|
+
"import": "./dist/actions.mjs"
|
|
23
|
+
},
|
|
20
24
|
"./allow": {
|
|
21
25
|
"types": "./dist/allow/index.d.mts",
|
|
22
26
|
"import": "./dist/allow/index.mjs"
|
|
@@ -47,8 +51,9 @@
|
|
|
47
51
|
},
|
|
48
52
|
"dependencies": {
|
|
49
53
|
"@epic-web/invariant": "1.0.0",
|
|
50
|
-
"@zodiaceco/api-types": "^1.
|
|
54
|
+
"@zodiaceco/api-types": "^1.9.0",
|
|
51
55
|
"commander": "^14.0.3",
|
|
56
|
+
"defi-kit": "^2.26.12",
|
|
52
57
|
"dotenv": "^17.4.2",
|
|
53
58
|
"open": "^11.0.0",
|
|
54
59
|
"ts-morph": "^27.0.2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"allow-CzPpzclq.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(node.address, abi as InterfaceAbi)\n )\n }\n return kit\n}\n\nfunction attachAt(root: Record<string, any>, segments: string[], value: any) {\n let cursor = root\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!\n if (!(seg in cursor)) cursor[seg] = {}\n cursor = cursor[seg]\n }\n cursor[segments[segments.length - 1]!] = value\n}\n\nfunction missingAbiProxy(node: ContractNode) {\n const explain = () => {\n throw new Error(\n `ABI missing for ${node.chain}.${node.segments.join('.')} ` +\n `(${node.address}). Run \\`zodiac pull-contracts\\` to fetch it, or ` +\n `paste the ABI JSON manually at <abisDir>/${node.chain}/${node.segments.join('/')}.json`\n )\n }\n return new Proxy(\n {},\n {\n get: explain,\n has: explain,\n }\n )\n}\n\nfunction makeAllowContract(\n address: `0x${string}`,\n abi: InterfaceAbi\n): Record<string | symbol, any> {\n const iface = Interface.from(abi)\n const lowerAddr = address.toLowerCase() as `0x${string}`\n\n const allowEverything = (options?: Options): TargetPermission => ({\n targetAddress: lowerAddr,\n send: options?.send,\n delegatecall: options?.delegatecall,\n })\n\n const has = (name: string) => {\n try {\n const fn = iface.getFunction(name)\n if (!fn) return false\n return fn.stateMutability !== 'view' && fn.stateMutability !== 'pure'\n } catch (error) {\n if (!isError(error as any, 'INVALID_ARGUMENT')) throw error\n return false\n }\n }\n\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === EVERYTHING) return allowEverything\n if (typeof prop !== 'string') return undefined\n if (!has(prop)) return undefined\n const fn = iface.getFunction(prop)!\n return makeAllowFunction(fn, lowerAddr)\n },\n has: (_target, prop) => {\n if (prop === EVERYTHING) return true\n return typeof prop === 'string' && has(prop)\n },\n }\n )\n}\n\nfunction makeAllowFunction(\n fn: FunctionFragment,\n targetAddress: `0x${string}`\n): (...args: any[]) => FunctionPermission {\n const inputs = fn.inputs\n return (...args: any[]) => {\n const scopings = args.slice(0, inputs.length)\n const hasScopings = scopings.some((s) => s !== undefined && s !== null)\n const options: Options = args[inputs.length] ?? {}\n const condition = hasScopings\n ? c.calldataMatches(scopings, inputs)()\n : undefined\n const preset = {\n targetAddress,\n signature: fn.format('sighash'),\n condition,\n }\n return applyOptions(coercePermission(preset as any) as any, options)\n }\n}\n\nconst emptyCalldataMatches: Condition = {\n paramType: ParameterType.Calldata,\n operator: Operator.Matches,\n children: [],\n}\n\nconst applyGlobalAllowance = (\n condition: Condition | undefined,\n allowanceCondition: Condition\n): Condition => {\n const base = condition ?? emptyCalldataMatches\n if (\n base.paramType !== ParameterType.Calldata ||\n base.operator !== Operator.Matches\n ) {\n throw new Error(\n 'Global allowance can only be applied to calldata matches nodes'\n )\n }\n return {\n ...base,\n children: [...(base.children ?? []), allowanceCondition],\n }\n}\n\nconst applyOptions = (\n permission: FunctionPermission & { condition?: Condition },\n options: Options\n): FunctionPermission => {\n let condition = permission.condition\n if (options.etherWithinAllowance) {\n if (!options.send) {\n throw new Error(\n '`etherWithinAllowance` can only be used if `send` is allowed'\n )\n }\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.EtherWithinAllowance,\n compValue: options.etherWithinAllowance,\n })\n }\n if (options.callWithinAllowance) {\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.CallWithinAllowance,\n compValue: options.callWithinAllowance,\n })\n }\n return {\n ...permission,\n send: options.send,\n delegatecall: options.delegatecall,\n condition,\n }\n}\n"],"mappings":"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"}
|