@zodiaceco/sdk 1.13.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -1
- package/dist/actions-Dn5NC3oq.d.mts +352 -0
- package/dist/actions.d.mts +2 -0
- package/dist/actions.mjs +92 -0
- package/dist/actions.mjs.map +1 -0
- package/dist/allow/index.d.mts +1 -1
- package/dist/allow/index.mjs +2 -2
- package/dist/{allow-CzPpzclq.mjs → allow-CrDzDtxx.mjs} +16 -5
- package/dist/allow-CrDzDtxx.mjs.map +1 -0
- package/dist/cli/config.mjs +1 -1
- package/dist/cli.mjs +44 -12
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-D5ArAp07.mjs → config-D0FqysME.mjs} +1 -1
- package/dist/{config-D5ArAp07.mjs.map → config-D0FqysME.mjs.map} +1 -1
- package/dist/{index-DoZbhpS6.d.mts → index-CnH26Z_A.d.mts} +1 -1
- package/dist/index.d.mts +5 -199
- package/dist/index.mjs +21 -11
- package/dist/index.mjs.map +1 -1
- package/dist/{networks-BTW1qAAa.mjs → networks-B0H4Uoyh.mjs} +1 -1
- package/dist/{networks-BTW1qAAa.mjs.map → networks-B0H4Uoyh.mjs.map} +1 -1
- package/dist/permissionEntries-D3fSVhwO.mjs +61 -0
- package/dist/permissionEntries-D3fSVhwO.mjs.map +1 -0
- package/package.json +7 -2
- package/dist/allow-CzPpzclq.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Zodiac
|
|
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
|
package/dist/actions.mjs
ADDED
|
@@ -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"}
|
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-CnH26Z_A.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-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-
|
|
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-
|
|
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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"}
|