@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 CHANGED
@@ -1,4 +1,4 @@
1
- # Zodiac OS SDK
1
+ # Zodiac SDK
2
2
 
3
3
  Programmatically manage [Zodiac](https://www.zodiac.eco) account constellations.
4
4
 
@@ -137,6 +137,90 @@ References are resolved by label at `push()` time, so both sides of the cycle mu
137
137
  const aliceAddress = eth.user['Alice Sample']
138
138
  ```
139
139
 
140
+ ### Describing what a role may do
141
+
142
+ A Roles mod carries roles, and every role lists `permissions` — entries that
143
+ describe what the role is allowed to do. Entries carry parameters and a label,
144
+ never compiled permissions: they are compiled when the constellation is
145
+ deployed, so a stored revision always goes through the current compilers
146
+ instead of replaying a copy made when it was pushed.
147
+
148
+ ```ts
149
+ import { swap, transfer, custom, defikit } from '@zodiaceco/sdk/actions'
150
+ // `allow` is your project's generated permission kit — a global in template
151
+ // projects, created by `zodiac pull-contracts`.
152
+
153
+ const treasuryRoles = eth.roles['GG Treasury Roles']({
154
+ nonce: 0n,
155
+ target: ggTreasury,
156
+ allowances: { usdc_payouts },
157
+ roles: {
158
+ treasury_ops: {
159
+ members: [eth.user['Alice Sample']],
160
+ permissions: [
161
+ swap({ label: 'Rebalance stables', sell: [USDC, DAI], buy: [WETH] }),
162
+
163
+ transfer({
164
+ label: 'Grant payouts',
165
+ tokens: [USDC],
166
+ to: [eth.safe['Grants Safe']],
167
+ bridge: [{ to: [gno.safe['Ops Safe']], receive: [GNO_USDC] }],
168
+ allowance: usdc_payouts,
169
+ }),
170
+
171
+ defikit.aave_v3.deposit({
172
+ label: 'Aave deposits',
173
+ market: 'Core',
174
+ targets: ['WETH'],
175
+ }),
176
+
177
+ custom({
178
+ label: 'Bot ops',
179
+ permissions: [
180
+ allow.eth.weth.deposit({ send: true }),
181
+ allow.eth.weth.withdraw(),
182
+ ],
183
+ }),
184
+ ],
185
+ },
186
+ },
187
+ })
188
+ ```
189
+
190
+ Each helper covers a different kind of action:
191
+
192
+ - `swap()` allows signing CoW orders between the tokens it names.
193
+ - `transfer()` allows sending tokens to the addresses it names, optionally
194
+ capped by an allowance declared on the same Roles mod. Pass the zero address
195
+ to allow sending the native token. `bridge` names destinations on other
196
+ chains, sent over Across: each target pins both the recipients and the tokens
197
+ they may receive there. A target takes its chain from its recipient nodes, or
198
+ name one with `chain` when the recipients are plain addresses. Tokens without
199
+ an Across route to a target are skipped, the same way the app skips them —
200
+ routes change between writing a spec and deploying it — but a target nothing
201
+ can reach at all is refused at deploy rather than deployed half-working.
202
+ - `defikit` mirrors the DeFi Kit allow kit — same protocols, verbs and
203
+ parameters, plus a `label`. A DeFi Kit entry is nothing but its annotation;
204
+ the permissions behind it are fetched from the annotation's uri at deploy, so
205
+ `push()` fetches nothing. Protocols and parameters are typed against the
206
+ Ethereum kit, the widest of the chains DeFi Kit serves.
207
+ - `custom()` labels a bag of plain `allow`-kit permissions — everything the
208
+ other helpers don't cover. It takes permissions, not other actions.
209
+
210
+ Every helper takes a `label`. The label names the action in Zodiac and never
211
+ reaches the chain. A bare permission with no enclosing helper stays valid, but
212
+ it has nowhere to appear in the app beyond the targets it allows.
213
+
214
+ Allowance keys are plain labels — `key: 'usdc_payouts'` on the declaration, and
215
+ `allowance: usdc_payouts` on the transfer, which reads the key off it. They are
216
+ encoded to bytes32 when the constellation is deployed, so nothing calls
217
+ `encodeKey` by hand. A label has to fit in 32 bytes.
218
+
219
+ Tokens are named by address, not by symbol. A `transfer()` recipient may also
220
+ be a node — an account from your codegen, or one bound by address — which
221
+ stands for the address it lives at. A node whose address is only known once the
222
+ constellation is deployed is rejected at compile time.
223
+
140
224
  ### Pushing the constellation
141
225
 
142
226
  The `push()` function takes all nodes and sends them to the Zodiac OS API. Pass either a named object (keys become refs) or an array:
@@ -0,0 +1,352 @@
1
+ import { Permission } from "zodiac-roles-sdk";
2
+ import { Address, ApplyConstellationPayload, ChainId } from "@zodiaceco/api-types";
3
+ import { allow } from "defi-kit/eth";
4
+ import { UUID } from "crypto";
5
+
6
+ //#region src/types.d.ts
7
+ type ApiNewRolesSpec = Extract<ApplyConstellationPayload['specification'][number], {
8
+ type: 'ROLES';
9
+ nonce: string;
10
+ }>;
11
+ /** A role definition in the API's serialized format (bigints as template literal strings). */
12
+ /** An allowance definition in the API's serialized format (bigints as template literal strings). */
13
+ type ApiAllowanceSpec = Extract<NonNullable<ApiNewRolesSpec['allowances']>, readonly any[]>[number];
14
+ /** Recursively converts `${bigint}` template literal strings to `bigint`. */
15
+ type RealBigints<T> = T extends `${bigint}` ? bigint : T extends readonly (infer U)[] ? RealBigints<U>[] : T extends Record<string, any> ? { [K in keyof T]: RealBigints<T[K]> } : T;
16
+ /** A role definition with bigint fields as actual bigints. */
17
+ /** An allowance definition with bigint fields as actual bigints. */
18
+ type AllowanceSpec = RealBigints<ApiAllowanceSpec>;
19
+ //#endregion
20
+ //#region src/constellation.d.ts
21
+ /**
22
+ * A role definition keyed by role name. Entries describe what the role may do —
23
+ * parameters and labels, never compiled permissions. They are expanded into
24
+ * `{ targets, annotations }` when the constellation is deployed, so that a
25
+ * stored revision always compiles through the current compilers.
26
+ */
27
+ type RoleDef = {
28
+ members: readonly AddressOrRef[];
29
+ permissions: readonly PermissionEntry[];
30
+ };
31
+ type User = {
32
+ id: UUID;
33
+ fullName: string;
34
+ personalSafes: Record<number, {
35
+ address: Lowercase<Address>;
36
+ active: boolean;
37
+ }>;
38
+ };
39
+ type Account = {
40
+ id: UUID;
41
+ label: string;
42
+ address: Lowercase<Address>;
43
+ chain: ChainId; /** True for accounts promoted to a workspace vault. */
44
+ vault: boolean;
45
+ };
46
+ /**
47
+ * Accounts grouped by node type within a workspace. Per-type maps keep
48
+ * bracket-accessor namespaces separate: a SAFE and a ROLES mod sharing a
49
+ * label don't collide, and `eth.safe[...]` IntelliSense doesn't suggest
50
+ * ROLES mod labels (and vice versa).
51
+ */
52
+ type WorkspaceAccounts = {
53
+ workspaceId: UUID;
54
+ workspaceName: string;
55
+ safes: Readonly<Record<string, Account>>;
56
+ rolesMods: Readonly<Record<string, Account>>;
57
+ delays: Readonly<Record<string, Account>>;
58
+ };
59
+ /** Shape of the codegen data produced by `zodiac pull-org`. */
60
+ type CodegenData = {
61
+ users: Readonly<Record<string, User>>;
62
+ accounts: Readonly<Record<string, WorkspaceAccounts>>;
63
+ };
64
+ type GeneratedCodegen = ZodiacGeneratedCodegen extends CodegenData ? ZodiacGeneratedCodegen : CodegenData;
65
+ type ConstellationOpts<C extends CodegenData> = {
66
+ /** Workspace to scope accounts and roles to. */workspace: keyof C['accounts'] & string; /** Human-readable label for this constellation. */
67
+ label: string; /** Target chain for all nodes in this constellation. */
68
+ chain: ChainId;
69
+ };
70
+ type ConstellationInternalOpts<C extends CodegenData> = {
71
+ /** Injected codegen data (used for testing). */codegen?: C;
72
+ };
73
+ type Prettify<T> = { readonly [K in keyof T]: T[K] } & {};
74
+ type SafeEntries<C extends CodegenData, W extends keyof C['accounts']> = C['accounts'][W]['safes'];
75
+ type RolesEntries<C extends CodegenData, W extends keyof C['accounts']> = C['accounts'][W]['rolesMods'];
76
+ type NodeType = 'SAFE' | 'ROLES' | 'DELAY';
77
+ /** A reference to a node used in `owners`, `modules`, `target`, etc. */
78
+ type NodeRef = Readonly<{
79
+ type: NodeType;
80
+ label: string;
81
+ chain: ChainId;
82
+ }>;
83
+ /** A blockchain address (checksummed or lowercase) or a reference to another
84
+ * node in the constellation. Values are normalized to lowercase before being
85
+ * sent to the API. */
86
+ type AddressOrRef = Address | NodeRef;
87
+ type NodeBase = Readonly<{
88
+ /** Human-readable identifier, unique within the constellation. */label: string; /** Chain the node is deployed on. */
89
+ chain: ChainId;
90
+ /** Set for existing nodes (from codegen or bound by address), absent for new
91
+ * nodes. Accepts checksummed or lowercase; normalized to lowercase on push. */
92
+ address?: Address; /** Deployment nonce — required for new nodes, optional for existing. */
93
+ nonce?: bigint;
94
+ }>;
95
+ /** A safe node spec — existing vault ref or new safe with required config. */
96
+ type SafeNode = NodeBase & Readonly<{
97
+ /** Discriminator identifying this node as a Safe. */type: 'SAFE'; /** Number of owner signatures required to execute a transaction. */
98
+ threshold: number; /** Safe owner addresses or node references. */
99
+ owners: readonly (string | NodeRef)[]; /** Module addresses or node references enabled on the safe. */
100
+ modules?: readonly (string | NodeRef)[]; /** Whether this safe shall appear as a vault in the workspace. @default false */
101
+ vault?: boolean;
102
+ }>;
103
+ /** A roles modifier node spec — existing vault ref or new roles with modifier config. */
104
+ type RolesNode = NodeBase & Readonly<{
105
+ /** Discriminator identifying this node as a Roles modifier. */type: 'ROLES'; /** The safe that this roles modifier controls. */
106
+ target?: AddressOrRef; /** The account that is allowed to update the configuration of the Roles mod. */
107
+ owner?: AddressOrRef; /** The account that calls will be executed from. */
108
+ avatar?: AddressOrRef; /** MultiSend contract addresses for batched transactions. */
109
+ multisend?: readonly Address[];
110
+ /** Role definitions configured on this modifier. Pass `null` for a key to
111
+ * clear that role; unmentioned roles are left untouched. */
112
+ roles?: Record<string, RoleDef | null>;
113
+ /** Spending allowances configured on this modifier, keyed by name. Pass
114
+ * `null` for a key to clear that allowance; unmentioned allowances are
115
+ * left untouched. */
116
+ allowances?: Record<string, AllowanceSpec | null>;
117
+ }>;
118
+ /** Any complete node that can be passed to `push()`. */
119
+ type ConstellationNode = SafeNode | RolesNode;
120
+ type NewSafeProps = {
121
+ /** Deployment nonce for CREATE2 address derivation. */nonce: bigint; /** Number of owner signatures required to execute a transaction. */
122
+ threshold: number; /** Safe owner addresses or node references. */
123
+ owners: readonly AddressOrRef[]; /** Module addresses or node references to enable on the safe. */
124
+ modules?: readonly AddressOrRef[]; /** Whether this safe is a workspace vault. @default false */
125
+ vault?: boolean;
126
+ };
127
+ /** Configuration shared by both ways of declaring a roles modifier. */
128
+ type RolesConfig = {
129
+ /** The safe that this roles modifier controls. Defaults to the new safe with the same label, when one exists. */target?: AddressOrRef; /** The account that calls will be executed from. Defaults to `target` value */
130
+ avatar?: AddressOrRef; /** The account that is allowed to update the configuration of the Roles Mod. Defaults to `target` value */
131
+ owner?: AddressOrRef; /** MultiSend contract addresses for batched transactions. Defaults to `['0x38869bf66a61cf6bdb996a6ae40d5853fd43b526', '0x9641d764fc13c8b624c04430c7356c1c7c8102e2']` */
132
+ multisend?: readonly Address[];
133
+ /** Role definitions to configure on this modifier. Pass `null` for a key to
134
+ * clear that role; unmentioned roles are left untouched. */
135
+ roles?: Record<string, RoleDef | null>;
136
+ /** Spending allowances to configure on this modifier, keyed by name. Pass
137
+ * `null` for a key to clear that allowance; unmentioned allowances are
138
+ * left untouched. */
139
+ allowances?: Record<string, AllowanceSpec | null>;
140
+ };
141
+ /** Declare a brand-new roles modifier (address derived via CREATE2 from `nonce`). */
142
+ type NewRolesByNonce = RolesConfig & {
143
+ /** Deployment nonce for CREATE2 address derivation. */nonce: bigint;
144
+ };
145
+ /** Bind to a roles modifier already deployed on-chain at a known address, to
146
+ * reconfigure its roles/allowances. Use this for mods not (yet) imported into
147
+ * the workspace — pass the address instead of a deployment `nonce`. */
148
+ type ExistingRolesByAddress = RolesConfig & {
149
+ /** Address of the existing Roles mod to bind to and reconfigure. */address: Address;
150
+ };
151
+ type NewRolesProps = NewRolesByNonce | ExistingRolesByAddress;
152
+ type ExistingNodeAccessor<Type extends string, K extends string, E, Ch extends ChainId, NP extends Record<string, any>> = Readonly<Prettify<E & {
153
+ type: Type;
154
+ label: K;
155
+ chain: Ch;
156
+ }>> & (<O extends { [P in Exclude<keyof E & string, 'id' | 'label'>]?: any } & Partial<NP> = {}>(overrides?: { [P in Exclude<keyof E & string, 'id' | 'label'>]?: any } & Partial<NP> & O) => Readonly<Prettify<Omit<E, keyof O> & O & Partial<NP> & {
157
+ type: Type;
158
+ label: K;
159
+ chain: Ch;
160
+ }>>);
161
+ type NewNodeAccessor<Type extends string, Ch extends ChainId, NP extends Record<string, any>> = Readonly<Prettify<{
162
+ type: Type;
163
+ label: string;
164
+ chain: Ch;
165
+ }>> & (<P extends NP>(props: P) => Readonly<Prettify<P & {
166
+ type: Type;
167
+ label: string;
168
+ chain: Ch;
169
+ }>>);
170
+ type EntityAccessor<Type extends string, Entries extends Record<string, any>, Ch extends ChainId = ChainId, NP extends Record<string, any> = Record<string, any>> = { readonly [K in keyof Entries & string]: ExistingNodeAccessor<Type, K, Entries[K], Ch, NP> } & {
171
+ readonly [key: string]: NewNodeAccessor<Type, Ch, NP>;
172
+ };
173
+ type UserAccessor<C extends CodegenData, Ch extends number> = { readonly [K in keyof C['users'] & string]: C['users'][K]['personalSafes'][Ch]['address'] };
174
+ type ConstellationResult<C extends CodegenData, W extends keyof C['accounts'] = keyof C['accounts'], Ch extends ChainId = ChainId> = {
175
+ /** Access existing safes by label or create new ones with a new label.
176
+ * Only SAFE-typed accounts are suggested in IntelliSense. */
177
+ safe: EntityAccessor<'SAFE', SafeEntries<C, W>, Ch, NewSafeProps>;
178
+ /** Access existing roles modifiers by label or create new ones with a
179
+ * new label. Only ROLES-typed accounts are suggested in IntelliSense. */
180
+ roles: EntityAccessor<'ROLES', RolesEntries<C, W>, Ch, NewRolesProps>; /** Resolve a user's personal safe address on the constellation's chain. */
181
+ user: UserAccessor<C, Ch>;
182
+ };
183
+ /** @internal */
184
+ /**
185
+ * Creates a constellation scoped to a workspace and chain.
186
+ *
187
+ * Use bracket access to reference existing accounts (vaults and other
188
+ * applied constellation nodes) or define new ones:
189
+ * ```ts
190
+ * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })
191
+ *
192
+ * const dao = eth.safe['GG DAO'] // existing account ref
193
+ * const roles = eth.roles['GG DAO'] // existing roles ref
194
+ * const newSafe = eth.safe['New Safe']({ nonce: 0n, threshold: 2, owners: [...], modules: [...] })
195
+ * ```
196
+ */
197
+ declare function constellation<const C extends CodegenData = GeneratedCodegen, const W extends keyof C['accounts'] & string = keyof C['accounts'] & string, const Ch extends ChainId = ChainId>(opts: ConstellationOpts<C> & {
198
+ workspace: W;
199
+ chain: Ch;
200
+ }, internal?: ConstellationInternalOpts<C>): ConstellationResult<C, W, Ch>;
201
+ //#endregion
202
+ //#region src/permissionEntries.d.ts
203
+ /**
204
+ * An allowance key. Authored as a plain label, the way a role key is, and
205
+ * encoded to bytes32 when the constellation is deployed. An already-encoded
206
+ * key still works — `encodeKey` passes 32-byte hex through unchanged.
207
+ */
208
+ type AllowanceKey = string;
209
+ type SwapEntry = {
210
+ label: string;
211
+ action: {
212
+ type: 'swap';
213
+ sell: Address[];
214
+ buy: Address[];
215
+ };
216
+ };
217
+ type TransferEntry = {
218
+ label: string;
219
+ action: {
220
+ type: 'transfer';
221
+ tokens: Address[];
222
+ to: Address[];
223
+ bridge?: {
224
+ chain: ChainId;
225
+ to: Address[];
226
+ receive: Address[];
227
+ }[];
228
+ allowance?: AllowanceKey;
229
+ };
230
+ };
231
+ /**
232
+ * A permission's target, written either as an address or as another node in the
233
+ * same constellation.
234
+ *
235
+ * A policy is usually pushed alongside the accounts it governs, so the address
236
+ * a permission points at often does not exist yet. Naming the node instead
237
+ * sends a reference, and the address is substituted once it is derived at
238
+ * deploy — the same way a node is named as an owner, a module or an avatar.
239
+ */
240
+ type PermissionTarget = `0x${string}` | ConstellationNode;
241
+ type WithNodeTarget<P> = P extends {
242
+ targetAddress: `0x${string}`;
243
+ } ? Omit<P, 'targetAddress'> & {
244
+ targetAddress: PermissionTarget;
245
+ } : P;
246
+ /** A `zodiac-roles-sdk` permission that may name a node as its target. */
247
+ type ConstellationPermission = WithNodeTarget<Permission>;
248
+ type LabelledPermissions = {
249
+ label: string;
250
+ permissions: ConstellationPermission[];
251
+ };
252
+ type DeFiKitEntry = {
253
+ label: string;
254
+ defiKit: {
255
+ protocol: string;
256
+ verb: string;
257
+ params: Record<string, DeFiKitParamValue>;
258
+ };
259
+ };
260
+ type DeFiKitParamValue = string | number | boolean | readonly (string | number | boolean)[] | null | undefined;
261
+ /**
262
+ * One entry of a role's `permissions`. A bare permission stays valid, but every
263
+ * labelled form names something the app can show as a single card.
264
+ */
265
+ type PermissionEntry = ConstellationPermission | LabelledPermissions | SwapEntry | TransferEntry | DeFiKitEntry;
266
+ /** A role's full permission list — the type `permissions.ts` files satisfy. */
267
+ type Permissions = readonly PermissionEntry[];
268
+ //#endregion
269
+ //#region src/actions.d.ts
270
+ /**
271
+ * Allows signing CoW orders selling any of `sell` for any of `buy`. The
272
+ * parameters are compiled into permissions when the constellation is deployed,
273
+ * never here — a stored revision that carried its own copy would go stale as
274
+ * soon as the compiler changed.
275
+ */
276
+ declare const swap: ({
277
+ label,
278
+ sell,
279
+ buy
280
+ }: SwapParams) => SwapEntry;
281
+ /**
282
+ * Allows transferring any of `tokens` to any of `to` on the role's own chain,
283
+ * and — through `bridge` — to recipients on other chains over Across. Both are
284
+ * optional on their own; a transfer names at least one recipient somewhere.
285
+ */
286
+ declare const transfer: ({
287
+ label,
288
+ tokens,
289
+ to,
290
+ bridge,
291
+ allowance
292
+ }: TransferParams) => TransferEntry;
293
+ /**
294
+ * A labelled bag of `allow`-kit permissions — the code-side counterpart of the
295
+ * app's Custom action, for everything the other action types don't cover. It
296
+ * takes plain permissions, not other actions.
297
+ */
298
+ declare const custom: ({
299
+ label,
300
+ permissions
301
+ }: CustomParams) => LabelledPermissions;
302
+ /**
303
+ * The DeFi Kit allow kit — same protocol, verb and parameter surface, plus a
304
+ * `label`. A DeFi Kit entry is nothing but its annotation: the permissions are
305
+ * fetched from the annotation's uri at deploy, so nothing is fetched here.
306
+ *
307
+ * Protocols and parameters are typed against the Ethereum kit, the widest of
308
+ * the chains DeFi Kit serves. Values that only exist on another chain still
309
+ * type-check; the API validates them when the annotation is resolved.
310
+ */
311
+ declare const defikit: DeFiKit;
312
+ type SwapParams = {
313
+ /** Names the action in the app. Never reaches the chain. */label: string; /** Tokens the role may sell. */
314
+ sell: readonly Address[]; /** Tokens the role may buy. */
315
+ buy: readonly Address[];
316
+ };
317
+ type TransferParams = {
318
+ /** Names the action in the app. Never reaches the chain. */label: string; /** Tokens the role may transfer. Use the zero address for the native token. */
319
+ tokens: readonly Address[];
320
+ /** Addresses the role may transfer to on the role's own chain. A node stands
321
+ * for its address, so only nodes that already have one — an account from your
322
+ * codegen, or one bound by address — can be named here. */
323
+ to?: readonly Recipient[]; /** Destinations on other chains, bridged over Across. */
324
+ bridge?: readonly BridgeTarget[];
325
+ /** Allowance capping what may be sent. Pass the allowance declared on the
326
+ * roles modifier, or its key. */
327
+ allowance?: AllowanceSpec | AllowanceKey;
328
+ };
329
+ type CustomParams = {
330
+ /** Names the action in the app. Never reaches the chain. */label: string;
331
+ permissions: readonly ConstellationPermission[];
332
+ };
333
+ /** An address, or a node that already knows the address it lives at. */
334
+ type Recipient = Address | {
335
+ readonly address: Address;
336
+ readonly chain?: ChainId;
337
+ };
338
+ type BridgeTarget = {
339
+ /** The destination chain. Inferred from the recipients when they are nodes. */chain?: ChainId; /** Addresses the role may bridge to on the destination chain. */
340
+ to: readonly Recipient[]; /** Tokens the role may receive on the destination chain. */
341
+ receive: readonly Address[];
342
+ };
343
+ type EthereumKit = typeof allow;
344
+ type DeFiKit = { readonly [Protocol in keyof EthereumKit]: { readonly [Verb in keyof EthereumKit[Protocol]]: DeFiKitVerb<EthereumKit[Protocol][Verb]> } };
345
+ type DeFiKitVerb<Verb> = Verb extends ((params: infer Params) => unknown) ? (params: Params & {
346
+ label: string;
347
+ }) => DeFiKitEntry : (params: {
348
+ label: string;
349
+ }) => DeFiKitEntry;
350
+ //#endregion
351
+ export { PermissionEntry as a, ConstellationNode as c, transfer as i, constellation as l, defikit as n, PermissionTarget as o, swap as r, Permissions as s, custom as t };
352
+ //# sourceMappingURL=actions-Dn5NC3oq.d.mts.map
@@ -0,0 +1,2 @@
1
+ import { a as PermissionEntry, i as transfer, n as defikit, o as PermissionTarget, r as swap, s as Permissions, t as custom } from "./actions-Dn5NC3oq.mjs";
2
+ export { PermissionEntry, PermissionTarget, Permissions, custom, defikit, swap, transfer };
@@ -0,0 +1,92 @@
1
+ import { r as recipientChains } from "./permissionEntries-D3fSVhwO.mjs";
2
+ //#region src/actions.ts
3
+ /**
4
+ * Allows signing CoW orders selling any of `sell` for any of `buy`. The
5
+ * parameters are compiled into permissions when the constellation is deployed,
6
+ * never here — a stored revision that carried its own copy would go stale as
7
+ * soon as the compiler changed.
8
+ */
9
+ const swap = ({ label, sell, buy }) => ({
10
+ label,
11
+ action: {
12
+ type: "swap",
13
+ sell: [...sell],
14
+ buy: [...buy]
15
+ }
16
+ });
17
+ /**
18
+ * Allows transferring any of `tokens` to any of `to` on the role's own chain,
19
+ * and — through `bridge` — to recipients on other chains over Across. Both are
20
+ * optional on their own; a transfer names at least one recipient somewhere.
21
+ */
22
+ const transfer = ({ label, tokens, to = [], bridge = [], allowance }) => {
23
+ const entry = {
24
+ label,
25
+ action: {
26
+ type: "transfer",
27
+ tokens: [...tokens],
28
+ to: to.map(toAddress),
29
+ ...bridge.length > 0 && { bridge: bridge.map(toBridgeTarget) },
30
+ ...allowance != null && { allowance: allowanceKey(allowance) }
31
+ }
32
+ };
33
+ const chains = to.flatMap((recipient) => typeof recipient === "string" || recipient.chain == null ? [] : [recipient.chain]);
34
+ if (chains.length > 0) Object.defineProperty(entry, recipientChains, {
35
+ value: chains,
36
+ enumerable: false
37
+ });
38
+ return entry;
39
+ };
40
+ /**
41
+ * A labelled bag of `allow`-kit permissions — the code-side counterpart of the
42
+ * app's Custom action, for everything the other action types don't cover. It
43
+ * takes plain permissions, not other actions.
44
+ */
45
+ const custom = ({ label, permissions }) => ({
46
+ label,
47
+ permissions: [...permissions]
48
+ });
49
+ /**
50
+ * The DeFi Kit allow kit — same protocol, verb and parameter surface, plus a
51
+ * `label`. A DeFi Kit entry is nothing but its annotation: the permissions are
52
+ * fetched from the annotation's uri at deploy, so nothing is fetched here.
53
+ *
54
+ * Protocols and parameters are typed against the Ethereum kit, the widest of
55
+ * the chains DeFi Kit serves. Values that only exist on another chain still
56
+ * type-check; the API validates them when the annotation is resolved.
57
+ */
58
+ const defikit = new Proxy({}, { get: (_, protocol) => new Proxy({}, { get: (_, verb) => ({ label, ...params }) => ({
59
+ label,
60
+ defiKit: {
61
+ protocol,
62
+ verb,
63
+ params
64
+ }
65
+ }) }) });
66
+ const allowanceKey = (allowance) => typeof allowance === "string" ? allowance : allowance.key;
67
+ const toAddress = (recipient) => typeof recipient === "string" ? recipient : recipient.address;
68
+ /**
69
+ * A node knows the chain it lives on, so a bridge target written with nodes
70
+ * needs no `chain` of its own. Naming one anyway is allowed — a plain address
71
+ * carries no chain — but a node that disagrees with it is a mistake worth
72
+ * catching here rather than at deploy.
73
+ */
74
+ const toBridgeTarget = ({ chain, to, receive }) => ({
75
+ chain: destinationChain(chain, to),
76
+ to: to.map(toAddress),
77
+ receive: [...receive]
78
+ });
79
+ const destinationChain = (chain, recipients) => {
80
+ let destination = chain;
81
+ for (const recipient of recipients) {
82
+ if (typeof recipient === "string" || recipient.chain == null) continue;
83
+ if (destination != null && destination !== recipient.chain) throw new Error(`A bridge target has one destination, but this one spans chains "${destination}" and "${recipient.chain}"`);
84
+ destination = recipient.chain;
85
+ }
86
+ if (destination == null) throw new Error("A bridge target needs a `chain` unless its recipients are nodes that know one");
87
+ return destination;
88
+ };
89
+ //#endregion
90
+ export { custom, defikit, swap, transfer };
91
+
92
+ //# sourceMappingURL=actions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.mjs","names":[],"sources":["../src/actions.ts"],"sourcesContent":["import type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { allow as ethereumKit } from 'defi-kit/eth'\nimport { recipientChains } from './permissionEntries'\nimport type {\n AllowanceKey,\n ConstellationPermission,\n DeFiKitEntry,\n LabelledPermissions,\n PermissionEntry,\n PermissionTarget,\n SwapEntry,\n TransferEntry,\n} from './permissionEntries'\nimport type { AllowanceSpec } from './types'\n\nexport type { PermissionEntry, PermissionTarget }\nexport type { Permissions } from './permissionEntries'\n\n/**\n * Allows signing CoW orders selling any of `sell` for any of `buy`. The\n * parameters are compiled into permissions when the constellation is deployed,\n * never here — a stored revision that carried its own copy would go stale as\n * soon as the compiler changed.\n */\nexport const swap = ({ label, sell, buy }: SwapParams): SwapEntry => ({\n label,\n action: { type: 'swap', sell: [...sell], buy: [...buy] },\n})\n\n/**\n * Allows transferring any of `tokens` to any of `to` on the role's own chain,\n * and — through `bridge` — to recipients on other chains over Across. Both are\n * optional on their own; a transfer names at least one recipient somewhere.\n */\nexport const transfer = ({\n label,\n tokens,\n to = [],\n bridge = [],\n allowance,\n}: TransferParams): TransferEntry => {\n const entry: TransferEntry = {\n label,\n action: {\n type: 'transfer',\n tokens: [...tokens],\n to: to.map(toAddress),\n ...(bridge.length > 0 && { bridge: bridge.map(toBridgeTarget) }),\n ...(allowance != null && { allowance: allowanceKey(allowance) }),\n },\n }\n\n // Remember which chains the `to` nodes live on so push() can refuse a\n // recipient from another chain. Symbol-keyed: never serialized.\n const chains = to.flatMap((recipient) =>\n typeof recipient === 'string' || recipient.chain == null\n ? []\n : [recipient.chain]\n )\n\n if (chains.length > 0) {\n Object.defineProperty(entry, recipientChains, {\n value: chains,\n enumerable: false,\n })\n }\n\n return entry\n}\n\n/**\n * A labelled bag of `allow`-kit permissions — the code-side counterpart of the\n * app's Custom action, for everything the other action types don't cover. It\n * takes plain permissions, not other actions.\n */\nexport const custom = ({\n label,\n permissions,\n}: CustomParams): LabelledPermissions => ({\n label,\n permissions: [...permissions],\n})\n\n/**\n * The DeFi Kit allow kit — same protocol, verb and parameter surface, plus a\n * `label`. A DeFi Kit entry is nothing but its annotation: the permissions are\n * fetched from the annotation's uri at deploy, so nothing is fetched here.\n *\n * Protocols and parameters are typed against the Ethereum kit, the widest of\n * the chains DeFi Kit serves. Values that only exist on another chain still\n * type-check; the API validates them when the annotation is resolved.\n */\nexport const defikit: DeFiKit = new Proxy({} as DeFiKit, {\n get: (_, protocol: string) =>\n new Proxy(\n {},\n {\n get:\n (_, verb: string) =>\n ({ label, ...params }: { label: string }) => ({\n label,\n defiKit: { protocol, verb, params },\n }),\n }\n ),\n})\n\nconst allowanceKey = (allowance: AllowanceSpec | AllowanceKey) =>\n typeof allowance === 'string' ? allowance : allowance.key\n\nconst toAddress = (recipient: Recipient): Address =>\n typeof recipient === 'string' ? recipient : recipient.address\n\n/**\n * A node knows the chain it lives on, so a bridge target written with nodes\n * needs no `chain` of its own. Naming one anyway is allowed — a plain address\n * carries no chain — but a node that disagrees with it is a mistake worth\n * catching here rather than at deploy.\n */\nconst toBridgeTarget = ({ chain, to, receive }: BridgeTarget) => ({\n chain: destinationChain(chain, to),\n to: to.map(toAddress),\n receive: [...receive],\n})\n\nconst destinationChain = (\n chain: ChainId | undefined,\n recipients: readonly Recipient[]\n): ChainId => {\n let destination = chain\n\n for (const recipient of recipients) {\n if (typeof recipient === 'string' || recipient.chain == null) {\n continue\n }\n\n if (destination != null && destination !== recipient.chain) {\n throw new Error(\n `A bridge target has one destination, but this one spans chains \"${destination}\" and \"${recipient.chain}\"`\n )\n }\n\n destination = recipient.chain\n }\n\n if (destination == null) {\n throw new Error(\n 'A bridge target needs a `chain` unless its recipients are nodes that know one'\n )\n }\n\n return destination\n}\n\ntype SwapParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n /** Tokens the role may sell. */\n sell: readonly Address[]\n /** Tokens the role may buy. */\n buy: readonly Address[]\n}\n\ntype TransferParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n /** Tokens the role may transfer. Use the zero address for the native token. */\n tokens: readonly Address[]\n /** Addresses the role may transfer to on the role's own chain. A node stands\n * for its address, so only nodes that already have one — an account from your\n * codegen, or one bound by address — can be named here. */\n to?: readonly Recipient[]\n /** Destinations on other chains, bridged over Across. */\n bridge?: readonly BridgeTarget[]\n /** Allowance capping what may be sent. Pass the allowance declared on the\n * roles modifier, or its key. */\n allowance?: AllowanceSpec | AllowanceKey\n}\n\ntype CustomParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n permissions: readonly ConstellationPermission[]\n}\n\n/** An address, or a node that already knows the address it lives at. */\ntype Recipient =\n | Address\n | { readonly address: Address; readonly chain?: ChainId }\n\ntype BridgeTarget = {\n /** The destination chain. Inferred from the recipients when they are nodes. */\n chain?: ChainId\n /** Addresses the role may bridge to on the destination chain. */\n to: readonly Recipient[]\n /** Tokens the role may receive on the destination chain. */\n receive: readonly Address[]\n}\n\ntype EthereumKit = typeof ethereumKit\n\ntype DeFiKit = {\n readonly [Protocol in keyof EthereumKit]: {\n readonly [Verb in keyof EthereumKit[Protocol]]: DeFiKitVerb<\n EthereumKit[Protocol][Verb]\n >\n }\n}\n\ntype DeFiKitVerb<Verb> = Verb extends (params: infer Params) => unknown\n ? (params: Params & { label: string }) => DeFiKitEntry\n : (params: { label: string }) => DeFiKitEntry\n"],"mappings":";;;;;;;;AAwBA,MAAa,QAAQ,EAAE,OAAO,MAAM,WAAkC;CACpE;CACA,QAAQ;EAAE,MAAM;EAAQ,MAAM,CAAC,GAAG,KAAK;EAAE,KAAK,CAAC,GAAG,IAAI;EAAE;CACzD;;;;;;AAOD,MAAa,YAAY,EACvB,OACA,QACA,KAAK,EAAE,EACP,SAAS,EAAE,EACX,gBACmC;CACnC,MAAM,QAAuB;EAC3B;EACA,QAAQ;GACN,MAAM;GACN,QAAQ,CAAC,GAAG,OAAO;GACnB,IAAI,GAAG,IAAI,UAAU;GACrB,GAAI,OAAO,SAAS,KAAK,EAAE,QAAQ,OAAO,IAAI,eAAe,EAAE;GAC/D,GAAI,aAAa,QAAQ,EAAE,WAAW,aAAa,UAAU,EAAE;GAChE;EACF;CAID,MAAM,SAAS,GAAG,SAAS,cACzB,OAAO,cAAc,YAAY,UAAU,SAAS,OAChD,EAAE,GACF,CAAC,UAAU,MAAM,CACtB;AAED,KAAI,OAAO,SAAS,EAClB,QAAO,eAAe,OAAO,iBAAiB;EAC5C,OAAO;EACP,YAAY;EACb,CAAC;AAGJ,QAAO;;;;;;;AAQT,MAAa,UAAU,EACrB,OACA,mBACwC;CACxC;CACA,aAAa,CAAC,GAAG,YAAY;CAC9B;;;;;;;;;;AAWD,MAAa,UAAmB,IAAI,MAAM,EAAE,EAAa,EACvD,MAAM,GAAG,aACP,IAAI,MACF,EAAE,EACF,EACE,MACG,GAAG,UACH,EAAE,OAAO,GAAG,cAAiC;CAC5C;CACA,SAAS;EAAE;EAAU;EAAM;EAAQ;CACpC,GACJ,CACF,EACJ,CAAC;AAEF,MAAM,gBAAgB,cACpB,OAAO,cAAc,WAAW,YAAY,UAAU;AAExD,MAAM,aAAa,cACjB,OAAO,cAAc,WAAW,YAAY,UAAU;;;;;;;AAQxD,MAAM,kBAAkB,EAAE,OAAO,IAAI,eAA6B;CAChE,OAAO,iBAAiB,OAAO,GAAG;CAClC,IAAI,GAAG,IAAI,UAAU;CACrB,SAAS,CAAC,GAAG,QAAQ;CACtB;AAED,MAAM,oBACJ,OACA,eACY;CACZ,IAAI,cAAc;AAElB,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,OAAO,cAAc,YAAY,UAAU,SAAS,KACtD;AAGF,MAAI,eAAe,QAAQ,gBAAgB,UAAU,MACnD,OAAM,IAAI,MACR,mEAAmE,YAAY,SAAS,UAAU,MAAM,GACzG;AAGH,gBAAc,UAAU;;AAG1B,KAAI,eAAe,KACjB,OAAM,IAAI,MACR,gFACD;AAGH,QAAO"}
@@ -1,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-DoZbhpS6.mjs";
1
+ import { a as EVERYTHING, c as Scoping, i as ConditionFunction, l as TargetPermission, n as ChainPrefix, o as FunctionPermission, r as chainIdFor, s as Options, t as CHAIN_IDS, u as buildAllowKit } from "../index-CnH26Z_A.mjs";
2
2
  export { CHAIN_IDS, ChainPrefix, ConditionFunction, EVERYTHING, FunctionPermission, Options, Scoping, TargetPermission, buildAllowKit, chainIdFor };
