@claritydao/midnight-agora-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +1 -0
- package/LICENSE +0 -0
- package/README.md +1134 -0
- package/dist/browser-providers.d.ts +21 -0
- package/dist/browser-providers.d.ts.map +1 -0
- package/dist/browser-providers.js +147 -0
- package/dist/browser-providers.js.map +1 -0
- package/dist/common-types.d.ts +29 -0
- package/dist/common-types.d.ts.map +1 -0
- package/dist/common-types.js +2 -0
- package/dist/common-types.js.map +1 -0
- package/dist/errors.d.ts +67 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +108 -0
- package/dist/errors.js.map +1 -0
- package/dist/governor-api.d.ts +348 -0
- package/dist/governor-api.d.ts.map +1 -0
- package/dist/governor-api.js +716 -0
- package/dist/governor-api.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +87 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +93 -0
- package/dist/manager.js.map +1 -0
- package/dist/types.d.ts +307 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +77 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +34 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +57 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/validators.d.ts +52 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +160 -0
- package/dist/validators.js.map +1 -0
- package/eslint.config.mjs +48 -0
- package/package.json +39 -0
- package/src/browser-providers.ts +274 -0
- package/src/common-types.ts +51 -0
- package/src/errors.ts +124 -0
- package/src/governor-api.ts +948 -0
- package/src/index.ts +51 -0
- package/src/manager.ts +203 -0
- package/src/types.ts +403 -0
- package/src/utils/index.ts +60 -0
- package/src/validators.ts +245 -0
- package/test/README.md +409 -0
- package/test/errors.test.ts +179 -0
- package/test/integration/governor-api.test.ts +370 -0
- package/test/mocks/providers.ts +130 -0
- package/test/types.test.ts +112 -0
- package/test/utils.test.ts +111 -0
- package/test/validators.test.ts +368 -0
- package/test/wasm-init.test.ts +132 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,1134 @@
|
|
|
1
|
+
# Agora DAO Midnight SDK
|
|
2
|
+
|
|
3
|
+
Production-ready TypeScript SDK for governance operations on the Midnight blockchain.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Agora DAO Midnight SDK provides a type-safe, professional-grade interface for interacting with governance contracts on Midnight Network. It integrates with the Lace wallet extension and provides comprehensive APIs for DAO governance operations.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
12
|
+
- **Production-Ready**: No hardcoded values, proper error handling, validation
|
|
13
|
+
- **Browser-Native**: Works directly in modern browsers with Lace wallet
|
|
14
|
+
- **Observable State**: Reactive patterns with RxJS for real-time updates
|
|
15
|
+
- **Zero-Knowledge**: Privacy-preserving governance with ZK proofs
|
|
16
|
+
- **Well-Documented**: Complete API reference and examples
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @agora-dao-midnight/sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Deploy a New Governor Contract
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { GovernorAPI, initializeProviders, ConfigPresets } from '@agora-dao-midnight/sdk';
|
|
30
|
+
import { randomBytes } from '@midnight-ntwrk/midnight-js-utils';
|
|
31
|
+
import pino from 'pino';
|
|
32
|
+
|
|
33
|
+
const logger = pino({ level: 'info' });
|
|
34
|
+
|
|
35
|
+
// Initialize providers (connects to Lace wallet)
|
|
36
|
+
const providers = await initializeProviders(logger);
|
|
37
|
+
|
|
38
|
+
// Create deployment configuration
|
|
39
|
+
const config = {
|
|
40
|
+
governor: {
|
|
41
|
+
governance: ConfigPresets.testing(), // or ConfigPresets.production()
|
|
42
|
+
executionDelay: BigInt(60),
|
|
43
|
+
gracePeriod: BigInt(300),
|
|
44
|
+
maxActiveProposals: BigInt(100),
|
|
45
|
+
proposalLifetime: BigInt(86400),
|
|
46
|
+
emergencyVetoEnabled: true,
|
|
47
|
+
emergencyVetoThreshold: BigInt(75000),
|
|
48
|
+
},
|
|
49
|
+
token: {
|
|
50
|
+
name: 'My DAO Token',
|
|
51
|
+
symbol: 'MDAO',
|
|
52
|
+
decimals: BigInt(18),
|
|
53
|
+
},
|
|
54
|
+
adminKey: randomBytes(32), // IMPORTANT: Use secure random bytes!
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Deploy contract
|
|
58
|
+
const api = await GovernorAPI.deploy(providers, config, logger);
|
|
59
|
+
console.log('Contract deployed at:', api.deployedContractAddress);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. Join an Existing Contract
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { GovernorAPI, initializeProviders } from '@agora-dao-midnight/sdk';
|
|
66
|
+
import pino from 'pino';
|
|
67
|
+
|
|
68
|
+
const logger = pino({ level: 'info' });
|
|
69
|
+
const providers = await initializeProviders(logger);
|
|
70
|
+
|
|
71
|
+
// Join existing contract
|
|
72
|
+
const contractAddress = 'existing-contract-address';
|
|
73
|
+
const api = await GovernorAPI.join(providers, contractAddress, logger);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Create a Proposal
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { VoteChoice } from '@agora-dao-midnight/sdk';
|
|
80
|
+
import type { ProposalAction } from '@agora-dao-midnight/sdk';
|
|
81
|
+
|
|
82
|
+
// Define proposal actions (what happens if proposal passes)
|
|
83
|
+
const actions: ProposalAction[] = [
|
|
84
|
+
{
|
|
85
|
+
typ: 0, // Transfer type
|
|
86
|
+
token: tokenAddress,
|
|
87
|
+
amount: BigInt(10000),
|
|
88
|
+
to: recipientAddress,
|
|
89
|
+
configKey: new Uint8Array(32),
|
|
90
|
+
configValue: new Uint8Array(32),
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
// Create proposal (requires sufficient stake)
|
|
95
|
+
const { txData, proposalId } = await api.createProposal(
|
|
96
|
+
'Fund Community Initiative',
|
|
97
|
+
'Transfer 10,000 tokens to community wallet for Q1 initiatives',
|
|
98
|
+
creatorAddress,
|
|
99
|
+
actions
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
console.log('Proposal created with ID:', proposalId);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Cast a Vote
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { VoteChoice } from '@agora-dao-midnight/sdk';
|
|
109
|
+
|
|
110
|
+
// Vote on proposal (type-safe!)
|
|
111
|
+
await api.castVote(
|
|
112
|
+
proposalId,
|
|
113
|
+
voterAddress,
|
|
114
|
+
VoteChoice.For // VoteChoice.Against or VoteChoice.Abstain
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
console.log('Vote cast successfully');
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Configuration Presets
|
|
121
|
+
|
|
122
|
+
The SDK provides two configuration presets:
|
|
123
|
+
|
|
124
|
+
### Testing Configuration
|
|
125
|
+
|
|
126
|
+
For development and testnet:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { ConfigPresets } from '@agora-dao-midnight/sdk';
|
|
130
|
+
|
|
131
|
+
const governanceConfig = ConfigPresets.testing();
|
|
132
|
+
// Returns:
|
|
133
|
+
// {
|
|
134
|
+
// votingPeriod: BigInt(300), // 5 minutes
|
|
135
|
+
// quorumThreshold: BigInt(100), // 100 tokens
|
|
136
|
+
// proposalThreshold: BigInt(50), // 50 tokens to create proposals
|
|
137
|
+
// proposalLifetime: BigInt(3600), // 1 hour
|
|
138
|
+
// executionDelay: BigInt(60), // 1 minute
|
|
139
|
+
// gracePeriod: BigInt(300), // 5 minutes
|
|
140
|
+
// minStakeAmount: BigInt(10), // 10 tokens minimum
|
|
141
|
+
// stakingPeriod: BigInt(60), // 1 minute lock
|
|
142
|
+
// }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Production Configuration
|
|
146
|
+
|
|
147
|
+
For mainnet:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { ConfigPresets } from '@agora-dao-midnight/sdk';
|
|
151
|
+
|
|
152
|
+
const governanceConfig = ConfigPresets.production();
|
|
153
|
+
// Returns:
|
|
154
|
+
// {
|
|
155
|
+
// votingPeriod: BigInt(604800), // 7 days
|
|
156
|
+
// quorumThreshold: BigInt(100000), // 100K tokens
|
|
157
|
+
// proposalThreshold: BigInt(50000), // 50K tokens to create proposals
|
|
158
|
+
// proposalLifetime: BigInt(1209600), // 14 days
|
|
159
|
+
// executionDelay: BigInt(86400), // 24 hours
|
|
160
|
+
// gracePeriod: BigInt(259200), // 3 days
|
|
161
|
+
// minStakeAmount: BigInt(1000), // 1K tokens minimum
|
|
162
|
+
// stakingPeriod: BigInt(604800), // 7 days lock
|
|
163
|
+
// }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## API Reference
|
|
167
|
+
|
|
168
|
+
### Types
|
|
169
|
+
|
|
170
|
+
#### DeploymentConfig
|
|
171
|
+
|
|
172
|
+
Complete deployment configuration:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
interface DeploymentConfig {
|
|
176
|
+
governor: GovernorConfig;
|
|
177
|
+
token: TokenConfig;
|
|
178
|
+
adminKey: Uint8Array; // 32 bytes - use randomBytes(32)
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### GovernorConfig
|
|
183
|
+
|
|
184
|
+
Governor contract parameters:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
interface GovernorConfig {
|
|
188
|
+
governance: GovernanceConfig;
|
|
189
|
+
executionDelay: bigint;
|
|
190
|
+
gracePeriod: bigint;
|
|
191
|
+
maxActiveProposals: bigint;
|
|
192
|
+
proposalLifetime: bigint;
|
|
193
|
+
emergencyVetoEnabled: boolean;
|
|
194
|
+
emergencyVetoThreshold: bigint;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### GovernanceConfig
|
|
199
|
+
|
|
200
|
+
Core governance parameters:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
interface GovernanceConfig {
|
|
204
|
+
votingPeriod: bigint; // How long voting lasts
|
|
205
|
+
quorumThreshold: bigint; // Minimum votes to pass
|
|
206
|
+
proposalThreshold: bigint; // Stake required to create proposals
|
|
207
|
+
proposalLifetime: bigint; // How long proposals remain valid
|
|
208
|
+
executionDelay: bigint; // Time-lock before execution
|
|
209
|
+
gracePeriod: bigint; // Window to execute after delay
|
|
210
|
+
minStakeAmount: bigint; // Minimum stake to vote
|
|
211
|
+
stakingPeriod: bigint; // Stake lock duration
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### TokenConfig
|
|
216
|
+
|
|
217
|
+
Token parameters:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
interface TokenConfig {
|
|
221
|
+
name: string; // Max 64 characters
|
|
222
|
+
symbol: string; // Max 16 characters
|
|
223
|
+
decimals: bigint; // Max 18
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### ProposalAction
|
|
228
|
+
|
|
229
|
+
Actions to execute when proposal passes:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
interface ProposalAction {
|
|
233
|
+
typ: number; // Action type (0 = transfer)
|
|
234
|
+
token: Uint8Array; // Token address (32 bytes)
|
|
235
|
+
amount: bigint; // Amount to transfer
|
|
236
|
+
to: Uint8Array; // Recipient address (32 bytes)
|
|
237
|
+
configKey: Uint8Array; // Config key (32 bytes)
|
|
238
|
+
configValue: Uint8Array; // Config value (32 bytes)
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
#### VoteChoice
|
|
243
|
+
|
|
244
|
+
Type-safe vote choices:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
enum VoteChoice {
|
|
248
|
+
For = 0, // Vote in favor
|
|
249
|
+
Against = 1, // Vote against
|
|
250
|
+
Abstain = 2, // Abstain from voting
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### GovernorAPI
|
|
255
|
+
|
|
256
|
+
#### Static Methods
|
|
257
|
+
|
|
258
|
+
##### deploy()
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
static async deploy(
|
|
262
|
+
providers: GovernorProviders,
|
|
263
|
+
config: DeploymentConfig,
|
|
264
|
+
logger?: Logger
|
|
265
|
+
): Promise<GovernorAPI>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Deploys a new governor contract with the specified configuration.
|
|
269
|
+
|
|
270
|
+
**Parameters:**
|
|
271
|
+
- `providers`: Initialized providers (from `initializeProviders`)
|
|
272
|
+
- `config`: Deployment configuration
|
|
273
|
+
- `logger` (optional): Pino logger instance
|
|
274
|
+
|
|
275
|
+
**Returns:** GovernorAPI instance
|
|
276
|
+
|
|
277
|
+
**Throws:**
|
|
278
|
+
- `InvalidConfigurationError`: Invalid configuration parameters
|
|
279
|
+
- `DeploymentError`: Contract deployment failed
|
|
280
|
+
- `WalletNotConnectedError`: Wallet not connected
|
|
281
|
+
|
|
282
|
+
**Example:**
|
|
283
|
+
```typescript
|
|
284
|
+
const api = await GovernorAPI.deploy(providers, config, logger);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
##### join()
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
static async join(
|
|
291
|
+
providers: GovernorProviders,
|
|
292
|
+
contractAddress: ContractAddress,
|
|
293
|
+
logger?: Logger
|
|
294
|
+
): Promise<GovernorAPI>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Joins an existing governor contract.
|
|
298
|
+
|
|
299
|
+
**Parameters:**
|
|
300
|
+
- `providers`: Initialized providers
|
|
301
|
+
- `contractAddress`: Address of deployed contract
|
|
302
|
+
- `logger` (optional): Pino logger instance
|
|
303
|
+
|
|
304
|
+
**Returns:** GovernorAPI instance
|
|
305
|
+
|
|
306
|
+
**Throws:**
|
|
307
|
+
- `ContractNotFoundError`: Contract not found at address
|
|
308
|
+
- `WalletNotConnectedError`: Wallet not connected
|
|
309
|
+
|
|
310
|
+
#### Instance Methods
|
|
311
|
+
|
|
312
|
+
##### createProposal()
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
async createProposal(
|
|
316
|
+
title: string,
|
|
317
|
+
description: string,
|
|
318
|
+
creator: Uint8Array,
|
|
319
|
+
actions: ProposalAction[]
|
|
320
|
+
): Promise<{ txData: FinalizedTxData; proposalId: bigint }>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Creates a new governance proposal.
|
|
324
|
+
|
|
325
|
+
**Parameters:**
|
|
326
|
+
- `title`: Proposal title (max 256 chars)
|
|
327
|
+
- `description`: Proposal description (max 10,000 chars)
|
|
328
|
+
- `creator`: Creator address (32 bytes, must have sufficient stake)
|
|
329
|
+
- `actions`: Array of actions to execute if proposal passes
|
|
330
|
+
|
|
331
|
+
**Returns:** Object with transaction data and proposal ID
|
|
332
|
+
|
|
333
|
+
**Throws:**
|
|
334
|
+
- `InvalidParameterError`: Invalid parameters
|
|
335
|
+
- `InsufficientStakeError`: Creator has insufficient stake
|
|
336
|
+
- `TransactionError`: Transaction failed
|
|
337
|
+
|
|
338
|
+
**Example:**
|
|
339
|
+
```typescript
|
|
340
|
+
const { proposalId } = await api.createProposal(
|
|
341
|
+
'Increase Treasury Allocation',
|
|
342
|
+
'Detailed description of the proposal...',
|
|
343
|
+
creatorAddress,
|
|
344
|
+
[
|
|
345
|
+
{
|
|
346
|
+
typ: 0,
|
|
347
|
+
token: tokenAddress,
|
|
348
|
+
amount: BigInt(10000),
|
|
349
|
+
to: recipientAddress,
|
|
350
|
+
configKey: new Uint8Array(32),
|
|
351
|
+
configValue: new Uint8Array(32),
|
|
352
|
+
},
|
|
353
|
+
]
|
|
354
|
+
);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
##### castVote()
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
async castVote(
|
|
361
|
+
proposalId: bigint,
|
|
362
|
+
voter: Uint8Array,
|
|
363
|
+
choice: VoteChoice
|
|
364
|
+
): Promise<FinalizedTxData>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Casts a vote on an active proposal.
|
|
368
|
+
|
|
369
|
+
**Parameters:**
|
|
370
|
+
- `proposalId`: Proposal ID to vote on
|
|
371
|
+
- `voter`: Voter address (32 bytes, must have voting power)
|
|
372
|
+
- `choice`: Vote choice (VoteChoice.For, VoteChoice.Against, or VoteChoice.Abstain)
|
|
373
|
+
|
|
374
|
+
**Returns:** Transaction data
|
|
375
|
+
|
|
376
|
+
**Throws:**
|
|
377
|
+
- `InvalidParameterError`: Invalid parameters
|
|
378
|
+
- `InsufficientStakeError`: Voter has insufficient voting power
|
|
379
|
+
- `ProposalNotFoundError`: Proposal not found or not active
|
|
380
|
+
- `TransactionError`: Transaction failed
|
|
381
|
+
|
|
382
|
+
**Example:**
|
|
383
|
+
```typescript
|
|
384
|
+
await api.castVote(proposalId, voterAddress, VoteChoice.For);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
##### registerStake()
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
async registerStake(
|
|
391
|
+
userId: Uint8Array,
|
|
392
|
+
amount: bigint
|
|
393
|
+
): Promise<FinalizedTxData>
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Registers a stake for governance participation.
|
|
397
|
+
|
|
398
|
+
**Parameters:**
|
|
399
|
+
- `userId`: User address (32 bytes)
|
|
400
|
+
- `amount`: Amount to stake
|
|
401
|
+
|
|
402
|
+
**Returns:** Transaction data
|
|
403
|
+
|
|
404
|
+
##### finalizeProposal()
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
async finalizeProposal(proposalId: bigint): Promise<FinalizedTxData>
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Finalizes a proposal after voting period ends.
|
|
411
|
+
|
|
412
|
+
##### queueProposal()
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
async queueProposal(proposalId: bigint): Promise<FinalizedTxData>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Queues a finalized proposal for execution.
|
|
419
|
+
|
|
420
|
+
##### executeProposal()
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
async executeProposal(
|
|
424
|
+
proposalId: bigint,
|
|
425
|
+
effectId: bigint
|
|
426
|
+
): Promise<FinalizedTxData>
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Executes a queued proposal after execution delay.
|
|
430
|
+
|
|
431
|
+
##### getProposalDetails()
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
async getProposalDetails(proposalId: bigint): Promise<ProposalDetails>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Gets detailed information about a proposal.
|
|
438
|
+
|
|
439
|
+
##### getProposalVoteCount()
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
async getProposalVoteCount(proposalId: bigint): Promise<VoteCount>
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Gets vote counts for a proposal.
|
|
446
|
+
|
|
447
|
+
##### getStakeInfo()
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
async getStakeInfo(userId: Uint8Array): Promise<StakeInfo>
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Gets staking information for a user.
|
|
454
|
+
|
|
455
|
+
### initializeProviders()
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
async function initializeProviders(logger: Logger): Promise<GovernorProviders>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Initializes all required providers for governance operations.
|
|
462
|
+
|
|
463
|
+
**What it does:**
|
|
464
|
+
1. Connects to Lace wallet extension
|
|
465
|
+
2. Requests user authorization
|
|
466
|
+
3. Retrieves wallet state and service URIs
|
|
467
|
+
4. Configures all providers (IndexedDB, HTTP, Indexer, Wallet, Proof server)
|
|
468
|
+
|
|
469
|
+
**Parameters:**
|
|
470
|
+
- `logger`: Pino logger instance
|
|
471
|
+
|
|
472
|
+
**Returns:** Configured providers object
|
|
473
|
+
|
|
474
|
+
**Throws:**
|
|
475
|
+
- Error if Lace wallet not found
|
|
476
|
+
- Error if wallet connection rejected
|
|
477
|
+
- Error if incompatible wallet version
|
|
478
|
+
|
|
479
|
+
**Example:**
|
|
480
|
+
```typescript
|
|
481
|
+
const providers = await initializeProviders(logger);
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### GovernorManager
|
|
485
|
+
|
|
486
|
+
High-level API with observable state management.
|
|
487
|
+
|
|
488
|
+
#### Constructor
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
constructor(
|
|
492
|
+
deploymentConfig: DeploymentConfig,
|
|
493
|
+
logger: Logger
|
|
494
|
+
)
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
**Parameters:**
|
|
498
|
+
- `deploymentConfig`: Deployment configuration
|
|
499
|
+
- `logger`: Pino logger instance
|
|
500
|
+
|
|
501
|
+
**Example:**
|
|
502
|
+
```typescript
|
|
503
|
+
const manager = new GovernorManager(config, logger);
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
#### Methods
|
|
507
|
+
|
|
508
|
+
##### resolve()
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
resolve(contractAddress?: ContractAddress): Observable<GovernorDeployment>
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Deploys a new contract or joins an existing one, with observable state.
|
|
515
|
+
|
|
516
|
+
**Returns:** Observable that emits deployment state changes
|
|
517
|
+
|
|
518
|
+
**Deployment States:**
|
|
519
|
+
```typescript
|
|
520
|
+
{ status: 'in-progress' }
|
|
521
|
+
{ status: 'deployed', api: DeployedGovernorAPI }
|
|
522
|
+
{ status: 'failed', error: Error }
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
## Error Handling
|
|
526
|
+
|
|
527
|
+
### Custom Error Classes
|
|
528
|
+
|
|
529
|
+
The SDK provides specific error classes for different failure scenarios:
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
import {
|
|
533
|
+
GovernorSDKError,
|
|
534
|
+
WalletNotConnectedError,
|
|
535
|
+
InsufficientStakeError,
|
|
536
|
+
InvalidParameterError,
|
|
537
|
+
ContractNotFoundError,
|
|
538
|
+
ProposalNotFoundError,
|
|
539
|
+
TransactionError,
|
|
540
|
+
DeploymentError,
|
|
541
|
+
InvalidConfigurationError,
|
|
542
|
+
NetworkNotConfiguredError,
|
|
543
|
+
} from '@agora-dao-midnight/sdk';
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Error Handling Pattern
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
try {
|
|
550
|
+
await api.createProposal(title, description, creator, actions);
|
|
551
|
+
} catch (error) {
|
|
552
|
+
if (error instanceof InsufficientStakeError) {
|
|
553
|
+
console.error('Need more stake:', error.message);
|
|
554
|
+
} else if (error instanceof InvalidParameterError) {
|
|
555
|
+
console.error('Invalid input:', error.message);
|
|
556
|
+
} else if (error instanceof TransactionError) {
|
|
557
|
+
console.error('Transaction failed:', error.message);
|
|
558
|
+
} else {
|
|
559
|
+
console.error('Unexpected error:', error);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Validation
|
|
565
|
+
|
|
566
|
+
The SDK includes comprehensive input validation:
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
import { validators } from '@agora-dao-midnight/sdk';
|
|
570
|
+
|
|
571
|
+
// Validate proposal parameters
|
|
572
|
+
validators.validateProposal(title, description, creator);
|
|
573
|
+
|
|
574
|
+
// Validate vote choice
|
|
575
|
+
validators.validateVoteChoice(choice);
|
|
576
|
+
|
|
577
|
+
// Validate deployment config
|
|
578
|
+
validators.validateDeploymentConfig(config);
|
|
579
|
+
|
|
580
|
+
// Validate governance config
|
|
581
|
+
validators.validateGovernanceConfig(governanceConfig);
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
## Browser Integration
|
|
585
|
+
|
|
586
|
+
### Critical: Frontend Setup Requirements
|
|
587
|
+
|
|
588
|
+
Before integrating this SDK into a browser application, you MUST configure your build tool correctly to handle WASM modules. **Failure to do so will result in the error `ocrt.maxField is not a function`.**
|
|
589
|
+
|
|
590
|
+
### Required: Vite Configuration
|
|
591
|
+
|
|
592
|
+
**IMPORTANT:** Use Vite, not Next.js. Next.js 15's Turbopack does not support the webpack configurations required for this WASM pattern.
|
|
593
|
+
|
|
594
|
+
#### 1. Install Required Vite Plugins
|
|
595
|
+
|
|
596
|
+
```bash
|
|
597
|
+
pnpm add -D vite vite-plugin-wasm vite-plugin-top-level-await @vitejs/plugin-react
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
#### 2. Create `vite.config.ts`
|
|
601
|
+
|
|
602
|
+
This exact configuration is required. It's based on Midnight Network's official `example-bboard` implementation:
|
|
603
|
+
|
|
604
|
+
```typescript
|
|
605
|
+
import { defineConfig } from 'vite';
|
|
606
|
+
import react from '@vitejs/plugin-react';
|
|
607
|
+
import wasm from 'vite-plugin-wasm';
|
|
608
|
+
import topLevelAwait from 'vite-plugin-top-level-await';
|
|
609
|
+
|
|
610
|
+
export default defineConfig({
|
|
611
|
+
cacheDir: './.vite',
|
|
612
|
+
build: {
|
|
613
|
+
target: 'esnext',
|
|
614
|
+
minify: false,
|
|
615
|
+
rollupOptions: {
|
|
616
|
+
output: {
|
|
617
|
+
manualChunks: {
|
|
618
|
+
// CRITICAL: Separate chunk for WASM modules
|
|
619
|
+
wasm: ['@midnight-ntwrk/onchain-runtime'],
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
commonjsOptions: {
|
|
624
|
+
// CRITICAL: Transform CommonJS to ESM
|
|
625
|
+
transformMixedEsModules: true,
|
|
626
|
+
extensions: ['.js', '.cjs'],
|
|
627
|
+
ignoreDynamicRequires: true,
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
plugins: [
|
|
631
|
+
react(),
|
|
632
|
+
wasm(),
|
|
633
|
+
topLevelAwait({
|
|
634
|
+
promiseExportName: '__tla',
|
|
635
|
+
promiseImportName: (i) => `__tla_${i}`,
|
|
636
|
+
}),
|
|
637
|
+
// CRITICAL: Custom resolver for WASM module handling
|
|
638
|
+
{
|
|
639
|
+
name: 'wasm-module-resolver',
|
|
640
|
+
resolveId(source, importer) {
|
|
641
|
+
if (
|
|
642
|
+
source === '@midnight-ntwrk/onchain-runtime' &&
|
|
643
|
+
importer &&
|
|
644
|
+
importer.includes('@midnight-ntwrk/compact-runtime')
|
|
645
|
+
) {
|
|
646
|
+
return {
|
|
647
|
+
id: source,
|
|
648
|
+
external: false,
|
|
649
|
+
moduleSideEffects: true,
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
return null;
|
|
653
|
+
},
|
|
654
|
+
},
|
|
655
|
+
],
|
|
656
|
+
optimizeDeps: {
|
|
657
|
+
esbuildOptions: {
|
|
658
|
+
target: 'esnext',
|
|
659
|
+
supported: { 'top-level-await': true },
|
|
660
|
+
platform: 'browser',
|
|
661
|
+
format: 'esm',
|
|
662
|
+
loader: { '.wasm': 'binary' },
|
|
663
|
+
},
|
|
664
|
+
// CRITICAL: Pre-bundle for CJS→ESM conversion
|
|
665
|
+
include: ['@midnight-ntwrk/compact-runtime'],
|
|
666
|
+
// CRITICAL: Exclude WASM from optimization
|
|
667
|
+
exclude: [
|
|
668
|
+
'@midnight-ntwrk/onchain-runtime',
|
|
669
|
+
'@midnight-ntwrk/onchain-runtime/midnight_onchain_runtime_wasm_bg.wasm',
|
|
670
|
+
'@midnight-ntwrk/onchain-runtime/midnight_onchain_runtime_wasm.js',
|
|
671
|
+
],
|
|
672
|
+
},
|
|
673
|
+
resolve: {
|
|
674
|
+
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.wasm'],
|
|
675
|
+
mainFields: ['browser', 'module', 'main'],
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
#### Why This Configuration is Required
|
|
681
|
+
|
|
682
|
+
The SDK depends on `@midnight-ntwrk/compact-runtime`, which:
|
|
683
|
+
1. Uses CommonJS (`require()`)
|
|
684
|
+
2. Imports `@midnight-ntwrk/onchain-runtime` (WASM module with top-level await)
|
|
685
|
+
3. Executes `ocrt.maxField()` at the top level
|
|
686
|
+
|
|
687
|
+
This creates a CJS/ESM/WASM incompatibility that requires the custom resolver plugin and specific optimization settings.
|
|
688
|
+
|
|
689
|
+
### Why Next.js Doesn't Work
|
|
690
|
+
|
|
691
|
+
Next.js 15 uses **Turbopack** by default, which doesn't support the webpack configurations needed:
|
|
692
|
+
|
|
693
|
+
```typescript
|
|
694
|
+
// These webpack configs don't work with Turbopack:
|
|
695
|
+
config.experiments = {
|
|
696
|
+
asyncWebAssembly: true,
|
|
697
|
+
topLevelAwait: true,
|
|
698
|
+
};
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
Even with the `--webpack` flag, the CJS/ESM/WASM interaction is too complex for Next.js to handle correctly.
|
|
702
|
+
|
|
703
|
+
**Recommendation:** Use Vite for browser dApps (officially supported by Midnight Network).
|
|
704
|
+
|
|
705
|
+
### React Example
|
|
706
|
+
|
|
707
|
+
```tsx
|
|
708
|
+
import { GovernorManager, VoteChoice, ConfigPresets } from '@agora-dao-midnight/sdk';
|
|
709
|
+
import { randomBytes } from '@midnight-ntwrk/midnight-js-utils';
|
|
710
|
+
import { useEffect, useState } from 'react';
|
|
711
|
+
import pino from 'pino';
|
|
712
|
+
|
|
713
|
+
const logger = pino({ level: 'info' });
|
|
714
|
+
|
|
715
|
+
const config = {
|
|
716
|
+
governor: {
|
|
717
|
+
governance: ConfigPresets.testing(),
|
|
718
|
+
executionDelay: BigInt(60),
|
|
719
|
+
gracePeriod: BigInt(300),
|
|
720
|
+
maxActiveProposals: BigInt(100),
|
|
721
|
+
proposalLifetime: BigInt(3600),
|
|
722
|
+
emergencyVetoEnabled: true,
|
|
723
|
+
emergencyVetoThreshold: BigInt(75000),
|
|
724
|
+
},
|
|
725
|
+
token: {
|
|
726
|
+
name: 'Test DAO',
|
|
727
|
+
symbol: 'TDAO',
|
|
728
|
+
decimals: BigInt(18),
|
|
729
|
+
},
|
|
730
|
+
adminKey: randomBytes(32), // CRITICAL: Use secure random bytes!
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
const manager = new GovernorManager(config, logger);
|
|
734
|
+
|
|
735
|
+
function GovernanceApp() {
|
|
736
|
+
const [deployment, setDeployment] = useState(null);
|
|
737
|
+
|
|
738
|
+
useEffect(() => {
|
|
739
|
+
const subscription = manager.resolve().subscribe(setDeployment);
|
|
740
|
+
return () => subscription.unsubscribe();
|
|
741
|
+
}, []);
|
|
742
|
+
|
|
743
|
+
if (!deployment || deployment.status === 'in-progress') {
|
|
744
|
+
return <div>Connecting to wallet and deploying contract...</div>;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (deployment.status === 'failed') {
|
|
748
|
+
return <div>Error: {deployment.error.message}</div>;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const api = deployment.api;
|
|
752
|
+
|
|
753
|
+
const handleCreateProposal = async () => {
|
|
754
|
+
try {
|
|
755
|
+
const actions = [
|
|
756
|
+
{
|
|
757
|
+
typ: 0,
|
|
758
|
+
token: tokenAddress,
|
|
759
|
+
amount: BigInt(1000),
|
|
760
|
+
to: recipientAddress,
|
|
761
|
+
configKey: new Uint8Array(32),
|
|
762
|
+
configValue: new Uint8Array(32),
|
|
763
|
+
},
|
|
764
|
+
];
|
|
765
|
+
|
|
766
|
+
const { proposalId } = await api.createProposal(
|
|
767
|
+
'My Proposal',
|
|
768
|
+
'Description here',
|
|
769
|
+
creatorAddress,
|
|
770
|
+
actions
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
alert(`Proposal created: ${proposalId}`);
|
|
774
|
+
} catch (error) {
|
|
775
|
+
alert(`Error: ${error.message}`);
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
const handleVote = async (proposalId) => {
|
|
780
|
+
try {
|
|
781
|
+
await api.castVote(proposalId, voterAddress, VoteChoice.For);
|
|
782
|
+
alert('Vote cast successfully');
|
|
783
|
+
} catch (error) {
|
|
784
|
+
alert(`Error: ${error.message}`);
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
return (
|
|
789
|
+
<div>
|
|
790
|
+
<h1>Governor Contract</h1>
|
|
791
|
+
<p>Address: {api.deployedContractAddress}</p>
|
|
792
|
+
|
|
793
|
+
<button onClick={handleCreateProposal}>Create Proposal</button>
|
|
794
|
+
<button onClick={() => handleVote(1n)}>Vote For Proposal #1</button>
|
|
795
|
+
</div>
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
## Security Best Practices
|
|
801
|
+
|
|
802
|
+
### 1. Always Use Secure Random for Admin Keys
|
|
803
|
+
|
|
804
|
+
```typescript
|
|
805
|
+
import { randomBytes } from '@midnight-ntwrk/midnight-js-utils';
|
|
806
|
+
|
|
807
|
+
// ✅ Good: Cryptographically secure random
|
|
808
|
+
const adminKey = randomBytes(32);
|
|
809
|
+
|
|
810
|
+
// ❌ Bad: Predictable key
|
|
811
|
+
const adminKey = new Uint8Array(32); // All zeros - INSECURE!
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
### 2. Use Configuration Presets
|
|
815
|
+
|
|
816
|
+
```typescript
|
|
817
|
+
// ✅ Good: Use tested presets
|
|
818
|
+
const config = ConfigPresets.production();
|
|
819
|
+
|
|
820
|
+
// ❌ Bad: Hardcoded magic numbers
|
|
821
|
+
const config = {
|
|
822
|
+
votingPeriod: BigInt(300), // What does 300 mean?
|
|
823
|
+
// ...
|
|
824
|
+
};
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### 3. Validate User Inputs
|
|
828
|
+
|
|
829
|
+
```typescript
|
|
830
|
+
import { validators } from '@agora-dao-midnight/sdk';
|
|
831
|
+
|
|
832
|
+
// ✅ Good: Validate before submission
|
|
833
|
+
validators.validateProposal(title, description, creator);
|
|
834
|
+
await api.createProposal(title, description, creator, actions);
|
|
835
|
+
|
|
836
|
+
// ❌ Bad: No validation
|
|
837
|
+
await api.createProposal(title, description, creator, actions);
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
### 4. Handle Errors Gracefully
|
|
841
|
+
|
|
842
|
+
```typescript
|
|
843
|
+
// ✅ Good: Specific error handling
|
|
844
|
+
try {
|
|
845
|
+
await api.castVote(proposalId, voter, choice);
|
|
846
|
+
} catch (error) {
|
|
847
|
+
if (error instanceof InsufficientStakeError) {
|
|
848
|
+
showMessage('You need more stake to vote');
|
|
849
|
+
} else {
|
|
850
|
+
showMessage('An error occurred');
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// ❌ Bad: Silent failures
|
|
855
|
+
try {
|
|
856
|
+
await api.castVote(proposalId, voter, choice);
|
|
857
|
+
} catch (error) {
|
|
858
|
+
// Silent failure
|
|
859
|
+
}
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
## Troubleshooting
|
|
863
|
+
|
|
864
|
+
### Critical: WASM Initialization Error
|
|
865
|
+
|
|
866
|
+
**Problem:** `TypeError: ocrt.maxField is not a function`
|
|
867
|
+
|
|
868
|
+
**Root Cause:** CJS/ESM/WASM module incompatibility. The SDK depends on `@midnight-ntwrk/compact-runtime` (CommonJS) which requires `@midnight-ntwrk/onchain-runtime` (WASM with top-level await).
|
|
869
|
+
|
|
870
|
+
**Solutions:**
|
|
871
|
+
|
|
872
|
+
1. **Use Vite, not Next.js**
|
|
873
|
+
```bash
|
|
874
|
+
# Install Vite and required plugins
|
|
875
|
+
pnpm add -D vite vite-plugin-wasm vite-plugin-top-level-await @vitejs/plugin-react
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
2. **Configure vite.config.ts correctly** - See "Browser Integration" section above for complete configuration
|
|
879
|
+
|
|
880
|
+
3. **Key configuration requirements:**
|
|
881
|
+
- Custom `wasm-module-resolver` plugin
|
|
882
|
+
- `transformMixedEsModules: true` in commonjsOptions
|
|
883
|
+
- Manual chunks for WASM modules
|
|
884
|
+
- Proper include/exclude in optimizeDeps
|
|
885
|
+
|
|
886
|
+
**Error Location:**
|
|
887
|
+
```
|
|
888
|
+
File: node_modules/@midnight-ntwrk/compact-runtime/dist/runtime.js:90
|
|
889
|
+
Code: exports.MAX_FIELD = ocrt.maxField();
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
**Why Next.js Doesn't Work:**
|
|
893
|
+
- Next.js 15 uses Turbopack which doesn't support required webpack configurations
|
|
894
|
+
- Even with `--webpack` flag, the CJS/ESM/WASM interaction is too complex
|
|
895
|
+
|
|
896
|
+
**Reference Implementations:**
|
|
897
|
+
- Working example: Midnight Network's `example-bboard` application
|
|
898
|
+
- Configuration tested and verified in production
|
|
899
|
+
|
|
900
|
+
### Admin Key Security Error
|
|
901
|
+
|
|
902
|
+
**Problem:** "Insecure admin key detected" or predictable deployment behavior
|
|
903
|
+
|
|
904
|
+
**Root Cause:** Using an all-zeros Uint8Array for admin key
|
|
905
|
+
|
|
906
|
+
**Bad Example:**
|
|
907
|
+
```typescript
|
|
908
|
+
const adminKey = new Uint8Array(32); // All zeros - INSECURE!
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
**Correct Solution:**
|
|
912
|
+
```typescript
|
|
913
|
+
import { randomBytes } from '@midnight-ntwrk/midnight-js-utils';
|
|
914
|
+
|
|
915
|
+
const adminKey = randomBytes(32); // Cryptographically secure
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
**Why It Matters:**
|
|
919
|
+
- Admin keys control contract upgrades and governance
|
|
920
|
+
- Predictable keys can be exploited
|
|
921
|
+
- All production deployments MUST use secure random keys
|
|
922
|
+
|
|
923
|
+
### Build Configuration Issues
|
|
924
|
+
|
|
925
|
+
**Problem:** "require is not defined" in browser
|
|
926
|
+
|
|
927
|
+
**Root Cause:** Contract module uses CommonJS but wasn't transformed to ESM
|
|
928
|
+
|
|
929
|
+
**Solution:**
|
|
930
|
+
```typescript
|
|
931
|
+
// In vite.config.ts
|
|
932
|
+
commonjsOptions: {
|
|
933
|
+
transformMixedEsModules: true,
|
|
934
|
+
extensions: ['.js', '.cjs'],
|
|
935
|
+
}
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
**Problem:** "Top-level await is not available"
|
|
939
|
+
|
|
940
|
+
**Root Cause:** WASM module with top-level await in wrong bundle chunk
|
|
941
|
+
|
|
942
|
+
**Solution:**
|
|
943
|
+
```typescript
|
|
944
|
+
// In vite.config.ts
|
|
945
|
+
rollupOptions: {
|
|
946
|
+
output: {
|
|
947
|
+
manualChunks: {
|
|
948
|
+
wasm: ['@midnight-ntwrk/onchain-runtime'],
|
|
949
|
+
},
|
|
950
|
+
},
|
|
951
|
+
}
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
**Problem:** Module optimization errors during build
|
|
955
|
+
|
|
956
|
+
**Root Cause:** Vite trying to optimize WASM and CJS modules together
|
|
957
|
+
|
|
958
|
+
**Solution:**
|
|
959
|
+
```typescript
|
|
960
|
+
// In vite.config.ts
|
|
961
|
+
optimizeDeps: {
|
|
962
|
+
include: ['@midnight-ntwrk/compact-runtime'], // Pre-bundle for CJS→ESM
|
|
963
|
+
exclude: [
|
|
964
|
+
'@midnight-ntwrk/onchain-runtime', // Don't optimize WASM
|
|
965
|
+
'@agora-dao-midnight/contract', // Don't optimize contract
|
|
966
|
+
],
|
|
967
|
+
}
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### Wallet Connection Issues
|
|
971
|
+
|
|
972
|
+
**Problem:** "Could not find Midnight Lace wallet"
|
|
973
|
+
|
|
974
|
+
**Solutions:**
|
|
975
|
+
- Install Lace wallet extension from [lace.io](https://www.lace.io/)
|
|
976
|
+
- Refresh the browser page
|
|
977
|
+
- Check browser console for errors
|
|
978
|
+
- Ensure extension is enabled
|
|
979
|
+
|
|
980
|
+
**Problem:** "Incompatible version of Midnight Lace wallet"
|
|
981
|
+
|
|
982
|
+
**Solutions:**
|
|
983
|
+
- Update Lace extension to latest version
|
|
984
|
+
- SDK requires Lace API v1.x
|
|
985
|
+
- Check compatibility in browser console
|
|
986
|
+
|
|
987
|
+
### Transaction Failures
|
|
988
|
+
|
|
989
|
+
**Problem:** InsufficientStakeError
|
|
990
|
+
|
|
991
|
+
**Solutions:**
|
|
992
|
+
- Register stake before creating proposals
|
|
993
|
+
- Ensure stake amount ≥ proposalThreshold
|
|
994
|
+
- Check staking period hasn't expired
|
|
995
|
+
|
|
996
|
+
**Problem:** TransactionError: "User rejected transaction"
|
|
997
|
+
|
|
998
|
+
**Solutions:**
|
|
999
|
+
- User must approve transaction in Lace wallet
|
|
1000
|
+
- Check wallet has sufficient tDUST balance
|
|
1001
|
+
- Verify transaction parameters are correct
|
|
1002
|
+
|
|
1003
|
+
### Build Errors
|
|
1004
|
+
|
|
1005
|
+
**Problem:** TypeScript compilation errors
|
|
1006
|
+
|
|
1007
|
+
**Solutions:**
|
|
1008
|
+
```bash
|
|
1009
|
+
# Update dependencies
|
|
1010
|
+
pnpm install
|
|
1011
|
+
|
|
1012
|
+
# Rebuild SDK
|
|
1013
|
+
pnpm run build
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
**Problem:** Module not found errors
|
|
1017
|
+
|
|
1018
|
+
**Solutions:**
|
|
1019
|
+
- Ensure all dependencies are installed
|
|
1020
|
+
- Check `node_modules` exists
|
|
1021
|
+
- Verify import paths are correct
|
|
1022
|
+
|
|
1023
|
+
### Runtime Errors
|
|
1024
|
+
|
|
1025
|
+
**Problem:** "Contract not found at address"
|
|
1026
|
+
|
|
1027
|
+
**Solutions:**
|
|
1028
|
+
- Verify contract address is correct
|
|
1029
|
+
- Check network connection
|
|
1030
|
+
- Ensure contract is deployed on current network
|
|
1031
|
+
|
|
1032
|
+
**Problem:** "InvalidParameterError"
|
|
1033
|
+
|
|
1034
|
+
**Solutions:**
|
|
1035
|
+
- Check parameter types and formats
|
|
1036
|
+
- Validate inputs before submission
|
|
1037
|
+
- Review API documentation for correct usage
|
|
1038
|
+
|
|
1039
|
+
### Cache Issues
|
|
1040
|
+
|
|
1041
|
+
**Problem:** Changes not reflected after rebuild
|
|
1042
|
+
|
|
1043
|
+
**Solutions:**
|
|
1044
|
+
```bash
|
|
1045
|
+
# Clear Vite cache
|
|
1046
|
+
rm -rf node_modules/.vite
|
|
1047
|
+
|
|
1048
|
+
# Clear browser cache or hard refresh
|
|
1049
|
+
# Chrome/Edge: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)
|
|
1050
|
+
# Firefox: Ctrl+F5 (Windows) or Cmd+Shift+R (Mac)
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
## Development
|
|
1054
|
+
|
|
1055
|
+
### Building from Source
|
|
1056
|
+
|
|
1057
|
+
```bash
|
|
1058
|
+
# Clone repository
|
|
1059
|
+
git clone https://github.com/your-org/agora-dao-midnight-sdk
|
|
1060
|
+
cd internalAgoraMidnightSDK
|
|
1061
|
+
|
|
1062
|
+
# Install dependencies
|
|
1063
|
+
pnpm install
|
|
1064
|
+
|
|
1065
|
+
# Build contract first
|
|
1066
|
+
cd contract
|
|
1067
|
+
pnpm run build
|
|
1068
|
+
|
|
1069
|
+
# Build SDK
|
|
1070
|
+
cd ../sdk
|
|
1071
|
+
pnpm run build
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
### Project Structure
|
|
1075
|
+
|
|
1076
|
+
```
|
|
1077
|
+
sdk/
|
|
1078
|
+
├── src/
|
|
1079
|
+
│ ├── browser-providers.ts # Lace wallet integration
|
|
1080
|
+
│ ├── governor-api.ts # Main API implementation
|
|
1081
|
+
│ ├── manager.ts # Observable state manager
|
|
1082
|
+
│ ├── types.ts # Type definitions
|
|
1083
|
+
│ ├── errors.ts # Error classes
|
|
1084
|
+
│ ├── validators.ts # Input validation
|
|
1085
|
+
│ ├── common-types.ts # Shared types
|
|
1086
|
+
│ ├── index.ts # Main exports
|
|
1087
|
+
│ └── utils/ # Utility functions
|
|
1088
|
+
├── package.json
|
|
1089
|
+
├── tsconfig.json
|
|
1090
|
+
└── README.md
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
## Prerequisites
|
|
1094
|
+
|
|
1095
|
+
### Required Software
|
|
1096
|
+
|
|
1097
|
+
1. **Lace Wallet Extension**
|
|
1098
|
+
- Install from [lace.io](https://www.lace.io/)
|
|
1099
|
+
- Create or import wallet
|
|
1100
|
+
- Switch to Midnight TestNet
|
|
1101
|
+
|
|
1102
|
+
2. **TestNet Tokens (tDUST)**
|
|
1103
|
+
- Request from [Midnight Faucet](https://faucet.testnet.midnight.network/)
|
|
1104
|
+
- Needed to pay for transactions
|
|
1105
|
+
|
|
1106
|
+
3. **Modern Browser**
|
|
1107
|
+
- Chrome/Edge 89+
|
|
1108
|
+
- Firefox 89+
|
|
1109
|
+
- Safari 15+
|
|
1110
|
+
|
|
1111
|
+
### Browser APIs Required
|
|
1112
|
+
|
|
1113
|
+
- IndexedDB (private state storage)
|
|
1114
|
+
- Web Crypto API (cryptographic operations)
|
|
1115
|
+
- Fetch API (HTTP requests)
|
|
1116
|
+
- WebSocket (real-time updates)
|
|
1117
|
+
|
|
1118
|
+
## Resources
|
|
1119
|
+
|
|
1120
|
+
- [Midnight Documentation](https://docs.midnight.network/)
|
|
1121
|
+
- [Lace Wallet](https://www.lace.io/)
|
|
1122
|
+
- [TestNet Faucet](https://faucet.testnet.midnight.network/)
|
|
1123
|
+
- [Compact Language Guide](https://docs.midnight.network/develop/compact/)
|
|
1124
|
+
- [Production Readiness Guide](./PRODUCTION_READY.md)
|
|
1125
|
+
|
|
1126
|
+
## License
|
|
1127
|
+
|
|
1128
|
+
Apache-2.0
|
|
1129
|
+
|
|
1130
|
+
## Support
|
|
1131
|
+
|
|
1132
|
+
For issues and questions:
|
|
1133
|
+
- GitHub Issues: [Create an issue](https://github.com/your-org/agora-dao-midnight-sdk/issues)
|
|
1134
|
+
- Documentation: [Read the docs](https://docs.midnight.network/)
|