@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.
Files changed (47) hide show
  1. package/README.md +58 -55
  2. package/dist/browser-providers.d.ts +24 -19
  3. package/dist/browser-providers.d.ts.map +1 -1
  4. package/dist/browser-providers.js +654 -139
  5. package/dist/browser-providers.js.map +1 -1
  6. package/dist/common-types.d.ts +13 -24
  7. package/dist/common-types.d.ts.map +1 -1
  8. package/dist/common-types.js +1 -1
  9. package/dist/common-types.js.map +1 -1
  10. package/dist/governor-api.d.ts +42 -17
  11. package/dist/governor-api.d.ts.map +1 -1
  12. package/dist/governor-api.js +233 -248
  13. package/dist/governor-api.js.map +1 -1
  14. package/dist/index.d.ts +11 -10
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +10 -9
  17. package/dist/index.js.map +1 -1
  18. package/dist/manager.d.ts +11 -11
  19. package/dist/manager.d.ts.map +1 -1
  20. package/dist/manager.js +17 -20
  21. package/dist/manager.js.map +1 -1
  22. package/dist/types.d.ts +9 -17
  23. package/dist/types.d.ts.map +1 -1
  24. package/dist/types.js.map +1 -1
  25. package/package.json +26 -26
  26. package/.prettierrc +0 -1
  27. package/LICENSE +0 -0
  28. package/eslint.config.mjs +0 -48
  29. package/src/browser-providers.ts +0 -278
  30. package/src/common-types.ts +0 -51
  31. package/src/errors.ts +0 -124
  32. package/src/governor-api.ts +0 -948
  33. package/src/index.ts +0 -51
  34. package/src/manager.ts +0 -205
  35. package/src/types.ts +0 -403
  36. package/src/utils/index.ts +0 -60
  37. package/src/validators.ts +0 -245
  38. package/test/README.md +0 -409
  39. package/test/errors.test.ts +0 -179
  40. package/test/integration/governor-api.test.ts +0 -370
  41. package/test/mocks/providers.ts +0 -130
  42. package/test/types.test.ts +0 -112
  43. package/test/utils.test.ts +0 -111
  44. package/test/validators.test.ts +0 -368
  45. package/test/wasm-init.test.ts +0 -132
  46. package/tsconfig.json +0 -18
  47. package/vitest.config.ts +0 -9
@@ -1,12 +1,11 @@
1
- import { GovernorPrivateStateId } from "./common-types";
2
- import * as utils from "./utils/index";
3
- import { deployContract, findDeployedContract } from "@midnight-ntwrk/midnight-js-contracts";
4
- import { Contract, witnesses } from "@agora-dao-midnight/contract";
5
- import { VoteChoice, ConfigPresets } from "./types";
6
- import { validateDeploymentConfig, validateProposal, validateVoteChoice, validateAddress, validatePositiveBigInt } from "./validators";
7
- import { InvalidParameterError } from "./errors";
8
- /** @internal */
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 actions Array of proposal actions to execute if passed.
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 actions: ProposalAction[] = [{
58
- * typ: 0,
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
- * actions
107
+ * action
71
108
  * );
72
109
  * ```
73
110
  */
74
- async createProposal(title, description, creator, actions) {
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 (!Array.isArray(actions) || actions.length === 0) {
80
- throw new InvalidParameterError("actions", "must be a non-empty array of ProposalAction");
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: stakeAmount,
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.deployedContract.callTx.createProposalWithEffect(creator, title, description, actions, stakeWitness, currentTime, currentTime + 300n);
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: "createProposalWithEffect",
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-dao-midnight/sdk';
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 ? "For" : choice === VoteChoice.Against ? "Against" : "Abstain"}`);
178
+ this.logger?.info(`Vote choice: ${choice === VoteChoice.For ? 'For' : choice === VoteChoice.Against ? 'Against' : 'Abstain'}`);
143
179
  // Validate parameters