@@ -1,3 +1,3 @@
1
- import { n as chainIdFor, t as CHAIN_IDS } from "../networks-BTW1qAAa.mjs";
2
- import { n as EVERYTHING, t as buildAllowKit } from "../allow-CzPpzclq.mjs";
1
+ import { n as chainIdFor, t as CHAIN_IDS } from "../networks-B0H4Uoyh.mjs";
2
+ import { n as EVERYTHING, t as buildAllowKit } from "../allow-CrDzDtxx.mjs";
3
3
  export { CHAIN_IDS, EVERYTHING, buildAllowKit, chainIdFor };
@@ -1,4 +1,4 @@
1
- import { a as walkContracts, i as readAbi } from "./networks-BTW1qAAa.mjs";
1
+ import { a as walkContracts, i as readAbi } from "./networks-B0H4Uoyh.mjs";
2
2
  import { Operator, ParameterType, c, coercePermission } from "zodiac-roles-sdk";
3
3
  import { Interface, isError } from "ethers";
4
4
  //#region src/allow/types.ts
@@ -13,7 +13,7 @@ function buildAllowKit(abisDir, contractsConfig) {
13
13
  attachAt(kit, [node.chain, ...node.segments], missingAbiProxy(node));
14
14
  continue;
15
15
  }
16
- attachAt(kit, [node.chain, ...node.segments], makeAllowContract(node.address, abi));
16
+ attachAt(kit, [node.chain, ...node.segments], makeAllowContract(node.address, abi, [node.chain, ...node.segments].join(".")));
17
17
  }
