@zodiaceco/sdk 1.13.2 → 2.0.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-DS5XoMiq.d.mts +152 -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 +9 -21
- 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 +6 -1
- 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,152 @@
|
|
|
1
|
+
import { Permission } from "zodiac-roles-sdk";
|
|
2
|
+
import { Address, ApplyConstellationPayload, ChainId } from "@zodiaceco/api-types";
|
|
3
|
+
import { allow } from "defi-kit/eth";
|
|
4
|
+
|
|
5
|
+
//#region src/permissionEntries.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* An allowance key. Authored as a plain label, the way a role key is, and
|
|
8
|
+
* encoded to bytes32 when the constellation is deployed. An already-encoded
|
|
9
|
+
* key still works — `encodeKey` passes 32-byte hex through unchanged.
|
|
10
|
+
*/
|
|
11
|
+
type AllowanceKey = string;
|
|
12
|
+
type SwapEntry = {
|
|
13
|
+
label: string;
|
|
14
|
+
action: {
|
|
15
|
+
type: 'swap';
|
|
16
|
+
sell: Address[];
|
|
17
|
+
buy: Address[];
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
type TransferEntry = {
|
|
21
|
+
label: string;
|
|
22
|
+
action: {
|
|
23
|
+
type: 'transfer';
|
|
24
|
+
tokens: Address[];
|
|
25
|
+
to: Address[];
|
|
26
|
+
bridge?: {
|
|
27
|
+
chain: ChainId;
|
|
28
|
+
to: Address[];
|
|
29
|
+
receive: Address[];
|
|
30
|
+
}[];
|
|
31
|
+
allowance?: AllowanceKey;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
type LabelledPermissions = {
|
|
35
|
+
label: string;
|
|
36
|
+
permissions: Permission[];
|
|
37
|
+
};
|
|
38
|
+
type DeFiKitEntry = {
|
|
39
|
+
label: string;
|
|
40
|
+
defiKit: {
|
|
41
|
+
protocol: string;
|
|
42
|
+
verb: string;
|
|
43
|
+
params: Record<string, DeFiKitParamValue>;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
type DeFiKitParamValue = string | number | boolean | readonly (string | number | boolean)[] | null | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* One entry of a role's `permissions`. A bare permission stays valid, but every
|
|
49
|
+
* labelled form names something the app can show as a single card.
|
|
50
|
+
*/
|
|
51
|
+
type PermissionEntry = Permission | LabelledPermissions | SwapEntry | TransferEntry | DeFiKitEntry;
|
|
52
|
+
/** A role's full permission list — the type `permissions.ts` files satisfy. */
|
|
53
|
+
type Permissions = readonly PermissionEntry[];
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/types.d.ts
|
|
56
|
+
type ApiNewRolesSpec = Extract<ApplyConstellationPayload['specification'][number], {
|
|
57
|
+
type: 'ROLES';
|
|
58
|
+
nonce: string;
|
|
59
|
+
}>;
|
|
60
|
+
/** A role definition in the API's serialized format (bigints as template literal strings). */
|
|
61
|
+
/** An allowance definition in the API's serialized format (bigints as template literal strings). */
|
|
62
|
+
type ApiAllowanceSpec = Extract<NonNullable<ApiNewRolesSpec['allowances']>, readonly any[]>[number];
|
|
63
|
+
/** Recursively converts `${bigint}` template literal strings to `bigint`. */
|
|
64
|
+
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;
|
|
65
|
+
/** A role definition with bigint fields as actual bigints. */
|
|
66
|
+
/** An allowance definition with bigint fields as actual bigints. */
|
|
67
|
+
type AllowanceSpec = RealBigints<ApiAllowanceSpec>;
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/actions.d.ts
|
|
70
|
+
/**
|
|
71
|
+
* Allows signing CoW orders selling any of `sell` for any of `buy`. The
|
|
72
|
+
* parameters are compiled into permissions when the constellation is deployed,
|
|
73
|
+
* never here — a stored revision that carried its own copy would go stale as
|
|
74
|
+
* soon as the compiler changed.
|
|
75
|
+
*/
|
|
76
|
+
declare const swap: ({
|
|
77
|
+
label,
|
|
78
|
+
sell,
|
|
79
|
+
buy
|
|
80
|
+
}: SwapParams) => SwapEntry;
|
|
81
|
+
/**
|
|
82
|
+
* Allows transferring any of `tokens` to any of `to` on the role's own chain,
|
|
83
|
+
* and — through `bridge` — to recipients on other chains over Across. Both are
|
|
84
|
+
* optional on their own; a transfer names at least one recipient somewhere.
|
|
85
|
+
*/
|
|
86
|
+
declare const transfer: ({
|
|
87
|
+
label,
|
|
88
|
+
tokens,
|
|
89
|
+
to,
|
|
90
|
+
bridge,
|
|
91
|
+
allowance
|
|
92
|
+
}: TransferParams) => TransferEntry;
|
|
93
|
+
/**
|
|
94
|
+
* A labelled bag of `allow`-kit permissions — the code-side counterpart of the
|
|
95
|
+
* app's Custom action, for everything the other action types don't cover. It
|
|
96
|
+
* takes plain permissions, not other actions.
|
|
97
|
+
*/
|
|
98
|
+
declare const custom: ({
|
|
99
|
+
label,
|
|
100
|
+
permissions
|
|
101
|
+
}: CustomParams) => LabelledPermissions;
|
|
102
|
+
/**
|
|
103
|
+
* The DeFi Kit allow kit — same protocol, verb and parameter surface, plus a
|
|
104
|
+
* `label`. A DeFi Kit entry is nothing but its annotation: the permissions are
|
|
105
|
+
* fetched from the annotation's uri at deploy, so nothing is fetched here.
|
|
106
|
+
*
|
|
107
|
+
* Protocols and parameters are typed against the Ethereum kit, the widest of
|
|
108
|
+
* the chains DeFi Kit serves. Values that only exist on another chain still
|
|
109
|
+
* type-check; the API validates them when the annotation is resolved.
|
|
110
|
+
*/
|
|
111
|
+
declare const defikit: DeFiKit;
|
|
112
|
+
type SwapParams = {
|
|
113
|
+
/** Names the action in the app. Never reaches the chain. */label: string; /** Tokens the role may sell. */
|
|
114
|
+
sell: readonly Address[]; /** Tokens the role may buy. */
|
|
115
|
+
buy: readonly Address[];
|
|
116
|
+
};
|
|
117
|
+
type TransferParams = {
|
|
118
|
+
/** 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. */
|
|
119
|
+
tokens: readonly Address[];
|
|
120
|
+
/** Addresses the role may transfer to on the role's own chain. A node stands
|
|
121
|
+
* for its address, so only nodes that already have one — an account from your
|
|
122
|
+
* codegen, or one bound by address — can be named here. */
|
|
123
|
+
to?: readonly Recipient[]; /** Destinations on other chains, bridged over Across. */
|
|
124
|
+
bridge?: readonly BridgeTarget[];
|
|
125
|
+
/** Allowance capping what may be sent. Pass the allowance declared on the
|
|
126
|
+
* roles modifier, or its key. */
|
|
127
|
+
allowance?: AllowanceSpec | AllowanceKey;
|
|
128
|
+
};
|
|
129
|
+
type CustomParams = {
|
|
130
|
+
/** Names the action in the app. Never reaches the chain. */label: string;
|
|
131
|
+
permissions: readonly Permission[];
|
|
132
|
+
};
|
|
133
|
+
/** An address, or a node that already knows the address it lives at. */
|
|
134
|
+
type Recipient = Address | {
|
|
135
|
+
readonly address: Address;
|
|
136
|
+
readonly chain?: ChainId;
|
|
137
|
+
};
|
|
138
|
+
type BridgeTarget = {
|
|
139
|
+
/** The destination chain. Inferred from the recipients when they are nodes. */chain?: ChainId; /** Addresses the role may bridge to on the destination chain. */
|
|
140
|
+
to: readonly Recipient[]; /** Tokens the role may receive on the destination chain. */
|
|
141
|
+
receive: readonly Address[];
|
|
142
|
+
};
|
|
143
|
+
type EthereumKit = typeof allow;
|
|
144
|
+
type DeFiKit = { readonly [Protocol in keyof EthereumKit]: { readonly [Verb in keyof EthereumKit[Protocol]]: DeFiKitVerb<EthereumKit[Protocol][Verb]> } };
|
|
145
|
+
type DeFiKitVerb<Verb> = Verb extends ((params: infer Params) => unknown) ? (params: Params & {
|
|
146
|
+
label: string;
|
|
147
|
+
}) => DeFiKitEntry : (params: {
|
|
148
|
+
label: string;
|
|
149
|
+
}) => DeFiKitEntry;
|
|
150
|
+
//#endregion
|
|
151
|
+
export { AllowanceSpec as a, transfer as i, defikit as n, PermissionEntry as o, swap as r, Permissions as s, custom as t };
|
|
152
|
+
//# sourceMappingURL=actions-DS5XoMiq.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 type { Permission } from 'zodiac-roles-sdk'\nimport { recipientChains } from './permissionEntries'\nimport type {\n AllowanceKey,\n DeFiKitEntry,\n LabelledPermissions,\n PermissionEntry,\n SwapEntry,\n TransferEntry,\n} from './permissionEntries'\nimport type { AllowanceSpec } from './types'\n\nexport type { PermissionEntry }\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 Permission[]\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":";;;;;;;;AAuBA,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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"}
|
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-D0FqysME.mjs";
|
|
2
2
|
export { DEFAULT_ABIS_DIR, defineConfig, ensureConfigStub, loadConfig, resolveAbisDir };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as resolveZodiacDir, t as ApiClient } from "./api-C49DoaPC.mjs";
|
|
3
|
-
import { a as walkContracts, i as readAbi, n as chainIdFor, o as writeAbi, r as abiFilePath } from "./networks-
|
|
4
|
-
import { a as resolveAbisDir, i as loadConfig, o as findProjectRoot, r as ensureConfigStub } from "./config-
|
|
3
|
+
import { a as walkContracts, i as readAbi, n as chainIdFor, o as writeAbi, r as abiFilePath } from "./networks-B0H4Uoyh.mjs";
|
|
4
|
+
import { a as resolveAbisDir, i as loadConfig, o as findProjectRoot, r as ensureConfigStub } from "./config-D0FqysME.mjs";
|
|
5
5
|
import fs, { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import path, { basename, join, resolve } from "node:path";
|
|
7
7
|
import { invariant } from "@epic-web/invariant";
|
|
@@ -381,15 +381,13 @@ function renderPropKey(name) {
|
|
|
381
381
|
}
|
|
382
382
|
function renderContractType(node, abi, indent) {
|
|
383
383
|
const members = [];
|
|
384
|
-
const seen = /* @__PURE__ */ new Set();
|
|
385
384
|
members.push(`${indent} [EVERYTHING]: (options?: Options) => TargetPermission;`);
|
|
386
|
-
for (const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
members.push(renderFunctionSignature(fragment, indent + " "));
|
|
385
|
+
for (const overloads of groupByName(abi).values()) {
|
|
386
|
+
const overloaded = overloads.length > 1;
|
|
387
|
+
for (const fragment of overloads) {
|
|
388
|
+
const key = overloaded ? signatureFor(fragment) : fragment.name;
|
|
389
|
+
members.push(renderFunctionSignature(fragment, key, indent + " "));
|
|
390
|
+
}
|
|
393
391
|
}
|
|
394
392
|
return [
|
|
395
393
|
"{",
|
|
@@ -397,8 +395,42 @@ function renderContractType(node, abi, indent) {
|
|
|
397
395
|
`${indent}}`
|
|
398
396
|
].join("\n");
|
|
399
397
|
}
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
/**
|
|
399
|
+
* Permissionable fragments grouped by function name, in ABI order. Two
|
|
400
|
+
* fragments that render the same signature are the same function listed twice,
|
|
401
|
+
* so they collapse — emitting the key twice would not compile.
|
|
402
|
+
*/
|
|
403
|
+
function groupByName(abi) {
|
|
404
|
+
const byName = /* @__PURE__ */ new Map();
|
|
405
|
+
for (const fragment of abi) {
|
|
406
|
+
if (fragment.type !== "function") continue;
|
|
407
|
+
if (fragment.stateMutability === "view" || fragment.stateMutability === "pure") continue;
|
|
408
|
+
const name = fragment.name;
|
|
409
|
+
if (!name) continue;
|
|
410
|
+
const overloads = byName.get(name) ?? [];
|
|
411
|
+
if (overloads.some((f) => signatureFor(f) === signatureFor(fragment))) continue;
|
|
412
|
+
byName.set(name, [...overloads, fragment]);
|
|
413
|
+
}
|
|
414
|
+
return byName;
|
|
415
|
+
}
|
|
416
|
+
/** The canonical `name(type,…)` form, which is how `Interface.getFunction`
|
|
417
|
+
* addresses one overload of a name. */
|
|
418
|
+
function signatureFor(fragment) {
|
|
419
|
+
const inputs = fragment.inputs ?? [];
|
|
420
|
+
return `${fragment.name}(${inputs.map(canonicalTypeFor).join(",")})`;
|
|
421
|
+
}
|
|
422
|
+
function canonicalTypeFor(fragment) {
|
|
423
|
+
const type = fragment.type;
|
|
424
|
+
const arrayMatch = /^(.*)\[(\d*)\]$/.exec(type);
|
|
425
|
+
if (arrayMatch) return `${canonicalTypeFor({
|
|
426
|
+
...fragment,
|
|
427
|
+
type: arrayMatch[1]
|
|
428
|
+
})}[${arrayMatch[2]}]`;
|
|
429
|
+
if (type === "tuple") return `(${(fragment.components ?? []).map(canonicalTypeFor).join(",")})`;
|
|
430
|
+
return type;
|
|
431
|
+
}
|
|
432
|
+
function renderFunctionSignature(fragment, key, indent) {
|
|
433
|
+
const name = renderPropKey(key);
|
|
402
434
|
const params = [];
|
|
403
435
|
for (const input of fragment.inputs ?? []) {
|
|
404
436
|
const paramName = sanitizeParamName(input.name || `arg${params.length}`, params.length);
|