@zodiaceco/sdk 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -6
- package/dist/{actions-tA_Oe_kC.d.mts → actions-Cri7InV-.d.mts} +57 -4
- package/dist/actions.d.mts +1 -1
- package/dist/actions.mjs +18 -2
- package/dist/actions.mjs.map +1 -1
- package/dist/allow/index.d.mts +1 -1
- package/dist/allow/index.mjs +2 -2
- package/dist/{allow-CrDzDtxx.mjs → allow-DX3iyizB.mjs} +2 -2
- package/dist/{allow-CrDzDtxx.mjs.map → allow-DX3iyizB.mjs.map} +1 -1
- package/dist/cli/config.mjs +1 -1
- package/dist/cli/push.d.mts +24 -0
- package/dist/cli/push.mjs +36 -0
- package/dist/cli/push.mjs.map +1 -0
- package/dist/cli.mjs +15 -6
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-D0FqysME.mjs → config-BaChz2bq.mjs} +10 -5
- package/dist/config-BaChz2bq.mjs.map +1 -0
- package/dist/{index-CnH26Z_A.d.mts → index-D49x1ozw.d.mts} +1 -1
- package/dist/index.d.mts +34 -5
- package/dist/index.mjs +8 -180
- package/dist/index.mjs.map +1 -1
- package/dist/{networks-B0H4Uoyh.mjs → networks-UKtJ8QDG.mjs} +1 -1
- package/dist/{networks-B0H4Uoyh.mjs.map → networks-UKtJ8QDG.mjs.map} +1 -1
- package/dist/paths-LsD6ULk0.mjs +34 -0
- package/dist/paths-LsD6ULk0.mjs.map +1 -0
- package/dist/{permissionEntries-D3fSVhwO.mjs → permissionEntries-C4dAwZBb.mjs} +3 -1
- package/dist/permissionEntries-C4dAwZBb.mjs.map +1 -0
- package/dist/push-DdnHsLpO.mjs +356 -0
- package/dist/push-DdnHsLpO.mjs.map +1 -0
- package/package.json +7 -3
- package/dist/api-kKgzHQlu.mjs +0 -158
- package/dist/api-kKgzHQlu.mjs.map +0 -1
- package/dist/config-D0FqysME.mjs.map +0 -1
- package/dist/permissionEntries-D3fSVhwO.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ your code ◀──pull── Zodiac OS ◀───────────
|
|
|
65
65
|
|
|
66
66
|
## Constellation API
|
|
67
67
|
|
|
68
|
-
The `constellation()` function is the main SDK entry point. It returns an API for declaring account constellations — the set of Safes, Roles mods, and users that make up your on-chain setup.
|
|
68
|
+
The `constellation()` function is the main SDK entry point. It returns an API for declaring account constellations — the set of Safes, Roles mods, Delay mods, and users that make up your on-chain setup.
|
|
69
69
|
|
|
70
70
|
```ts
|
|
71
71
|
import { constellation } from '@zodiaceco/sdk'
|
|
@@ -85,7 +85,7 @@ const eth = constellation({
|
|
|
85
85
|
|
|
86
86
|
### Referencing existing accounts
|
|
87
87
|
|
|
88
|
-
Bracket access gives you existing Safes and
|
|
88
|
+
Bracket access gives you existing Safes, Roles mods and Delay mods from the selected workspace and chain — both **vault accounts** (manually-promoted entries surfaced in the workspace UI) and any **constellation accounts** previously created by a `push()`. The codegen records them under the same `accounts` map, marked with a `vault` flag for the subset that are also workspace vaults. Names auto-complete from the codegen output.
|
|
89
89
|
|
|
90
90
|
A label only ever names an account on the constellation's own chain. The same name on another chain is a different account whose address means nothing here, so it reads as a new node rather than as a reference — two workspaces can both have a `Treasury` on mainnet and on Gnosis without either having to be addressed by address.
|
|
91
91
|
|
|
@@ -96,6 +96,9 @@ const ggDao = eth.safe['GG DAO']
|
|
|
96
96
|
// Reference an existing Roles mod
|
|
97
97
|
const ggDaoRoles = eth.roles['GG DAO Roles']
|
|
98
98
|
|
|
99
|
+
// Reference an existing Delay mod
|
|
100
|
+
const ggDaoDelay = eth.delay['GG DAO Timelock']
|
|
101
|
+
|
|
99
102
|
// Optionally invoke with overrides
|
|
100
103
|
const ggDaoOverridden = eth.safe['GG DAO']({ threshold: 5 })
|
|
101
104
|
```
|
|
@@ -104,7 +107,7 @@ const ggDaoOverridden = eth.safe['GG DAO']({ threshold: 5 })
|
|
|
104
107
|
|
|
105
108
|
### Creating new accounts
|
|
106
109
|
|
|
107
|
-
Use bracket access with a new label to create new nodes. Every mandatory field (`nonce`, `threshold`, `owners` for Safes; `nonce` for Roles mods) must be supplied explicitly — the SDK does not inject any runtime defaults. The type system surfaces a missing field as a compile-time error so you can't ship an incomplete spec.
|
|
110
|
+
Use bracket access with a new label to create new nodes. Every mandatory field (`nonce`, `threshold`, `owners` for Safes; `nonce` for Roles mods; `nonce`, `cooldown`, `expiration` for Delay mods) must be supplied explicitly — the SDK does not inject any runtime defaults. The type system surfaces a missing field as a compile-time error so you can't ship an incomplete spec.
|
|
108
111
|
|
|
109
112
|
```ts
|
|
110
113
|
// New Safe — nonce, threshold, owners are required
|
|
@@ -123,6 +126,42 @@ const newRoles = eth.roles['New Roles']({
|
|
|
123
126
|
nonce: 0n,
|
|
124
127
|
target: ggDao,
|
|
125
128
|
})
|
|
129
|
+
|
|
130
|
+
// New Delay mod targeting an existing Safe
|
|
131
|
+
const newDelay = eth.delay['New Timelock']({
|
|
132
|
+
nonce: 0n,
|
|
133
|
+
target: ggDao,
|
|
134
|
+
cooldown: 86400n, // a day before a queued transaction may execute
|
|
135
|
+
expiration: 604800n, // a week to execute it in, `0n` to never expire
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Delay mods
|
|
140
|
+
|
|
141
|
+
A Delay mod holds every transaction sent through it for `cooldown` seconds before it can be executed, and drops it again after `expiration` seconds have passed (`0n` means it never expires). Both are declared in seconds.
|
|
142
|
+
|
|
143
|
+
The accounts allowed to queue a transaction through the delay are its `modules` — a complete array replaces the enabled set:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
const timelock = eth.delay['Treasury Timelock']({
|
|
147
|
+
nonce: 0n,
|
|
148
|
+
target: ggDao,
|
|
149
|
+
cooldown: 172800n,
|
|
150
|
+
expiration: 0n,
|
|
151
|
+
modules: [ggDaoRoles],
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// The safe executes what has been through the delay
|
|
155
|
+
const treasury = eth.safe['Treasury']({ modules: [timelock] })
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
To reconfigure a Delay mod that is already on chain but not in your workspace, bind it by `address` instead of declaring a `nonce` — the same either/or that applies to Roles mods:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const existing = eth.delay['Existing timelock']({
|
|
162
|
+
address: '0x88A51CcB262d04B334065Ad425928dF79c4CB7d7',
|
|
163
|
+
cooldown: 3600n,
|
|
164
|
+
})
|
|
126
165
|
```
|
|
127
166
|
|
|
128
167
|
When a bracket label matches an existing account from your codegen, all overrides become optional — you pass only the fields you want to change against the live configuration.
|
|
@@ -217,7 +256,12 @@ Each helper covers a different kind of action:
|
|
|
217
256
|
name one with `chain` when the recipients are plain addresses. Tokens without
|
|
218
257
|
an Across route to a target are skipped, the same way the app skips them —
|
|
219
258
|
routes change between writing a spec and deploying it — but a target nothing
|
|
220
|
-
can reach at all is refused at deploy rather than deployed half-working.
|
|
259
|
+
can reach at all is refused at deploy rather than deployed half-working. A
|
|
260
|
+
transfer bridges to each chain once: every recipient of a target may receive
|
|
261
|
+
every token it names, so recipients that receive other tokens on the same
|
|
262
|
+
chain get a transfer of their own. `transfer()` throws on a second target for
|
|
263
|
+
a chain, and on `to` recipients that live on different chains; `push()`
|
|
264
|
+
throws on `to` recipients off the role's chain, and on a bridge to it.
|
|
221
265
|
- `defikit` mirrors the DeFi Kit allow kit — same protocols, verbs and
|
|
222
266
|
parameters, plus a `label`. A DeFi Kit entry is nothing but its annotation;
|
|
223
267
|
the permissions behind it are fetched from the annotation's uri at deploy, so
|
|
@@ -233,13 +277,36 @@ it has nowhere to appear in the app beyond the targets it allows.
|
|
|
233
277
|
Allowance keys are plain labels — `key: 'usdc_payouts'` on the declaration, and
|
|
234
278
|
`allowance: usdc_payouts` on the transfer, which reads the key off it. They are
|
|
235
279
|
encoded to bytes32 when the constellation is deployed, so nothing calls
|
|
236
|
-
`encodeKey` by hand.
|
|
280
|
+
`encodeKey` by hand. Allowance and role keys consist of 1 to 31 letters,
|
|
281
|
+
digits, underscores or hyphens.
|
|
237
282
|
|
|
238
283
|
Tokens are named by address, not by symbol. A `transfer()` recipient may also
|
|
239
284
|
be a node — an account from your codegen, or one bound by address — which
|
|
240
285
|
stands for the address it lives at. A node whose address is only known once the
|
|
241
286
|
constellation is deployed is rejected at compile time.
|
|
242
287
|
|
|
288
|
+
### Showing accounts and roles in the Zodiac app
|
|
289
|
+
|
|
290
|
+
A constellation deploys whatever it describes, but only what you mark shows up
|
|
291
|
+
in the Zodiac app for the rest of your org:
|
|
292
|
+
|
|
293
|
+
- **`vault: true`** on a Safe lists it under **Vaults**.
|
|
294
|
+
- **`policy`** on a role lists it under **Policies**, labelled as given, so
|
|
295
|
+
everyone in your org can see what it permits right in the app. A
|
|
296
|
+
`description` is optional. In a template project, export both from the
|
|
297
|
+
role's folder:
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
// constellation/roles/treasury_ops/index.ts
|
|
301
|
+
export const policy = 'Treasury Ops'
|
|
302
|
+
export const description = 'Day-to-day treasury operations'
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The role key (`treasury_ops`) is the policy's identity and its on-chain role
|
|
306
|
+
key, so renaming the label changes nothing on chain. Every Roles mod carrying
|
|
307
|
+
the key has to grant it the same way, and no two of them may act for the
|
|
308
|
+
same Safe.
|
|
309
|
+
|
|
243
310
|
### Pushing the constellation
|
|
244
311
|
|
|
245
312
|
The `push()` function takes all nodes and sends them to the Zodiac OS API. Pass either a named object (keys become refs) or an array:
|
|
@@ -258,12 +325,22 @@ By default, `push()` creates an API client from the `ZODIAC_API_KEY` environment
|
|
|
258
325
|
await push({ ggDao, newRoles }, { api: new ApiClient({ apiKey: '...' }) })
|
|
259
326
|
```
|
|
260
327
|
|
|
328
|
+
When Zodiac refuses a constellation as it stands — a policy it could not hold,
|
|
329
|
+
a key it cannot encode — `push()` rejects with a `ConstellationRejectedError`
|
|
330
|
+
that lists every issue, starting at the node you pushed. `zodiac push` prints
|
|
331
|
+
it like this:
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
Zodiac refused the constellation "Production". Some policies of this constellation cannot be held in Zodiac:
|
|
335
|
+
• opsRoles ("Ops Roles") › roles › treasury_ops › policy: "Treasury Ops" is a policy on another Roles modifier. Mark this role as that policy too.
|
|
336
|
+
```
|
|
337
|
+
|
|
261
338
|
## CLI reference
|
|
262
339
|
|
|
263
340
|
```
|
|
264
341
|
Usage: zodiac [options] [command]
|
|
265
342
|
|
|
266
|
-
Zodiac SDK CLI – pull org data and contract ABIs
|
|
343
|
+
Zodiac SDK CLI – pull org data and contract ABIs, push constellations
|
|
267
344
|
|
|
268
345
|
Options:
|
|
269
346
|
-V, --version output the version number
|
|
@@ -275,5 +352,19 @@ Commands:
|
|
|
275
352
|
pull-org Fetch Zodiac users and accounts, generate TypeScript types
|
|
276
353
|
pull-contracts Fetch contract ABIs, generate typed permissions kit
|
|
277
354
|
pull Fetch Zodiac org and contracts ABI, generate SDK functions
|
|
355
|
+
push [entrypoint] Push the nodes an entrypoint exports and open them for review (--no-open to skip the browser)
|
|
278
356
|
help [command] display help for command
|
|
279
357
|
```
|
|
358
|
+
|
|
359
|
+
`zodiac push` imports the entrypoint (`constellation/index.ts` by default),
|
|
360
|
+
pushes every named export as a node, and prints where each constellation can be
|
|
361
|
+
reviewed. When Zodiac refuses a constellation, it prints what was refused and
|
|
362
|
+
exits with code 1. A project whose entrypoint relies on globals it sets up
|
|
363
|
+
itself runs that setup first and then calls the same command:
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
import './globals'
|
|
367
|
+
import { pushEntrypoint } from '@zodiaceco/sdk/cli/push'
|
|
368
|
+
|
|
369
|
+
await pushEntrypoint({ entrypoint: process.argv[2] })
|
|
370
|
+
```
|
|
@@ -23,11 +23,23 @@ type AllowanceSpec = RealBigints<ApiAllowanceSpec>;
|
|
|
23
23
|
* parameters and labels, never compiled permissions. They are expanded into
|
|
24
24
|
* `{ targets, annotations }` when the constellation is deployed, so that a
|
|
25
25
|
* stored revision always compiles through the current compilers.
|
|
26
|
+
*
|
|
27
|
+
* A role carrying `policy` is a policy in Zodiac. Its key is the policy's
|
|
28
|
+
* identity, so the same key on several Roles mods is one policy, active on
|
|
29
|
+
* each of their vaults. Only a policy has a `description`.
|
|
26
30
|
*/
|
|
27
31
|
type RoleDef = {
|
|
28
32
|
members: readonly AddressOrRef[];
|
|
29
33
|
permissions: readonly PermissionEntry[];
|
|
30
|
-
}
|
|
34
|
+
} & ({
|
|
35
|
+
/** Marks the role as a policy, labelled with this value. Never reaches
|
|
36
|
+
* the chain. */
|
|
37
|
+
policy: string; /** Describes the policy in Zodiac. Never reaches the chain. */
|
|
38
|
+
description?: string;
|
|
39
|
+
} | {
|
|
40
|
+
policy?: undefined;
|
|
41
|
+
description?: undefined;
|
|
42
|
+
});
|
|
31
43
|
type User = {
|
|
32
44
|
id: UUID;
|
|
33
45
|
fullName: string;
|
|
@@ -84,6 +96,7 @@ type Prettify<T> = { readonly [K in keyof T]: T[K] } & {};
|
|
|
84
96
|
type ChainEntries<A, Ch extends ChainId> = Ch extends keyof A ? NonNullable<A[Ch]> : {};
|
|
85
97
|
type SafeEntries<C extends CodegenData, W extends keyof C['accounts'], Ch extends ChainId> = ChainEntries<C['accounts'][W]['safes'], Ch>;
|
|
86
98
|
type RolesEntries<C extends CodegenData, W extends keyof C['accounts'], Ch extends ChainId> = ChainEntries<C['accounts'][W]['rolesMods'], Ch>;
|
|
99
|
+
type DelayEntries<C extends CodegenData, W extends keyof C['accounts'], Ch extends ChainId> = ChainEntries<C['accounts'][W]['delays'], Ch>;
|
|
87
100
|
type NodeType = 'SAFE' | 'ROLES' | 'DELAY';
|
|
88
101
|
/** A reference to a node used in `owners`, `modules`, `target`, etc. */
|
|
89
102
|
type NodeRef = Readonly<{
|
|
@@ -128,8 +141,19 @@ type RolesNode = NodeBase & Readonly<{
|
|
|
128
141
|
* left untouched. */
|
|
129
142
|
allowances?: Record<string, AllowanceSpec | null>;
|
|
130
143
|
}>;
|
|
144
|
+
/** A delay modifier node — a reference to one the workspace already has, or a
|
|
145
|
+
* new one with the config it is deployed with. */
|
|
146
|
+
type DelayNode = NodeBase & Readonly<{
|
|
147
|
+
/** Discriminator identifying this node as a Delay modifier. */type: 'DELAY'; /** The safe that this delay modifier controls. */
|
|
148
|
+
target?: AddressOrRef; /** The account that is allowed to update the configuration of the Delay mod. */
|
|
149
|
+
owner?: AddressOrRef; /** The account that calls will be executed from. */
|
|
150
|
+
avatar?: AddressOrRef; /** Seconds a queued transaction waits before it becomes executable. */
|
|
151
|
+
cooldown?: bigint; /** Seconds a transaction stays executable once its cooldown has passed. */
|
|
152
|
+
expiration?: bigint; /** Module addresses or node references enabled on the delay. */
|
|
153
|
+
modules?: readonly (string | NodeRef)[];
|
|
154
|
+
}>;
|
|
131
155
|
/** Any complete node that can be passed to `push()`. */
|
|
132
|
-
type ConstellationNode = SafeNode | RolesNode;
|
|
156
|
+
type ConstellationNode = SafeNode | RolesNode | DelayNode;
|
|
133
157
|
type NewSafeProps = {
|
|
134
158
|
/** Deployment nonce for CREATE2 address derivation. */nonce: bigint; /** Number of owner signatures required to execute a transaction. */
|
|
135
159
|
threshold: number; /** Safe owner addresses or node references. */
|
|
@@ -162,6 +186,31 @@ type ExistingRolesByAddress = RolesConfig & {
|
|
|
162
186
|
/** Address of the existing Roles mod to bind to and reconfigure. */address: Address;
|
|
163
187
|
};
|
|
164
188
|
type NewRolesProps = NewRolesByNonce | ExistingRolesByAddress;
|
|
189
|
+
/** Configuration shared by both ways of declaring a delay modifier. */
|
|
190
|
+
type DelayConfig = {
|
|
191
|
+
/** The safe that this delay 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 */
|
|
192
|
+
avatar?: AddressOrRef; /** The account that is allowed to update the configuration of the Delay Mod. Defaults to `target` value */
|
|
193
|
+
owner?: AddressOrRef;
|
|
194
|
+
/** Module addresses or node references allowed to queue transactions through
|
|
195
|
+
* this delay. A complete array replaces the enabled modules. */
|
|
196
|
+
modules?: readonly AddressOrRef[];
|
|
197
|
+
};
|
|
198
|
+
/** Declare a brand-new delay modifier (address derived via CREATE2 from `nonce`). */
|
|
199
|
+
type NewDelayByNonce = DelayConfig & {
|
|
200
|
+
/** Deployment nonce for CREATE2 address derivation. */nonce: bigint; /** Seconds a queued transaction waits before it becomes executable. */
|
|
201
|
+
cooldown: bigint; /** Seconds a transaction stays executable once its cooldown has passed. */
|
|
202
|
+
expiration: bigint;
|
|
203
|
+
};
|
|
204
|
+
/** Bind to a delay modifier already deployed on-chain at a known address, to
|
|
205
|
+
* reconfigure its cooldown, expiration or modules. Use this for mods not (yet)
|
|
206
|
+
* imported into the workspace — pass the address instead of a deployment
|
|
207
|
+
* `nonce`. */
|
|
208
|
+
type ExistingDelayByAddress = DelayConfig & {
|
|
209
|
+
/** Address of the existing Delay mod to bind to and reconfigure. */address: Address; /** Seconds a queued transaction waits before it becomes executable. */
|
|
210
|
+
cooldown?: bigint; /** Seconds a transaction stays executable once its cooldown has passed. */
|
|
211
|
+
expiration?: bigint;
|
|
212
|
+
};
|
|
213
|
+
type NewDelayProps = NewDelayByNonce | ExistingDelayByAddress;
|
|
165
214
|
type ExistingNodeAccessor<Type extends string, K extends string, E, Ch extends ChainId, NP extends Record<string, any>> = Readonly<Prettify<E & {
|
|
166
215
|
type: Type;
|
|
167
216
|
label: K;
|
|
@@ -190,7 +239,10 @@ type ConstellationResult<C extends CodegenData, W extends keyof C['accounts'] =
|
|
|
190
239
|
safe: EntityAccessor<'SAFE', SafeEntries<C, W, Ch>, Ch, NewSafeProps>;
|
|
191
240
|
/** Access existing roles modifiers by label or create new ones with a
|
|
192
241
|
* new label. Only ROLES-typed accounts are suggested in IntelliSense. */
|
|
193
|
-
roles: EntityAccessor<'ROLES', RolesEntries<C, W, Ch>, Ch, NewRolesProps>;
|
|
242
|
+
roles: EntityAccessor<'ROLES', RolesEntries<C, W, Ch>, Ch, NewRolesProps>;
|
|
243
|
+
/** Access existing delay modifiers by label or create new ones with a
|
|
244
|
+
* new label. Only DELAY-typed accounts are suggested in IntelliSense. */
|
|
245
|
+
delay: EntityAccessor<'DELAY', DelayEntries<C, W, Ch>, Ch, NewDelayProps>; /** Resolve a user's personal safe address on the constellation's chain. */
|
|
194
246
|
user: UserAccessor<C, Ch>;
|
|
195
247
|
};
|
|
196
248
|
/** @internal */
|
|
@@ -208,6 +260,7 @@ type ConstellationResult<C extends CodegenData, W extends keyof C['accounts'] =
|
|
|
208
260
|
* const dao = eth.safe['GG DAO'] // the safe of that name on chain 1
|
|
209
261
|
* const roles = eth.roles['GG DAO'] // the roles mod of that name
|
|
210
262
|
* const newSafe = eth.safe['New Safe']({ nonce: 0n, threshold: 2, owners: [...], modules: [...] })
|
|
263
|
+
* const timelock = eth.delay['Timelock']({ nonce: 0n, target: dao, cooldown: 86400n, expiration: 0n })
|
|
211
264
|
* ```
|
|
212
265
|
*
|
|
213
266
|
* Names come from the last `pull-org`, so an account deployed since then is
|
|
@@ -373,4 +426,4 @@ type DeFiKitVerb<Verb> = Verb extends ((params: infer Params) => unknown) ? (par
|
|
|
373
426
|
}) => DeFiKitEntry;
|
|
374
427
|
//#endregion
|
|
375
428
|
export { PermissionEntry as a, ConstellationNode as c, transfer as i, constellation as l, defikit as n, PermissionTarget as o, swap as r, Permissions as s, custom as t };
|
|
376
|
-
//# sourceMappingURL=actions-
|
|
429
|
+
//# sourceMappingURL=actions-Cri7InV-.d.mts.map
|
package/dist/actions.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as PermissionEntry, i as transfer, n as defikit, o as PermissionTarget, r as swap, s as Permissions, t as custom } from "./actions-
|
|
1
|
+
import { a as PermissionEntry, i as transfer, n as defikit, o as PermissionTarget, r as swap, s as Permissions, t as custom } from "./actions-Cri7InV-.mjs";
|
|
2
2
|
export { PermissionEntry, PermissionTarget, Permissions, custom, defikit, swap, transfer };
|
package/dist/actions.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as recipientChains } from "./permissionEntries-
|
|
1
|
+
import { r as recipientChains } from "./permissionEntries-C4dAwZBb.mjs";
|
|
2
2
|
//#region src/actions.ts
|
|
3
3
|
/**
|
|
4
4
|
* Allows signing CoW orders selling any of `sell` for any of `buy`. The
|
|
@@ -20,17 +20,22 @@ const swap = ({ label, sell, buy }) => ({
|
|
|
20
20
|
* optional on their own; a transfer names at least one recipient somewhere.
|
|
21
21
|
*/
|
|
22
22
|
const transfer = ({ label, tokens, to = [], bridge = [], allowance }) => {
|
|
23
|
+
const bridges = bridge.map(toBridgeTarget);
|
|
24
|
+
assertOneTargetPerChain(bridges);
|
|
23
25
|
const entry = {
|
|
24
26
|
label,
|
|
25
27
|
action: {
|
|
26
28
|
type: "transfer",
|
|
27
29
|
tokens: [...tokens],
|
|
28
30
|
to: to.map(toAddress),
|
|
29
|
-
...
|
|
31
|
+
...bridges.length > 0 && { bridge: bridges },
|
|
30
32
|
...allowance != null && { allowance: allowanceKey(allowance) }
|
|
31
33
|
}
|
|
32
34
|
};
|
|
33
35
|
const chains = to.flatMap((recipient) => typeof recipient === "string" || recipient.chain == null ? [] : [recipient.chain]);
|
|
36
|
+
const [chain] = chains;
|
|
37
|
+
const otherChain = chains.find((other) => other !== chain);
|
|
38
|
+
if (otherChain != null) throw new Error(`The recipients of "${label}" span chains "${chain}" and "${otherChain}". \`to\` names recipients on the role's own chain; use \`bridge\` for the others.`);
|
|
34
39
|
if (chains.length > 0) Object.defineProperty(entry, recipientChains, {
|
|
35
40
|
value: chains,
|
|
36
41
|
enumerable: false
|
|
@@ -76,6 +81,17 @@ const toBridgeTarget = ({ chain, to, receive }) => ({
|
|
|
76
81
|
to: to.map(toAddress),
|
|
77
82
|
receive: [...receive]
|
|
78
83
|
});
|
|
84
|
+
/**
|
|
85
|
+
* Every recipient of a bridge target may receive every token it names, so two
|
|
86
|
+
* targets on one chain would merge into one that allows more than either.
|
|
87
|
+
*/
|
|
88
|
+
const assertOneTargetPerChain = (bridges) => {
|
|
89
|
+
const chains = /* @__PURE__ */ new Set();
|
|
90
|
+
for (const { chain } of bridges) {
|
|
91
|
+
if (chains.has(chain)) throw new Error(`A transfer bridges to each chain once, but this one bridges to chain "${chain}" twice. Write a separate transfer for recipients that receive other tokens on the same chain.`);
|
|
92
|
+
chains.add(chain);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
79
95
|
const destinationChain = (chain, recipients) => {
|
|
80
96
|
let destination = chain;
|
|
81
97
|
for (const recipient of recipients) {
|
package/dist/actions.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.mjs","names":[],"sources":["../src/actions.ts"],"sourcesContent":["import type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { allow as ethereumKit } from 'defi-kit/eth'\nimport { recipientChains } from './permissionEntries'\nimport type {\n AllowanceKey,\n ConstellationPermission,\n DeFiKitEntry,\n LabelledPermissions,\n PermissionEntry,\n PermissionTarget,\n SwapEntry,\n TransferEntry,\n} from './permissionEntries'\nimport type { AllowanceSpec } from './types'\n\nexport type { PermissionEntry, PermissionTarget }\nexport type { Permissions } from './permissionEntries'\n\n/**\n * Allows signing CoW orders selling any of `sell` for any of `buy`. The\n * parameters are compiled into permissions when the constellation is deployed,\n * never here — a stored revision that carried its own copy would go stale as\n * soon as the compiler changed.\n */\nexport const swap = ({ label, sell, buy }: SwapParams): SwapEntry => ({\n label,\n action: { type: 'swap', sell: [...sell], buy: [...buy] },\n})\n\n/**\n * Allows transferring any of `tokens` to any of `to` on the role's own chain,\n * and — through `bridge` — to recipients on other chains over Across. Both are\n * optional on their own; a transfer names at least one recipient somewhere.\n */\nexport const transfer = ({\n label,\n tokens,\n to = [],\n bridge = [],\n allowance,\n}: TransferParams): TransferEntry => {\n const entry: TransferEntry = {\n label,\n action: {\n type: 'transfer',\n tokens: [...tokens],\n to: to.map(toAddress),\n ...(
|
|
1
|
+
{"version":3,"file":"actions.mjs","names":[],"sources":["../src/actions.ts"],"sourcesContent":["import type { Address, ChainId } from '@zodiaceco/api-types'\nimport type { allow as ethereumKit } from 'defi-kit/eth'\nimport { recipientChains } from './permissionEntries'\nimport type {\n AllowanceKey,\n ConstellationPermission,\n DeFiKitEntry,\n LabelledPermissions,\n PermissionEntry,\n PermissionTarget,\n SwapEntry,\n TransferEntry,\n} from './permissionEntries'\nimport type { AllowanceSpec } from './types'\n\nexport type { PermissionEntry, PermissionTarget }\nexport type { Permissions } from './permissionEntries'\n\n/**\n * Allows signing CoW orders selling any of `sell` for any of `buy`. The\n * parameters are compiled into permissions when the constellation is deployed,\n * never here — a stored revision that carried its own copy would go stale as\n * soon as the compiler changed.\n */\nexport const swap = ({ label, sell, buy }: SwapParams): SwapEntry => ({\n label,\n action: { type: 'swap', sell: [...sell], buy: [...buy] },\n})\n\n/**\n * Allows transferring any of `tokens` to any of `to` on the role's own chain,\n * and — through `bridge` — to recipients on other chains over Across. Both are\n * optional on their own; a transfer names at least one recipient somewhere.\n */\nexport const transfer = ({\n label,\n tokens,\n to = [],\n bridge = [],\n allowance,\n}: TransferParams): TransferEntry => {\n const bridges = bridge.map(toBridgeTarget)\n\n assertOneTargetPerChain(bridges)\n\n const entry: TransferEntry = {\n label,\n action: {\n type: 'transfer',\n tokens: [...tokens],\n to: to.map(toAddress),\n ...(bridges.length > 0 && { bridge: bridges }),\n ...(allowance != null && { allowance: allowanceKey(allowance) }),\n },\n }\n\n // Remember which chains the `to` nodes live on so push() can refuse a\n // recipient from another chain. Symbol-keyed: never serialized.\n const chains = to.flatMap((recipient) =>\n typeof recipient === 'string' || recipient.chain == null\n ? []\n : [recipient.chain]\n )\n\n const [chain] = chains\n const otherChain = chains.find((other) => other !== chain)\n\n if (otherChain != null) {\n throw new Error(\n `The recipients of \"${label}\" span chains \"${chain}\" and \"${otherChain}\". \\`to\\` names recipients on the role's own chain; use \\`bridge\\` for the others.`\n )\n }\n\n if (chains.length > 0) {\n Object.defineProperty(entry, recipientChains, {\n value: chains,\n enumerable: false,\n })\n }\n\n return entry\n}\n\n/**\n * A labelled bag of `allow`-kit permissions — the code-side counterpart of the\n * app's Custom action, for everything the other action types don't cover. It\n * takes plain permissions, not other actions.\n */\nexport const custom = ({\n label,\n permissions,\n}: CustomParams): LabelledPermissions => ({\n label,\n permissions: [...permissions],\n})\n\n/**\n * The DeFi Kit allow kit — same protocol, verb and parameter surface, plus a\n * `label`. A DeFi Kit entry is nothing but its annotation: the permissions are\n * fetched from the annotation's uri at deploy, so nothing is fetched here.\n *\n * Protocols and parameters are typed against the Ethereum kit, the widest of\n * the chains DeFi Kit serves. Values that only exist on another chain still\n * type-check; the API validates them when the annotation is resolved.\n */\nexport const defikit: DeFiKit = new Proxy({} as DeFiKit, {\n get: (_, protocol: string) =>\n new Proxy(\n {},\n {\n get:\n (_, verb: string) =>\n ({ label, ...params }: { label: string }) => ({\n label,\n defiKit: { protocol, verb, params },\n }),\n }\n ),\n})\n\nconst allowanceKey = (allowance: AllowanceSpec | AllowanceKey) =>\n typeof allowance === 'string' ? allowance : allowance.key\n\nconst toAddress = (recipient: Recipient): Address =>\n typeof recipient === 'string' ? recipient : recipient.address\n\n/**\n * A node knows the chain it lives on, so a bridge target written with nodes\n * needs no `chain` of its own. Naming one anyway is allowed — a plain address\n * carries no chain — but a node that disagrees with it is a mistake worth\n * catching here rather than at deploy.\n */\nconst toBridgeTarget = ({ chain, to, receive }: BridgeTarget) => ({\n chain: destinationChain(chain, to),\n to: to.map(toAddress),\n receive: [...receive],\n})\n\n/**\n * Every recipient of a bridge target may receive every token it names, so two\n * targets on one chain would merge into one that allows more than either.\n */\nconst assertOneTargetPerChain = (bridges: { chain: ChainId }[]) => {\n const chains = new Set<ChainId>()\n\n for (const { chain } of bridges) {\n if (chains.has(chain)) {\n throw new Error(\n `A transfer bridges to each chain once, but this one bridges to chain \"${chain}\" twice. Write a separate transfer for recipients that receive other tokens on the same chain.`\n )\n }\n\n chains.add(chain)\n }\n}\n\nconst destinationChain = (\n chain: ChainId | undefined,\n recipients: readonly Recipient[]\n): ChainId => {\n let destination = chain\n\n for (const recipient of recipients) {\n if (typeof recipient === 'string' || recipient.chain == null) {\n continue\n }\n\n if (destination != null && destination !== recipient.chain) {\n throw new Error(\n `A bridge target has one destination, but this one spans chains \"${destination}\" and \"${recipient.chain}\"`\n )\n }\n\n destination = recipient.chain\n }\n\n if (destination == null) {\n throw new Error(\n 'A bridge target needs a `chain` unless its recipients are nodes that know one'\n )\n }\n\n return destination\n}\n\ntype SwapParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n /** Tokens the role may sell. */\n sell: readonly Address[]\n /** Tokens the role may buy. */\n buy: readonly Address[]\n}\n\ntype TransferParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n /** Tokens the role may transfer. Use the zero address for the native token. */\n tokens: readonly Address[]\n /** Addresses the role may transfer to on the role's own chain. A node stands\n * for its address, so only nodes that already have one — an account from your\n * codegen, or one bound by address — can be named here. */\n to?: readonly Recipient[]\n /** Destinations on other chains, bridged over Across. */\n bridge?: readonly BridgeTarget[]\n /** Allowance capping what may be sent. Pass the allowance declared on the\n * roles modifier, or its key. */\n allowance?: AllowanceSpec | AllowanceKey\n}\n\ntype CustomParams = {\n /** Names the action in the app. Never reaches the chain. */\n label: string\n permissions: readonly ConstellationPermission[]\n}\n\n/** An address, or a node that already knows the address it lives at. */\ntype Recipient =\n | Address\n | { readonly address: Address; readonly chain?: ChainId }\n\ntype BridgeTarget = {\n /** The destination chain. Inferred from the recipients when they are nodes. */\n chain?: ChainId\n /** Addresses the role may bridge to on the destination chain. */\n to: readonly Recipient[]\n /** Tokens the role may receive on the destination chain. */\n receive: readonly Address[]\n}\n\ntype EthereumKit = typeof ethereumKit\n\ntype DeFiKit = {\n readonly [Protocol in keyof EthereumKit]: {\n readonly [Verb in keyof EthereumKit[Protocol]]: DeFiKitVerb<\n EthereumKit[Protocol][Verb]\n >\n }\n}\n\ntype DeFiKitVerb<Verb> = Verb extends (params: infer Params) => unknown\n ? (params: Params & { label: string }) => DeFiKitEntry\n : (params: { label: string }) => DeFiKitEntry\n"],"mappings":";;;;;;;;AAwBA,MAAa,QAAQ,EAAE,OAAO,MAAM,WAAkC;CACpE;CACA,QAAQ;EAAE,MAAM;EAAQ,MAAM,CAAC,GAAG,KAAK;EAAE,KAAK,CAAC,GAAG,IAAI;EAAE;CACzD;;;;;;AAOD,MAAa,YAAY,EACvB,OACA,QACA,KAAK,EAAE,EACP,SAAS,EAAE,EACX,gBACmC;CACnC,MAAM,UAAU,OAAO,IAAI,eAAe;AAE1C,yBAAwB,QAAQ;CAEhC,MAAM,QAAuB;EAC3B;EACA,QAAQ;GACN,MAAM;GACN,QAAQ,CAAC,GAAG,OAAO;GACnB,IAAI,GAAG,IAAI,UAAU;GACrB,GAAI,QAAQ,SAAS,KAAK,EAAE,QAAQ,SAAS;GAC7C,GAAI,aAAa,QAAQ,EAAE,WAAW,aAAa,UAAU,EAAE;GAChE;EACF;CAID,MAAM,SAAS,GAAG,SAAS,cACzB,OAAO,cAAc,YAAY,UAAU,SAAS,OAChD,EAAE,GACF,CAAC,UAAU,MAAM,CACtB;CAED,MAAM,CAAC,SAAS;CAChB,MAAM,aAAa,OAAO,MAAM,UAAU,UAAU,MAAM;AAE1D,KAAI,cAAc,KAChB,OAAM,IAAI,MACR,sBAAsB,MAAM,iBAAiB,MAAM,SAAS,WAAW,oFACxE;AAGH,KAAI,OAAO,SAAS,EAClB,QAAO,eAAe,OAAO,iBAAiB;EAC5C,OAAO;EACP,YAAY;EACb,CAAC;AAGJ,QAAO;;;;;;;AAQT,MAAa,UAAU,EACrB,OACA,mBACwC;CACxC;CACA,aAAa,CAAC,GAAG,YAAY;CAC9B;;;;;;;;;;AAWD,MAAa,UAAmB,IAAI,MAAM,EAAE,EAAa,EACvD,MAAM,GAAG,aACP,IAAI,MACF,EAAE,EACF,EACE,MACG,GAAG,UACH,EAAE,OAAO,GAAG,cAAiC;CAC5C;CACA,SAAS;EAAE;EAAU;EAAM;EAAQ;CACpC,GACJ,CACF,EACJ,CAAC;AAEF,MAAM,gBAAgB,cACpB,OAAO,cAAc,WAAW,YAAY,UAAU;AAExD,MAAM,aAAa,cACjB,OAAO,cAAc,WAAW,YAAY,UAAU;;;;;;;AAQxD,MAAM,kBAAkB,EAAE,OAAO,IAAI,eAA6B;CAChE,OAAO,iBAAiB,OAAO,GAAG;CAClC,IAAI,GAAG,IAAI,UAAU;CACrB,SAAS,CAAC,GAAG,QAAQ;CACtB;;;;;AAMD,MAAM,2BAA2B,YAAkC;CACjE,MAAM,yBAAS,IAAI,KAAc;AAEjC,MAAK,MAAM,EAAE,WAAW,SAAS;AAC/B,MAAI,OAAO,IAAI,MAAM,CACnB,OAAM,IAAI,MACR,yEAAyE,MAAM,gGAChF;AAGH,SAAO,IAAI,MAAM;;;AAIrB,MAAM,oBACJ,OACA,eACY;CACZ,IAAI,cAAc;AAElB,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,OAAO,cAAc,YAAY,UAAU,SAAS,KACtD;AAGF,MAAI,eAAe,QAAQ,gBAAgB,UAAU,MACnD,OAAM,IAAI,MACR,mEAAmE,YAAY,SAAS,UAAU,MAAM,GACzG;AAGH,gBAAc,UAAU;;AAG1B,KAAI,eAAe,KACjB,OAAM,IAAI,MACR,gFACD;AAGH,QAAO"}
|
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-D49x1ozw.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
|
-
import { n as chainIdFor, t as CHAIN_IDS } from "../networks-
|
|
2
|
-
import { n as EVERYTHING, t as buildAllowKit } from "../allow-
|
|
1
|
+
import { n as chainIdFor, t as CHAIN_IDS } from "../networks-UKtJ8QDG.mjs";
|
|
2
|
+
import { n as EVERYTHING, t as buildAllowKit } from "../allow-DX3iyizB.mjs";
|
|
3
3
|
export { CHAIN_IDS, EVERYTHING, buildAllowKit, chainIdFor };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as walkContracts, i as readAbi } from "./networks-
|
|
1
|
+
import { a as walkContracts, i as readAbi } from "./networks-UKtJ8QDG.mjs";
|
|
2
2
|
import { Operator, ParameterType, c, coercePermission } from "zodiac-roles-sdk";
|
|
3
3
|
import { Interface, isError } from "ethers";
|
|
4
4
|
//#region src/allow/types.ts
|
|
@@ -129,4 +129,4 @@ const applyOptions = (permission, options) => {
|
|
|
129
129
|
//#endregion
|
|
130
130
|
export { EVERYTHING as n, buildAllowKit as t };
|
|
131
131
|
|
|
132
|
-
//# sourceMappingURL=allow-
|
|
132
|
+
//# sourceMappingURL=allow-DX3iyizB.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"allow-CrDzDtxx.mjs","names":[],"sources":["../src/allow/types.ts","../src/allow/runtime.ts"],"sourcesContent":["import type { BigNumberish, BytesLike, ParamType } from 'ethers'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\n\nexport type Options = {\n send?: boolean\n delegatecall?: boolean\n etherWithinAllowance?: `0x${string}`\n callWithinAllowance?: `0x${string}`\n}\n\nexport type PrimitiveValue = BigNumberish | BytesLike | string | boolean\n\n// Signature matches `zodiac-roles-sdk` so values from `c.*` are assignable.\nexport type ConditionFunction<T = unknown> = (\n abiType: ParamType,\n _?: T\n) => Condition\n\ntype RequireAtLeastOne<T> = {\n [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>\n}[keyof T]\n\ntype ArrayElement<T extends readonly unknown[]> = T extends readonly (infer U)[]\n ? U\n : never\n\nexport type PrimitiveScoping<T extends PrimitiveValue> =\n | T\n | ConditionFunction<T>\n\nexport type ArrayScoping<T extends readonly any[]> =\n | readonly Scoping<ArrayElement<T>>[]\n | ConditionFunction<T>\n\nexport type StructScoping<T extends { [key: string]: any }> =\n | RequireAtLeastOne<{ [K in keyof T]?: Scoping<T[K]> }>\n | ConditionFunction<T>\n\nexport type Scoping<T> = T extends PrimitiveValue\n ? PrimitiveScoping<T>\n : T extends readonly any[]\n ? ArrayScoping<T>\n : T extends { [key: string]: any }\n ? StructScoping<T>\n : unknown\n\nexport type { FunctionPermission, TargetPermission }\n\nexport const EVERYTHING = Symbol.for('@zodiaceco/allow-kit/EVERYTHING')\nexport type EVERYTHING = typeof EVERYTHING\n","import { Interface, FunctionFragment, isError, type InterfaceAbi } from 'ethers'\nimport { c, coercePermission, ParameterType, Operator } from 'zodiac-roles-sdk'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\nimport { readAbi, walkContracts, type ContractNode } from './abi'\nimport { EVERYTHING, type Options } from './types'\n\nexport function buildAllowKit(\n abisDir: string,\n contractsConfig: Record<string, any>\n): Record<string, any> {\n const kit: Record<string, any> = {}\n for (const node of walkContracts(contractsConfig)) {\n const abi = readAbi(abisDir, node)\n if (!abi) {\n // Defer the error until the user touches this contract — otherwise an\n // ABI missing for one chain crashes all unrelated role definitions.\n attachAt(kit, [node.chain, ...node.segments], missingAbiProxy(node))\n continue\n }\n attachAt(\n kit,\n [node.chain, ...node.segments],\n makeAllowContract(\n node.address,\n abi as InterfaceAbi,\n [node.chain, ...node.segments].join('.')\n )\n )\n }\n return kit\n}\n\nfunction attachAt(root: Record<string, any>, segments: string[], value: any) {\n let cursor = root\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!\n if (!(seg in cursor)) cursor[seg] = {}\n cursor = cursor[seg]\n }\n cursor[segments[segments.length - 1]!] = value\n}\n\nfunction missingAbiProxy(node: ContractNode) {\n const explain = () => {\n throw new Error(\n `ABI missing for ${node.chain}.${node.segments.join('.')} ` +\n `(${node.address}). Run \\`zodiac pull-contracts\\` to fetch it, or ` +\n `paste the ABI JSON manually at <abisDir>/${node.chain}/${node.segments.join('/')}.json`\n )\n }\n return new Proxy(\n {},\n {\n get: explain,\n has: explain,\n }\n )\n}\n\nfunction makeAllowContract(\n address: `0x${string}`,\n abi: InterfaceAbi,\n path: string\n): Record<string | symbol, any> {\n const iface = Interface.from(abi)\n const lowerAddr = address.toLowerCase() as `0x${string}`\n\n // Permissionable functions grouped by bare name, so an ambiguous one can be\n // told apart from an unknown one. `Interface.getFunction` reports both as\n // INVALID_ARGUMENT, and the two want opposite answers: an unknown name is\n // simply not a member, an ambiguous one is a member the caller has not\n // finished naming.\n const overloads = new Map<string, FunctionFragment[]>()\n iface.forEachFunction((fn) => {\n if (fn.stateMutability === 'view' || fn.stateMutability === 'pure') return\n overloads.set(fn.name, [...(overloads.get(fn.name) ?? []), fn])\n })\n\n const ambiguous = (prop: string) => (overloads.get(prop)?.length ?? 0) > 1\n\n const explainAmbiguity = (prop: string): never => {\n const signatures = overloads.get(prop)!.map((fn) => fn.format('sighash'))\n throw new Error(\n `\\`allow.${path}.${prop}\\` is ambiguous: ${address} has ` +\n `${signatures.length} functions named \"${prop}\". Name the overload ` +\n `you mean by its full signature — ` +\n signatures\n .map((sig) => `allow.${path}[${JSON.stringify(sig)}]`)\n .join(' or ') +\n `.`\n )\n }\n\n const allowEverything = (options?: Options): TargetPermission => ({\n targetAddress: lowerAddr,\n send: options?.send,\n delegatecall: options?.delegatecall,\n })\n\n const has = (name: string) => {\n try {\n const fn = iface.getFunction(name)\n if (!fn) return false\n return fn.stateMutability !== 'view' && fn.stateMutability !== 'pure'\n } catch (error) {\n if (!isError(error as any, 'INVALID_ARGUMENT')) throw error\n return false\n }\n }\n\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === EVERYTHING) return allowEverything\n if (typeof prop !== 'string') return undefined\n if (ambiguous(prop)) explainAmbiguity(prop)\n if (!has(prop)) return undefined\n const fn = iface.getFunction(prop)!\n return makeAllowFunction(fn, lowerAddr)\n },\n // An ambiguous bare name is not a member — only the full signatures are.\n // `in` answers that question; reaching for the property explains it.\n has: (_target, prop) => {\n if (prop === EVERYTHING) return true\n return typeof prop === 'string' && !ambiguous(prop) && has(prop)\n },\n }\n )\n}\n\nfunction makeAllowFunction(\n fn: FunctionFragment,\n targetAddress: `0x${string}`\n): (...args: any[]) => FunctionPermission {\n const inputs = fn.inputs\n return (...args: any[]) => {\n const scopings = args.slice(0, inputs.length)\n const hasScopings = scopings.some((s) => s !== undefined && s !== null)\n const options: Options = args[inputs.length] ?? {}\n const condition = hasScopings\n ? c.calldataMatches(scopings, inputs)()\n : undefined\n const preset = {\n targetAddress,\n signature: fn.format('sighash'),\n condition,\n }\n return applyOptions(coercePermission(preset as any) as any, options)\n }\n}\n\nconst emptyCalldataMatches: Condition = {\n paramType: ParameterType.Calldata,\n operator: Operator.Matches,\n children: [],\n}\n\nconst applyGlobalAllowance = (\n condition: Condition | undefined,\n allowanceCondition: Condition\n): Condition => {\n const base = condition ?? emptyCalldataMatches\n if (\n base.paramType !== ParameterType.Calldata ||\n base.operator !== Operator.Matches\n ) {\n throw new Error(\n 'Global allowance can only be applied to calldata matches nodes'\n )\n }\n return {\n ...base,\n children: [...(base.children ?? []), allowanceCondition],\n }\n}\n\nconst applyOptions = (\n permission: FunctionPermission & { condition?: Condition },\n options: Options\n): FunctionPermission => {\n let condition = permission.condition\n if (options.etherWithinAllowance) {\n if (!options.send) {\n throw new Error(\n '`etherWithinAllowance` can only be used if `send` is allowed'\n )\n }\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.EtherWithinAllowance,\n compValue: options.etherWithinAllowance,\n })\n }\n if (options.callWithinAllowance) {\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.CallWithinAllowance,\n compValue: options.callWithinAllowance,\n })\n }\n return {\n ...permission,\n send: options.send,\n delegatecall: options.delegatecall,\n condition,\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"allow-DX3iyizB.mjs","names":[],"sources":["../src/allow/types.ts","../src/allow/runtime.ts"],"sourcesContent":["import type { BigNumberish, BytesLike, ParamType } from 'ethers'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\n\nexport type Options = {\n send?: boolean\n delegatecall?: boolean\n etherWithinAllowance?: `0x${string}`\n callWithinAllowance?: `0x${string}`\n}\n\nexport type PrimitiveValue = BigNumberish | BytesLike | string | boolean\n\n// Signature matches `zodiac-roles-sdk` so values from `c.*` are assignable.\nexport type ConditionFunction<T = unknown> = (\n abiType: ParamType,\n _?: T\n) => Condition\n\ntype RequireAtLeastOne<T> = {\n [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>\n}[keyof T]\n\ntype ArrayElement<T extends readonly unknown[]> = T extends readonly (infer U)[]\n ? U\n : never\n\nexport type PrimitiveScoping<T extends PrimitiveValue> =\n | T\n | ConditionFunction<T>\n\nexport type ArrayScoping<T extends readonly any[]> =\n | readonly Scoping<ArrayElement<T>>[]\n | ConditionFunction<T>\n\nexport type StructScoping<T extends { [key: string]: any }> =\n | RequireAtLeastOne<{ [K in keyof T]?: Scoping<T[K]> }>\n | ConditionFunction<T>\n\nexport type Scoping<T> = T extends PrimitiveValue\n ? PrimitiveScoping<T>\n : T extends readonly any[]\n ? ArrayScoping<T>\n : T extends { [key: string]: any }\n ? StructScoping<T>\n : unknown\n\nexport type { FunctionPermission, TargetPermission }\n\nexport const EVERYTHING = Symbol.for('@zodiaceco/allow-kit/EVERYTHING')\nexport type EVERYTHING = typeof EVERYTHING\n","import { Interface, FunctionFragment, isError, type InterfaceAbi } from 'ethers'\nimport { c, coercePermission, ParameterType, Operator } from 'zodiac-roles-sdk'\nimport type {\n Condition,\n FunctionPermission,\n TargetPermission,\n} from 'zodiac-roles-sdk'\nimport { readAbi, walkContracts, type ContractNode } from './abi'\nimport { EVERYTHING, type Options } from './types'\n\nexport function buildAllowKit(\n abisDir: string,\n contractsConfig: Record<string, any>\n): Record<string, any> {\n const kit: Record<string, any> = {}\n for (const node of walkContracts(contractsConfig)) {\n const abi = readAbi(abisDir, node)\n if (!abi) {\n // Defer the error until the user touches this contract — otherwise an\n // ABI missing for one chain crashes all unrelated role definitions.\n attachAt(kit, [node.chain, ...node.segments], missingAbiProxy(node))\n continue\n }\n attachAt(\n kit,\n [node.chain, ...node.segments],\n makeAllowContract(\n node.address,\n abi as InterfaceAbi,\n [node.chain, ...node.segments].join('.')\n )\n )\n }\n return kit\n}\n\nfunction attachAt(root: Record<string, any>, segments: string[], value: any) {\n let cursor = root\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!\n if (!(seg in cursor)) cursor[seg] = {}\n cursor = cursor[seg]\n }\n cursor[segments[segments.length - 1]!] = value\n}\n\nfunction missingAbiProxy(node: ContractNode) {\n const explain = () => {\n throw new Error(\n `ABI missing for ${node.chain}.${node.segments.join('.')} ` +\n `(${node.address}). Run \\`zodiac pull-contracts\\` to fetch it, or ` +\n `paste the ABI JSON manually at <abisDir>/${node.chain}/${node.segments.join('/')}.json`\n )\n }\n return new Proxy(\n {},\n {\n get: explain,\n has: explain,\n }\n )\n}\n\nfunction makeAllowContract(\n address: `0x${string}`,\n abi: InterfaceAbi,\n path: string\n): Record<string | symbol, any> {\n const iface = Interface.from(abi)\n const lowerAddr = address.toLowerCase() as `0x${string}`\n\n // Permissionable functions grouped by bare name, so an ambiguous one can be\n // told apart from an unknown one. `Interface.getFunction` reports both as\n // INVALID_ARGUMENT, and the two want opposite answers: an unknown name is\n // simply not a member, an ambiguous one is a member the caller has not\n // finished naming.\n const overloads = new Map<string, FunctionFragment[]>()\n iface.forEachFunction((fn) => {\n if (fn.stateMutability === 'view' || fn.stateMutability === 'pure') return\n overloads.set(fn.name, [...(overloads.get(fn.name) ?? []), fn])\n })\n\n const ambiguous = (prop: string) => (overloads.get(prop)?.length ?? 0) > 1\n\n const explainAmbiguity = (prop: string): never => {\n const signatures = overloads.get(prop)!.map((fn) => fn.format('sighash'))\n throw new Error(\n `\\`allow.${path}.${prop}\\` is ambiguous: ${address} has ` +\n `${signatures.length} functions named \"${prop}\". Name the overload ` +\n `you mean by its full signature — ` +\n signatures\n .map((sig) => `allow.${path}[${JSON.stringify(sig)}]`)\n .join(' or ') +\n `.`\n )\n }\n\n const allowEverything = (options?: Options): TargetPermission => ({\n targetAddress: lowerAddr,\n send: options?.send,\n delegatecall: options?.delegatecall,\n })\n\n const has = (name: string) => {\n try {\n const fn = iface.getFunction(name)\n if (!fn) return false\n return fn.stateMutability !== 'view' && fn.stateMutability !== 'pure'\n } catch (error) {\n if (!isError(error as any, 'INVALID_ARGUMENT')) throw error\n return false\n }\n }\n\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === EVERYTHING) return allowEverything\n if (typeof prop !== 'string') return undefined\n if (ambiguous(prop)) explainAmbiguity(prop)\n if (!has(prop)) return undefined\n const fn = iface.getFunction(prop)!\n return makeAllowFunction(fn, lowerAddr)\n },\n // An ambiguous bare name is not a member — only the full signatures are.\n // `in` answers that question; reaching for the property explains it.\n has: (_target, prop) => {\n if (prop === EVERYTHING) return true\n return typeof prop === 'string' && !ambiguous(prop) && has(prop)\n },\n }\n )\n}\n\nfunction makeAllowFunction(\n fn: FunctionFragment,\n targetAddress: `0x${string}`\n): (...args: any[]) => FunctionPermission {\n const inputs = fn.inputs\n return (...args: any[]) => {\n const scopings = args.slice(0, inputs.length)\n const hasScopings = scopings.some((s) => s !== undefined && s !== null)\n const options: Options = args[inputs.length] ?? {}\n const condition = hasScopings\n ? c.calldataMatches(scopings, inputs)()\n : undefined\n const preset = {\n targetAddress,\n signature: fn.format('sighash'),\n condition,\n }\n return applyOptions(coercePermission(preset as any) as any, options)\n }\n}\n\nconst emptyCalldataMatches: Condition = {\n paramType: ParameterType.Calldata,\n operator: Operator.Matches,\n children: [],\n}\n\nconst applyGlobalAllowance = (\n condition: Condition | undefined,\n allowanceCondition: Condition\n): Condition => {\n const base = condition ?? emptyCalldataMatches\n if (\n base.paramType !== ParameterType.Calldata ||\n base.operator !== Operator.Matches\n ) {\n throw new Error(\n 'Global allowance can only be applied to calldata matches nodes'\n )\n }\n return {\n ...base,\n children: [...(base.children ?? []), allowanceCondition],\n }\n}\n\nconst applyOptions = (\n permission: FunctionPermission & { condition?: Condition },\n options: Options\n): FunctionPermission => {\n let condition = permission.condition\n if (options.etherWithinAllowance) {\n if (!options.send) {\n throw new Error(\n '`etherWithinAllowance` can only be used if `send` is allowed'\n )\n }\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.EtherWithinAllowance,\n compValue: options.etherWithinAllowance,\n })\n }\n if (options.callWithinAllowance) {\n condition = applyGlobalAllowance(condition, {\n paramType: ParameterType.None,\n operator: Operator.CallWithinAllowance,\n compValue: options.callWithinAllowance,\n })\n }\n return {\n ...permission,\n send: options.send,\n delegatecall: options.delegatecall,\n condition,\n }\n}\n"],"mappings":"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"}
|
package/dist/cli/config.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as resolveAbisDir, i as loadConfig, n as defineConfig, r as ensureConfigStub, t as DEFAULT_ABIS_DIR } from "../config-
|
|
1
|
+
import { a as resolveAbisDir, i as loadConfig, n as defineConfig, r as ensureConfigStub, t as DEFAULT_ABIS_DIR } from "../config-BaChz2bq.mjs";
|
|
2
2
|
export { DEFAULT_ABIS_DIR, defineConfig, ensureConfigStub, loadConfig, resolveAbisDir };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/cli/commands/push.d.ts
|
|
2
|
+
type PushEntrypointOptions = {
|
|
3
|
+
/** The module whose named exports are the nodes to push, relative to the
|
|
4
|
+
* working directory. @default constellation/index.ts in the project root */
|
|
5
|
+
entrypoint?: string; /** Open every pushed constellation's review page. @default true */
|
|
6
|
+
openInBrowser?: boolean; /** The config file whose API key to push with, as for `zodiac pull`. */
|
|
7
|
+
config?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Pushes every node an entrypoint exports — each export name becomes the
|
|
11
|
+
* node's ref — and prints where each constellation can be reviewed.
|
|
12
|
+
*
|
|
13
|
+
* A constellation Zodiac refuses is printed as the list of what it refused,
|
|
14
|
+
* and the process exits with code 1. Projects that set up globals the
|
|
15
|
+
* entrypoint relies on run their setup first and call this afterwards.
|
|
16
|
+
*/
|
|
17
|
+
declare const pushEntrypoint: ({
|
|
18
|
+
entrypoint,
|
|
19
|
+
openInBrowser,
|
|
20
|
+
config: configPath
|
|
21
|
+
}?: PushEntrypointOptions) => Promise<void>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { pushEntrypoint };
|
|
24
|
+
//# sourceMappingURL=push.d.mts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { n as push, t as ConstellationRejectedError } from "../push-DdnHsLpO.mjs";
|
|
2
|
+
import { i as loadConfig } from "../config-BaChz2bq.mjs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import open from "open";
|
|
6
|
+
//#region src/cli/commands/push.ts
|
|
7
|
+
const DEFAULT_ENTRYPOINT = "constellation/index.ts";
|
|
8
|
+
/**
|
|
9
|
+
* Pushes every node an entrypoint exports — each export name becomes the
|
|
10
|
+
* node's ref — and prints where each constellation can be reviewed.
|
|
11
|
+
*
|
|
12
|
+
* A constellation Zodiac refuses is printed as the list of what it refused,
|
|
13
|
+
* and the process exits with code 1. Projects that set up globals the
|
|
14
|
+
* entrypoint relies on run their setup first and call this afterwards.
|
|
15
|
+
*/
|
|
16
|
+
const pushEntrypoint = async ({ entrypoint, openInBrowser = true, config: configPath } = {}) => {
|
|
17
|
+
const { apiKey, rootDir } = await loadConfig(configPath);
|
|
18
|
+
const entrypointPath = entrypoint == null ? resolve(rootDir, DEFAULT_ENTRYPOINT) : resolve(process.cwd(), entrypoint);
|
|
19
|
+
const { default: defaultExport, ...nodes } = await import(pathToFileURL(entrypointPath).href);
|
|
20
|
+
if (defaultExport !== void 0) console.warn(`warning: ${entrypointPath} has a default export which will be ignored. Use named exports — each export becomes a ref in the pushed spec.`);
|
|
21
|
+
try {
|
|
22
|
+
const results = await push(nodes, { apiKey });
|
|
23
|
+
for (const { url } of results) {
|
|
24
|
+
console.log(`Pushed constellation to: ${url}`);
|
|
25
|
+
if (openInBrowser) await open(url);
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (!(error instanceof ConstellationRejectedError)) throw error;
|
|
29
|
+
console.error(error.message);
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { pushEntrypoint };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=push.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.mjs","names":[],"sources":["../../src/cli/commands/push.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport open from 'open'\nimport { ConstellationRejectedError, push } from '../../push'\nimport { loadConfig } from '../config'\n\nconst DEFAULT_ENTRYPOINT = 'constellation/index.ts'\n\ntype PushEntrypointOptions = {\n /** The module whose named exports are the nodes to push, relative to the\n * working directory. @default constellation/index.ts in the project root */\n entrypoint?: string\n /** Open every pushed constellation's review page. @default true */\n openInBrowser?: boolean\n /** The config file whose API key to push with, as for `zodiac pull`. */\n config?: string\n}\n\n/**\n * Pushes every node an entrypoint exports — each export name becomes the\n * node's ref — and prints where each constellation can be reviewed.\n *\n * A constellation Zodiac refuses is printed as the list of what it refused,\n * and the process exits with code 1. Projects that set up globals the\n * entrypoint relies on run their setup first and call this afterwards.\n */\nexport const pushEntrypoint = async ({\n entrypoint,\n openInBrowser = true,\n config: configPath,\n}: PushEntrypointOptions = {}) => {\n const { apiKey, rootDir } = await loadConfig(configPath)\n const entrypointPath =\n entrypoint == null\n ? resolve(rootDir, DEFAULT_ENTRYPOINT)\n : resolve(process.cwd(), entrypoint)\n const { default: defaultExport, ...nodes } = await import(\n pathToFileURL(entrypointPath).href\n )\n\n if (defaultExport !== undefined) {\n console.warn(\n `warning: ${entrypointPath} has a default export which will be ignored. ` +\n `Use named exports — each export becomes a ref in the pushed spec.`\n )\n }\n\n try {\n const results = await push(nodes, { apiKey })\n\n for (const { url } of results) {\n console.log(`Pushed constellation to: ${url}`)\n\n if (openInBrowser) {\n await open(url)\n }\n }\n } catch (error) {\n if (!(error instanceof ConstellationRejectedError)) {\n throw error\n }\n\n console.error(error.message)\n process.exitCode = 1\n }\n}\n"],"mappings":";;;;;;AAMA,MAAM,qBAAqB;;;;;;;;;AAoB3B,MAAa,iBAAiB,OAAO,EACnC,YACA,gBAAgB,MAChB,QAAQ,eACiB,EAAE,KAAK;CAChC,MAAM,EAAE,QAAQ,YAAY,MAAM,WAAW,WAAW;CACxD,MAAM,iBACJ,cAAc,OACV,QAAQ,SAAS,mBAAmB,GACpC,QAAQ,QAAQ,KAAK,EAAE,WAAW;CACxC,MAAM,EAAE,SAAS,eAAe,GAAG,UAAU,MAAM,OACjD,cAAc,eAAe,CAAC;AAGhC,KAAI,kBAAkB,KAAA,EACpB,SAAQ,KACN,YAAY,eAAe,gHAE5B;AAGH,KAAI;EACF,MAAM,UAAU,MAAM,KAAK,OAAO,EAAE,QAAQ,CAAC;AAE7C,OAAK,MAAM,EAAE,SAAS,SAAS;AAC7B,WAAQ,IAAI,4BAA4B,MAAM;AAE9C,OAAI,cACF,OAAM,KAAK,IAAI;;UAGZ,OAAO;AACd,MAAI,EAAE,iBAAiB,4BACrB,OAAM;AAGR,UAAQ,MAAM,MAAM,QAAQ;AAC5B,UAAQ,WAAW"}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { a as
|
|
2
|
+
import { t as resolveZodiacDir } from "./paths-LsD6ULk0.mjs";
|
|
3
|
+
import { r as ApiClient } from "./push-DdnHsLpO.mjs";
|
|
4
|
+
import { a as walkContracts, i as readAbi, n as chainIdFor, o as writeAbi, r as abiFilePath } from "./networks-UKtJ8QDG.mjs";
|
|
5
|
+
import { a as resolveAbisDir, i as loadConfig, o as findProjectRoot, r as ensureConfigStub } from "./config-BaChz2bq.mjs";
|
|
6
|
+
import { pushEntrypoint } from "./cli/push.mjs";
|
|
5
7
|
import fs, { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
8
|
import path, { basename, dirname, join, resolve } from "node:path";
|
|
7
9
|
import { getAddress } from "ethers";
|
|
@@ -703,14 +705,14 @@ const loadConfigOrInit = (configPath) => loadConfig(configPath, {
|
|
|
703
705
|
});
|
|
704
706
|
const run = async (argv = process.argv) => {
|
|
705
707
|
const program = new Command();
|
|
706
|
-
program.name("zodiac").description("Zodiac SDK CLI – pull org data and contract ABIs").version(ownVersion()).option("-c, --config <path>", "path to the config file", "zodiac.config.ts");
|
|
708
|
+
program.name("zodiac").description("Zodiac SDK CLI – pull org data and contract ABIs, push constellations").version(ownVersion()).option("-c, --config <path>", "path to the config file", "zodiac.config.ts");
|
|
707
709
|
let updateNotice = Promise.resolve(null);
|
|
708
710
|
program.hook("preAction", () => {
|
|
709
711
|
updateNotice = checkForUpdate();
|
|
710
712
|
});
|
|
711
713
|
program.hook("postAction", async () => {
|
|
712
714
|
const notice = await updateNotice;
|
|
713
|
-
if (notice != null) console.warn(`\n${notice}`);
|
|
715
|
+
if (notice != null && !process.exitCode) console.warn(`\n${notice}`);
|
|
714
716
|
});
|
|
715
717
|
program.command("pull").description("Fetch Zodiac org data and contract ABIs, generating typed SDK functions. Authorizes this directory automatically on first run.").action(async (_opts, cmd) => {
|
|
716
718
|
const config = await loadConfigOrInit(cmd.optsWithGlobals().config);
|
|
@@ -722,6 +724,13 @@ const run = async (argv = process.argv) => {
|
|
|
722
724
|
program.command("pull-contracts").description("Fetch contract ABIs, generate typed permissions kit").action(async (_opts, cmd) => {
|
|
723
725
|
await pullContracts(await loadConfigOrInit(cmd.optsWithGlobals().config));
|
|
724
726
|
});
|
|
727
|
+
program.command("push").description("Push the nodes an entrypoint exports to Zodiac and open them for review. Projects that set up globals for their entrypoint call `pushEntrypoint` from `@zodiaceco/sdk/cli/push` after that setup instead.").argument("[entrypoint]", "module exporting the nodes (default: constellation/index.ts in the project root)").option("--no-open", "do not open the review pages in a browser").action(async (entrypoint, opts, cmd) => {
|
|
728
|
+
await pushEntrypoint({
|
|
729
|
+
entrypoint,
|
|
730
|
+
openInBrowser: opts.open,
|
|
731
|
+
config: cmd.optsWithGlobals().config
|
|
732
|
+
});
|
|
733
|
+
});
|
|
725
734
|
program.command("init").description("Re-authorize this directory / mint a new API key (run automatically by `pull` on first use).").option("--app-url <url>", "Override the Zodiac app URL (defaults to ZODIAC_APP_URL or app.zodiac.eco)").action(async (opts) => {
|
|
726
735
|
await init({ appUrl: opts.appUrl });
|
|
727
736
|
});
|
|
@@ -730,7 +739,7 @@ const run = async (argv = process.argv) => {
|
|
|
730
739
|
//#endregion
|
|
731
740
|
//#region src/cli/index.ts
|
|
732
741
|
run().then(() => {
|
|
733
|
-
process.exit(0);
|
|
742
|
+
process.exit(process.exitCode ?? 0);
|
|
734
743
|
}, (error) => {
|
|
735
744
|
if (error) console.error(error);
|
|
736
745
|
process.exit(1);
|