144
- validatePositiveBigInt(proposalId, "proposalId");
145
- validateAddress(voter, "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: stakeAmount,
154
- nonce: utils.randomBytes(32)
185
+ amount: DEFAULT_BIGINT_VALUE,
186
+ nonce: utils.randomBytes(32),
155
187
  };
156
- const tx = await this.deployedContract.callTx.castVoteWithValidation(proposalId, voter, choice, stakeWitness);
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: "castVoteWithValidation",
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.deployedContract.callTx.finalizeProposalWithValidation(proposalId, proofOfGovernorApproval);
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.deployedContract.callTx.queueProposalWithValidation(proposalId, proofOfGovernorApproval);
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: getCurrentUnixTimestamp(),
226
- nonce: utils.randomBytes(32)
258
+ approvalTime: executionTime,
259
+ nonce: utils.randomBytes(32),
227
260
  };
228
- const tx = await this.deployedContract.callTx.executeQueuedProposal(proposalId, effectId, proofOfGovernorApproval);
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.deployedContract.callTx.registerStakeWithValidation(userId, amount, proofOfStake);
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
- try {
260
- const stakeInfo = await this.deployedContract.callTx.getStakeInfo(userId);
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
- try {
282
- const result = await this.deployedContract.callTx.getTotalDelegatedPower(userId);
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
- try {
304
- const result = await this.deployedContract.callTx.getStakeRecord(userId);
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
- try {
325
- const result = await this.deployedContract.callTx.hasProposalThreshold(userId);
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
- try {
341
- const result = await this.deployedContract.callTx.hasVotingPower(userId);
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
- try {
358
- const result = await this.deployedContract.callTx.getProposal(proposalId);
359
- const loggableResult = {
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
- try {
387
- const result = await this.deployedContract.callTx.getEffect(effectId);
388
- const loggableResult = {
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
- try {
412
- if (!this.deployedContract?.callTx) {
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
- try {
433
- const result = await this.deployedContract.callTx.getQueuedProposalCount();
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
- try {
450
- const result = await this.deployedContract.callTx.getProposalVoteCount(proposalId);
451
- return result;
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
- try {
466
- const result = await this.deployedContract.callTx.getGovernorStatus();
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
- try {
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
- try {
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("Governor contract state found");
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
- this.logger?.warn("WARNING: updateTime() is for testing only. Do not use in production!");
549
- this.logger?.info(`Updating contract time to: ${newTime}`);
550
- const tx = await this.deployedContract.callTx.storeTimestamp(newTime);
551
- this.logger?.info(`Time updated with txHash: ${tx.public.txHash}`);
552
- return tx.public;
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
- this.logger?.info(`Current time: ${Number(status.currentTime)}`);
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-dao-midnight/sdk';
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("Deploying governor contract");
582
+ logger?.info('Deploying governor contract');
627
583
  // Validate configuration
628
584
  validateDeploymentConfig(config);
629
- const args = [
630
- config.governor,
631
- config.token.name,
632
- config.token.symbol,
633
- config.token.decimals,
634
- config.adminKey
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
- contract: governorContractInstance,
639
- initialPrivateState: await GovernorAPI.getPrivateState(providers),
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
- const existingPrivateState = await providers.privateStateProvider.get(GovernorPrivateStateId);
690
- if (existingPrivateState) {
691
- return existingPrivateState;
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
- await providers.privateStateProvider.set(GovernorPrivateStateId, newPrivateState);
693
+ if (contractAddress) {
694
+ await providers.privateStateProvider.set(GovernorPrivateStateId, newPrivateState);
695
+ }
711
696
  return newPrivateState;
712
697
  }
713
698
  }
714
- export * as utils from "./utils/index";
715
- export * from "./common-types";
699
+ export * as utils from './utils/index';
700
+ export * from './common-types';
716
701
  //# sourceMappingURL=governor-api.js.map