midnight-mcp 0.1.2 → 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,820 +86,647 @@ 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
|
|
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
|
|
93
101
|
|
|
94
|
-
|
|
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
|
+
## State Management
|
|
102
103
|
|
|
103
|
-
|
|
104
|
+
### Public State
|
|
105
|
+
\`\`\`compact
|
|
106
|
+
ledger {
|
|
107
|
+
publicCounter: Counter;
|
|
108
|
+
publicMap: Map<Field, Field>;
|
|
109
|
+
}
|
|
110
|
+
\`\`\`
|
|
104
111
|
|
|
112
|
+
### Private State
|
|
105
113
|
\`\`\`compact
|
|
106
114
|
ledger {
|
|
107
|
-
publicData: Field; // Visible on-chain
|
|
108
115
|
@private
|
|
109
|
-
|
|
116
|
+
secretBalance: Field;
|
|
117
|
+
|
|
118
|
+
@private
|
|
119
|
+
hiddenVotes: Map<Address, Field>;
|
|
120
|
+
}
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
## Common Patterns
|
|
124
|
+
|
|
125
|
+
### Access Control
|
|
126
|
+
\`\`\`compact
|
|
127
|
+
ledger {
|
|
128
|
+
owner: Opaque<"address">;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
witness getCaller(): Opaque<"address"> {
|
|
132
|
+
return context.caller;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export circuit ownerOnly(): Void {
|
|
136
|
+
assert(getCaller() == ledger.owner, "Not owner");
|
|
110
137
|
}
|
|
111
138
|
\`\`\`
|
|
139
|
+
|
|
140
|
+
### Disclosure
|
|
141
|
+
\`\`\`compact
|
|
142
|
+
export circuit revealSecret(): Field {
|
|
143
|
+
const secret = getPrivateData();
|
|
144
|
+
return disclose(secret); // Makes private data public
|
|
145
|
+
}
|
|
146
|
+
\`\`\`
|
|
147
|
+
|
|
148
|
+
### Assertions
|
|
149
|
+
\`\`\`compact
|
|
150
|
+
assert(condition); // Basic assertion
|
|
151
|
+
assert(condition, "Error message"); // With message
|
|
152
|
+
\`\`\`
|
|
112
153
|
`,
|
|
113
|
-
"midnight://docs/sdk-api": `# Midnight TypeScript SDK
|
|
154
|
+
"midnight://docs/sdk-api": `# Midnight TypeScript SDK Quick Reference
|
|
114
155
|
|
|
115
156
|
## Installation
|
|
116
157
|
|
|
117
158
|
\`\`\`bash
|
|
118
|
-
npm install @midnight-ntwrk/midnight-js-contracts
|
|
159
|
+
npm install @midnight-ntwrk/midnight-js-contracts
|
|
119
160
|
\`\`\`
|
|
120
161
|
|
|
121
|
-
## Core
|
|
162
|
+
## Core Types
|
|
122
163
|
|
|
123
|
-
###
|
|
124
|
-
Contract interaction layer for deploying and calling Midnight smart contracts.
|
|
164
|
+
### Contract Deployment
|
|
125
165
|
|
|
126
166
|
\`\`\`typescript
|
|
127
|
-
import {
|
|
167
|
+
import { deployContract, ContractDeployment } from '@midnight-ntwrk/midnight-js-contracts';
|
|
128
168
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
);
|
|
169
|
+
const deployment: ContractDeployment = await deployContract({
|
|
170
|
+
contract: compiledContract,
|
|
171
|
+
privateState: initialPrivateState,
|
|
172
|
+
args: constructorArgs,
|
|
173
|
+
});
|
|
135
174
|
|
|
136
|
-
|
|
137
|
-
const result = await deployed.call('increment', { amount: 1n });
|
|
175
|
+
const { contractAddress, initialState } = deployment;
|
|
138
176
|
\`\`\`
|
|
139
177
|
|
|
140
|
-
###
|
|
141
|
-
Shared types and interfaces for the SDK.
|
|
178
|
+
### Contract Interaction
|
|
142
179
|
|
|
143
180
|
\`\`\`typescript
|
|
144
|
-
import
|
|
145
|
-
Address,
|
|
146
|
-
Transaction,
|
|
147
|
-
Proof,
|
|
148
|
-
ContractState
|
|
149
|
-
} from '@midnight-ntwrk/midnight-js-types';
|
|
150
|
-
\`\`\`
|
|
181
|
+
import { callContract } from '@midnight-ntwrk/midnight-js-contracts';
|
|
151
182
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
183
|
+
// Call a circuit
|
|
184
|
+
const result = await callContract({
|
|
185
|
+
contractAddress,
|
|
186
|
+
circuitName: 'increment',
|
|
187
|
+
args: [amount],
|
|
188
|
+
privateState: currentPrivateState,
|
|
189
|
+
});
|
|
157
190
|
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
const balance = await wallet.getBalance();
|
|
191
|
+
// Result contains new state and return value
|
|
192
|
+
const { newPrivateState, returnValue, proof } = result;
|
|
161
193
|
\`\`\`
|
|
162
194
|
|
|
163
|
-
|
|
195
|
+
### Providers
|
|
164
196
|
|
|
165
|
-
### Contract Deployment
|
|
166
197
|
\`\`\`typescript
|
|
167
|
-
import {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
\`\`\`
|
|
198
|
+
import {
|
|
199
|
+
MidnightProvider,
|
|
200
|
+
createMidnightProvider
|
|
201
|
+
} from '@midnight-ntwrk/midnight-js-contracts';
|
|
181
202
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const newValue = await contract.query('counter');
|
|
189
|
-
return newValue;
|
|
190
|
-
}
|
|
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
|
+
});
|
|
191
208
|
\`\`\`
|
|
192
209
|
|
|
193
|
-
|
|
210
|
+
## State Management
|
|
211
|
+
|
|
194
212
|
\`\`\`typescript
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return publicState;
|
|
213
|
+
interface ContractState<T> {
|
|
214
|
+
publicState: PublicState;
|
|
215
|
+
privateState: T;
|
|
199
216
|
}
|
|
200
|
-
\`\`\`
|
|
201
|
-
`,
|
|
202
|
-
"midnight://docs/concepts/zero-knowledge": `# Zero-Knowledge Proofs in Midnight
|
|
203
|
-
|
|
204
|
-
## What are Zero-Knowledge Proofs?
|
|
205
217
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
218
|
+
// Subscribe to state changes
|
|
219
|
+
provider.subscribeToContract(contractAddress, (state) => {
|
|
220
|
+
console.log('New state:', state);
|
|
221
|
+
});
|
|
222
|
+
\`\`\`
|
|
209
223
|
|
|
210
|
-
|
|
224
|
+
## Transaction Building
|
|
211
225
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
3. **Only the proof** (not the inputs) is submitted to the blockchain
|
|
215
|
-
4. **Validators verify** the proof without knowing the inputs
|
|
226
|
+
\`\`\`typescript
|
|
227
|
+
import { buildTransaction } from '@midnight-ntwrk/midnight-js-contracts';
|
|
216
228
|
|
|
217
|
-
|
|
229
|
+
const tx = await buildTransaction({
|
|
230
|
+
contractAddress,
|
|
231
|
+
circuitName: 'transfer',
|
|
232
|
+
args: [recipient, amount],
|
|
233
|
+
privateState,
|
|
234
|
+
});
|
|
218
235
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const age = currentYear - birthYear;
|
|
223
|
-
|
|
224
|
-
// Proves user is over 18 without revealing exact age
|
|
225
|
-
assert(age >= 18);
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
236
|
+
// Sign and submit
|
|
237
|
+
const signedTx = await wallet.signTransaction(tx);
|
|
238
|
+
const txHash = await provider.submitTransaction(signedTx);
|
|
228
239
|
\`\`\`
|
|
229
240
|
|
|
230
|
-
|
|
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
|
|
241
|
+
## Error Handling
|
|
238
242
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
3. **Zero-knowledge**: Nothing beyond validity is revealed
|
|
242
|
-
|
|
243
|
-
## Privacy Patterns
|
|
243
|
+
\`\`\`typescript
|
|
244
|
+
import { MidnightError, ContractError } from '@midnight-ntwrk/midnight-js-contracts';
|
|
244
245
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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);
|
|
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
|
+
}
|
|
266
254
|
}
|
|
267
255
|
\`\`\`
|
|
268
256
|
`,
|
|
269
|
-
"midnight://docs/
|
|
257
|
+
"midnight://docs/openzeppelin": `# OpenZeppelin Contracts for Compact
|
|
270
258
|
|
|
271
|
-
|
|
259
|
+
> **Official Documentation**: https://docs.openzeppelin.com/contracts-compact
|
|
260
|
+
> **GitHub Repository**: https://github.com/OpenZeppelin/compact-contracts
|
|
272
261
|
|
|
273
|
-
|
|
262
|
+
The official OpenZeppelin library for Midnight smart contracts provides battle-tested, audited implementations of common patterns.
|
|
274
263
|
|
|
275
|
-
|
|
264
|
+
## Installation
|
|
276
265
|
|
|
277
|
-
\`\`\`
|
|
278
|
-
|
|
279
|
-
totalSupply: Counter; // Public counter
|
|
280
|
-
balances: Map<Address, Field>; // Public mapping
|
|
281
|
-
}
|
|
266
|
+
\`\`\`bash
|
|
267
|
+
npm install @openzeppelin/compact-contracts
|
|
282
268
|
\`\`\`
|
|
283
269
|
|
|
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:
|
|
270
|
+
## Available Modules
|
|
292
271
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
secretKey: Bytes<32>;
|
|
297
|
-
|
|
298
|
-
@private
|
|
299
|
-
privateBalance: Field;
|
|
300
|
-
}
|
|
301
|
-
\`\`\`
|
|
272
|
+
### Token Standards
|
|
273
|
+
- **FungibleToken** - Privacy-preserving token with shielded balances
|
|
274
|
+
- **NFT** - Non-fungible tokens with optional privacy
|
|
302
275
|
|
|
303
|
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
306
|
-
-
|
|
276
|
+
### Access Control
|
|
277
|
+
- **Ownable** - Single-owner access pattern
|
|
278
|
+
- **Roles** - Role-based access control
|
|
279
|
+
- **AccessControl** - Flexible permission system
|
|
307
280
|
|
|
308
|
-
|
|
281
|
+
### Security
|
|
282
|
+
- **Pausable** - Emergency stop mechanism
|
|
283
|
+
- **ReentrancyGuard** - Prevent reentrancy attacks
|
|
309
284
|
|
|
310
|
-
|
|
285
|
+
## Usage Example
|
|
311
286
|
|
|
312
287
|
\`\`\`compact
|
|
288
|
+
include "std";
|
|
289
|
+
include "@openzeppelin/compact-contracts/token/FungibleToken.compact";
|
|
290
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
291
|
+
|
|
313
292
|
ledger {
|
|
314
|
-
//
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
// Private: only owner sees message contents
|
|
318
|
-
@private
|
|
319
|
-
messages: Map<Field, Opaque<"string">>;
|
|
293
|
+
// Inherit from OpenZeppelin contracts
|
|
294
|
+
...FungibleToken.ledger;
|
|
295
|
+
...Ownable.ledger;
|
|
320
296
|
}
|
|
321
297
|
|
|
322
|
-
export circuit
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
// Public increment
|
|
326
|
-
ledger.messageCount.increment(1);
|
|
327
|
-
|
|
328
|
-
// Private storage
|
|
329
|
-
ledger.messages.insert(id, content);
|
|
298
|
+
export circuit mint(to: Address, amount: Field): Void {
|
|
299
|
+
Ownable.assertOnlyOwner();
|
|
300
|
+
FungibleToken.mint(to, amount);
|
|
330
301
|
}
|
|
331
302
|
\`\`\`
|
|
332
303
|
|
|
333
|
-
##
|
|
334
|
-
|
|
335
|
-
### Disclose: Private → Public
|
|
336
|
-
\`\`\`compact
|
|
337
|
-
export circuit revealBalance(): Field {
|
|
338
|
-
// Reveal private balance publicly
|
|
339
|
-
return disclose(ledger.privateBalance);
|
|
340
|
-
}
|
|
341
|
-
\`\`\`
|
|
304
|
+
## Best Practices
|
|
342
305
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return commit(value);
|
|
348
|
-
}
|
|
349
|
-
\`\`\`
|
|
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
|
|
350
310
|
`,
|
|
351
|
-
"midnight://docs/
|
|
352
|
-
|
|
353
|
-
Witnesses provide off-chain data to circuits in Midnight.
|
|
311
|
+
"midnight://docs/openzeppelin/token": `# OpenZeppelin FungibleToken
|
|
354
312
|
|
|
355
|
-
|
|
313
|
+
The recommended standard for privacy-preserving tokens on Midnight.
|
|
356
314
|
|
|
357
|
-
|
|
358
|
-
- Cannot make network requests
|
|
359
|
-
- Cannot access system time
|
|
360
|
-
- Cannot read external files
|
|
361
|
-
- Must be deterministic
|
|
315
|
+
## Features
|
|
362
316
|
|
|
363
|
-
|
|
317
|
+
- Shielded balances (private by default)
|
|
318
|
+
- Optional public balance disclosure
|
|
319
|
+
- Transfer with ZK proofs
|
|
320
|
+
- Mint/burn capabilities
|
|
364
321
|
|
|
365
|
-
## Basic
|
|
322
|
+
## Basic Usage
|
|
366
323
|
|
|
367
324
|
\`\`\`compact
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
return getCurrentUnixTime();
|
|
371
|
-
}
|
|
325
|
+
include "std";
|
|
326
|
+
include "@openzeppelin/compact-contracts/token/FungibleToken.compact";
|
|
372
327
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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);
|
|
377
346
|
}
|
|
378
|
-
\`\`\`
|
|
379
347
|
|
|
380
|
-
|
|
348
|
+
// Shielded transfer
|
|
349
|
+
export circuit transfer(to: Address, amount: Field): Void {
|
|
350
|
+
FungibleToken.transfer(to, amount);
|
|
351
|
+
}
|
|
381
352
|
|
|
382
|
-
|
|
383
|
-
witness
|
|
384
|
-
|
|
385
|
-
return callPriceOracle(asset);
|
|
353
|
+
// Check balance (private)
|
|
354
|
+
witness myBalance(): Field {
|
|
355
|
+
return FungibleToken.balanceOf(context.caller);
|
|
386
356
|
}
|
|
387
357
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
// ... execute swap
|
|
358
|
+
// Reveal balance publicly (optional)
|
|
359
|
+
export circuit revealBalance(): Field {
|
|
360
|
+
return disclose(myBalance());
|
|
392
361
|
}
|
|
393
362
|
\`\`\`
|
|
394
363
|
|
|
395
|
-
##
|
|
396
|
-
|
|
397
|
-
Witnesses can access private ledger state:
|
|
364
|
+
## Minting and Burning
|
|
398
365
|
|
|
399
366
|
\`\`\`compact
|
|
367
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
368
|
+
|
|
400
369
|
ledger {
|
|
401
|
-
|
|
402
|
-
|
|
370
|
+
...FungibleToken.ledger;
|
|
371
|
+
...Ownable.ledger;
|
|
403
372
|
}
|
|
404
373
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
374
|
+
export circuit mint(to: Address, amount: Field): Void {
|
|
375
|
+
Ownable.assertOnlyOwner();
|
|
376
|
+
FungibleToken.mint(to, amount);
|
|
408
377
|
}
|
|
409
378
|
|
|
410
|
-
export circuit
|
|
411
|
-
|
|
412
|
-
return hash(data, nonce);
|
|
379
|
+
export circuit burn(amount: Field): Void {
|
|
380
|
+
FungibleToken.burn(context.caller, amount);
|
|
413
381
|
}
|
|
414
382
|
\`\`\`
|
|
415
383
|
|
|
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
|
|
384
|
+
## Privacy Model
|
|
422
385
|
|
|
423
|
-
|
|
386
|
+
| Operation | Privacy |
|
|
387
|
+
|-----------|---------|
|
|
388
|
+
| Balance | Shielded (private) |
|
|
389
|
+
| Transfer amount | Shielded |
|
|
390
|
+
| Sender | Shielded |
|
|
391
|
+
| Recipient | Shielded |
|
|
392
|
+
| Transaction occurred | Public (proof exists) |
|
|
424
393
|
|
|
425
|
-
|
|
426
|
-
- Circuit verifies witness output is used correctly
|
|
427
|
-
- But doesn't verify HOW witness computed the value
|
|
428
|
-
- Malicious witnesses can provide false data
|
|
394
|
+
## Important Notes
|
|
429
395
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
export circuit usePrice(asset: Opaque<"string">): Void {
|
|
434
|
-
const price = fetchPrice(asset);
|
|
435
|
-
|
|
436
|
-
// Validate witness data
|
|
437
|
-
assert(price > 0);
|
|
438
|
-
assert(price < MAX_REASONABLE_PRICE);
|
|
439
|
-
|
|
440
|
-
// ... use price
|
|
441
|
-
}
|
|
442
|
-
\`\`\`
|
|
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
|
|
443
399
|
`,
|
|
444
|
-
"midnight://docs/
|
|
445
|
-
|
|
446
|
-
Kachina is the cryptographic protocol underlying Midnight's privacy features.
|
|
447
|
-
|
|
448
|
-
## Overview
|
|
449
|
-
|
|
450
|
-
Kachina enables:
|
|
451
|
-
- Private smart contracts with public verifiability
|
|
452
|
-
- Composable privacy across contracts
|
|
453
|
-
- Efficient on-chain verification
|
|
400
|
+
"midnight://docs/openzeppelin/access": `# OpenZeppelin Access Control
|
|
454
401
|
|
|
455
|
-
|
|
402
|
+
Patterns for controlling who can call contract functions.
|
|
456
403
|
|
|
457
|
-
|
|
458
|
-
┌─────────────────┐ ┌─────────────────┐
|
|
459
|
-
│ User Wallet │────▶│ Compact Code │
|
|
460
|
-
└─────────────────┘ └────────┬────────┘
|
|
461
|
-
│
|
|
462
|
-
┌────────▼────────┐
|
|
463
|
-
│ ZK Circuit │
|
|
464
|
-
│ (Prover) │
|
|
465
|
-
└────────┬────────┘
|
|
466
|
-
│
|
|
467
|
-
┌────────▼────────┐
|
|
468
|
-
│ Proof │
|
|
469
|
-
└────────┬────────┘
|
|
470
|
-
│
|
|
471
|
-
┌────────▼────────┐
|
|
472
|
-
│ Midnight │
|
|
473
|
-
│ Validators │
|
|
474
|
-
└─────────────────┘
|
|
475
|
-
\`\`\`
|
|
404
|
+
## Ownable
|
|
476
405
|
|
|
477
|
-
|
|
406
|
+
Simple single-owner access control.
|
|
478
407
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
- **Private State**: Stored off-chain, encrypted
|
|
482
|
-
- **Commitments**: On-chain references to private state
|
|
408
|
+
\`\`\`compact
|
|
409
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
483
410
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
3. Transaction + proof submitted to network
|
|
488
|
-
4. Validators verify proof (not re-execute)
|
|
489
|
-
5. State updates applied
|
|
411
|
+
ledger {
|
|
412
|
+
...Ownable.ledger;
|
|
413
|
+
}
|
|
490
414
|
|
|
491
|
-
|
|
492
|
-
|
|
415
|
+
export circuit initialize(owner: Address): Void {
|
|
416
|
+
Ownable.initialize(owner);
|
|
417
|
+
}
|
|
493
418
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
// Private transfer logic
|
|
419
|
+
export circuit adminFunction(): Void {
|
|
420
|
+
Ownable.assertOnlyOwner();
|
|
421
|
+
// Only owner can execute this
|
|
498
422
|
}
|
|
499
423
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
tokenB: Address,
|
|
504
|
-
amountA: Field,
|
|
505
|
-
amountB: Field
|
|
506
|
-
): Void {
|
|
507
|
-
// Both transfers happen atomically
|
|
508
|
-
// Privacy preserved for both
|
|
424
|
+
export circuit transferOwnership(newOwner: Address): Void {
|
|
425
|
+
Ownable.assertOnlyOwner();
|
|
426
|
+
Ownable.transferOwnership(newOwner);
|
|
509
427
|
}
|
|
510
428
|
\`\`\`
|
|
511
429
|
|
|
512
|
-
##
|
|
430
|
+
## Role-Based Access Control
|
|
513
431
|
|
|
514
|
-
|
|
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
|
-
`,
|
|
519
|
-
"midnight://docs/openzeppelin": `# OpenZeppelin Contracts for Compact
|
|
432
|
+
For more complex permission systems.
|
|
520
433
|
|
|
521
|
-
|
|
522
|
-
|
|
434
|
+
\`\`\`compact
|
|
435
|
+
include "@openzeppelin/compact-contracts/access/AccessControl.compact";
|
|
523
436
|
|
|
524
|
-
|
|
437
|
+
ledger {
|
|
438
|
+
...AccessControl.ledger;
|
|
439
|
+
}
|
|
525
440
|
|
|
526
|
-
|
|
441
|
+
const ADMIN_ROLE: Bytes<32> = keccak256("ADMIN_ROLE");
|
|
442
|
+
const MINTER_ROLE: Bytes<32> = keccak256("MINTER_ROLE");
|
|
527
443
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
444
|
+
export circuit initialize(admin: Address): Void {
|
|
445
|
+
AccessControl.grantRole(ADMIN_ROLE, admin);
|
|
446
|
+
AccessControl.setRoleAdmin(MINTER_ROLE, ADMIN_ROLE);
|
|
447
|
+
}
|
|
531
448
|
|
|
532
|
-
|
|
533
|
-
|
|
449
|
+
export circuit mint(to: Address, amount: Field): Void {
|
|
450
|
+
AccessControl.assertHasRole(MINTER_ROLE);
|
|
451
|
+
// Mint tokens
|
|
452
|
+
}
|
|
534
453
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
454
|
+
export circuit grantMinterRole(account: Address): Void {
|
|
455
|
+
AccessControl.assertHasRole(ADMIN_ROLE);
|
|
456
|
+
AccessControl.grantRole(MINTER_ROLE, account);
|
|
457
|
+
}
|
|
538
458
|
\`\`\`
|
|
539
459
|
|
|
540
|
-
##
|
|
541
|
-
|
|
542
|
-
### Token
|
|
543
|
-
- **FungibleToken**: Standard token implementation with transfer, mint, burn
|
|
544
|
-
- Recommended for all token contracts on Midnight
|
|
545
|
-
|
|
546
|
-
### Access Control
|
|
547
|
-
- **Ownable**: Single owner access control
|
|
548
|
-
- **AccessControl**: Role-based access control
|
|
549
|
-
|
|
550
|
-
### Security
|
|
551
|
-
- **Pausable**: Emergency stop mechanism
|
|
552
|
-
|
|
553
|
-
## Usage Example
|
|
460
|
+
## Combining Patterns
|
|
554
461
|
|
|
555
462
|
\`\`\`compact
|
|
556
|
-
|
|
463
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
464
|
+
include "@openzeppelin/compact-contracts/security/Pausable.compact";
|
|
557
465
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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);
|
|
466
|
+
ledger {
|
|
467
|
+
...Ownable.ledger;
|
|
468
|
+
...Pausable.ledger;
|
|
582
469
|
}
|
|
583
470
|
|
|
584
|
-
export circuit
|
|
585
|
-
|
|
586
|
-
|
|
471
|
+
export circuit criticalFunction(): Void {
|
|
472
|
+
Ownable.assertOnlyOwner();
|
|
473
|
+
Pausable.assertNotPaused();
|
|
474
|
+
// Execute critical logic
|
|
587
475
|
}
|
|
588
476
|
|
|
589
|
-
export circuit
|
|
590
|
-
|
|
591
|
-
|
|
477
|
+
export circuit pause(): Void {
|
|
478
|
+
Ownable.assertOnlyOwner();
|
|
479
|
+
Pausable.pause();
|
|
592
480
|
}
|
|
593
481
|
\`\`\`
|
|
594
|
-
|
|
595
|
-
## Compilation
|
|
596
|
-
|
|
597
|
-
\`\`\`bash
|
|
598
|
-
compact compile MyContract.compact artifacts/MyContract
|
|
599
|
-
\`\`\`
|
|
600
|
-
|
|
601
|
-
## Why Use OpenZeppelin?
|
|
602
|
-
|
|
603
|
-
1. **Security Audited**: Contracts are professionally audited
|
|
604
|
-
2. **Battle-Tested**: Used in production across the ecosystem
|
|
605
|
-
3. **Official Recommendation**: Midnight's recommended library for tokens
|
|
606
|
-
4. **Modularity**: Use only what you need
|
|
607
|
-
5. **Best Practices**: Follows Compact language best practices
|
|
608
482
|
`,
|
|
609
|
-
"midnight://docs/openzeppelin/
|
|
610
|
-
|
|
611
|
-
> **This is the official and recommended token standard for Midnight.**
|
|
612
|
-
|
|
613
|
-
The FungibleToken module provides a complete implementation for fungible tokens on Midnight.
|
|
483
|
+
"midnight://docs/openzeppelin/security": `# OpenZeppelin Security Patterns
|
|
614
484
|
|
|
615
|
-
|
|
485
|
+
Security utilities for Compact contracts.
|
|
616
486
|
|
|
617
|
-
|
|
618
|
-
- Transfer with balance tracking
|
|
619
|
-
- Mint and burn operations
|
|
620
|
-
- Approval and transferFrom patterns
|
|
621
|
-
- Privacy-preserving by default
|
|
487
|
+
## Pausable
|
|
622
488
|
|
|
623
|
-
|
|
489
|
+
Emergency stop mechanism for contracts.
|
|
624
490
|
|
|
625
491
|
\`\`\`compact
|
|
626
|
-
|
|
492
|
+
include "@openzeppelin/compact-contracts/security/Pausable.compact";
|
|
493
|
+
include "@openzeppelin/compact-contracts/access/Ownable.compact";
|
|
627
494
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
_name: Opaque<"string">,
|
|
632
|
-
_symbol: Opaque<"string">,
|
|
633
|
-
_decimals: Uint<8>,
|
|
634
|
-
_recipient: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
635
|
-
_initialSupply: Uint<128>,
|
|
636
|
-
) {
|
|
637
|
-
FungibleToken_initialize(_name, _symbol, _decimals);
|
|
638
|
-
FungibleToken__mint(_recipient, _initialSupply);
|
|
495
|
+
ledger {
|
|
496
|
+
...Pausable.ledger;
|
|
497
|
+
...Ownable.ledger;
|
|
639
498
|
}
|
|
640
499
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
value: Uint<128>,
|
|
645
|
-
): Boolean {
|
|
646
|
-
return FungibleToken_transfer(to, value);
|
|
500
|
+
export circuit transfer(to: Address, amount: Field): Void {
|
|
501
|
+
Pausable.assertNotPaused();
|
|
502
|
+
// Transfer logic
|
|
647
503
|
}
|
|
648
504
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
): Uint<128> {
|
|
653
|
-
return FungibleToken_balanceOf(account);
|
|
505
|
+
export circuit pause(): Void {
|
|
506
|
+
Ownable.assertOnlyOwner();
|
|
507
|
+
Pausable.pause();
|
|
654
508
|
}
|
|
655
509
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
510
|
+
export circuit unpause(): Void {
|
|
511
|
+
Ownable.assertOnlyOwner();
|
|
512
|
+
Pausable.unpause();
|
|
659
513
|
}
|
|
660
514
|
\`\`\`
|
|
661
515
|
|
|
662
|
-
##
|
|
516
|
+
## When to Use Pausable
|
|
517
|
+
|
|
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
|
|
522
|
+
|
|
523
|
+
## Implementation Details
|
|
663
524
|
|
|
664
525
|
\`\`\`compact
|
|
665
|
-
//
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
value: Uint<128>,
|
|
669
|
-
): Boolean {
|
|
670
|
-
return FungibleToken_approve(spender, value);
|
|
526
|
+
// Pausable module internals (simplified)
|
|
527
|
+
ledger {
|
|
528
|
+
paused: Boolean;
|
|
671
529
|
}
|
|
672
530
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
from: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
676
|
-
to: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
677
|
-
value: Uint<128>,
|
|
678
|
-
): Boolean {
|
|
679
|
-
return FungibleToken_transferFrom(from, to, value);
|
|
531
|
+
circuit Pausable_assertNotPaused(): Void {
|
|
532
|
+
assert(!ledger.paused, "Contract is paused");
|
|
680
533
|
}
|
|
681
|
-
\`\`\`
|
|
682
|
-
|
|
683
|
-
## Mint and Burn (Owner-Only)
|
|
684
|
-
|
|
685
|
-
\`\`\`compact
|
|
686
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/access/Ownable" prefix Ownable_;
|
|
687
534
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
amount: Uint<128>,
|
|
691
|
-
): [] {
|
|
692
|
-
Ownable_assertOnlyOwner();
|
|
693
|
-
FungibleToken__mint(to, amount);
|
|
535
|
+
circuit Pausable_pause(): Void {
|
|
536
|
+
ledger.paused = true;
|
|
694
537
|
}
|
|
695
538
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
amount: Uint<128>,
|
|
699
|
-
): [] {
|
|
700
|
-
Ownable_assertOnlyOwner();
|
|
701
|
-
FungibleToken__burn(from, amount);
|
|
539
|
+
circuit Pausable_unpause(): Void {
|
|
540
|
+
ledger.paused = false;
|
|
702
541
|
}
|
|
703
542
|
\`\`\`
|
|
704
|
-
`,
|
|
705
|
-
"midnight://docs/openzeppelin/access": `# OpenZeppelin Access Control
|
|
706
|
-
|
|
707
|
-
Access control modules for managing permissions in your contracts.
|
|
708
543
|
|
|
709
|
-
##
|
|
544
|
+
## Best Practices
|
|
710
545
|
|
|
711
|
-
|
|
546
|
+
1. **Always use Pausable** for contracts handling value
|
|
547
|
+
2. **Combine with Ownable** for admin-only pause control
|
|
548
|
+
3. **Test pause scenarios** thoroughly
|
|
549
|
+
4. **Document pause conditions** for users
|
|
550
|
+
5. **Consider timelock** for unpause in high-value contracts
|
|
551
|
+
`,
|
|
552
|
+
"midnight://docs/tokenomics": `# Midnight Tokenomics Summary
|
|
712
553
|
|
|
713
|
-
|
|
714
|
-
pragma language_version >= 0.16.0;
|
|
554
|
+
A curated summary of the Midnight Tokenomics Whitepaper (June 2025).
|
|
715
555
|
|
|
716
|
-
|
|
556
|
+
## Dual-Token Model
|
|
717
557
|
|
|
718
|
-
|
|
719
|
-
_owner: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
720
|
-
) {
|
|
721
|
-
Ownable_initialize(_owner);
|
|
722
|
-
}
|
|
558
|
+
Midnight uses two components: **NIGHT** (token) and **DUST** (resource).
|
|
723
559
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
|
729
566
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
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
|
|
737
573
|
|
|
738
|
-
|
|
739
|
-
export circuit renounceOwnership(): [] {
|
|
740
|
-
Ownable_assertOnlyOwner();
|
|
741
|
-
Ownable_renounceOwnership();
|
|
742
|
-
}
|
|
574
|
+
## Key Insight: NIGHT Generates DUST
|
|
743
575
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
}
|
|
576
|
+
\`\`\`
|
|
577
|
+
Hold NIGHT → Generates DUST → Pay for transactions
|
|
578
|
+
(continuous) (consumed on use)
|
|
748
579
|
\`\`\`
|
|
749
580
|
|
|
750
|
-
|
|
581
|
+
This means: **Hold NIGHT, transact "for free"** (no recurring token spend)
|
|
751
582
|
|
|
752
|
-
|
|
583
|
+
## Block Rewards
|
|
753
584
|
|
|
754
|
-
|
|
755
|
-
|
|
585
|
+
**Formula**:
|
|
586
|
+
\`\`\`
|
|
587
|
+
Actual Reward = Base Reward × [S + (1-S) × U]
|
|
756
588
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
589
|
+
Where:
|
|
590
|
+
- S = Subsidy rate (95% at launch)
|
|
591
|
+
- U = Block utilization (target: 50%)
|
|
592
|
+
\`\`\`
|
|
760
593
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
AC__grantRole(PAUSER_ROLE, _admin);
|
|
765
|
-
}
|
|
594
|
+
- Full blocks: Producer gets 100% of base reward
|
|
595
|
+
- Empty blocks: Producer gets only subsidy (95%)
|
|
596
|
+
- Remainder goes to Treasury
|
|
766
597
|
|
|
767
|
-
|
|
768
|
-
export circuit mint(to: Address, amount: Uint<128>): [] {
|
|
769
|
-
AC_assertOnlyRole(MINTER_ROLE);
|
|
770
|
-
// ... mint logic
|
|
771
|
-
}
|
|
598
|
+
## Token Distribution
|
|
772
599
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
}
|
|
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
|
|
778
604
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
): [] {
|
|
784
|
-
AC_assertOnlyRole(AC_DEFAULT_ADMIN_ROLE());
|
|
785
|
-
AC__grantRole(role, account);
|
|
786
|
-
}
|
|
787
|
-
\`\`\`
|
|
788
|
-
`,
|
|
789
|
-
"midnight://docs/openzeppelin/security": `# OpenZeppelin Security Patterns
|
|
605
|
+
### Phase 2: Scavenger Mine (30 days)
|
|
606
|
+
- Computational puzzles (accessible to public)
|
|
607
|
+
- Claims unclaimed Glacier Drop tokens
|
|
608
|
+
- Seeds network constituents
|
|
790
609
|
|
|
791
|
-
|
|
610
|
+
### Phase 3: Lost-and-Found (4 years)
|
|
611
|
+
- Second chance for Glacier Drop eligible
|
|
612
|
+
- Fractional allocation
|
|
792
613
|
|
|
793
|
-
##
|
|
614
|
+
## Key Differentiators
|
|
794
615
|
|
|
795
|
-
|
|
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
|
|
796
622
|
|
|
797
|
-
|
|
798
|
-
pragma language_version >= 0.16.0;
|
|
623
|
+
A guide for integrating Midnight Lace wallet into your DApp.
|
|
799
624
|
|
|
800
|
-
|
|
801
|
-
import "./compact-contracts/node_modules/@openzeppelin-compact/contracts/src/access/Ownable" prefix Ownable_;
|
|
625
|
+
## Browser Detection
|
|
802
626
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
627
|
+
\`\`\`typescript
|
|
628
|
+
declare global {
|
|
629
|
+
interface Window {
|
|
630
|
+
midnight?: {
|
|
631
|
+
mnLace?: MidnightProvider;
|
|
632
|
+
};
|
|
633
|
+
}
|
|
806
634
|
}
|
|
807
635
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
amount: Uint<128>,
|
|
812
|
-
): Boolean {
|
|
813
|
-
Pausable_assertNotPaused();
|
|
814
|
-
// ... transfer logic
|
|
636
|
+
function isWalletAvailable(): boolean {
|
|
637
|
+
return typeof window !== 'undefined'
|
|
638
|
+
&& window.midnight?.mnLace !== undefined;
|
|
815
639
|
}
|
|
640
|
+
\`\`\`
|
|
816
641
|
|
|
817
|
-
|
|
818
|
-
export circuit pause(): [] {
|
|
819
|
-
Ownable_assertOnlyOwner();
|
|
820
|
-
Pausable__pause();
|
|
821
|
-
}
|
|
642
|
+
## DApp Connector API
|
|
822
643
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
644
|
+
\`\`\`typescript
|
|
645
|
+
interface DAppConnectorAPI {
|
|
646
|
+
enable(): Promise<MidnightAPI>;
|
|
647
|
+
isEnabled(): Promise<boolean>;
|
|
648
|
+
apiVersion(): string;
|
|
649
|
+
name(): string;
|
|
650
|
+
icon(): string;
|
|
827
651
|
}
|
|
828
652
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
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();
|
|
832
658
|
}
|
|
833
659
|
\`\`\`
|
|
834
660
|
|
|
835
|
-
##
|
|
836
|
-
|
|
837
|
-
\`\`\`compact
|
|
838
|
-
pragma language_version >= 0.16.0;
|
|
839
|
-
|
|
840
|
-
import CompactStandardLibrary;
|
|
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_;
|
|
661
|
+
## MidnightAPI Interface
|
|
844
662
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
)
|
|
852
|
-
Ownable_initialize(_owner);
|
|
853
|
-
FungibleToken_initialize(_name, _symbol, _decimals);
|
|
854
|
-
FungibleToken__mint(_owner, _initialSupply);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// Pausable transfer
|
|
858
|
-
export circuit transfer(
|
|
859
|
-
to: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
860
|
-
value: Uint<128>,
|
|
861
|
-
): Boolean {
|
|
862
|
-
Pausable_assertNotPaused();
|
|
863
|
-
return FungibleToken_transfer(to, value);
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
// Owner-only mint
|
|
867
|
-
export circuit mint(
|
|
868
|
-
to: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
869
|
-
amount: Uint<128>,
|
|
870
|
-
): [] {
|
|
871
|
-
Ownable_assertOnlyOwner();
|
|
872
|
-
Pausable_assertNotPaused();
|
|
873
|
-
FungibleToken__mint(to, amount);
|
|
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>;
|
|
874
670
|
}
|
|
671
|
+
\`\`\`
|
|
875
672
|
|
|
876
|
-
|
|
877
|
-
export circuit pause(): [] {
|
|
878
|
-
Ownable_assertOnlyOwner();
|
|
879
|
-
Pausable__pause();
|
|
880
|
-
}
|
|
673
|
+
## React Hook
|
|
881
674
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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
|
|
886
721
|
\`\`\`
|
|
887
722
|
|
|
888
723
|
## Best Practices
|
|
889
724
|
|
|
890
|
-
1.
|
|
891
|
-
2.
|
|
892
|
-
3.
|
|
893
|
-
4.
|
|
894
|
-
5.
|
|
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
|
|
895
730
|
`,
|
|
896
731
|
};
|
|
897
732
|
//# sourceMappingURL=docs-content.js.map
|