@claritydao/midnight-agora-sdk 0.2.0 → 1.2.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 +58 -55
- package/dist/browser-providers.d.ts +24 -19
- package/dist/browser-providers.d.ts.map +1 -1
- package/dist/browser-providers.js +654 -139
- package/dist/browser-providers.js.map +1 -1
- package/dist/common-types.d.ts +13 -24
- package/dist/common-types.d.ts.map +1 -1
- package/dist/common-types.js +1 -1
- package/dist/common-types.js.map +1 -1
- package/dist/governor-api.d.ts +42 -17
- package/dist/governor-api.d.ts.map +1 -1
- package/dist/governor-api.js +233 -248
- package/dist/governor-api.js.map +1 -1
- package/dist/index.d.ts +11 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -9
- package/dist/index.js.map +1 -1
- package/dist/manager.d.ts +11 -11
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +17 -20
- package/dist/manager.js.map +1 -1
- package/dist/types.d.ts +9 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +26 -26
- package/.prettierrc +0 -1
- package/LICENSE +0 -0
- package/eslint.config.mjs +0 -48
- package/src/browser-providers.ts +0 -278
- package/src/common-types.ts +0 -51
- package/src/errors.ts +0 -124
- package/src/governor-api.ts +0 -948
- package/src/index.ts +0 -51
- package/src/manager.ts +0 -205
- package/src/types.ts +0 -403
- package/src/utils/index.ts +0 -60
- package/src/validators.ts +0 -245
- package/test/README.md +0 -409
- package/test/errors.test.ts +0 -179
- package/test/integration/governor-api.test.ts +0 -370
- package/test/mocks/providers.ts +0 -130
- package/test/types.test.ts +0 -112
- package/test/utils.test.ts +0 -111
- package/test/validators.test.ts +0 -368
- package/test/wasm-init.test.ts +0 -132
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -9
package/dist/governor-api.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
const governorContractInstance = new Contract(witnesses);
|
|
1
|
+
import { sampleSigningKey } from '@midnight-ntwrk/compact-runtime';
|
|
2
|
+
import { GovernorPrivateStateId, } from './common-types';
|
|
3
|
+
import * as utils from './utils/index';
|
|
4
|
+
import { createCircuitCallTxInterface, createCircuitMaintenanceTxInterfaces, createContractMaintenanceTxInterface, deployContract, } from '@midnight-ntwrk/midnight-js-contracts';
|
|
5
|
+
import { compiledGovernorBaseContract, compiledGovernorFullContract, GOVERNOR_CIRCUIT_BUNDLES, GOVERNOR_FULL_CIRCUITS, } from '@claritydao/midnight-agora-contracts';
|
|
6
|
+
import { VoteChoice, ConfigPresets } from './types';
|
|
7
|
+
import { validateDeploymentConfig, validateProposal, validateVoteChoice, validateAddress, validatePositiveBigInt } from './validators';
|
|
8
|
+
import { InvalidParameterError } from './errors';
|
|
10
9
|
// Constants to replace magic numbers
|
|
11
10
|
/** @internal */
|
|
12
11
|
const MILLISECONDS_TO_SECONDS = 1000;
|
|
@@ -16,33 +15,71 @@ const INITIAL_EFFECT_ID = BigInt(0);
|
|
|
16
15
|
const DEFAULT_BIGINT_VALUE = BigInt(0);
|
|
17
16
|
/** @internal */
|
|
18
17
|
const getCurrentUnixTimestamp = () => BigInt(Math.floor(Date.now() / MILLISECONDS_TO_SECONDS));
|
|
18
|
+
/** @internal */
|
|
19
|
+
const minimalDeployQueryError = (circuitName) => new Error(`${circuitName} is not exported in the minimal preprod deploy build. Use indexed public state or a separate query package for reads.`);
|
|
19
20
|
/**
|
|
20
21
|
* Provides an implementation of {@link DeployedGovernorAPI} by adapting a deployed governor
|
|
21
22
|
* contract.
|
|
22
23
|
*/
|
|
23
24
|
export class GovernorAPI {
|
|
24
25
|
deployedContract;
|
|
26
|
+
baseDeployedContract;
|
|
25
27
|
providers;
|
|
26
28
|
logger;
|
|
27
29
|
/** @internal */
|
|
28
|
-
constructor(deployedContract, providers, logger) {
|
|
30
|
+
constructor(deployedContract, baseDeployedContract, providers, logger) {
|
|
29
31
|
this.deployedContract = deployedContract;
|
|
32
|
+
this.baseDeployedContract = baseDeployedContract;
|
|
30
33
|
this.providers = providers;
|
|
31
34
|
this.logger = logger;
|
|
32
|
-
this.deployedContractAddress =
|
|
33
|
-
deployedContract.deployTxData.public.contractAddress;
|
|
35
|
+
this.deployedContractAddress = deployedContract.deployTxData.public.contractAddress;
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
38
|
* Gets the address of the current deployed contract.
|
|
37
39
|
*/
|
|
38
40
|
deployedContractAddress;
|
|
41
|
+
static withGovernorProfile(providers, profile) {
|
|
42
|
+
const profileProviders = providers.governorProfiles?.[profile];
|
|
43
|
+
if (!profileProviders) {
|
|
44
|
+
return providers;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
...providers,
|
|
48
|
+
zkConfigProvider: profileProviders.zkConfigProvider,
|
|
49
|
+
proofProvider: profileProviders.proofProvider,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
static createFullDeployedContract(providers, deployTxData, contractAddress) {
|
|
53
|
+
providers.privateStateProvider.setContractAddress(contractAddress);
|
|
54
|
+
return {
|
|
55
|
+
deployTxData,
|
|
56
|
+
callTx: createCircuitCallTxInterface(providers, compiledGovernorFullContract, contractAddress, GovernorPrivateStateId),
|
|
57
|
+
circuitMaintenanceTx: createCircuitMaintenanceTxInterfaces(providers, compiledGovernorFullContract, contractAddress),
|
|
58
|
+
contractMaintenanceTx: createContractMaintenanceTxInterface(providers, compiledGovernorFullContract, contractAddress),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
static createBaseDeployedContract(providers, deployTxData, contractAddress) {
|
|
62
|
+
const baseProviders = GovernorAPI.withGovernorProfile(providers, 'base');
|
|
63
|
+
baseProviders.privateStateProvider.setContractAddress(contractAddress);
|
|
64
|
+
return {
|
|
65
|
+
deployTxData,
|
|
66
|
+
callTx: createCircuitCallTxInterface(baseProviders, compiledGovernorBaseContract, contractAddress, GovernorPrivateStateId),
|
|
67
|
+
circuitMaintenanceTx: createCircuitMaintenanceTxInterfaces(baseProviders, compiledGovernorBaseContract, contractAddress),
|
|
68
|
+
contractMaintenanceTx: createContractMaintenanceTxInterface(baseProviders, compiledGovernorBaseContract, contractAddress),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
static assertGovernorCircuitId(circuitId) {
|
|
72
|
+
if (!GOVERNOR_FULL_CIRCUITS.includes(circuitId)) {
|
|
73
|
+
throw new InvalidParameterError('circuitId', `Unknown governor circuit '${circuitId}'`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
39
76
|
/**
|
|
40
77
|
* Creates a governance proposal.
|
|
41
78
|
*
|
|
42
79
|
* @param title The proposal title (max 256 characters).
|
|
43
80
|
* @param description The proposal description (max 10,000 characters).
|
|
44
81
|
* @param creator The creator's address (32 bytes).
|
|
45
|
-
* @param
|
|
82
|
+
* @param action The single effect action to execute if passed.
|
|
46
83
|
* @returns Transaction data and proposal ID.
|
|
47
84
|
*
|
|
48
85
|
* @throws {InvalidParameterError} If title, description, or creator are invalid.
|
|
@@ -54,56 +91,55 @@ export class GovernorAPI {
|
|
|
54
91
|
*
|
|
55
92
|
* @example
|
|
56
93
|
* ```typescript
|
|
57
|
-
* const
|
|
58
|
-
* typ:
|
|
94
|
+
* const action: EffectAction = {
|
|
95
|
+
* typ: 1,
|
|
59
96
|
* token: tokenAddress,
|
|
60
97
|
* amount: BigInt(10000),
|
|
61
98
|
* to: recipientAddress,
|
|
62
99
|
* configKey: new Uint8Array(32),
|
|
63
100
|
* configValue: new Uint8Array(32),
|
|
64
|
-
* }
|
|
101
|
+
* };
|
|
65
102
|
*
|
|
66
103
|
* const { proposalId, txData } = await governorAPI.createProposal(
|
|
67
104
|
* 'Fund Community Initiative',
|
|
68
105
|
* 'Transfer 10,000 tokens to community wallet',
|
|
69
106
|
* creatorAddress,
|
|
70
|
-
*
|
|
107
|
+
* action
|
|
71
108
|
* );
|
|
72
109
|
* ```
|
|
73
110
|
*/
|
|
74
|
-
async createProposal(title, description, creator,
|
|
111
|
+
async createProposal(title, description, creator, action) {
|
|
75
112
|
this.logger?.info(`Creating proposal: ${title}`);
|
|
76
113
|
this.logger?.info(`Description: ${description}`);
|
|
77
114
|
// Validate parameters
|
|
78
115
|
validateProposal(title, description, creator);
|
|
79
|
-
if (
|
|
80
|
-
throw new InvalidParameterError(
|
|
116
|
+
if (Array.isArray(action)) {
|
|
117
|
+
throw new InvalidParameterError('action', 'must be exactly one EffectAction, not an array');
|
|
118
|
+
}
|
|
119
|
+
if (!action || typeof action !== 'object') {
|
|
120
|
+
throw new InvalidParameterError('action', 'must be an EffectAction');
|
|
81
121
|
}
|
|
82
122
|
try {
|
|
83
|
-
// Get creator's current stake
|
|
84
|
-
const stakeInfo = await this.deployedContract.callTx.getStakeInfo(creator);
|
|
85
|
-
const stakeAmount = stakeInfo.private.result.stakedAmount;
|
|
86
|
-
this.logger?.info(`Creator stake amount: ${stakeAmount}`);
|
|
87
123
|
const stakeWitness = {
|
|
88
124
|
userId: creator,
|
|
89
|
-
amount:
|
|
90
|
-
nonce: utils.randomBytes(32)
|
|
125
|
+
amount: DEFAULT_BIGINT_VALUE,
|
|
126
|
+
nonce: utils.randomBytes(32),
|
|
91
127
|
};
|
|
92
128
|
const currentTime = getCurrentUnixTimestamp();
|
|
93
129
|
this.logger?.info(`Current time: ${currentTime}`);
|
|
94
|
-
const createdProposal = await this.
|
|
130
|
+
const createdProposal = await this.baseDeployedContract.callTx.createProposalWithEffect(creator, title, description, action, stakeWitness, currentTime, currentTime + 300n);
|
|
95
131
|
const proposalId = createdProposal.private.result;
|
|
96
132
|
this.logger?.info(`Proposal created with ID: ${proposalId}, txHash: ${createdProposal.public.txHash}`);
|
|
97
133
|
this.logger?.trace({
|
|
98
134
|
transactionAdded: {
|
|
99
|
-
circuit:
|
|
135
|
+
circuit: 'createProposalWithEffect',
|
|
100
136
|
txHash: createdProposal.public.txHash,
|
|
101
|
-
blockHeight: createdProposal.public.blockHeight
|
|
102
|
-
}
|
|
137
|
+
blockHeight: createdProposal.public.blockHeight,
|
|
138
|
+
},
|
|
103
139
|
});
|
|
104
140
|
return {
|
|
105
141
|
txData: createdProposal.public,
|
|
106
|
-
proposalId: proposalId
|
|
142
|
+
proposalId: proposalId,
|
|
107
143
|
};
|
|
108
144
|
}
|
|
109
145
|
catch (error) {
|
|
@@ -128,7 +164,7 @@ export class GovernorAPI {
|
|
|
128
164
|
*
|
|
129
165
|
* @example
|
|
130
166
|
* ```typescript
|
|
131
|
-
* import { VoteChoice } from '@agora-
|
|
167
|
+
* import { VoteChoice } from '@claritydao/midnight-agora-sdk';
|
|
132
168
|
*
|
|
133
169
|
* await governorAPI.castVote(
|
|
134
170
|
* proposalId,
|
|
@@ -139,28 +175,24 @@ export class GovernorAPI {
|
|
|
139
175
|
*/
|
|
140
176
|
async castVote(proposalId, voter, choice) {
|
|
141
177
|
this.logger?.info(`Casting vote on proposal ${proposalId}`);
|
|
142
|
-
this.logger?.info(`Vote choice: ${choice === VoteChoice.For ?
|
|
178
|
+
this.logger?.info(`Vote choice: ${choice === VoteChoice.For ? 'For' : choice === VoteChoice.Against ? 'Against' : 'Abstain'}`);
|
|
143
179
|
// Validate parameters
|
|
144
|
-
validatePositiveBigInt(proposalId,
|
|
145
|
-
validateAddress(voter,
|
|
180
|
+
validatePositiveBigInt(proposalId, 'proposalId');
|
|
181
|
+
validateAddress(voter, 'voter');
|
|
146
182
|
validateVoteChoice(choice);
|
|
147
|
-
// Get voter's current stake
|
|
148
|
-
const stakeInfo = await this.deployedContract.callTx.getStakeInfo(voter);
|
|
149
|
-
const stakeAmount = stakeInfo.private.result.stakedAmount;
|
|
150
|
-
this.logger?.info(`Voter stake amount: ${stakeAmount}`);
|
|
151
183
|
const stakeWitness = {
|
|
152
184
|
userId: voter,
|
|
153
|
-
amount:
|
|
154
|
-
nonce: utils.randomBytes(32)
|
|
185
|
+
amount: DEFAULT_BIGINT_VALUE,
|
|
186
|
+
nonce: utils.randomBytes(32),
|
|
155
187
|
};
|
|
156
|
-
const tx = await this.
|
|
188
|
+
const tx = await this.baseDeployedContract.callTx.castVoteWithValidation(proposalId, voter, choice, stakeWitness);
|
|
157
189
|
this.logger?.info(`Vote cast with txHash: ${tx.public.txHash}`);
|
|
158
190
|
this.logger?.trace({
|
|
159
191
|
transactionAdded: {
|
|
160
|
-
circuit:
|
|
192
|
+
circuit: 'castVoteWithValidation',
|
|
161
193
|
txHash: tx.public.txHash,
|
|
162
|
-
blockHeight: tx.public.blockHeight
|
|
163
|
-
}
|
|
194
|
+
blockHeight: tx.public.blockHeight,
|
|
195
|
+
},
|
|
164
196
|
});
|
|
165
197
|
return tx.public;
|
|
166
198
|
}
|
|
@@ -179,9 +211,9 @@ export class GovernorAPI {
|
|
|
179
211
|
proposalId: proposalId,
|
|
180
212
|
effectId: INITIAL_EFFECT_ID,
|
|
181
213
|
approvalTime: getCurrentUnixTimestamp(),
|
|
182
|
-
nonce: utils.randomBytes(32)
|
|
214
|
+
nonce: utils.randomBytes(32),
|
|
183
215
|
};
|
|
184
|
-
const tx = await this.
|
|
216
|
+
const tx = await this.baseDeployedContract.callTx.finalizeProposalWithValidation(proposalId, proofOfGovernorApproval);
|
|
185
217
|
this.logger?.info(`Proposal finalized with txHash: ${tx.public.txHash}`);
|
|
186
218
|
return tx.public;
|
|
187
219
|
}
|
|
@@ -200,9 +232,9 @@ export class GovernorAPI {
|
|
|
200
232
|
proposalId: proposalId,
|
|
201
233
|
effectId: INITIAL_EFFECT_ID,
|
|
202
234
|
approvalTime: getCurrentUnixTimestamp(),
|
|
203
|
-
nonce: utils.randomBytes(32)
|
|
235
|
+
nonce: utils.randomBytes(32),
|
|
204
236
|
};
|
|
205
|
-
const tx = await this.
|
|
237
|
+
const tx = await this.baseDeployedContract.callTx.queueProposalWithValidation(proposalId, proofOfGovernorApproval);
|
|
206
238
|
this.logger?.info(`Proposal queued with txHash: ${tx.public.txHash}`);
|
|
207
239
|
return tx.public;
|
|
208
240
|
}
|
|
@@ -219,13 +251,14 @@ export class GovernorAPI {
|
|
|
219
251
|
async executeProposal(proposalId, effectId) {
|
|
220
252
|
this.logger?.info(`Executing proposal ${proposalId}`);
|
|
221
253
|
this.logger?.info(`Effect ID: ${effectId}`);
|
|
254
|
+
const executionTime = getCurrentUnixTimestamp();
|
|
222
255
|
const proofOfGovernorApproval = {
|
|
223
256
|
proposalId: proposalId,
|
|
224
257
|
effectId: effectId,
|
|
225
|
-
approvalTime:
|
|
226
|
-
nonce: utils.randomBytes(32)
|
|
258
|
+
approvalTime: executionTime,
|
|
259
|
+
nonce: utils.randomBytes(32),
|
|
227
260
|
};
|
|
228
|
-
const tx = await this.
|
|
261
|
+
const tx = await this.baseDeployedContract.callTx.executeQueuedProposal(proposalId, effectId, proofOfGovernorApproval);
|
|
229
262
|
this.logger?.info(`Proposal executed with txHash: ${tx.public.txHash}`);
|
|
230
263
|
return tx.public;
|
|
231
264
|
}
|
|
@@ -242,9 +275,9 @@ export class GovernorAPI {
|
|
|
242
275
|
const proofOfStake = {
|
|
243
276
|
userId: userId,
|
|
244
277
|
amount: amount,
|
|
245
|
-
nonce: utils.randomBytes(32)
|
|
278
|
+
nonce: utils.randomBytes(32),
|
|
246
279
|
};
|
|
247
|
-
const tx = await this.
|
|
280
|
+
const tx = await this.baseDeployedContract.callTx.registerStakeWithValidation(userId, amount, proofOfStake, getCurrentUnixTimestamp());
|
|
248
281
|
this.logger?.info(`Stake registered with txHash: ${tx.public.txHash}`);
|
|
249
282
|
return tx.public;
|
|
250
283
|
}
|
|
@@ -256,19 +289,8 @@ export class GovernorAPI {
|
|
|
256
289
|
*/
|
|
257
290
|
async getStakeInfo(userId) {
|
|
258
291
|
this.logger?.info(`Getting stake info for user`);
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
this.logger?.info({
|
|
262
|
-
message: "Stake info retrieved",
|
|
263
|
-
txHash: stakeInfo.public.txHash,
|
|
264
|
-
blockHeight: stakeInfo.public.blockHeight
|
|
265
|
-
});
|
|
266
|
-
return stakeInfo;
|
|
267
|
-
}
|
|
268
|
-
catch (error) {
|
|
269
|
-
this.logger?.error(`Error getting stake info: ${error}`);
|
|
270
|
-
throw error;
|
|
271
|
-
}
|
|
292
|
+
validateAddress(userId, 'userId');
|
|
293
|
+
throw minimalDeployQueryError('getStakeInfo');
|
|
272
294
|
}
|
|
273
295
|
/**
|
|
274
296
|
* Gets total delegated power for a user.
|
|
@@ -278,19 +300,8 @@ export class GovernorAPI {
|
|
|
278
300
|
*/
|
|
279
301
|
async getTotalDelegatedPower(userId) {
|
|
280
302
|
this.logger?.info(`Getting total delegated power for user`);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.logger?.info({
|
|
284
|
-
message: "Total delegated power retrieved",
|
|
285
|
-
txHash: result.public.txHash,
|
|
286
|
-
blockHeight: result.public.blockHeight
|
|
287
|
-
});
|
|
288
|
-
return result;
|
|
289
|
-
}
|
|
290
|
-
catch (error) {
|
|
291
|
-
this.logger?.error(`Error getting total delegated power: ${error}`);
|
|
292
|
-
throw error;
|
|
293
|
-
}
|
|
303
|
+
validateAddress(userId, 'userId');
|
|
304
|
+
throw minimalDeployQueryError('getTotalDelegatedPower');
|
|
294
305
|
}
|
|
295
306
|
/**
|
|
296
307
|
* Gets stake record for a user.
|
|
@@ -300,19 +311,8 @@ export class GovernorAPI {
|
|
|
300
311
|
*/
|
|
301
312
|
async getStakeRecord(userId) {
|
|
302
313
|
this.logger?.info(`Getting stake record for user`);
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
this.logger?.info({
|
|
306
|
-
message: "Stake record retrieved",
|
|
307
|
-
txHash: result.public.txHash,
|
|
308
|
-
blockHeight: result.public.blockHeight
|
|
309
|
-
});
|
|
310
|
-
return result;
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
this.logger?.error(`Error getting stake record: ${error}`);
|
|
314
|
-
throw error;
|
|
315
|
-
}
|
|
314
|
+
validateAddress(userId, 'userId');
|
|
315
|
+
throw minimalDeployQueryError('getStakeRecord');
|
|
316
316
|
}
|
|
317
317
|
/**
|
|
318
318
|
* Checks if user has sufficient stake to create proposals.
|
|
@@ -321,14 +321,8 @@ export class GovernorAPI {
|
|
|
321
321
|
* @returns Result indicating if user meets threshold.
|
|
322
322
|
*/
|
|
323
323
|
async hasProposalThreshold(userId) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return result;
|
|
327
|
-
}
|
|
328
|
-
catch (error) {
|
|
329
|
-
this.logger?.error(`Error checking proposal threshold: ${error}`);
|
|
330
|
-
throw error;
|
|
331
|
-
}
|
|
324
|
+
validateAddress(userId, 'userId');
|
|
325
|
+
throw minimalDeployQueryError('hasProposalThreshold');
|
|
332
326
|
}
|
|
333
327
|
/**
|
|
334
328
|
* Checks if user has voting power.
|
|
@@ -337,14 +331,8 @@ export class GovernorAPI {
|
|
|
337
331
|
* @returns Result indicating if user has voting power.
|
|
338
332
|
*/
|
|
339
333
|
async hasVotingPower(userId) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return result;
|
|
343
|
-
}
|
|
344
|
-
catch (error) {
|
|
345
|
-
this.logger?.error(`Error checking voting power: ${error}`);
|
|
346
|
-
throw error;
|
|
347
|
-
}
|
|
334
|
+
validateAddress(userId, 'userId');
|
|
335
|
+
throw minimalDeployQueryError('hasVotingPower');
|
|
348
336
|
}
|
|
349
337
|
/**
|
|
350
338
|
* Gets proposal details by ID.
|
|
@@ -354,26 +342,9 @@ export class GovernorAPI {
|
|
|
354
342
|
*/
|
|
355
343
|
async getProposalDetails(proposalId) {
|
|
356
344
|
this.logger?.info(`Getting proposal details for ID: ${proposalId}`);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
message: "Proposal details",
|
|
361
|
-
id: result.private.result.id?.toString(),
|
|
362
|
-
status: result.private.result.status,
|
|
363
|
-
votingStartTime: result.private.result.votingStartTime?.toString(),
|
|
364
|
-
votingEndTime: result.private.result.votingEndTime?.toString(),
|
|
365
|
-
creator: result.private.result.creator,
|
|
366
|
-
votesFor: result.private.result.votesFor?.toString(),
|
|
367
|
-
votesAgainst: result.private.result.votesAgainst?.toString(),
|
|
368
|
-
effectId: result.private.result.effectId?.toString()
|
|
369
|
-
};
|
|
370
|
-
this.logger?.info(loggableResult);
|
|
371
|
-
return result.private.result;
|
|
372
|
-
}
|
|
373
|
-
catch (error) {
|
|
374
|
-
this.logger?.error(`Error getting proposal details: ${error}`);
|
|
375
|
-
throw error;
|
|
376
|
-
}
|
|
345
|
+
validatePositiveBigInt(proposalId, 'proposalId');
|
|
346
|
+
const result = await this.deployedContract.callTx.getProposal(proposalId);
|
|
347
|
+
return result.private.result;
|
|
377
348
|
}
|
|
378
349
|
/**
|
|
379
350
|
* Gets effect details by ID.
|
|
@@ -383,23 +354,9 @@ export class GovernorAPI {
|
|
|
383
354
|
*/
|
|
384
355
|
async getEffectDetails(effectId) {
|
|
385
356
|
this.logger?.info(`Getting effect details for ID: ${effectId}`);
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
message: "Effect details",
|
|
390
|
-
id: result.private.result.id?.toString(),
|
|
391
|
-
proposalId: result.private.result.proposalId?.toString(),
|
|
392
|
-
status: result.private.result.status,
|
|
393
|
-
executedAt: result.private.result.executedAt?.toString(),
|
|
394
|
-
actionsCount: result.private.result.actions?.length || 0
|
|
395
|
-
};
|
|
396
|
-
this.logger?.info(loggableResult);
|
|
397
|
-
return result.private.result;
|
|
398
|
-
}
|
|
399
|
-
catch (error) {
|
|
400
|
-
this.logger?.error(`Error getting effect details: ${error}`);
|
|
401
|
-
throw error;
|
|
402
|
-
}
|
|
357
|
+
validatePositiveBigInt(effectId, 'effectId');
|
|
358
|
+
const result = await this.deployedContract.callTx.getEffect(effectId);
|
|
359
|
+
return result.private.result;
|
|
403
360
|
}
|
|
404
361
|
/**
|
|
405
362
|
* Gets the count of active proposals.
|
|
@@ -408,19 +365,8 @@ export class GovernorAPI {
|
|
|
408
365
|
*/
|
|
409
366
|
async getActiveProposalCount() {
|
|
410
367
|
this.logger?.info(`Getting active proposal count`);
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
this.logger?.warn("Governor contract or callTx is undefined");
|
|
414
|
-
return DEFAULT_BIGINT_VALUE;
|
|
415
|
-
}
|
|
416
|
-
const result = await this.deployedContract.callTx.getActiveProposalCount();
|
|
417
|
-
this.logger?.info(`Active proposals: ${result.private.result}`);
|
|
418
|
-
return result.private.result;
|
|
419
|
-
}
|
|
420
|
-
catch (error) {
|
|
421
|
-
this.logger?.warn(`Could not get active proposal count: ${error}`);
|
|
422
|
-
return DEFAULT_BIGINT_VALUE;
|
|
423
|
-
}
|
|
368
|
+
this.logger?.warn('getActiveProposalCount is not exported in the minimal preprod deploy build');
|
|
369
|
+
return DEFAULT_BIGINT_VALUE;
|
|
424
370
|
}
|
|
425
371
|
/**
|
|
426
372
|
* Gets the count of queued proposals.
|
|
@@ -429,15 +375,8 @@ export class GovernorAPI {
|
|
|
429
375
|
*/
|
|
430
376
|
async getQueuedProposalCount() {
|
|
431
377
|
this.logger?.info(`Getting queued proposal count`);
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
this.logger?.info(`Queued proposals: ${result.private.result}`);
|
|
435
|
-
return result.private.result;
|
|
436
|
-
}
|
|
437
|
-
catch (error) {
|
|
438
|
-
this.logger?.warn(`Could not get queued proposal count: ${error}`);
|
|
439
|
-
return DEFAULT_BIGINT_VALUE;
|
|
440
|
-
}
|
|
378
|
+
this.logger?.warn('getQueuedProposalCount is not exported in the minimal preprod deploy build');
|
|
379
|
+
return DEFAULT_BIGINT_VALUE;
|
|
441
380
|
}
|
|
442
381
|
/**
|
|
443
382
|
* Gets vote count for a proposal.
|
|
@@ -446,14 +385,9 @@ export class GovernorAPI {
|
|
|
446
385
|
* @returns Vote count information.
|
|
447
386
|
*/
|
|
448
387
|
async getProposalVoteCount(proposalId) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
catch (error) {
|
|
454
|
-
this.logger?.error(`Error getting proposal vote count: ${error}`);
|
|
455
|
-
throw error;
|
|
456
|
-
}
|
|
388
|
+
validatePositiveBigInt(proposalId, 'proposalId');
|
|
389
|
+
const result = await this.deployedContract.callTx.getProposalVoteCount(proposalId);
|
|
390
|
+
return result.private.result;
|
|
457
391
|
}
|
|
458
392
|
/**
|
|
459
393
|
* Gets the governor contract status.
|
|
@@ -462,15 +396,8 @@ export class GovernorAPI {
|
|
|
462
396
|
*/
|
|
463
397
|
async getGovernorStatus() {
|
|
464
398
|
this.logger?.info(`Getting governor status`);
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
this.logger?.info(`Governor status retrieved`);
|
|
468
|
-
return result.private.result;
|
|
469
|
-
}
|
|
470
|
-
catch (error) {
|
|
471
|
-
this.logger?.warn(`Could not get governor status: ${error}`);
|
|
472
|
-
return null;
|
|
473
|
-
}
|
|
399
|
+
this.logger?.warn('getGovernorStatus is not exported in the minimal preprod deploy build');
|
|
400
|
+
return null;
|
|
474
401
|
}
|
|
475
402
|
/**
|
|
476
403
|
* Gets the current time from the contract.
|
|
@@ -478,14 +405,7 @@ export class GovernorAPI {
|
|
|
478
405
|
* @returns Current time.
|
|
479
406
|
*/
|
|
480
407
|
async getCurrentTime() {
|
|
481
|
-
|
|
482
|
-
const result = await this.deployedContract.callTx.getCurrentTime();
|
|
483
|
-
return result;
|
|
484
|
-
}
|
|
485
|
-
catch (error) {
|
|
486
|
-
this.logger?.error(`Error getting current time: ${error}`);
|
|
487
|
-
throw error;
|
|
488
|
-
}
|
|
408
|
+
throw minimalDeployQueryError('getCurrentTime');
|
|
489
409
|
}
|
|
490
410
|
/**
|
|
491
411
|
* Gets the current contract time as a bigint.
|
|
@@ -494,16 +414,7 @@ export class GovernorAPI {
|
|
|
494
414
|
*/
|
|
495
415
|
async getCurrentContractTime() {
|
|
496
416
|
this.logger?.info(`Getting current contract time`);
|
|
497
|
-
|
|
498
|
-
const status = await this.deployedContract.callTx.getGovernorStatus();
|
|
499
|
-
const currentTime = status.private.result.currentTime;
|
|
500
|
-
this.logger?.info(`Current contract time: ${currentTime}`);
|
|
501
|
-
return currentTime;
|
|
502
|
-
}
|
|
503
|
-
catch (error) {
|
|
504
|
-
this.logger?.error(`Error getting current contract time: ${error}`);
|
|
505
|
-
throw error;
|
|
506
|
-
}
|
|
417
|
+
throw minimalDeployQueryError('getGovernorStatus');
|
|
507
418
|
}
|
|
508
419
|
/**
|
|
509
420
|
* Gets the treasury balance.
|
|
@@ -520,7 +431,7 @@ export class GovernorAPI {
|
|
|
520
431
|
try {
|
|
521
432
|
const state = await this.providers.publicDataProvider.queryContractState(this.deployedContractAddress);
|
|
522
433
|
if (state != null) {
|
|
523
|
-
this.logger?.info(
|
|
434
|
+
this.logger?.info('Governor contract state found');
|
|
524
435
|
// Contract exists but doesn't currently expose treasury balance in public state
|
|
525
436
|
return DEFAULT_BIGINT_VALUE;
|
|
526
437
|
}
|
|
@@ -545,11 +456,54 @@ export class GovernorAPI {
|
|
|
545
456
|
* operations and proposal lifecycles.
|
|
546
457
|
*/
|
|
547
458
|
async updateTime(newTime) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
459
|
+
validatePositiveBigInt(newTime, 'newTime');
|
|
460
|
+
throw new Error('updateTime is not supported: Governor time is derived from Midnight block time.');
|
|
461
|
+
}
|
|
462
|
+
async getInstalledGovernorCircuits(contractAddress = this.deployedContractAddress) {
|
|
463
|
+
const contractState = await this.providers.publicDataProvider.queryContractState(contractAddress);
|
|
464
|
+
if (!contractState) {
|
|
465
|
+
throw new Error(`No contract state found on chain for contract address '${contractAddress}'`);
|
|
466
|
+
}
|
|
467
|
+
return GOVERNOR_FULL_CIRCUITS.filter((circuitId) => contractState.operation(circuitId) !== undefined);
|
|
468
|
+
}
|
|
469
|
+
async getMissingGovernorCircuits(contractAddress = this.deployedContractAddress, circuitIds = GOVERNOR_FULL_CIRCUITS) {
|
|
470
|
+
const installed = new Set(await this.getInstalledGovernorCircuits(contractAddress));
|
|
471
|
+
return circuitIds.filter((circuitId) => !installed.has(circuitId));
|
|
472
|
+
}
|
|
473
|
+
async installGovernorCircuit(contractAddress, circuitId) {
|
|
474
|
+
GovernorAPI.assertGovernorCircuitId(circuitId);
|
|
475
|
+
const missing = await this.getMissingGovernorCircuits(contractAddress, [circuitId]);
|
|
476
|
+
if (missing.length === 0) {
|
|
477
|
+
return { circuitId, status: 'already-installed' };
|
|
478
|
+
}
|
|
479
|
+
const signingKey = await this.providers.privateStateProvider.getSigningKey(contractAddress);
|
|
480
|
+
if (!signingKey) {
|
|
481
|
+
throw new Error('Cannot install circuits because the maintenance signing key for this contract is missing.');
|
|
482
|
+
}
|
|
483
|
+
const verifierKey = await this.providers.zkConfigProvider.getVerifierKey(circuitId);
|
|
484
|
+
const txData = await this.deployedContract.circuitMaintenanceTx[circuitId].insertVerifierKey(verifierKey);
|
|
485
|
+
return {
|
|
486
|
+
circuitId,
|
|
487
|
+
status: 'installed',
|
|
488
|
+
txData,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
async installGovernorCircuitBundle(contractAddress, bundle) {
|
|
492
|
+
const circuitIds = GOVERNOR_CIRCUIT_BUNDLES[bundle];
|
|
493
|
+
if (!circuitIds) {
|
|
494
|
+
throw new InvalidParameterError('bundle', `Unknown governor circuit bundle '${bundle}'`);
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
bundle,
|
|
498
|
+
results: await this.ensureGovernorCircuits(contractAddress, circuitIds),
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
async ensureGovernorCircuits(contractAddress, circuitIds) {
|
|
502
|
+
const results = [];
|
|
503
|
+
for (const circuitId of circuitIds) {
|
|
504
|
+
results.push(await this.installGovernorCircuit(contractAddress, circuitId));
|
|
505
|
+
}
|
|
506
|
+
return results;
|
|
553
507
|
}
|
|
554
508
|
/**
|
|
555
509
|
* Displays comprehensive governor status.
|
|
@@ -567,13 +521,15 @@ export class GovernorAPI {
|
|
|
567
521
|
this.logger?.info(`Active proposals: ${Number(activeProposals)}`);
|
|
568
522
|
this.logger?.info(`Queued proposals: ${Number(queuedProposals)}`);
|
|
569
523
|
this.logger?.info(`Treasury balance: ${Number(treasuryBalance)}`);
|
|
570
|
-
|
|
524
|
+
if (status?.currentTime != null) {
|
|
525
|
+
this.logger?.info(`Current time: ${Number(status.currentTime)}`);
|
|
526
|
+
}
|
|
571
527
|
return {
|
|
572
528
|
contractAddress,
|
|
573
529
|
activeProposals,
|
|
574
530
|
queuedProposals,
|
|
575
531
|
treasuryBalance,
|
|
576
|
-
isActive: true
|
|
532
|
+
isActive: true,
|
|
577
533
|
};
|
|
578
534
|
}
|
|
579
535
|
catch (error) {
|
|
@@ -583,7 +539,7 @@ export class GovernorAPI {
|
|
|
583
539
|
activeProposals: DEFAULT_BIGINT_VALUE,
|
|
584
540
|
queuedProposals: DEFAULT_BIGINT_VALUE,
|
|
585
541
|
treasuryBalance: DEFAULT_BIGINT_VALUE,
|
|
586
|
-
isActive: false
|
|
542
|
+
isActive: false,
|
|
587
543
|
};
|
|
588
544
|
}
|
|
589
545
|
}
|
|
@@ -598,7 +554,7 @@ export class GovernorAPI {
|
|
|
598
554
|
*
|
|
599
555
|
* @example
|
|
600
556
|
* ```typescript
|
|
601
|
-
* import { GovernorAPI, ConfigPresets } from '@agora-
|
|
557
|
+
* import { GovernorAPI, ConfigPresets } from '@claritydao/midnight-agora-sdk';
|
|
602
558
|
* import { randomBytes } from '@midnight-ntwrk/midnight-js-utils';
|
|
603
559
|
*
|
|
604
560
|
* const config = {
|
|
@@ -623,28 +579,29 @@ export class GovernorAPI {
|
|
|
623
579
|
* ```
|
|
624
580
|
*/
|
|
625
581
|
static async deploy(providers, config, logger) {
|
|
626
|
-
logger?.info(
|
|
582
|
+
logger?.info('Deploying governor contract');
|
|
627
583
|
// Validate configuration
|
|
628
584
|
validateDeploymentConfig(config);
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
config.
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
const deployedGovernorContract = await deployContract(providers, {
|
|
585
|
+
const baseProviders = GovernorAPI.withGovernorProfile(providers, 'base');
|
|
586
|
+
const initialPrivateState = {
|
|
587
|
+
...(await GovernorAPI.getPrivateState(baseProviders)),
|
|
588
|
+
secretKey: config.adminKey,
|
|
589
|
+
};
|
|
590
|
+
const args = [config.governor, config.token.name, config.token.symbol, config.token.decimals, initialPrivateState.secretKey];
|
|
591
|
+
const deployedGovernorBaseContract = await deployContract(baseProviders, {
|
|
637
592
|
privateStateId: GovernorPrivateStateId,
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
args
|
|
593
|
+
initialPrivateState,
|
|
594
|
+
compiledContract: compiledGovernorBaseContract,
|
|
595
|
+
args,
|
|
641
596
|
});
|
|
597
|
+
const deployedGovernorContract = GovernorAPI.createFullDeployedContract(providers, deployedGovernorBaseContract.deployTxData, deployedGovernorBaseContract.deployTxData.public.contractAddress);
|
|
598
|
+
const deployedGovernorBaseCallContract = GovernorAPI.createBaseDeployedContract(providers, deployedGovernorBaseContract.deployTxData, deployedGovernorBaseContract.deployTxData.public.contractAddress);
|
|
642
599
|
logger?.trace({
|
|
643
600
|
contractDeployed: {
|
|
644
|
-
finalizedDeployTxData: deployedGovernorContract.deployTxData.public
|
|
645
|
-
}
|
|
601
|
+
finalizedDeployTxData: deployedGovernorContract.deployTxData.public,
|
|
602
|
+
},
|
|
646
603
|
});
|
|
647
|
-
return new GovernorAPI(deployedGovernorContract, providers, logger);
|
|
604
|
+
return new GovernorAPI(deployedGovernorContract, deployedGovernorBaseCallContract, providers, logger);
|
|
648
605
|
}
|
|
649
606
|
/**
|
|
650
607
|
* Finds an already deployed governor contract on the network, and joins it.
|
|
@@ -658,21 +615,37 @@ export class GovernorAPI {
|
|
|
658
615
|
static async join(providers, contractAddress, logger) {
|
|
659
616
|
logger?.info({
|
|
660
617
|
joinContract: {
|
|
661
|
-
contractAddress
|
|
662
|
-
}
|
|
663
|
-
});
|
|
664
|
-
const deployedGovernorContract = await findDeployedContract(providers, {
|
|
665
|
-
contractAddress,
|
|
666
|
-
contract: governorContractInstance,
|
|
667
|
-
privateStateId: GovernorPrivateStateId,
|
|
668
|
-
initialPrivateState: await GovernorAPI.getPrivateState(providers)
|
|
618
|
+
contractAddress,
|
|
619
|
+
},
|
|
669
620
|
});
|
|
621
|
+
providers.privateStateProvider.setContractAddress(contractAddress);
|
|
622
|
+
const [finalizedTxData, initialContractState, existingSigningKey, initialPrivateState] = await Promise.all([
|
|
623
|
+
providers.publicDataProvider.watchForDeployTxData(contractAddress),
|
|
624
|
+
providers.publicDataProvider.queryDeployContractState(contractAddress),
|
|
625
|
+
providers.privateStateProvider.getSigningKey(contractAddress),
|
|
626
|
+
GovernorAPI.getPrivateState(providers, contractAddress),
|
|
627
|
+
]);
|
|
628
|
+
if (!initialContractState) {
|
|
629
|
+
throw new Error(`No contract deployed at contract address '${contractAddress}'`);
|
|
630
|
+
}
|
|
631
|
+
const deployedGovernorContract = GovernorAPI.createFullDeployedContract(providers, {
|
|
632
|
+
private: {
|
|
633
|
+
signingKey: existingSigningKey ?? sampleSigningKey(),
|
|
634
|
+
initialPrivateState,
|
|
635
|
+
},
|
|
636
|
+
public: {
|
|
637
|
+
...finalizedTxData,
|
|
638
|
+
contractAddress,
|
|
639
|
+
initialContractState,
|
|
640
|
+
},
|
|
641
|
+
}, contractAddress);
|
|
642
|
+
const deployedGovernorBaseContract = GovernorAPI.createBaseDeployedContract(providers, deployedGovernorContract.deployTxData, contractAddress);
|
|
670
643
|
logger?.trace({
|
|
671
644
|
contractJoined: {
|
|
672
|
-
finalizedDeployTxData: deployedGovernorContract.deployTxData.public
|
|
673
|
-
}
|
|
645
|
+
finalizedDeployTxData: deployedGovernorContract.deployTxData.public,
|
|
646
|
+
},
|
|
674
647
|
});
|
|
675
|
-
return new GovernorAPI(deployedGovernorContract, providers, logger);
|
|
648
|
+
return new GovernorAPI(deployedGovernorContract, deployedGovernorBaseContract, providers, logger);
|
|
676
649
|
}
|
|
677
650
|
/**
|
|
678
651
|
* Gets or initializes the private state for the governor contract.
|
|
@@ -685,10 +658,20 @@ export class GovernorAPI {
|
|
|
685
658
|
* if none exists. The governance config in the private state is initialized with testing defaults
|
|
686
659
|
* and should be updated from the actual contract configuration after joining.
|
|
687
660
|
*/
|
|
688
|
-
static async getPrivateState(providers) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
661
|
+
static async getPrivateState(providers, contractAddress) {
|
|
662
|
+
if (contractAddress) {
|
|
663
|
+
providers.privateStateProvider.setContractAddress(contractAddress);
|
|
664
|
+
}
|
|
665
|
+
try {
|
|
666
|
+
const existingPrivateState = await providers.privateStateProvider.get(GovernorPrivateStateId);
|
|
667
|
+
if (existingPrivateState) {
|
|
668
|
+
return existingPrivateState;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
if (contractAddress) {
|
|
673
|
+
throw error;
|
|
674
|
+
}
|
|
692
675
|
}
|
|
693
676
|
const secretKey = utils.randomBytes(32);
|
|
694
677
|
const currentTime = getCurrentUnixTimestamp();
|
|
@@ -701,16 +684,18 @@ export class GovernorAPI {
|
|
|
701
684
|
userStake: {
|
|
702
685
|
amount: DEFAULT_BIGINT_VALUE,
|
|
703
686
|
delegatedTo: new Uint8Array(32),
|
|
704
|
-
stakedAt: currentTime
|
|
687
|
+
stakedAt: currentTime,
|
|
705
688
|
},
|
|
706
689
|
votingHistory: [],
|
|
707
690
|
proposalHistory: [],
|
|
708
|
-
governanceConfig: defaultConfig
|
|
691
|
+
governanceConfig: defaultConfig,
|
|
709
692
|
};
|
|
710
|
-
|
|
693
|
+
if (contractAddress) {
|
|
694
|
+
await providers.privateStateProvider.set(GovernorPrivateStateId, newPrivateState);
|
|
695
|
+
}
|
|
711
696
|
return newPrivateState;
|
|
712
697
|
}
|
|
713
698
|
}
|
|
714
|
-
export * as utils from
|
|
715
|
-
export * from
|
|
699
|
+
export * as utils from './utils/index';
|
|
700
|
+
export * from './common-types';
|
|
716
701
|
//# sourceMappingURL=governor-api.js.map
|