midnight-mcp 0.1.3 → 0.1.4
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.
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Embedded documentation content
|
|
3
|
-
*
|
|
2
|
+
* Embedded documentation content
|
|
3
|
+
*
|
|
4
|
+
* DESIGN PRINCIPLE: This file contains ONLY curated/unique content that:
|
|
5
|
+
* 1. Doesn't exist in official docs (wallet-integration guide we created)
|
|
6
|
+
* 2. Is a synthesized summary (tokenomics whitepaper)
|
|
7
|
+
* 3. Is a quick reference card (compact-reference, sdk-api)
|
|
8
|
+
* 4. Is from external sources (OpenZeppelin Compact contracts)
|
|
9
|
+
*
|
|
10
|
+
* For official Midnight docs (glossary, Zswap, Kachina concepts),
|
|
11
|
+
* use the search_docs tool which queries the Vector DB.
|
|
4
12
|
*/
|
|
5
13
|
export const EMBEDDED_DOCS = {
|
|
6
|
-
"midnight://docs/compact-reference": `# Compact Language Reference
|
|
14
|
+
"midnight://docs/compact-reference": `# Compact Language Quick Reference
|
|
7
15
|
|
|
8
|
-
|
|
16
|
+
A curated syntax reference for Compact - Midnight's smart contract language.
|
|
9
17
|
|
|
10
18
|
## Basic Structure
|
|
11
19
|
|
|
@@ -78,810 +86,458 @@ witness getCurrentPrice(): Field {
|
|
|
78
86
|
return fetchPrice();
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
export circuit
|
|
89
|
+
export circuit buyAtMarketPrice(maxPrice: Field): Void {
|
|
82
90
|
const price = getCurrentPrice();
|
|
83
|
-
|
|
91
|
+
assert(price <= maxPrice);
|
|
92
|
+
// ... execute purchase
|
|
84
93
|
}
|
|
85
94
|
\`\`\`
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
- \`disclose(private)\` - Reveal private data
|
|
93
|
-
|
|
94
|
-
### State Operations
|
|
95
|
-
- \`Counter.increment(n)\` - Add to counter
|
|
96
|
-
- \`Counter.decrement(n)\` - Subtract from counter
|
|
97
|
-
- \`Counter.value()\` - Read current value
|
|
98
|
-
- \`Map.insert(k, v)\` - Add key-value
|
|
99
|
-
- \`Map.get(k)\` - Retrieve value
|
|
100
|
-
- \`Set.add(v)\` - Add to set
|
|
101
|
-
- \`Set.contains(v)\` - Check membership
|
|
102
|
-
|
|
103
|
-
## Privacy Annotations
|
|
104
|
-
|
|
105
|
-
\`\`\`compact
|
|
106
|
-
ledger {
|
|
107
|
-
publicData: Field; // Visible on-chain
|
|
108
|
-
@private
|
|
109
|
-
privateData: Field; // Only owner sees
|
|
110
|
-
}
|
|
111
|
-
\`\`\`
|
|
112
|
-
`,
|
|
113
|
-
"midnight://docs/sdk-api": `# Midnight TypeScript SDK API
|
|
114
|
-
|
|
115
|
-
## Installation
|
|
116
|
-
|
|
117
|
-
\`\`\`bash
|
|
118
|
-
npm install @midnight-ntwrk/midnight-js-contracts @midnight-ntwrk/midnight-js-types
|
|
119
|
-
\`\`\`
|
|
120
|
-
|
|
121
|
-
## Core Packages
|
|
122
|
-
|
|
123
|
-
### @midnight-ntwrk/midnight-js-contracts
|
|
124
|
-
Contract interaction layer for deploying and calling Midnight smart contracts.
|
|
125
|
-
|
|
126
|
-
\`\`\`typescript
|
|
127
|
-
import { Contract, DeployedContract } from '@midnight-ntwrk/midnight-js-contracts';
|
|
128
|
-
|
|
129
|
-
// Deploy a contract
|
|
130
|
-
const deployed = await Contract.deploy(
|
|
131
|
-
wallet,
|
|
132
|
-
contractArtifact,
|
|
133
|
-
initialState
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
// Call a circuit
|
|
137
|
-
const result = await deployed.call('increment', { amount: 1n });
|
|
138
|
-
\`\`\`
|
|
139
|
-
|
|
140
|
-
### @midnight-ntwrk/midnight-js-types
|
|
141
|
-
Shared types and interfaces for the SDK.
|
|
142
|
-
|
|
143
|
-
\`\`\`typescript
|
|
144
|
-
import type {
|
|
145
|
-
Address,
|
|
146
|
-
Transaction,
|
|
147
|
-
Proof,
|
|
148
|
-
ContractState
|
|
149
|
-
} from '@midnight-ntwrk/midnight-js-types';
|
|
150
|
-
\`\`\`
|
|
151
|
-
|
|
152
|
-
### @midnight-ntwrk/wallet-api
|
|
153
|
-
Wallet integration interface.
|
|
154
|
-
|
|
155
|
-
\`\`\`typescript
|
|
156
|
-
import { WalletAPI } from '@midnight-ntwrk/wallet-api';
|
|
157
|
-
|
|
158
|
-
const wallet = await WalletAPI.connect();
|
|
159
|
-
const address = await wallet.getAddress();
|
|
160
|
-
const balance = await wallet.getBalance();
|
|
161
|
-
\`\`\`
|
|
162
|
-
|
|
163
|
-
## Common Patterns
|
|
164
|
-
|
|
165
|
-
### Contract Deployment
|
|
166
|
-
\`\`\`typescript
|
|
167
|
-
import { Contract } from '@midnight-ntwrk/midnight-js-contracts';
|
|
168
|
-
import counterContract from './counter.json';
|
|
169
|
-
|
|
170
|
-
async function deployCounter() {
|
|
171
|
-
const deployed = await Contract.deploy(
|
|
172
|
-
wallet,
|
|
173
|
-
counterContract,
|
|
174
|
-
{ counter: 0n }
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
console.log('Deployed at:', deployed.address);
|
|
178
|
-
return deployed;
|
|
179
|
-
}
|
|
180
|
-
\`\`\`
|
|
181
|
-
|
|
182
|
-
### Calling Circuits
|
|
183
|
-
\`\`\`typescript
|
|
184
|
-
async function increment(contract: DeployedContract, amount: bigint) {
|
|
185
|
-
const tx = await contract.call('increment', { amount });
|
|
186
|
-
await tx.wait();
|
|
187
|
-
|
|
188
|
-
const newValue = await contract.query('counter');
|
|
189
|
-
return newValue;
|
|
190
|
-
}
|
|
191
|
-
\`\`\`
|
|
192
|
-
|
|
193
|
-
### Querying State
|
|
194
|
-
\`\`\`typescript
|
|
195
|
-
async function getState(contract: DeployedContract) {
|
|
196
|
-
const publicState = await contract.query('publicField');
|
|
197
|
-
// Note: Private state requires witness functions
|
|
198
|
-
return publicState;
|
|
199
|
-
}
|
|
200
|
-
\`\`\`
|
|
201
|
-
`,
|
|
202
|
-
"midnight://docs/concepts/zero-knowledge": `# Zero-Knowledge Proofs in Midnight
|
|
203
|
-
|
|
204
|
-
## What are Zero-Knowledge Proofs?
|
|
205
|
-
|
|
206
|
-
Zero-knowledge proofs (ZKPs) allow one party (the prover) to convince another party (the verifier) that a statement is true, without revealing any information beyond the validity of the statement.
|
|
207
|
-
|
|
208
|
-
## How Midnight Uses ZKPs
|
|
209
|
-
|
|
210
|
-
In Midnight, every circuit execution generates a zero-knowledge proof:
|
|
211
|
-
|
|
212
|
-
1. **User calls a circuit** with private inputs
|
|
213
|
-
2. **Proof is generated** off-chain
|
|
214
|
-
3. **Only the proof** (not the inputs) is submitted to the blockchain
|
|
215
|
-
4. **Validators verify** the proof without knowing the inputs
|
|
216
|
-
|
|
217
|
-
## Example
|
|
218
|
-
|
|
219
|
-
\`\`\`compact
|
|
220
|
-
export circuit proveAge(birthYear: Field): Boolean {
|
|
221
|
-
const currentYear = 2024;
|
|
222
|
-
const age = currentYear - birthYear;
|
|
223
|
-
|
|
224
|
-
// Proves user is over 18 without revealing exact age
|
|
225
|
-
assert(age >= 18);
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
228
|
-
\`\`\`
|
|
229
|
-
|
|
230
|
-
When this circuit runs:
|
|
231
|
-
- Input: \`birthYear = 1990\` (private)
|
|
232
|
-
- Output: \`true\` (public)
|
|
233
|
-
- Proof: "I know a birthYear that makes age >= 18" (public)
|
|
234
|
-
|
|
235
|
-
The verifier learns the user is over 18, but not their actual birth year.
|
|
236
|
-
|
|
237
|
-
## Key Properties
|
|
238
|
-
|
|
239
|
-
1. **Completeness**: Valid proofs always verify
|
|
240
|
-
2. **Soundness**: Invalid proofs cannot be forged
|
|
241
|
-
3. **Zero-knowledge**: Nothing beyond validity is revealed
|
|
242
|
-
|
|
243
|
-
## Privacy Patterns
|
|
244
|
-
|
|
245
|
-
### Selective Disclosure
|
|
246
|
-
\`\`\`compact
|
|
247
|
-
export circuit verifyCredential(
|
|
248
|
-
@private credential: Credential
|
|
249
|
-
): Field {
|
|
250
|
-
// Prove credential is valid
|
|
251
|
-
assert(credential.isValid());
|
|
252
|
-
|
|
253
|
-
// Only reveal specific fields
|
|
254
|
-
return disclose(credential.issuer);
|
|
255
|
-
}
|
|
256
|
-
\`\`\`
|
|
257
|
-
|
|
258
|
-
### Hidden Computation
|
|
259
|
-
\`\`\`compact
|
|
260
|
-
export circuit secretBid(
|
|
261
|
-
@private amount: Field,
|
|
262
|
-
commitment: Field
|
|
263
|
-
): Void {
|
|
264
|
-
// Prove bid matches commitment without revealing amount
|
|
265
|
-
assert(commit(amount) == commitment);
|
|
266
|
-
}
|
|
267
|
-
\`\`\`
|
|
268
|
-
`,
|
|
269
|
-
"midnight://docs/concepts/shielded-state": `# Shielded vs Unshielded State
|
|
270
|
-
|
|
271
|
-
Midnight supports two types of state: shielded (private) and unshielded (public).
|
|
272
|
-
|
|
273
|
-
## Unshielded State
|
|
96
|
+
### Key Points:
|
|
97
|
+
- Run locally, not on-chain
|
|
98
|
+
- Can access external APIs, databases
|
|
99
|
+
- Cannot modify ledger state directly
|
|
100
|
+
- Results are private unless disclosed
|
|
274
101
|
|
|
275
|
-
|
|
102
|
+
## State Management
|
|
276
103
|
|
|
104
|
+
### Public State
|
|
277
105
|
\`\`\`compact
|
|
278
106
|
ledger {
|
|
279
|
-
|
|
280
|
-
|
|
107
|
+
publicCounter: Counter;
|
|
108
|
+
publicMap: Map<Field, Field>;
|
|
281
109
|
}
|
|
282
110
|
\`\`\`
|
|
283
111
|
|
|
284
|
-
|
|
285
|
-
- Token total supply
|
|
286
|
-
- Public voting tallies
|
|
287
|
-
- Any data that should be transparent
|
|
288
|
-
|
|
289
|
-
## Shielded State
|
|
290
|
-
|
|
291
|
-
Private state only visible to the owner:
|
|
292
|
-
|
|
112
|
+
### Private State
|
|
293
113
|
\`\`\`compact
|
|
294
114
|
ledger {
|
|
295
115
|
@private
|
|
296
|
-
|
|
116
|
+
secretBalance: Field;
|
|
297
117
|
|
|
298
118
|
@private
|
|
299
|
-
|
|
119
|
+
hiddenVotes: Map<Address, Field>;
|
|
300
120
|
}
|
|
301
121
|
\`\`\`
|
|
302
122
|
|
|
303
|
-
|
|
304
|
-
- User credentials
|
|
305
|
-
- Private balances
|
|
306
|
-
- Sensitive personal data
|
|
307
|
-
|
|
308
|
-
## Hybrid Approach
|
|
309
|
-
|
|
310
|
-
Most contracts use both:
|
|
123
|
+
## Common Patterns
|
|
311
124
|
|
|
125
|
+
### Access Control
|
|
312
126
|
\`\`\`compact
|
|
313
127
|
ledger {
|
|
314
|
-
|
|
315
|
-
messageCount: Counter;
|
|
316
|
-
|
|
317
|
-
// Private: only owner sees message contents
|
|
318
|
-
@private
|
|
319
|
-
messages: Map<Field, Opaque<"string">>;
|
|
128
|
+
owner: Opaque<"address">;
|
|
320
129
|
}
|
|
321
130
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
// Public increment
|
|
326
|
-
ledger.messageCount.increment(1);
|
|
327
|
-
|
|
328
|
-
// Private storage
|
|
329
|
-
ledger.messages.insert(id, content);
|
|
131
|
+
witness getCaller(): Opaque<"address"> {
|
|
132
|
+
return context.caller;
|
|
330
133
|
}
|
|
331
|
-
\`\`\`
|
|
332
134
|
|
|
333
|
-
|
|
135
|
+
export circuit ownerOnly(): Void {
|
|
136
|
+
assert(getCaller() == ledger.owner, "Not owner");
|
|
137
|
+
}
|
|
138
|
+
\`\`\`
|
|
334
139
|
|
|
335
|
-
###
|
|
140
|
+
### Disclosure
|
|
336
141
|
\`\`\`compact
|
|
337
|
-
export circuit
|
|
338
|
-
|
|
339
|
-
return disclose(
|
|
142
|
+
export circuit revealSecret(): Field {
|
|
143
|
+
const secret = getPrivateData();
|
|
144
|
+
return disclose(secret); // Makes private data public
|
|
340
145
|
}
|
|
341
146
|
\`\`\`
|
|
342
147
|
|
|
343
|
-
###
|
|
148
|
+
### Assertions
|
|
344
149
|
\`\`\`compact
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return commit(value);
|
|
348
|
-
}
|
|
150
|
+
assert(condition); // Basic assertion
|
|
151
|
+
assert(condition, "Error message"); // With message
|
|
349
152
|
\`\`\`
|
|
350
153
|
`,
|
|
351
|
-
"midnight://docs/
|
|
352
|
-
|
|
353
|
-
Witnesses provide off-chain data to circuits in Midnight.
|
|
354
|
-
|
|
355
|
-
## Why Witnesses?
|
|
356
|
-
|
|
357
|
-
Circuits run in a ZK environment with limitations:
|
|
358
|
-
- Cannot make network requests
|
|
359
|
-
- Cannot access system time
|
|
360
|
-
- Cannot read external files
|
|
361
|
-
- Must be deterministic
|
|
154
|
+
"midnight://docs/sdk-api": `# Midnight TypeScript SDK Quick Reference
|
|
362
155
|
|
|
363
|
-
|
|
156
|
+
## Installation
|
|
364
157
|
|
|
365
|
-
|
|
158
|
+
\`\`\`bash
|
|
159
|
+
npm install @midnight-ntwrk/midnight-js-contracts
|
|
160
|
+
\`\`\`
|
|
366
161
|
|
|
367
|
-
|
|
368
|
-
// Runs off-chain, provides data to circuits
|
|
369
|
-
witness getTimestamp(): Field {
|
|
370
|
-
return getCurrentUnixTime();
|
|
371
|
-
}
|
|
162
|
+
## Core Types
|
|
372
163
|
|
|
373
|
-
|
|
374
|
-
const timestamp = getTimestamp();
|
|
375
|
-
assert(timestamp > ledger.deadline);
|
|
376
|
-
// ... perform action
|
|
377
|
-
}
|
|
378
|
-
\`\`\`
|
|
164
|
+
### Contract Deployment
|
|
379
165
|
|
|
380
|
-
|
|
166
|
+
\`\`\`typescript
|
|
167
|
+
import { deployContract, ContractDeployment } from '@midnight-ntwrk/midnight-js-contracts';
|
|
381
168
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
169
|
+
const deployment: ContractDeployment = await deployContract({
|
|
170
|
+
contract: compiledContract,
|
|
171
|
+
privateState: initialPrivateState,
|
|
172
|
+
args: constructorArgs,
|
|
173
|
+
});
|
|
387
174
|
|
|
388
|
-
|
|
389
|
-
const price = fetchPrice(asset);
|
|
390
|
-
const total = amount * price;
|
|
391
|
-
// ... execute swap
|
|
392
|
-
}
|
|
175
|
+
const { contractAddress, initialState } = deployment;
|
|
393
176
|
\`\`\`
|
|
394
177
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
Witnesses can access private ledger state:
|
|
178
|
+
### Contract Interaction
|
|
398
179
|
|
|
399
|
-
\`\`\`
|
|
400
|
-
|
|
401
|
-
@private
|
|
402
|
-
secretNonce: Field;
|
|
403
|
-
}
|
|
180
|
+
\`\`\`typescript
|
|
181
|
+
import { callContract } from '@midnight-ntwrk/midnight-js-contracts';
|
|
404
182
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
183
|
+
// Call a circuit
|
|
184
|
+
const result = await callContract({
|
|
185
|
+
contractAddress,
|
|
186
|
+
circuitName: 'increment',
|
|
187
|
+
args: [amount],
|
|
188
|
+
privateState: currentPrivateState,
|
|
189
|
+
});
|
|
409
190
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
return hash(data, nonce);
|
|
413
|
-
}
|
|
191
|
+
// Result contains new state and return value
|
|
192
|
+
const { newPrivateState, returnValue, proof } = result;
|
|
414
193
|
\`\`\`
|
|
415
194
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
1. **Keep witnesses simple** - Complex logic should be in circuits
|
|
419
|
-
2. **Handle failures gracefully** - Witnesses can fail
|
|
420
|
-
3. **Don't trust witness data blindly** - Validate in circuits
|
|
421
|
-
4. **Cache when possible** - Reduce off-chain calls
|
|
195
|
+
### Providers
|
|
422
196
|
|
|
423
|
-
|
|
197
|
+
\`\`\`typescript
|
|
198
|
+
import {
|
|
199
|
+
MidnightProvider,
|
|
200
|
+
createMidnightProvider
|
|
201
|
+
} from '@midnight-ntwrk/midnight-js-contracts';
|
|
424
202
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
203
|
+
const provider = await createMidnightProvider({
|
|
204
|
+
indexer: 'https://indexer.testnet.midnight.network',
|
|
205
|
+
node: 'https://node.testnet.midnight.network',
|
|
206
|
+
proofServer: 'https://prover.testnet.midnight.network',
|
|
207
|
+
});
|
|
208
|
+
\`\`\`
|
|
429
209
|
|
|
430
|
-
|
|
210
|
+
## State Management
|
|
431
211
|
|
|
432
|
-
\`\`\`
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
// Validate witness data
|
|
437
|
-
assert(price > 0);
|
|
438
|
-
assert(price < MAX_REASONABLE_PRICE);
|
|
439
|
-
|
|
440
|
-
// ... use price
|
|
212
|
+
\`\`\`typescript
|
|
213
|
+
interface ContractState<T> {
|
|
214
|
+
publicState: PublicState;
|
|
215
|
+
privateState: T;
|
|
441
216
|
}
|
|
442
|
-
\`\`\`
|
|
443
|
-
`,
|
|
444
|
-
"midnight://docs/concepts/kachina": `# Kachina Protocol
|
|
445
217
|
|
|
446
|
-
|
|
218
|
+
// Subscribe to state changes
|
|
219
|
+
provider.subscribeToContract(contractAddress, (state) => {
|
|
220
|
+
console.log('New state:', state);
|
|
221
|
+
});
|
|
222
|
+
\`\`\`
|
|
447
223
|
|
|
448
|
-
##
|
|
224
|
+
## Transaction Building
|
|
449
225
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
- Composable privacy across contracts
|
|
453
|
-
- Efficient on-chain verification
|
|
226
|
+
\`\`\`typescript
|
|
227
|
+
import { buildTransaction } from '@midnight-ntwrk/midnight-js-contracts';
|
|
454
228
|
|
|
455
|
-
|
|
229
|
+
const tx = await buildTransaction({
|
|
230
|
+
contractAddress,
|
|
231
|
+
circuitName: 'transfer',
|
|
232
|
+
args: [recipient, amount],
|
|
233
|
+
privateState,
|
|
234
|
+
});
|
|
456
235
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
└─────────────────┘ └────────┬────────┘
|
|
461
|
-
│
|
|
462
|
-
┌────────▼────────┐
|
|
463
|
-
│ ZK Circuit │
|
|
464
|
-
│ (Prover) │
|
|
465
|
-
└────────┬────────┘
|
|
466
|
-
│
|
|
467
|
-
┌────────▼────────┐
|
|
468
|
-
│ Proof │
|
|
469
|
-
└────────┬────────┘
|
|
470
|
-
│
|
|
471
|
-
┌────────▼────────┐
|
|
472
|
-
│ Midnight │
|
|
473
|
-
│ Validators │
|
|
474
|
-
└─────────────────┘
|
|
236
|
+
// Sign and submit
|
|
237
|
+
const signedTx = await wallet.signTransaction(tx);
|
|
238
|
+
const txHash = await provider.submitTransaction(signedTx);
|
|
475
239
|
\`\`\`
|
|
476
240
|
|
|
477
|
-
##
|
|
241
|
+
## Error Handling
|
|
478
242
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
- **Private State**: Stored off-chain, encrypted
|
|
482
|
-
- **Commitments**: On-chain references to private state
|
|
483
|
-
|
|
484
|
-
### Transaction Flow
|
|
485
|
-
1. User prepares transaction locally
|
|
486
|
-
2. Prover generates ZK proof
|
|
487
|
-
3. Transaction + proof submitted to network
|
|
488
|
-
4. Validators verify proof (not re-execute)
|
|
489
|
-
5. State updates applied
|
|
490
|
-
|
|
491
|
-
### Composability
|
|
492
|
-
Contracts can interact while preserving privacy:
|
|
493
|
-
|
|
494
|
-
\`\`\`compact
|
|
495
|
-
// Contract A
|
|
496
|
-
export circuit transferToken(to: Address, amount: Field): Void {
|
|
497
|
-
// Private transfer logic
|
|
498
|
-
}
|
|
243
|
+
\`\`\`typescript
|
|
244
|
+
import { MidnightError, ContractError } from '@midnight-ntwrk/midnight-js-contracts';
|
|
499
245
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
// Privacy preserved for both
|
|
246
|
+
try {
|
|
247
|
+
await callContract({ ... });
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error instanceof ContractError) {
|
|
250
|
+
console.error('Contract assertion failed:', error.message);
|
|
251
|
+
} else if (error instanceof MidnightError) {
|
|
252
|
+
console.error('Network error:', error.code);
|
|
253
|
+
}
|
|
509
254
|
}
|
|
510
255
|
\`\`\`
|
|
511
|
-
|
|
512
|
-
## Benefits
|
|
513
|
-
|
|
514
|
-
1. **Privacy by Default**: All computation is private unless explicitly disclosed
|
|
515
|
-
2. **Scalability**: Verification is faster than re-execution
|
|
516
|
-
3. **Flexibility**: Developers choose what to reveal
|
|
517
|
-
4. **Interoperability**: Works with existing blockchain infrastructure
|
|
518
256
|
`,
|
|
519
257
|
"midnight://docs/openzeppelin": `# OpenZeppelin Contracts for Compact
|
|
520
258
|
|
|
521
259
|
> **Official Documentation**: https://docs.openzeppelin.com/contracts-compact
|
|
522
260
|
> **GitHub Repository**: https://github.com/OpenZeppelin/compact-contracts
|
|
523
261
|
|
|
524
|
-
|
|
262
|
+
The official OpenZeppelin library for Midnight smart contracts provides battle-tested, audited implementations of common patterns.
|
|
525
263
|
|
|
526
264
|
## Installation
|
|
527
265
|
|
|
528
266
|
\`\`\`bash
|
|
529
|
-
|
|
530
|
-
mkdir my-project && cd my-project
|
|
531
|
-
|
|
532
|
-
# Initialize git and add as submodule
|
|
533
|
-
git init && git submodule add https://github.com/OpenZeppelin/compact-contracts.git
|
|
534
|
-
|
|
535
|
-
# Install dependencies
|
|
536
|
-
cd compact-contracts
|
|
537
|
-
nvm install && yarn && SKIP_ZK=true yarn compact
|
|
267
|
+
npm install @openzeppelin/compact-contracts
|
|
538
268
|
\`\`\`
|
|
539
269
|
|
|
540
270
|
## Available Modules
|
|
541
271
|
|
|
542
|
-
### Token
|
|
543
|
-
- **FungibleToken
|
|
544
|
-
-
|
|
272
|
+
### Token Standards
|
|
273
|
+
- **FungibleToken** - Privacy-preserving token with shielded balances
|
|
274
|
+
- **NFT** - Non-fungible tokens with optional privacy
|
|
545
275
|
|
|
546
276
|
### Access Control
|
|
547
|
-
- **Ownable
|
|
548
|
-
- **
|
|
277
|
+
- **Ownable** - Single-owner access pattern
|
|
278
|
+
- **Roles** - Role-based access control
|
|
279
|
+
- **AccessControl** - Flexible permission system
|
|
549
280
|
|
|
550
281
|
### Security
|
|
551
|
-
- **Pausable
|
|
282
|
+
- **Pausable** - Emergency stop mechanism
|
|
283
|
+
- **ReentrancyGuard** - Prevent reentrancy attacks
|
|
552
284
|
|
|
553
285
|
## Usage Example
|
|
554
286
|
|
|
555
287
|
\`\`\`compact
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/access/Ownable" prefix Ownable_;
|
|
560
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/security/Pausable" prefix Pausable_;
|
|
561
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/token/FungibleToken" prefix FungibleToken_;
|
|
562
|
-
|
|
563
|
-
constructor(
|
|
564
|
-
_name: Opaque<"string">,
|
|
565
|
-
_symbol: Opaque<"string">,
|
|
566
|
-
_decimals: Uint<8>,
|
|
567
|
-
_recipient: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
568
|
-
_amount: Uint<128>,
|
|
569
|
-
_initOwner: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
570
|
-
) {
|
|
571
|
-
Ownable_initialize(_initOwner);
|
|
572
|
-
FungibleToken_initialize(_name, _symbol, _decimals);
|
|
573
|
-
FungibleToken__mint(_recipient, _amount);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
export circuit transfer(
|
|
577
|
-
to: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
578
|
-
value: Uint<128>,
|
|
579
|
-
): Boolean {
|
|
580
|
-
Pausable_assertNotPaused();
|
|
581
|
-
return FungibleToken_transfer(to, value);
|
|
582
|
-
}
|
|
288
|
+
include "std";
|
|
289
|
+
include "@openzeppelin/compact-contracts/token/FungibleToken.compact";
|
|
290
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
583
291
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
292
|
+
ledger {
|
|
293
|
+
// Inherit from OpenZeppelin contracts
|
|
294
|
+
...FungibleToken.ledger;
|
|
295
|
+
...Ownable.ledger;
|
|
587
296
|
}
|
|
588
297
|
|
|
589
|
-
export circuit
|
|
590
|
-
|
|
591
|
-
|
|
298
|
+
export circuit mint(to: Address, amount: Field): Void {
|
|
299
|
+
Ownable.assertOnlyOwner();
|
|
300
|
+
FungibleToken.mint(to, amount);
|
|
592
301
|
}
|
|
593
302
|
\`\`\`
|
|
594
303
|
|
|
595
|
-
##
|
|
596
|
-
|
|
597
|
-
\`\`\`bash
|
|
598
|
-
compact compile MyContract.compact artifacts/MyContract
|
|
599
|
-
\`\`\`
|
|
600
|
-
|
|
601
|
-
## Why Use OpenZeppelin?
|
|
304
|
+
## Best Practices
|
|
602
305
|
|
|
603
|
-
1. **
|
|
604
|
-
2. **
|
|
605
|
-
3. **
|
|
606
|
-
4. **
|
|
607
|
-
5. **Best Practices**: Follows Compact language best practices
|
|
306
|
+
1. **Always use audited contracts** - Don't reinvent token standards
|
|
307
|
+
2. **Combine patterns** - Ownable + FungibleToken + Pausable
|
|
308
|
+
3. **Check for updates** - Security patches are released regularly
|
|
309
|
+
4. **Read the docs** - Each module has specific usage patterns
|
|
608
310
|
`,
|
|
609
311
|
"midnight://docs/openzeppelin/token": `# OpenZeppelin FungibleToken
|
|
610
312
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
The FungibleToken module provides a complete implementation for fungible tokens on Midnight.
|
|
313
|
+
The recommended standard for privacy-preserving tokens on Midnight.
|
|
614
314
|
|
|
615
315
|
## Features
|
|
616
316
|
|
|
617
|
-
-
|
|
618
|
-
-
|
|
619
|
-
-
|
|
620
|
-
-
|
|
621
|
-
- Privacy-preserving by default
|
|
317
|
+
- Shielded balances (private by default)
|
|
318
|
+
- Optional public balance disclosure
|
|
319
|
+
- Transfer with ZK proofs
|
|
320
|
+
- Mint/burn capabilities
|
|
622
321
|
|
|
623
322
|
## Basic Usage
|
|
624
323
|
|
|
625
324
|
\`\`\`compact
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
325
|
+
include "std";
|
|
326
|
+
include "@openzeppelin/compact-contracts/token/FungibleToken.compact";
|
|
327
|
+
|
|
328
|
+
ledger {
|
|
329
|
+
...FungibleToken.ledger;
|
|
330
|
+
name: Opaque<"string">;
|
|
331
|
+
symbol: Opaque<"string">;
|
|
332
|
+
decimals: Uint<8>;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export circuit initialize(
|
|
336
|
+
name: Opaque<"string">,
|
|
337
|
+
symbol: Opaque<"string">,
|
|
338
|
+
decimals: Uint<8>,
|
|
339
|
+
initialSupply: Field,
|
|
340
|
+
owner: Address
|
|
341
|
+
): Void {
|
|
342
|
+
ledger.name = name;
|
|
343
|
+
ledger.symbol = symbol;
|
|
344
|
+
ledger.decimals = decimals;
|
|
345
|
+
FungibleToken.mint(owner, initialSupply);
|
|
639
346
|
}
|
|
640
347
|
|
|
641
|
-
//
|
|
642
|
-
export circuit transfer(
|
|
643
|
-
to
|
|
644
|
-
value: Uint<128>,
|
|
645
|
-
): Boolean {
|
|
646
|
-
return FungibleToken_transfer(to, value);
|
|
348
|
+
// Shielded transfer
|
|
349
|
+
export circuit transfer(to: Address, amount: Field): Void {
|
|
350
|
+
FungibleToken.transfer(to, amount);
|
|
647
351
|
}
|
|
648
352
|
|
|
649
|
-
// Check balance (
|
|
650
|
-
witness
|
|
651
|
-
|
|
652
|
-
): Uint<128> {
|
|
653
|
-
return FungibleToken_balanceOf(account);
|
|
353
|
+
// Check balance (private)
|
|
354
|
+
witness myBalance(): Field {
|
|
355
|
+
return FungibleToken.balanceOf(context.caller);
|
|
654
356
|
}
|
|
655
357
|
|
|
656
|
-
//
|
|
657
|
-
|
|
658
|
-
return
|
|
358
|
+
// Reveal balance publicly (optional)
|
|
359
|
+
export circuit revealBalance(): Field {
|
|
360
|
+
return disclose(myBalance());
|
|
659
361
|
}
|
|
660
362
|
\`\`\`
|
|
661
363
|
|
|
662
|
-
##
|
|
364
|
+
## Minting and Burning
|
|
663
365
|
|
|
664
366
|
\`\`\`compact
|
|
665
|
-
|
|
666
|
-
export circuit approve(
|
|
667
|
-
spender: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
668
|
-
value: Uint<128>,
|
|
669
|
-
): Boolean {
|
|
670
|
-
return FungibleToken_approve(spender, value);
|
|
671
|
-
}
|
|
367
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
672
368
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
to: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
677
|
-
value: Uint<128>,
|
|
678
|
-
): Boolean {
|
|
679
|
-
return FungibleToken_transferFrom(from, to, value);
|
|
369
|
+
ledger {
|
|
370
|
+
...FungibleToken.ledger;
|
|
371
|
+
...Ownable.ledger;
|
|
680
372
|
}
|
|
681
|
-
\`\`\`
|
|
682
|
-
|
|
683
|
-
## Mint and Burn (Owner-Only)
|
|
684
373
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
export circuit mint(
|
|
689
|
-
to: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
690
|
-
amount: Uint<128>,
|
|
691
|
-
): [] {
|
|
692
|
-
Ownable_assertOnlyOwner();
|
|
693
|
-
FungibleToken__mint(to, amount);
|
|
374
|
+
export circuit mint(to: Address, amount: Field): Void {
|
|
375
|
+
Ownable.assertOnlyOwner();
|
|
376
|
+
FungibleToken.mint(to, amount);
|
|
694
377
|
}
|
|
695
378
|
|
|
696
|
-
export circuit burn(
|
|
697
|
-
|
|
698
|
-
amount: Uint<128>,
|
|
699
|
-
): [] {
|
|
700
|
-
Ownable_assertOnlyOwner();
|
|
701
|
-
FungibleToken__burn(from, amount);
|
|
379
|
+
export circuit burn(amount: Field): Void {
|
|
380
|
+
FungibleToken.burn(context.caller, amount);
|
|
702
381
|
}
|
|
703
382
|
\`\`\`
|
|
383
|
+
|
|
384
|
+
## Privacy Model
|
|
385
|
+
|
|
386
|
+
| Operation | Privacy |
|
|
387
|
+
|-----------|---------|
|
|
388
|
+
| Balance | Shielded (private) |
|
|
389
|
+
| Transfer amount | Shielded |
|
|
390
|
+
| Sender | Shielded |
|
|
391
|
+
| Recipient | Shielded |
|
|
392
|
+
| Transaction occurred | Public (proof exists) |
|
|
393
|
+
|
|
394
|
+
## Important Notes
|
|
395
|
+
|
|
396
|
+
1. **No approval mechanism** - Unlike ERC20, transfers are direct
|
|
397
|
+
2. **Balances are commitments** - Not stored as plain values
|
|
398
|
+
3. **Privacy by default** - Explicit disclosure required to reveal
|
|
704
399
|
`,
|
|
705
400
|
"midnight://docs/openzeppelin/access": `# OpenZeppelin Access Control
|
|
706
401
|
|
|
707
|
-
|
|
402
|
+
Patterns for controlling who can call contract functions.
|
|
708
403
|
|
|
709
404
|
## Ownable
|
|
710
405
|
|
|
711
406
|
Simple single-owner access control.
|
|
712
407
|
|
|
713
408
|
\`\`\`compact
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/access/Ownable" prefix Ownable_;
|
|
409
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
717
410
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
) {
|
|
721
|
-
Ownable_initialize(_owner);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
// Only owner can call this
|
|
725
|
-
export circuit adminFunction(): [] {
|
|
726
|
-
Ownable_assertOnlyOwner();
|
|
727
|
-
// ... admin logic
|
|
411
|
+
ledger {
|
|
412
|
+
...Ownable.ledger;
|
|
728
413
|
}
|
|
729
414
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
newOwner: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
733
|
-
): [] {
|
|
734
|
-
Ownable_assertOnlyOwner();
|
|
735
|
-
Ownable_transferOwnership(newOwner);
|
|
415
|
+
export circuit initialize(owner: Address): Void {
|
|
416
|
+
Ownable.initialize(owner);
|
|
736
417
|
}
|
|
737
418
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
Ownable_renounceOwnership();
|
|
419
|
+
export circuit adminFunction(): Void {
|
|
420
|
+
Ownable.assertOnlyOwner();
|
|
421
|
+
// Only owner can execute this
|
|
742
422
|
}
|
|
743
423
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
424
|
+
export circuit transferOwnership(newOwner: Address): Void {
|
|
425
|
+
Ownable.assertOnlyOwner();
|
|
426
|
+
Ownable.transferOwnership(newOwner);
|
|
747
427
|
}
|
|
748
428
|
\`\`\`
|
|
749
429
|
|
|
750
|
-
##
|
|
430
|
+
## Role-Based Access Control
|
|
751
431
|
|
|
752
|
-
For
|
|
432
|
+
For more complex permission systems.
|
|
753
433
|
|
|
754
434
|
\`\`\`compact
|
|
755
|
-
|
|
435
|
+
include "@openzeppelin/compact-contracts/access/AccessControl.compact";
|
|
756
436
|
|
|
757
|
-
|
|
437
|
+
ledger {
|
|
438
|
+
...AccessControl.ledger;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const ADMIN_ROLE: Bytes<32> = keccak256("ADMIN_ROLE");
|
|
758
442
|
const MINTER_ROLE: Bytes<32> = keccak256("MINTER_ROLE");
|
|
759
|
-
const PAUSER_ROLE: Bytes<32> = keccak256("PAUSER_ROLE");
|
|
760
443
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
AC__grantRole(PAUSER_ROLE, _admin);
|
|
444
|
+
export circuit initialize(admin: Address): Void {
|
|
445
|
+
AccessControl.grantRole(ADMIN_ROLE, admin);
|
|
446
|
+
AccessControl.setRoleAdmin(MINTER_ROLE, ADMIN_ROLE);
|
|
765
447
|
}
|
|
766
448
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
// ... mint logic
|
|
449
|
+
export circuit mint(to: Address, amount: Field): Void {
|
|
450
|
+
AccessControl.assertHasRole(MINTER_ROLE);
|
|
451
|
+
// Mint tokens
|
|
771
452
|
}
|
|
772
453
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
// ... pause logic
|
|
454
|
+
export circuit grantMinterRole(account: Address): Void {
|
|
455
|
+
AccessControl.assertHasRole(ADMIN_ROLE);
|
|
456
|
+
AccessControl.grantRole(MINTER_ROLE, account);
|
|
777
457
|
}
|
|
458
|
+
\`\`\`
|
|
778
459
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
460
|
+
## Combining Patterns
|
|
461
|
+
|
|
462
|
+
\`\`\`compact
|
|
463
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
464
|
+
include "@openzeppelin/compact-contracts/security/Pausable.compact";
|
|
465
|
+
|
|
466
|
+
ledger {
|
|
467
|
+
...Ownable.ledger;
|
|
468
|
+
...Pausable.ledger;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export circuit criticalFunction(): Void {
|
|
472
|
+
Ownable.assertOnlyOwner();
|
|
473
|
+
Pausable.assertNotPaused();
|
|
474
|
+
// Execute critical logic
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export circuit pause(): Void {
|
|
478
|
+
Ownable.assertOnlyOwner();
|
|
479
|
+
Pausable.pause();
|
|
786
480
|
}
|
|
787
481
|
\`\`\`
|
|
788
482
|
`,
|
|
789
483
|
"midnight://docs/openzeppelin/security": `# OpenZeppelin Security Patterns
|
|
790
484
|
|
|
791
|
-
Security
|
|
485
|
+
Security utilities for Compact contracts.
|
|
792
486
|
|
|
793
487
|
## Pausable
|
|
794
488
|
|
|
795
|
-
Emergency stop mechanism for
|
|
489
|
+
Emergency stop mechanism for contracts.
|
|
796
490
|
|
|
797
491
|
\`\`\`compact
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/security/Pausable" prefix Pausable_;
|
|
801
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/access/Ownable" prefix Ownable_;
|
|
802
|
-
|
|
803
|
-
constructor(_owner: Either<ZswapCoinPublicKey, ContractAddress>) {
|
|
804
|
-
Ownable_initialize(_owner);
|
|
805
|
-
// Contract starts unpaused
|
|
806
|
-
}
|
|
492
|
+
include "@openzeppelin/compact-contracts/security/Pausable.compact";
|
|
493
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
807
494
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
amount: Uint<128>,
|
|
812
|
-
): Boolean {
|
|
813
|
-
Pausable_assertNotPaused();
|
|
814
|
-
// ... transfer logic
|
|
495
|
+
ledger {
|
|
496
|
+
...Pausable.ledger;
|
|
497
|
+
...Ownable.ledger;
|
|
815
498
|
}
|
|
816
499
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
Pausable__pause();
|
|
500
|
+
export circuit transfer(to: Address, amount: Field): Void {
|
|
501
|
+
Pausable.assertNotPaused();
|
|
502
|
+
// Transfer logic
|
|
821
503
|
}
|
|
822
504
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
Pausable__unpause();
|
|
505
|
+
export circuit pause(): Void {
|
|
506
|
+
Ownable.assertOnlyOwner();
|
|
507
|
+
Pausable.pause();
|
|
827
508
|
}
|
|
828
509
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
510
|
+
export circuit unpause(): Void {
|
|
511
|
+
Ownable.assertOnlyOwner();
|
|
512
|
+
Pausable.unpause();
|
|
832
513
|
}
|
|
833
514
|
\`\`\`
|
|
834
515
|
|
|
835
|
-
##
|
|
516
|
+
## When to Use Pausable
|
|
836
517
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/access/Ownable" prefix Ownable_;
|
|
842
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/security/Pausable" prefix Pausable_;
|
|
843
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/token/FungibleToken" prefix FungibleToken_;
|
|
844
|
-
|
|
845
|
-
constructor(
|
|
846
|
-
_name: Opaque<"string">,
|
|
847
|
-
_symbol: Opaque<"string">,
|
|
848
|
-
_decimals: Uint<8>,
|
|
849
|
-
_initialSupply: Uint<128>,
|
|
850
|
-
_owner: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
851
|
-
) {
|
|
852
|
-
Ownable_initialize(_owner);
|
|
853
|
-
FungibleToken_initialize(_name, _symbol, _decimals);
|
|
854
|
-
FungibleToken__mint(_owner, _initialSupply);
|
|
855
|
-
}
|
|
518
|
+
- Token contracts handling real value
|
|
519
|
+
- DeFi protocols with liquidity
|
|
520
|
+
- Contracts with upgrade mechanisms
|
|
521
|
+
- Any contract where bugs could cause fund loss
|
|
856
522
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
return FungibleToken_transfer(to, value);
|
|
523
|
+
## Implementation Details
|
|
524
|
+
|
|
525
|
+
\`\`\`compact
|
|
526
|
+
// Pausable module internals (simplified)
|
|
527
|
+
ledger {
|
|
528
|
+
paused: Boolean;
|
|
864
529
|
}
|
|
865
530
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
to: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
869
|
-
amount: Uint<128>,
|
|
870
|
-
): [] {
|
|
871
|
-
Ownable_assertOnlyOwner();
|
|
872
|
-
Pausable_assertNotPaused();
|
|
873
|
-
FungibleToken__mint(to, amount);
|
|
531
|
+
circuit Pausable_assertNotPaused(): Void {
|
|
532
|
+
assert(!ledger.paused, "Contract is paused");
|
|
874
533
|
}
|
|
875
534
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
Ownable_assertOnlyOwner();
|
|
879
|
-
Pausable__pause();
|
|
535
|
+
circuit Pausable_pause(): Void {
|
|
536
|
+
ledger.paused = true;
|
|
880
537
|
}
|
|
881
538
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
Pausable__unpause();
|
|
539
|
+
circuit Pausable_unpause(): Void {
|
|
540
|
+
ledger.paused = false;
|
|
885
541
|
}
|
|
886
542
|
\`\`\`
|
|
887
543
|
|
|
@@ -893,281 +549,184 @@ export circuit unpause(): [] {
|
|
|
893
549
|
4. **Document pause conditions** for users
|
|
894
550
|
5. **Consider timelock** for unpause in high-value contracts
|
|
895
551
|
`,
|
|
896
|
-
"midnight://docs/tokenomics": `# Midnight Tokenomics
|
|
897
|
-
|
|
898
|
-
## Overview
|
|
899
|
-
|
|
900
|
-
Midnight introduces a novel dual-component tokenomics system with NIGHT (utility token) and DUST (transaction resource), designed for operational predictability, privacy, and cross-chain cooperation.
|
|
901
|
-
|
|
902
|
-
## Core Pillars
|
|
903
|
-
|
|
904
|
-
1. **Operational Predictability**: NIGHT generates DUST continuously, enabling transactions without direct token expenditure
|
|
905
|
-
2. **Rational Privacy**: DUST is shielded - transactions don't leave metadata trails
|
|
906
|
-
3. **Cooperative Tokenomics**: Multi-chain architecture enables cross-chain value creation
|
|
907
|
-
4. **Fair Distribution**: Free, multi-phase token distribution (Glacier Drop)
|
|
908
|
-
|
|
909
|
-
---
|
|
910
|
-
|
|
911
|
-
## The NIGHT Token
|
|
912
|
-
|
|
913
|
-
NIGHT is Midnight's native utility token. One NIGHT = 1,000,000 STARs.
|
|
914
|
-
|
|
915
|
-
### Key Properties
|
|
916
|
-
|
|
917
|
-
- **Unshielded**: NIGHT transactions are publicly visible on-chain
|
|
918
|
-
- **Transferable**: Can be freely transferred, listed on exchanges, bridged across networks
|
|
919
|
-
- **Total Supply**: 24 billion NIGHT tokens
|
|
920
|
-
- **Non-expendable**: Not consumed to execute transactions
|
|
921
|
-
- **Disinflationary**: Circulating supply expansion slows over time
|
|
922
|
-
- **Multi-chain Native**: Exists natively on both Cardano (as Native Asset) and Midnight
|
|
552
|
+
"midnight://docs/tokenomics": `# Midnight Tokenomics Summary
|
|
923
553
|
|
|
924
|
-
|
|
554
|
+
A curated summary of the Midnight Tokenomics Whitepaper (June 2025).
|
|
925
555
|
|
|
926
|
-
-
|
|
927
|
-
- **Midnight Block Producers (MBPs)**: Validate blocks, receive rewards
|
|
928
|
-
- **Midnight Foundation**: Long-term ecosystem development
|
|
929
|
-
- **On-chain Treasury**: Protocol-managed fund for ecosystem growth
|
|
930
|
-
- **Reserve**: Protocol-managed pool for block production rewards
|
|
556
|
+
## Dual-Token Model
|
|
931
557
|
|
|
932
|
-
|
|
558
|
+
Midnight uses two components: **NIGHT** (token) and **DUST** (resource).
|
|
933
559
|
|
|
934
|
-
|
|
935
|
-
- **
|
|
936
|
-
- **
|
|
560
|
+
### NIGHT Token
|
|
561
|
+
- **Supply**: 24 billion (fixed)
|
|
562
|
+
- **Subunit**: 1 NIGHT = 1,000,000 STARs
|
|
563
|
+
- **Visibility**: Unshielded (public)
|
|
564
|
+
- **Function**: Generates DUST, governance, block rewards
|
|
565
|
+
- **Multi-chain**: Native on both Cardano and Midnight
|
|
937
566
|
|
|
938
|
-
|
|
567
|
+
### DUST Resource
|
|
568
|
+
- **Type**: Shielded, non-transferable
|
|
569
|
+
- **Function**: Pay transaction fees
|
|
570
|
+
- **Generation**: Continuously from NIGHT holdings
|
|
571
|
+
- **Decay**: When disassociated from NIGHT
|
|
572
|
+
- **Privacy**: Transactions don't leak metadata
|
|
939
573
|
|
|
940
|
-
|
|
574
|
+
## Key Insight: NIGHT Generates DUST
|
|
941
575
|
|
|
942
576
|
\`\`\`
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
Where:
|
|
946
|
-
- C = Cardano, M = Midnight
|
|
947
|
-
- R = Reserve, L = Locked, U = Unlocked
|
|
948
|
-
- S = Total Supply
|
|
949
|
-
\`\`\`
|
|
950
|
-
|
|
951
|
-
---
|
|
952
|
-
|
|
953
|
-
## The DUST Resource
|
|
954
|
-
|
|
955
|
-
DUST is the shielded, renewable resource for transaction fees.
|
|
956
|
-
|
|
957
|
-
### Key Properties
|
|
958
|
-
|
|
959
|
-
- **Shielded**: Transactions don't expose wallet addresses or details
|
|
960
|
-
- **Consumable**: Burned when used (not recycled)
|
|
961
|
-
- **Renewable**: Continuously generated by NIGHT holdings
|
|
962
|
-
- **Decaying**: Balance decays when disassociated from generating NIGHT
|
|
963
|
-
- **Non-transferable**: Cannot be bought, sold, or transferred between addresses
|
|
964
|
-
- **MEV-resistant**: Shielding prevents attackers from identifying victims
|
|
965
|
-
|
|
966
|
-
### DUST Mechanics
|
|
967
|
-
|
|
968
|
-
**Generation**:
|
|
969
|
-
1. NIGHT holder designates a DUST recipient address
|
|
970
|
-
2. DUST accumulates linearly over time up to a cap
|
|
971
|
-
3. Cap is proportional to associated NIGHT balance
|
|
972
|
-
|
|
973
|
-
**DUST Cap**: Maximum DUST = f(associated NIGHT balance)
|
|
974
|
-
|
|
975
|
-
**Usage**:
|
|
976
|
-
- DUST is consumed/burned when used
|
|
977
|
-
- No DUST is collected by block producers
|
|
978
|
-
- Generation resumes after use if below cap
|
|
979
|
-
|
|
980
|
-
**Decay**:
|
|
981
|
-
- Severing NIGHT association causes linear decay
|
|
982
|
-
- Prevents double-spending through cap enforcement
|
|
983
|
-
|
|
984
|
-
### DUST Beneficiaries
|
|
985
|
-
|
|
986
|
-
1. **NIGHT Holders**: Generate and use their own DUST
|
|
987
|
-
2. **DUST Recipients**: Receive DUST generation from NIGHT holders
|
|
988
|
-
3. **DUST Sponsees**: Transactions paid by a DUST holder (enables tokenless UX)
|
|
989
|
-
|
|
990
|
-
---
|
|
991
|
-
|
|
992
|
-
## Transaction Fees
|
|
993
|
-
|
|
994
|
-
### Components
|
|
995
|
-
|
|
996
|
-
\`\`\`
|
|
997
|
-
TxFee = CongestionRate × TxWeight + MinFee
|
|
998
|
-
\`\`\`
|
|
999
|
-
|
|
1000
|
-
- **Minimum Fee**: Fixed fee preventing DDoS attacks
|
|
1001
|
-
- **Congestion Rate**: Dynamic multiplier based on network demand
|
|
1002
|
-
- **Transaction Weight**: Based on computational resources (initially storage in KB)
|
|
1003
|
-
|
|
1004
|
-
### Block Utilization Target: 50%
|
|
1005
|
-
|
|
1006
|
-
- Below 50%: Fees decrease to stimulate activity
|
|
1007
|
-
- Above 50%: Fees increase to manage congestion
|
|
1008
|
-
- Acts as automatic stabilizer for network efficiency
|
|
1009
|
-
|
|
1010
|
-
---
|
|
1011
|
-
|
|
1012
|
-
## Block Production & Rewards
|
|
1013
|
-
|
|
1014
|
-
### At Launch
|
|
1015
|
-
|
|
1016
|
-
- Federated block production by trusted permissioned nodes
|
|
1017
|
-
- Initial producers don't receive rewards
|
|
1018
|
-
- Progressive decentralization planned
|
|
1019
|
-
|
|
1020
|
-
### Moving to Permissionless
|
|
1021
|
-
|
|
1022
|
-
- Cardano SPOs can become Midnight Block Producers
|
|
1023
|
-
- Selection proportional to delegated ADA stake
|
|
1024
|
-
- Dual-network participation doesn't affect Cardano rewards
|
|
1025
|
-
|
|
1026
|
-
### Block Reward Formula
|
|
1027
|
-
|
|
1028
|
-
**Base Distribution Rate (R)**:
|
|
1029
|
-
\`\`\`
|
|
1030
|
-
R = π(1 - B - T) / (B × γ)
|
|
1031
|
-
|
|
1032
|
-
Where:
|
|
1033
|
-
- π = Initial annual inflation rate (~3.14%)
|
|
1034
|
-
- B = Reserve allocation percentage
|
|
1035
|
-
- T = Treasury allocation percentage
|
|
1036
|
-
- γ = Blocks per year
|
|
1037
|
-
\`\`\`
|
|
1038
|
-
|
|
1039
|
-
**Base Reward**:
|
|
1040
|
-
\`\`\`
|
|
1041
|
-
Nb = Bo × R
|
|
1042
|
-
|
|
1043
|
-
Where Bo = Outstanding tokens in Reserve
|
|
577
|
+
Hold NIGHT → Generates DUST → Pay for transactions
|
|
578
|
+
(continuous) (consumed on use)
|
|
1044
579
|
\`\`\`
|
|
1045
580
|
|
|
1046
|
-
|
|
581
|
+
This means: **Hold NIGHT, transact "for free"** (no recurring token spend)
|
|
1047
582
|
|
|
1048
|
-
|
|
583
|
+
## Block Rewards
|
|
1049
584
|
|
|
585
|
+
**Formula**:
|
|
1050
586
|
\`\`\`
|
|
1051
|
-
Actual Reward
|
|
1052
|
-
Treasury Share (Nt) = Nb - Na
|
|
587
|
+
Actual Reward = Base Reward × [S + (1-S) × U]
|
|
1053
588
|
|
|
1054
589
|
Where:
|
|
1055
590
|
- S = Subsidy rate (95% at launch)
|
|
1056
|
-
- U = Block utilization
|
|
591
|
+
- U = Block utilization (target: 50%)
|
|
1057
592
|
\`\`\`
|
|
1058
593
|
|
|
1059
|
-
-
|
|
1060
|
-
-
|
|
1061
|
-
-
|
|
1062
|
-
|
|
1063
|
-
---
|
|
594
|
+
- Full blocks: Producer gets 100% of base reward
|
|
595
|
+
- Empty blocks: Producer gets only subsidy (95%)
|
|
596
|
+
- Remainder goes to Treasury
|
|
1064
597
|
|
|
1065
598
|
## Token Distribution
|
|
1066
599
|
|
|
1067
|
-
### Design Principles
|
|
1068
|
-
|
|
1069
|
-
- **Broad**: No single party dominates
|
|
1070
|
-
- **Inclusive**: Open to participants beyond crypto
|
|
1071
|
-
- **Free**: Allocated at no cost
|
|
1072
|
-
- **Transparent**: Open-source audited smart contracts
|
|
1073
|
-
|
|
1074
600
|
### Phase 1: Glacier Drop (60 days)
|
|
601
|
+
- Free allocation to crypto holders
|
|
602
|
+
- 50% to Cardano, 20% to Bitcoin, 30% to others
|
|
603
|
+
- Minimum $100 USD equivalent required
|
|
1075
604
|
|
|
1076
|
-
|
|
1077
|
-
-
|
|
1078
|
-
-
|
|
1079
|
-
-
|
|
1080
|
-
|
|
1081
|
-
**Eligibility**:
|
|
1082
|
-
- Minimum $100 USD equivalent in native tokens at snapshot
|
|
1083
|
-
- Address not on OFAC sanctions list
|
|
1084
|
-
- Random historical snapshot to prevent gaming
|
|
1085
|
-
|
|
1086
|
-
**Mechanics**:
|
|
1087
|
-
1. Sign message proving address ownership
|
|
1088
|
-
2. Provide unused Cardano address for redemption
|
|
1089
|
-
3. Tokens initially frozen, thaw during redemption period
|
|
1090
|
-
|
|
1091
|
-
### Phase 2: Scavenger Mine (30 days)
|
|
1092
|
-
|
|
1093
|
-
- Process unclaimed Glacier Drop tokens
|
|
1094
|
-
- Computational puzzles accessible to general public
|
|
1095
|
-
- Daily allocation over 30 one-day slots
|
|
1096
|
-
|
|
1097
|
-
**Apportionment of Unclaimed Tokens**:
|
|
1098
|
-
- ~35% → Midnight Foundation
|
|
1099
|
-
- ~30% → Reserve (block rewards)
|
|
1100
|
-
- ~10% → Midnight TGE (partnerships/liquidity)
|
|
1101
|
-
- ~5% → On-chain Treasury
|
|
1102
|
-
- Rest → Scavenger Mine participants + Lost-and-Found
|
|
605
|
+
### Phase 2: Scavenger Mine (30 days)
|
|
606
|
+
- Computational puzzles (accessible to public)
|
|
607
|
+
- Claims unclaimed Glacier Drop tokens
|
|
608
|
+
- Seeds network constituents
|
|
1103
609
|
|
|
1104
610
|
### Phase 3: Lost-and-Found (4 years)
|
|
611
|
+
- Second chance for Glacier Drop eligible
|
|
612
|
+
- Fractional allocation
|
|
1105
613
|
|
|
1106
|
-
|
|
1107
|
-
- Fractional allocation of original entitlement
|
|
1108
|
-
- After 4 years, unclaimed tokens go to Treasury
|
|
1109
|
-
|
|
1110
|
-
### Redemption Period (450 days)
|
|
1111
|
-
|
|
1112
|
-
**Thawing Schedule**:
|
|
1113
|
-
- Random start day (1-90 days after genesis)
|
|
1114
|
-
- 25% unlock at start, then every 90 days
|
|
1115
|
-
- Total: 4 unlocks over 360 days
|
|
1116
|
-
|
|
1117
|
-
---
|
|
1118
|
-
|
|
1119
|
-
## Cooperative Tokenomics
|
|
614
|
+
## Key Differentiators
|
|
1120
615
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
-
|
|
1127
|
-
- Broker-managed leasing
|
|
1128
|
-
- Babel Station (DUST filling station using ZSwap)
|
|
616
|
+
1. **No token spend for transactions** - DUST is renewable
|
|
617
|
+
2. **MEV resistant** - Shielded transactions
|
|
618
|
+
3. **Cross-chain native** - Same token on Cardano + Midnight
|
|
619
|
+
4. **Fair distribution** - Free, multi-phase, broad eligibility
|
|
620
|
+
`,
|
|
621
|
+
"midnight://docs/wallet-integration": `# Midnight Wallet Integration Guide
|
|
1129
622
|
|
|
1130
|
-
|
|
1131
|
-
- Ledger-native capacity leasing
|
|
1132
|
-
- On-chain capacity exchange
|
|
623
|
+
A guide for integrating Midnight Lace wallet into your DApp.
|
|
1133
624
|
|
|
1134
|
-
|
|
625
|
+
## Browser Detection
|
|
1135
626
|
|
|
1136
|
-
|
|
627
|
+
\`\`\`typescript
|
|
628
|
+
declare global {
|
|
629
|
+
interface Window {
|
|
630
|
+
midnight?: {
|
|
631
|
+
mnLace?: MidnightProvider;
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
}
|
|
1137
635
|
|
|
1138
|
-
|
|
636
|
+
function isWalletAvailable(): boolean {
|
|
637
|
+
return typeof window !== 'undefined'
|
|
638
|
+
&& window.midnight?.mnLace !== undefined;
|
|
639
|
+
}
|
|
640
|
+
\`\`\`
|
|
1139
641
|
|
|
1140
|
-
|
|
642
|
+
## DApp Connector API
|
|
1141
643
|
|
|
1142
|
-
|
|
644
|
+
\`\`\`typescript
|
|
645
|
+
interface DAppConnectorAPI {
|
|
646
|
+
enable(): Promise<MidnightAPI>;
|
|
647
|
+
isEnabled(): Promise<boolean>;
|
|
648
|
+
apiVersion(): string;
|
|
649
|
+
name(): string;
|
|
650
|
+
icon(): string;
|
|
651
|
+
}
|
|
1143
652
|
|
|
1144
|
-
|
|
653
|
+
async function connectWallet(): Promise<MidnightAPI> {
|
|
654
|
+
if (!window.midnight?.mnLace) {
|
|
655
|
+
throw new Error('Midnight Lace wallet not found');
|
|
656
|
+
}
|
|
657
|
+
return await window.midnight.mnLace.enable();
|
|
658
|
+
}
|
|
659
|
+
\`\`\`
|
|
1145
660
|
|
|
1146
|
-
|
|
1147
|
-
- Multisig mechanism for protocol updates
|
|
1148
|
-
- Handles: parameter updates, protocol upgrades, hard forks
|
|
661
|
+
## MidnightAPI Interface
|
|
1149
662
|
|
|
1150
|
-
|
|
663
|
+
\`\`\`typescript
|
|
664
|
+
interface MidnightAPI {
|
|
665
|
+
getUsedAddresses(): Promise<string[]>;
|
|
666
|
+
getBalance(): Promise<Balance>;
|
|
667
|
+
signTx(tx: Transaction): Promise<SignedTransaction>;
|
|
668
|
+
submitTx(signedTx: SignedTransaction): Promise<TxHash>;
|
|
669
|
+
signData(address: string, payload: string): Promise<Signature>;
|
|
670
|
+
}
|
|
671
|
+
\`\`\`
|
|
1151
672
|
|
|
1152
|
-
|
|
1153
|
-
- Proposal submission and voting
|
|
1154
|
-
- Treasury access for approved proposals
|
|
1155
|
-
- Automated protocol updates
|
|
673
|
+
## React Hook
|
|
1156
674
|
|
|
1157
|
-
|
|
675
|
+
\`\`\`typescript
|
|
676
|
+
export function useWallet() {
|
|
677
|
+
const [state, setState] = useState({
|
|
678
|
+
isConnected: false,
|
|
679
|
+
address: null as string | null,
|
|
680
|
+
isLoading: false,
|
|
681
|
+
error: null as string | null,
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
const connect = useCallback(async () => {
|
|
685
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
686
|
+
try {
|
|
687
|
+
if (!window.midnight?.mnLace) {
|
|
688
|
+
throw new Error('Please install Midnight Lace wallet');
|
|
689
|
+
}
|
|
690
|
+
const api = await window.midnight.mnLace.enable();
|
|
691
|
+
const addresses = await api.getUsedAddresses();
|
|
692
|
+
setState({
|
|
693
|
+
isConnected: true,
|
|
694
|
+
address: addresses[0] || null,
|
|
695
|
+
isLoading: false,
|
|
696
|
+
error: null,
|
|
697
|
+
});
|
|
698
|
+
return api;
|
|
699
|
+
} catch (error) {
|
|
700
|
+
setState(prev => ({
|
|
701
|
+
...prev,
|
|
702
|
+
isLoading: false,
|
|
703
|
+
error: error instanceof Error ? error.message : 'Failed',
|
|
704
|
+
}));
|
|
705
|
+
throw error;
|
|
706
|
+
}
|
|
707
|
+
}, []);
|
|
708
|
+
|
|
709
|
+
return { ...state, connect };
|
|
710
|
+
}
|
|
711
|
+
\`\`\`
|
|
712
|
+
|
|
713
|
+
## Connection Flow
|
|
714
|
+
|
|
715
|
+
\`\`\`
|
|
716
|
+
1. User clicks "Connect Wallet"
|
|
717
|
+
2. DApp calls window.midnight.mnLace.enable()
|
|
718
|
+
3. Wallet popup asks user to approve
|
|
719
|
+
4. User approves → DApp receives MidnightAPI
|
|
720
|
+
5. DApp can now interact with wallet
|
|
721
|
+
\`\`\`
|
|
1158
722
|
|
|
1159
|
-
##
|
|
723
|
+
## Best Practices
|
|
1160
724
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
- **Treasury**: Protocol-managed fund for ecosystem growth
|
|
1167
|
-
- **Glacier Drop**: Initial free token distribution
|
|
1168
|
-
- **Scavenger Mine**: Computational task-based distribution
|
|
1169
|
-
- **ZSwap**: Atomic asset swap mechanism for privacy
|
|
1170
|
-
- **Babel Station**: Service enabling tokenless transactions
|
|
725
|
+
1. Always check wallet availability first
|
|
726
|
+
2. Handle user rejection gracefully
|
|
727
|
+
3. Store connection state in context
|
|
728
|
+
4. Provide clear loading/error feedback
|
|
729
|
+
5. Test with Midnight Lace extension
|
|
1171
730
|
`,
|
|
1172
731
|
};
|
|
1173
732
|
//# sourceMappingURL=docs-content.js.map
|