@zodiaceco/sdk 2.4.0 → 2.4.1
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 +5 -2
- 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 +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{push-DdnHsLpO.mjs → push-B7K4sB4m.mjs} +55 -8
- package/dist/push-B7K4sB4m.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/allow-DX3iyizB.mjs.map +0 -1
- package/dist/push-DdnHsLpO.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -277,8 +277,11 @@ it has nowhere to appear in the app beyond the targets it allows.
|
|
|
277
277
|
Allowance keys are plain labels — `key: 'usdc_payouts'` on the declaration, and
|
|
278
278
|
`allowance: usdc_payouts` on the transfer, which reads the key off it. They are
|
|
279
279
|
encoded to bytes32 when the constellation is deployed, so nothing calls
|
|
280
|
-
`encodeKey` by hand.
|
|
281
|
-
|
|
280
|
+
`encodeKey` by hand. The same holds where an `allow`-kit permission draws on an
|
|
281
|
+
allowance: `c.withinAllowance('usdc_payouts')` on a parameter, or
|
|
282
|
+
`{ send: true, etherWithinAllowance: 'eth_budget' }` and
|
|
283
|
+
`{ callWithinAllowance: 'daily_calls' }` in its options. Allowance and role
|
|
284
|
+
keys consist of 1 to 31 letters, digits, underscores or hyphens.
|
|
282
285
|
|
|
283
286
|
Tokens are named by address, not by symbol. A `transfer()` recipient may also
|
|
284
287
|
be a node — an account from your codegen, or one bound by address — which
|
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
2
|
import { a as PermissionEntry, c as ConstellationNode, l as constellation, s as Permissions } from "./actions-Cri7InV-.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-
|
|
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,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 +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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"}
|