@coordination-network/network-member-governance 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +250 -0
- package/dist/index.cjs +583 -0
- package/dist/index.js +555 -0
- package/package.json +54 -0
- package/src/constants.ts +19 -0
- package/src/governance-rule.ts +20 -0
- package/src/index.ts +43 -0
- package/src/network-member-calls.ts +321 -0
- package/src/safe-transaction.ts +361 -0
- package/src/types.ts +37 -0
- package/types/index.d.ts +472 -0
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import * as _safe_global_types_kit from '@safe-global/types-kit';
|
|
2
|
+
import { MetaTransactionData, SafeTransactionData, SafeSignature } from '@safe-global/types-kit';
|
|
3
|
+
export { MetaTransactionData, OperationType, SafeSignature, SafeTransactionData, SafeVersion } from '@safe-global/types-kit';
|
|
4
|
+
import { Address, Hex, TypedDataDefinition } from 'viem';
|
|
5
|
+
|
|
6
|
+
/** The zero address, the Safe's "unset" value for `gasToken` and `refundReceiver`. */
|
|
7
|
+
declare const ZERO_ADDRESS: "0x0000000000000000000000000000000000000000";
|
|
8
|
+
/**
|
|
9
|
+
* The gas and refund fields of every transaction this package builds, all zeroed.
|
|
10
|
+
*
|
|
11
|
+
* A Safe transaction can reimburse whoever submitted it out of the Safe's own
|
|
12
|
+
* balance. This flow does not: the network member that sends the transaction
|
|
13
|
+
* pays its own gas, and the Safe never holds or moves funds. Zeroing these
|
|
14
|
+
* fields is what turns that off, so every transaction this package builds
|
|
15
|
+
* refunds nobody.
|
|
16
|
+
*/
|
|
17
|
+
declare const NO_REFUND: {
|
|
18
|
+
readonly safeTxGas: "0";
|
|
19
|
+
readonly baseGas: "0";
|
|
20
|
+
readonly gasPrice: "0";
|
|
21
|
+
readonly gasToken: "0x0000000000000000000000000000000000000000";
|
|
22
|
+
readonly refundReceiver: "0x0000000000000000000000000000000000000000";
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The signatures a Safe of `memberCount` network members must require: a majority, one more than
|
|
27
|
+
* half. Five members need three, eight need five.
|
|
28
|
+
*
|
|
29
|
+
* The authoritative copy of this rule is on chain, in `NetworkMemberGovernance.governanceThreshold`,
|
|
30
|
+
* which every admission and removal sets the Safe's threshold from. This is the same expression for
|
|
31
|
+
* off-chain use: checking that a Safe is configured correctly, and reporting what a pending operation
|
|
32
|
+
* will change the threshold to. No call carries a threshold, so nothing depends on this value being
|
|
33
|
+
* right.
|
|
34
|
+
*
|
|
35
|
+
* @param memberCount How many members the Safe has owners for, after the change.
|
|
36
|
+
* @returns The required number of approvals.
|
|
37
|
+
*/
|
|
38
|
+
declare function governanceThreshold(memberCount: number): number;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Which Safe, on which chain, a transaction is bound to.
|
|
42
|
+
*
|
|
43
|
+
* `safeVersion` selects the encoding: Safe changed both the EIP-712 domain and
|
|
44
|
+
* the `SafeTx` struct across versions, so a digest computed for the wrong version
|
|
45
|
+
* is silently wrong. It defaults to the SDK's default version; read the Safe's
|
|
46
|
+
* own `VERSION()` and pass it when governing an older deployment.
|
|
47
|
+
*/
|
|
48
|
+
interface SafeContext {
|
|
49
|
+
/** The governance Safe's address. */
|
|
50
|
+
safeAddress: Address;
|
|
51
|
+
/** The chain the Safe is deployed on. */
|
|
52
|
+
chainId: number | bigint;
|
|
53
|
+
/** The Safe's contract version. Defaults to the SDK's default. */
|
|
54
|
+
safeVersion?: _safe_global_types_kit.SafeVersion;
|
|
55
|
+
/**
|
|
56
|
+
* MultiSendCallOnly to batch through. Defaults to the canonical deployment
|
|
57
|
+
* for this chain and Safe version, from `@safe-global/safe-deployments`. Pass
|
|
58
|
+
* one only for a chain where that deployment is missing.
|
|
59
|
+
*/
|
|
60
|
+
multiSend?: Address;
|
|
61
|
+
}
|
|
62
|
+
/** The result of checking a signature set against a Safe's owners. */
|
|
63
|
+
interface QuorumCheck {
|
|
64
|
+
/** Whether the set is enough for the Safe to execute. */
|
|
65
|
+
hasQuorum: boolean;
|
|
66
|
+
/** The distinct owners that signed, sorted ascending. */
|
|
67
|
+
signers: Address[];
|
|
68
|
+
/** How many more owner signatures are needed. Zero once quorum is met. */
|
|
69
|
+
missing: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The `NetworkMemberGovernance` surface: the governance operations, each one call.
|
|
74
|
+
*
|
|
75
|
+
* There is no threshold argument anywhere here. The contract computes it from the Safe's live owner
|
|
76
|
+
* count, and removal reads the member's commitment and signer from the registry, so no caller can
|
|
77
|
+
* carry a wrong threshold, point at another member's leaf, or take arbitrary calldata.
|
|
78
|
+
*
|
|
79
|
+
* `expectedMemberCount` is not a policy input either. It pins the member set a transaction was built
|
|
80
|
+
* against, so a transaction goes stale the moment that set changes. It has to exist because a
|
|
81
|
+
* voluntary exit changes the owner count and the threshold without advancing the Safe's nonce, which is
|
|
82
|
+
* what would otherwise invalidate transactions in flight.
|
|
83
|
+
*/
|
|
84
|
+
declare const NETWORK_MEMBER_GOVERNANCE_ABI: readonly [{
|
|
85
|
+
readonly type: "function";
|
|
86
|
+
readonly name: "admitMember";
|
|
87
|
+
readonly stateMutability: "nonpayable";
|
|
88
|
+
readonly inputs: readonly [{
|
|
89
|
+
readonly name: "memberNetworkId";
|
|
90
|
+
readonly type: "bytes32";
|
|
91
|
+
}, {
|
|
92
|
+
readonly name: "semaphoreCommitment";
|
|
93
|
+
readonly type: "uint256";
|
|
94
|
+
}, {
|
|
95
|
+
readonly name: "governanceSigner";
|
|
96
|
+
readonly type: "address";
|
|
97
|
+
}, {
|
|
98
|
+
readonly name: "expectedMemberCount";
|
|
99
|
+
readonly type: "uint256";
|
|
100
|
+
}];
|
|
101
|
+
readonly outputs: readonly [];
|
|
102
|
+
}, {
|
|
103
|
+
readonly type: "function";
|
|
104
|
+
readonly name: "removeMember";
|
|
105
|
+
readonly stateMutability: "nonpayable";
|
|
106
|
+
readonly inputs: readonly [{
|
|
107
|
+
readonly name: "memberNetworkId";
|
|
108
|
+
readonly type: "bytes32";
|
|
109
|
+
}, {
|
|
110
|
+
readonly name: "merkleProofSiblings";
|
|
111
|
+
readonly type: "uint256[]";
|
|
112
|
+
}, {
|
|
113
|
+
readonly name: "expectedMemberCount";
|
|
114
|
+
readonly type: "uint256";
|
|
115
|
+
}];
|
|
116
|
+
readonly outputs: readonly [];
|
|
117
|
+
}, {
|
|
118
|
+
readonly type: "function";
|
|
119
|
+
readonly name: "exitNetwork";
|
|
120
|
+
readonly stateMutability: "nonpayable";
|
|
121
|
+
readonly inputs: readonly [{
|
|
122
|
+
readonly name: "memberNetworkId";
|
|
123
|
+
readonly type: "bytes32";
|
|
124
|
+
}, {
|
|
125
|
+
readonly name: "merkleProofSiblings";
|
|
126
|
+
readonly type: "uint256[]";
|
|
127
|
+
}];
|
|
128
|
+
readonly outputs: readonly [];
|
|
129
|
+
}, {
|
|
130
|
+
readonly type: "function";
|
|
131
|
+
readonly name: "governanceThreshold";
|
|
132
|
+
readonly stateMutability: "pure";
|
|
133
|
+
readonly inputs: readonly [{
|
|
134
|
+
readonly name: "memberCount";
|
|
135
|
+
readonly type: "uint256";
|
|
136
|
+
}];
|
|
137
|
+
readonly outputs: readonly [{
|
|
138
|
+
readonly name: "";
|
|
139
|
+
readonly type: "uint256";
|
|
140
|
+
}];
|
|
141
|
+
}, {
|
|
142
|
+
readonly type: "function";
|
|
143
|
+
readonly name: "thresholdAfterAdmission";
|
|
144
|
+
readonly stateMutability: "view";
|
|
145
|
+
readonly inputs: readonly [];
|
|
146
|
+
readonly outputs: readonly [{
|
|
147
|
+
readonly name: "";
|
|
148
|
+
readonly type: "uint256";
|
|
149
|
+
}];
|
|
150
|
+
}];
|
|
151
|
+
/** The `NetworkMemberRegistry` surface a member server needs directly. */
|
|
152
|
+
declare const NETWORK_MEMBER_REGISTRY_ABI: readonly [{
|
|
153
|
+
readonly type: "function";
|
|
154
|
+
readonly name: "requestToJoin";
|
|
155
|
+
readonly stateMutability: "nonpayable";
|
|
156
|
+
readonly inputs: readonly [{
|
|
157
|
+
readonly name: "memberNetworkId";
|
|
158
|
+
readonly type: "bytes32";
|
|
159
|
+
}, {
|
|
160
|
+
readonly name: "semaphoreCommitment";
|
|
161
|
+
readonly type: "uint256";
|
|
162
|
+
}, {
|
|
163
|
+
readonly name: "governanceSigner";
|
|
164
|
+
readonly type: "address";
|
|
165
|
+
}, {
|
|
166
|
+
readonly name: "metadataURI";
|
|
167
|
+
readonly type: "string";
|
|
168
|
+
}];
|
|
169
|
+
readonly outputs: readonly [];
|
|
170
|
+
}, {
|
|
171
|
+
readonly type: "event";
|
|
172
|
+
readonly name: "NetworkMemberJoinRequested";
|
|
173
|
+
readonly inputs: readonly [{
|
|
174
|
+
readonly name: "memberNetworkId";
|
|
175
|
+
readonly type: "bytes32";
|
|
176
|
+
readonly indexed: true;
|
|
177
|
+
}, {
|
|
178
|
+
readonly name: "semaphoreCommitment";
|
|
179
|
+
readonly type: "uint256";
|
|
180
|
+
readonly indexed: false;
|
|
181
|
+
}, {
|
|
182
|
+
readonly name: "governanceSigner";
|
|
183
|
+
readonly type: "address";
|
|
184
|
+
readonly indexed: false;
|
|
185
|
+
}, {
|
|
186
|
+
readonly name: "metadataURI";
|
|
187
|
+
readonly type: "string";
|
|
188
|
+
readonly indexed: false;
|
|
189
|
+
}];
|
|
190
|
+
}];
|
|
191
|
+
/** Where the governance operations live. */
|
|
192
|
+
interface GovernanceContracts {
|
|
193
|
+
/** The `NetworkMemberGovernance` orchestrator, enabled as a module on the Safe. */
|
|
194
|
+
governance: Address;
|
|
195
|
+
/** The `NetworkMemberRegistry`, for reading records and for a candidate's own join request. */
|
|
196
|
+
registry: Address;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* What a candidate published in its `NetworkMemberJoinRequested` event, which is every argument its
|
|
200
|
+
* admission will carry.
|
|
201
|
+
*
|
|
202
|
+
* Approvers build this from the event rather than from anything another party sends them, so an
|
|
203
|
+
* admission is a function of the candidate's own announcement.
|
|
204
|
+
*/
|
|
205
|
+
interface NetworkMemberJoinRequest {
|
|
206
|
+
/** The candidate's networkId. */
|
|
207
|
+
memberNetworkId: Hex;
|
|
208
|
+
/** The Semaphore identity commitment it wants in the approved-member group. */
|
|
209
|
+
semaphoreCommitment: bigint;
|
|
210
|
+
/** The address it wants added to the governance Safe, so it can vote on later candidates. */
|
|
211
|
+
governanceSigner: Address;
|
|
212
|
+
/** Where the vetting material lives, or an empty string. */
|
|
213
|
+
metadataURI?: string;
|
|
214
|
+
}
|
|
215
|
+
/** What removing a member needs. */
|
|
216
|
+
interface NetworkMemberRemoval {
|
|
217
|
+
/** The member's networkId. Its commitment and signer are read on chain. */
|
|
218
|
+
memberNetworkId: Hex;
|
|
219
|
+
/**
|
|
220
|
+
* The membership proof for its group leaf, computed off chain against the group state this
|
|
221
|
+
* transaction will execute in. If another group write lands first, recompute it and approve the
|
|
222
|
+
* rebuilt transaction.
|
|
223
|
+
*/
|
|
224
|
+
merkleProofSiblings: bigint[];
|
|
225
|
+
}
|
|
226
|
+
/** The shape of a log this package can read: enough of viem's `Log` to decode it. */
|
|
227
|
+
interface JoinRequestLog {
|
|
228
|
+
/** The contract that emitted it. Checked against the registry when both are given. */
|
|
229
|
+
address?: Address;
|
|
230
|
+
/** The log's topics, the first being the event signature. */
|
|
231
|
+
topics: readonly Hex[];
|
|
232
|
+
/** The log's non-indexed data. */
|
|
233
|
+
data: Hex;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Turns a `NetworkMemberJoinRequested` log into the request it announced.
|
|
237
|
+
*
|
|
238
|
+
* This is the only honest source of a {NetworkMemberJoinRequest}: an approver reads the candidate's own
|
|
239
|
+
* event from a node it trusts and decodes it here, so the transaction it approves is a function of what
|
|
240
|
+
* the chain reported. Building the object by hand from something another party sent gives up that
|
|
241
|
+
* property, and with it the meaning of the approval.
|
|
242
|
+
*
|
|
243
|
+
* It stays free of any RPC dependency: fetching the log is the caller's job, with `getLogs` against its
|
|
244
|
+
* own node. What this removes is the field mapping, and the chance of a silent mismatch in it.
|
|
245
|
+
*
|
|
246
|
+
* @param log The log, as viem returns it from `getLogs`.
|
|
247
|
+
* @param registry The registry the log must have come from. Checked when the log carries an address.
|
|
248
|
+
* @returns The request the candidate published.
|
|
249
|
+
*/
|
|
250
|
+
declare function decodeJoinRequestLog(log: JoinRequestLog, registry?: Address): NetworkMemberJoinRequest;
|
|
251
|
+
/**
|
|
252
|
+
* The single call that admits a network member: its record, its group leaf, and its seat at the Safe.
|
|
253
|
+
*
|
|
254
|
+
* @param admission The vetted join request being approved.
|
|
255
|
+
* @param memberCount The Safe's live owner count, from `getOwners().length`. Pinned into the call so the
|
|
256
|
+
* transaction goes stale if the member set changes before it executes.
|
|
257
|
+
* @returns Calldata for the orchestrator.
|
|
258
|
+
*/
|
|
259
|
+
declare function encodeAdmitMember(admission: NetworkMemberJoinRequest, memberCount: number): Hex;
|
|
260
|
+
/**
|
|
261
|
+
* The single call that removes a network member from all three places.
|
|
262
|
+
*
|
|
263
|
+
* @param removal The member to remove, and its membership proof.
|
|
264
|
+
* @param memberCount The Safe's live owner count, pinned for the reason given on {encodeAdmitMember}.
|
|
265
|
+
* @returns Calldata for the orchestrator.
|
|
266
|
+
*/
|
|
267
|
+
declare function encodeRemoveMember(removal: NetworkMemberRemoval, memberCount: number): Hex;
|
|
268
|
+
/** The one call an admission is, as a Safe meta-transaction. */
|
|
269
|
+
declare function admissionCall(contracts: GovernanceContracts, admission: NetworkMemberJoinRequest, memberCount: number): MetaTransactionData;
|
|
270
|
+
/** The one call a removal is, as a Safe meta-transaction. */
|
|
271
|
+
declare function removalCall(contracts: GovernanceContracts, removal: NetworkMemberRemoval, memberCount: number): MetaTransactionData;
|
|
272
|
+
/**
|
|
273
|
+
* Builds the Safe transaction that admits a network member.
|
|
274
|
+
*
|
|
275
|
+
* One plain call to the orchestrator, so there is no MultiSend batch to decode and no threshold in the
|
|
276
|
+
* calldata to check. The result is a pure function of the on-chain request and the nonce, which is what
|
|
277
|
+
* makes building safe to do in parallel: two approvers who build from the same chain state produce
|
|
278
|
+
* byte-identical transactions and therefore the same `safeTxHash`, so their approvals accumulate
|
|
279
|
+
* against one hash instead of competing.
|
|
280
|
+
*
|
|
281
|
+
* @param contracts The governance contracts.
|
|
282
|
+
* @param admission The vetted join request being approved.
|
|
283
|
+
* @param context The Safe and chain the transaction is for.
|
|
284
|
+
* @param nonce The Safe's current `nonce()`.
|
|
285
|
+
* @param memberCount The Safe's live owner count, from `getOwners().length`.
|
|
286
|
+
* @returns The transaction data to sign.
|
|
287
|
+
*/
|
|
288
|
+
declare function buildAdmissionTransaction(contracts: GovernanceContracts, admission: NetworkMemberJoinRequest, context: SafeContext, nonce: number, memberCount: number): SafeTransactionData;
|
|
289
|
+
/**
|
|
290
|
+
* Builds the Safe transaction that removes a network member.
|
|
291
|
+
*
|
|
292
|
+
* @param contracts The governance contracts.
|
|
293
|
+
* @param removal The member to remove, and its membership proof.
|
|
294
|
+
* @param context The Safe and chain the transaction is for.
|
|
295
|
+
* @param nonce The Safe's current `nonce()`.
|
|
296
|
+
* @param memberCount The Safe's live owner count, from `getOwners().length`.
|
|
297
|
+
* @returns The transaction data to sign.
|
|
298
|
+
*/
|
|
299
|
+
declare function buildRemovalTransaction(contracts: GovernanceContracts, removal: NetworkMemberRemoval, context: SafeContext, nonce: number, memberCount: number): SafeTransactionData;
|
|
300
|
+
/**
|
|
301
|
+
* Calldata for a member leaving the network of its own accord: its record, its group leaf and its seat
|
|
302
|
+
* at the Safe, in one call.
|
|
303
|
+
*
|
|
304
|
+
* Not a Safe transaction. The member's own governance signer sends this directly to the orchestrator,
|
|
305
|
+
* because departure does not need the consent of the members being left, so there is no transaction
|
|
306
|
+
* for anyone else to approve. It is also how a member that is winding down avoids leaving a seat behind
|
|
307
|
+
* that nobody can sign for, which would still count toward everyone else's threshold.
|
|
308
|
+
*
|
|
309
|
+
* Rejoining means a new request and a new place in the group, and every credential the member issued
|
|
310
|
+
* stops verifying once its record is gone.
|
|
311
|
+
*
|
|
312
|
+
* @param memberNetworkId The member leaving. The caller must be its governance signer.
|
|
313
|
+
* @param merkleProofSiblings The membership proof for its group leaf, computed off chain against the
|
|
314
|
+
* group state this transaction will execute in.
|
|
315
|
+
* @returns Calldata to send to the orchestrator.
|
|
316
|
+
*/
|
|
317
|
+
declare function encodeExitNetwork(memberNetworkId: Hex, merkleProofSiblings: bigint[]): Hex;
|
|
318
|
+
/**
|
|
319
|
+
* Calldata for a candidate's own join request, the one permissionless call in this flow. It must be
|
|
320
|
+
* sent from the auth address the networkId encodes, and `governanceSigner` must be a different key.
|
|
321
|
+
*
|
|
322
|
+
* @param request The request to announce.
|
|
323
|
+
* @returns Calldata for the registry.
|
|
324
|
+
*/
|
|
325
|
+
declare function encodeRequestToJoin(request: NetworkMemberJoinRequest): Hex;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* The MultiSendCallOnly the Safe delegate-calls to batch several calls.
|
|
329
|
+
*
|
|
330
|
+
* Resolved from `@safe-global/safe-deployments` for this chain and Safe version,
|
|
331
|
+
* which is what keeps a wrong address out of a delegate call. Override it in the
|
|
332
|
+
* context only for a chain where the canonical deployment is missing.
|
|
333
|
+
*
|
|
334
|
+
* @param context The Safe and chain the batch is for.
|
|
335
|
+
* @returns The MultiSendCallOnly address.
|
|
336
|
+
*/
|
|
337
|
+
declare function resolveMultiSend(context: SafeContext): Address;
|
|
338
|
+
/**
|
|
339
|
+
* Builds the Safe transaction data for one or more calls, with the gas and refund
|
|
340
|
+
* fields zeroed so whoever submits it pays.
|
|
341
|
+
*
|
|
342
|
+
* A single call is executed directly. Several calls are batched through
|
|
343
|
+
* MultiSendCallOnly as one delegate call, so the Safe applies them together or
|
|
344
|
+
* not at all.
|
|
345
|
+
*
|
|
346
|
+
* @param transactions The calls, in the order the Safe should make them.
|
|
347
|
+
* @param context The Safe and chain the transaction is for.
|
|
348
|
+
* @param nonce The Safe's current `nonce()`. Read it from the chain: a
|
|
349
|
+
* transaction built against a stale nonce is rejected, and executing any Safe
|
|
350
|
+
* transaction invalidates every approval recorded against that nonce.
|
|
351
|
+
* @returns The transaction data to hash and sign.
|
|
352
|
+
*/
|
|
353
|
+
declare function buildSafeTransactionData(transactions: MetaTransactionData[], context: SafeContext, nonce: number): SafeTransactionData;
|
|
354
|
+
/**
|
|
355
|
+
* The digest the Safe's owners sign, as the Safe's own `getTransactionHash`
|
|
356
|
+
* computes it. Bound to the Safe and the chain by the EIP-712 domain, so a
|
|
357
|
+
* signature cannot be replayed against another Safe or another network.
|
|
358
|
+
*
|
|
359
|
+
* @param context The Safe and chain the transaction is for.
|
|
360
|
+
* @param transaction The transaction data to hash.
|
|
361
|
+
* @returns The EIP-712 digest.
|
|
362
|
+
*/
|
|
363
|
+
declare function hashSafeTransaction(context: SafeContext, transaction: SafeTransactionData): Hex;
|
|
364
|
+
/** What a member server needs from its governance key: EIP-712 signing. */
|
|
365
|
+
interface TypedDataSigner {
|
|
366
|
+
/** The owner address this key signs for. */
|
|
367
|
+
address: Address;
|
|
368
|
+
/** Signs EIP-712 typed data, returning a 65-byte signature. */
|
|
369
|
+
signTypedData(typedData: TypedDataDefinition): Promise<Hex>;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Signs a Safe transaction as one approver.
|
|
373
|
+
*
|
|
374
|
+
* Governance approves on chain with {encodeApproveHash} and never circulates a
|
|
375
|
+
* signature, so nothing in this package's flow calls this. It stays as a Safe
|
|
376
|
+
* primitive, and the tests use it to check the packing in
|
|
377
|
+
* {encodeSafeSignatures}.
|
|
378
|
+
*
|
|
379
|
+
* The signer must be an owner of the Safe. A viem account works directly, and so
|
|
380
|
+
* does any wrapper over a KMS or HSM that signs EIP-712 and knows its address.
|
|
381
|
+
*
|
|
382
|
+
* @param signer The approver's signing key.
|
|
383
|
+
* @param context The Safe and chain the transaction is for.
|
|
384
|
+
* @param transaction The transaction to approve.
|
|
385
|
+
* @returns The approver's signature.
|
|
386
|
+
*/
|
|
387
|
+
declare function signSafeTransaction(signer: TypedDataSigner, context: SafeContext, transaction: SafeTransactionData): Promise<SafeSignature>;
|
|
388
|
+
/**
|
|
389
|
+
* Recovers the owner behind a signature over a Safe transaction, trusting the
|
|
390
|
+
* signature rather than a claimed sender. Like {signSafeTransaction}, it serves
|
|
391
|
+
* off-chain signatures, which this package's flow does not produce.
|
|
392
|
+
*
|
|
393
|
+
* @param context The Safe and chain the transaction is for.
|
|
394
|
+
* @param transaction The signed transaction.
|
|
395
|
+
* @param signature The 65-byte signature.
|
|
396
|
+
* @returns The recovered signer address.
|
|
397
|
+
*/
|
|
398
|
+
declare function recoverSafeSigner(context: SafeContext, transaction: SafeTransactionData, signature: Hex): Promise<Address>;
|
|
399
|
+
/**
|
|
400
|
+
* Calldata for `approveHash`, which records an approval in the Safe's own storage instead of handing a
|
|
401
|
+
* signature to somebody.
|
|
402
|
+
*
|
|
403
|
+
* This is the fully on-chain approval path, and its appeal is that nothing is ever circulated: each
|
|
404
|
+
* approver derives the transaction from the chain, approves its hash from its own key, and the member
|
|
405
|
+
* that completes the quorum reads the approvals and executes. There is no channel to secure, and no
|
|
406
|
+
* approver ever acts on a payload another party assembled, which is what makes blind signing
|
|
407
|
+
* impossible rather than merely detectable.
|
|
408
|
+
*
|
|
409
|
+
* Only an owner may call it, and the hash must be the one from {hashSafeTransaction}: an approval is
|
|
410
|
+
* bound to one exact transaction, so approving one candidate's admission says nothing about another's.
|
|
411
|
+
*
|
|
412
|
+
* @param context The Safe and chain the transaction is for.
|
|
413
|
+
* @param safeTxHash The digest from {hashSafeTransaction}.
|
|
414
|
+
* @returns Calldata to send to the Safe address, from the approver's own key.
|
|
415
|
+
*/
|
|
416
|
+
declare function encodeApproveHash(context: SafeContext, safeTxHash: Hex): Hex;
|
|
417
|
+
/**
|
|
418
|
+
* The signature entry for an owner that approved on chain with {encodeApproveHash}.
|
|
419
|
+
*
|
|
420
|
+
* It carries no signature material: the Safe reads `v == 1` and looks the approval up in its own
|
|
421
|
+
* storage, so the entry only names the approver. Feed these to {encodeSafeSignatures} or
|
|
422
|
+
* {encodeExecTransaction} exactly like signatures made off chain, and the two kinds may be mixed in one
|
|
423
|
+
* transaction.
|
|
424
|
+
*
|
|
425
|
+
* The member that submits gets its own entry for free: the Safe treats the executor as having approved,
|
|
426
|
+
* so it can include itself without a prior `approveHash`, turning a 3-of-5 into two approval
|
|
427
|
+
* transactions plus the execution.
|
|
428
|
+
*
|
|
429
|
+
* @param owner The approver, which must still be an owner when the transaction executes.
|
|
430
|
+
* @returns A signature entry naming that approver.
|
|
431
|
+
*/
|
|
432
|
+
declare function onChainApproval(owner: Address): SafeSignature;
|
|
433
|
+
/**
|
|
434
|
+
* Packs signatures into the `signatures` argument of `execTransaction`.
|
|
435
|
+
*
|
|
436
|
+
* The Safe reads the packed signatures at fixed 65-byte offsets and requires each recovered signer to be
|
|
437
|
+
* strictly greater than the previous one, so the SDK sorts them by signer here.
|
|
438
|
+
* A set that counts the same owner twice would sort fine and then be rejected on
|
|
439
|
+
* chain, so it is refused up front.
|
|
440
|
+
*
|
|
441
|
+
* @param signatures The approvals, in any order. {onChainApproval} produces them for the on-chain
|
|
442
|
+
* flow.
|
|
443
|
+
* @returns The entries concatenated into the single `bytes` value `execTransaction` takes, 65 bytes
|
|
444
|
+
* per owner, ordered by signer.
|
|
445
|
+
*/
|
|
446
|
+
declare function encodeSafeSignatures(signatures: SafeSignature[]): Hex;
|
|
447
|
+
/**
|
|
448
|
+
* Checks a set of approvers against the Safe's owners and threshold, so a member
|
|
449
|
+
* knows whether it can submit yet and, if not, how many approvals are missing.
|
|
450
|
+
* Addresses that are not current owners are ignored rather than counted.
|
|
451
|
+
*
|
|
452
|
+
* @param owners The Safe's current owners, from `getOwners()`.
|
|
453
|
+
* @param threshold The Safe's current `getThreshold()`.
|
|
454
|
+
* @param signers The addresses that have approved, read from the chain.
|
|
455
|
+
* @returns Whether the set is enough, and what is still missing.
|
|
456
|
+
*/
|
|
457
|
+
declare function checkQuorum(owners: Address[], threshold: number, signers: Address[]): QuorumCheck;
|
|
458
|
+
/**
|
|
459
|
+
* Encodes the `execTransaction` call that lands the transaction on chain, against
|
|
460
|
+
* the Safe ABI for this context's version. The result is plain calldata to the
|
|
461
|
+
* Safe, so anyone can submit it: the approvals carry the authority, not the
|
|
462
|
+
* sender.
|
|
463
|
+
*
|
|
464
|
+
* @param context The Safe and chain the transaction is for.
|
|
465
|
+
* @param transaction The approved transaction.
|
|
466
|
+
* @param signatures The approvals, in any order.
|
|
467
|
+
* @returns Calldata to send to the Safe address.
|
|
468
|
+
*/
|
|
469
|
+
declare function encodeExecTransaction(context: SafeContext, transaction: SafeTransactionData, signatures: SafeSignature[]): Hex;
|
|
470
|
+
|
|
471
|
+
export { NETWORK_MEMBER_GOVERNANCE_ABI, NETWORK_MEMBER_REGISTRY_ABI, NO_REFUND, ZERO_ADDRESS, admissionCall, buildAdmissionTransaction, buildRemovalTransaction, buildSafeTransactionData, checkQuorum, decodeJoinRequestLog, encodeAdmitMember, encodeApproveHash, encodeExecTransaction, encodeExitNetwork, encodeRemoveMember, encodeRequestToJoin, encodeSafeSignatures, governanceThreshold, hashSafeTransaction, onChainApproval, recoverSafeSigner, removalCall, resolveMultiSend, signSafeTransaction };
|
|
472
|
+
export type { GovernanceContracts, JoinRequestLog, NetworkMemberJoinRequest, NetworkMemberRemoval, QuorumCheck, SafeContext, TypedDataSigner };
|