18
18
  return kit;
19
19
  }
@@ -35,9 +35,19 @@ function missingAbiProxy(node) {
35
35
  has: explain
36
36
  });
37
37
  }
38
- function makeAllowContract(address, abi) {
38
+ function makeAllowContract(address, abi, path) {
39
39
  const iface = Interface.from(abi);
40
40
  const lowerAddr = address.toLowerCase();
41
+ const overloads = /* @__PURE__ */ new Map();
42
+ iface.forEachFunction((fn) => {
43
+ if (fn.stateMutability === "view" || fn.stateMutability === "pure") return;
44
+ overloads.set(fn.name, [...overloads.get(fn.name) ?? [], fn]);
45
+ });
46
+ const ambiguous = (prop) => (overloads.get(prop)?.length ?? 0) > 1;
47
+ const explainAmbiguity = (prop) => {
48
+ const signatures = overloads.get(prop).map((fn) => fn.format("sighash"));
49
+ throw new Error(`\`allow.${path}.${prop}\` is ambiguous: ${address} has ${signatures.length} functions named "${prop}". Name the overload you mean by its full signature — ` + signatures.map((sig) => `allow.${path}[${JSON.stringify(sig)}]`).join(" or ") + `.`);
50
+ };
41
51
  const allowEverything = (options) => ({
42
52
  targetAddress: lowerAddr,
43
53
  send: options?.send,
@@ -57,12 +67,13 @@ function makeAllowContract(address, abi) {
57
67
  get: (_target, prop) => {
58
68
  if (prop === EVERYTHING) return allowEverything;
59
69
  if (typeof prop !== "string") return void 0;
70
+ if (ambiguous(prop)) explainAmbiguity(prop);
60
71
  if (!has(prop)) return void 0;
61
72
  return makeAllowFunction(iface.getFunction(prop), lowerAddr);
62
73
  },
63
74
  has: (_target, prop) => {
64
75
  if (prop === EVERYTHING) return true;
65
- return typeof prop === "string" && has(prop);
76
+ return typeof prop === "string" && !ambiguous(prop) && has(prop);
66
77
  }
67
78
  });
68
79
  }
@@ -118,4 +129,4 @@ const applyOptions = (permission, options) => {
118
129
  //#endregion
119
130
  export { EVERYTHING as n, buildAllowKit as t };
120
131
 
121
- //# sourceMappingURL=allow-CzPpzclq.mjs.map
132
+ //# sourceMappingURL=allow-CrDzDtxx.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allow-CrDzDtxx.mjs","names":[],"sources":["../src/allow/types.ts","../src/allow/runtime.ts"],"sourcesContent":["import type { BigNumberish, BytesLike, ParamType } from 'ethers'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\n\nexport type Options = {\n send?: boolean\n delegatecall?: boolean\n etherWithinAllowance?: `0x${string}`\n callWithinAllowance?: `0x${string}`\n}\n\nexport type PrimitiveValue = BigNumberish | BytesLike | string | boolean\n\n// Signature matches `zodiac-roles-sdk` so values from `c.*` are assignable.\nexport type ConditionFunction<T = unknown> = (\n abiType: ParamType,\n _?: T\n) => Condition\n\ntype RequireAtLeastOne<T> = {\n [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>\n}[keyof T]\n\ntype ArrayElement<T extends readonly unknown[]> = T extends readonly (infer U)[]\n ? U\n : never\n\nexport type PrimitiveScoping<T extends PrimitiveValue> =\n | T\n | ConditionFunction<T>\n\nexport type ArrayScoping<T extends readonly any[]> =\n | readonly Scoping<ArrayElement<T>>[]\n | ConditionFunction<T>\n\nexport type StructScoping<T extends { [key: string]: any }> =\n | RequireAtLeastOne<{ [K in keyof T]?: Scoping<T[K]> }>\n | ConditionFunction<T>\n\nexport type Scoping<T> = T extends PrimitiveValue\n ? PrimitiveScoping<T>\n : T extends readonly any[]\n ? ArrayScoping<T>\n : T extends { [key: string]: any }\n ? StructScoping<T>\n : unknown\n\nexport type { FunctionPermission, TargetPermission }\n\nexport const EVERYTHING = Symbol.for('@zodiaceco/allow-kit/EVERYTHING')\nexport type EVERYTHING = typeof EVERYTHING\n","import { Interface, FunctionFragment, isError, type InterfaceAbi } from 'ethers'\nimport { c, coercePermission, ParameterType, Operator } from 'zodiac-roles-sdk'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\nimport { readAbi, walkContracts, type ContractNode } from './abi'\nimport { EVERYTHING, type Options } from './types'\n\nexport function buildAllowKit(\n abisDir: string,\n contractsConfig: Record<string, any>\n): Record<string, any> {\n const kit: Record<string, any> = {}\n for (const node of walkContracts(contractsConfig)) {\n const abi = readAbi(abisDir, node)\n if (!abi) {\n // Defer the error until the user touches this contract — otherwise an\n // ABI missing for one chain crashes all unrelated role definitions.\n attachAt(kit, [node.chain, ...node.segments], missingAbiProxy(node))\n continue\n }\n attachAt(\n kit,\n [node.chain, ...node.segments],\n makeAllowContract(\n node.address,\n abi as InterfaceAbi,\n [node.chain, ...node.segments].join('.')\n )\n )\n }\n return kit\n}\n\nfunction attachAt(root: Record<string, any>, segments: string[], value: any) {\n let cursor = root\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!\n if (!(seg in cursor)) cursor[seg] = {}\n cursor = cursor[seg]\n }\n cursor[segments[segments.length - 1]!] = value\n}\n\nfunction missingAbiProxy(node: ContractNode) {\n const explain = () => {\n throw new Error(\n `ABI missing for ${node.chain}.${node.segments.join('.')} ` +\n `(${node.address}). Run \\`zodiac pull-contracts\\` to fetch it, or ` +\n `paste the ABI JSON manually at <abisDir>/${node.chain}/${node.segments.join('/')}.json`\n )\n }\n return new Proxy(\n {},\n {\n get: explain,\n has: explain,\n }\n )\n}\n\nfunction makeAllowContract(\n address: `0x${string}`,\n abi: InterfaceAbi,\n path: string\n): Record<string | symbol, any> {\n const iface = Interface.from(abi)\n const lowerAddr = address.toLowerCase() as `0x${string}`\n\n // Permissionable functions grouped by bare name, so an ambiguous one can be\n // told apart from an unknown one. `Interface.getFunction` reports both as\n // INVALID_ARGUMENT, and the two want opposite answers: an unknown name is\n // simply not a member, an ambiguous one is a member the caller has not\n // finished naming.\n const overloads = new Map<string, FunctionFragment[]>()\n iface.forEachFunction((fn) => {\n if (fn.stateMutability === 'view' || fn.stateMutability === 'pure') return\n overloads.set(fn.name, [...(overloads.get(fn.name) ?? []), fn])\n })\n\n const ambiguous = (prop: string) => (overloads.get(prop)?.length ?? 0) > 1\n\n const explainAmbiguity = (prop: string): never => {\n const signatures = overloads.get(prop)!.map((fn) => fn.format('sighash'))\n throw new Error(\n `\\`allow.${path}.${prop}\\` is ambiguous: ${address} has ` +\n `${signatures.length} functions named \"${prop}\". Name the overload ` +\n `you mean by its full signature — ` +\n signatures\n .map((sig) => `allow.${path}[${JSON.stringify(sig)}]`)\n .join(' or ') +\n `.`\n )\n }\n\n const allowEverything = (options?: Options): TargetPermission => ({\n targetAddress: lowerAddr,\n send: options?.send,\n delegatecall: options?.delegatecall,\n })\n\n const has = (name: string) => {\n try {\n const fn = iface.getFunction(name)\n if (!fn) return false\n return fn.stateMutability !== 'view' && fn.stateMutability !== 'pure'\n } catch (error) {\n if (!isError(error as any, 'INVALID_ARGUMENT')) throw error\n return false\n }\n }\n\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === EVERYTHING) return allowEverything\n if (typeof prop !== 'string') return undefined\n if (ambiguous(prop)) explainAmbiguity(prop)\n if (!has(prop)) return undefined\n const fn = iface.getFunction(prop)!\n return makeAllowFunction(fn, lowerAddr)\n },\n // An ambiguous bare name is not a member — only the full signatures are.\n // `in` answers that question; reaching for the property explains it.\n has: (_target, prop) => {\n if (prop === EVERYTHING) return true\n return typeof prop === 'string' && !ambiguous(prop) && has(prop)\n },\n }\n )\n}\n\nfunction makeAllowFunction(\n fn: FunctionFragment,\n targetAddress: `0x${string}`\n): (...args: any[]) => FunctionPermission {\n const inputs = fn.inputs\n return (...args: any[]) => {\n const scopings = args.slice(0, inputs.length)\n const hasScopings = scopings.some((s) => s !== undefined && s !== null)\n const options: Options = args[inputs.length] ?? {}\n const condition = hasScopings\n ? c.calldataMatches(scopings, inputs)()\n : undefined\n const preset = {\n targetAddress,\n signature: fn.format('sighash'),\n condition,\n }\n return applyOptions(coercePermission(preset as any) as any, options)\n }\n}\n\nconst emptyCalldataMatches: Condition = {\n paramType: ParameterType.Calldata,\n operator: Operator.Matches,\n children: [],\n}\n\nconst applyGlobalAllowance = (\n condition: Condition | undefined,\n allowanceCondition: Condition\n): Condition => {\n const base = condition ?? emptyCalldataMatches\n if (\n base.paramType !== ParameterType.Calldata ||\n base.operator !== Operator.Matches\n ) {\n throw new Error(\n 'Global allowance can only be applied to calldata matches nodes'\n )\n }\n return {\n ...base,\n children: [...(base.children ?? []), allowanceCondition],\n }\n}\n\nconst applyOptions = (\n permission: FunctionPermission & { condition?: Condition },\n options: Options\n): FunctionPermission => {\n let condition = permission.condition\n if (options.etherWithinAllowance) {\n if (!options.send) {\n throw new Error(\n '`etherWithinAllowance` can only be used if `send` is allowed'\n )\n }\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.EtherWithinAllowance,\n compValue: options.etherWithinAllowance,\n })\n }\n if (options.callWithinAllowance) {\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.CallWithinAllowance,\n compValue: options.callWithinAllowance,\n })\n }\n return {\n ...permission,\n send: options.send,\n delegatecall: options.delegatecall,\n condition,\n }\n}\n"],"mappings":"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"}