-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathNativeShieldedTokenSupply.compact
More file actions
142 lines (133 loc) · 5.17 KB
/
Copy pathNativeShieldedTokenSupply.compact
File metadata and controls
142 lines (133 loc) · 5.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// SPDX-License-Identifier: MIT
// OpenZeppelin Compact Contracts v0.2.0 (token/extensions/NativeShieldedTokenSupply.compact)
pragma language_version >= 0.23.0;
/**
* @module NativeShieldedTokenSupply
* @description Optional standalone extension that adds on-chain supply
* accounting to a `NativeShieldedToken`. It exposes scalar `_addMinted` /
* `_addBurned` building blocks plus the `totalMinted` / `totalBurned` /
* `totalSupply` getters. It imports no token module.
*
* It is a thin wrapper over `NativeShieldedTokenSupplyCore`, forwarding a
* single fixed key (the paired contract has exactly one token type), so the
* accounting logic and guarantees live in the shared core.
*
* @notice Pairs with `NativeShieldedToken`. The consuming contract composes the
* pieces, calling the accounting block alongside the matching token op:
*
* export circuit mint(
* recipient: Either<ZswapCoinPublicKey, ContractAddress>,
* amount: Uint<64>,
* nonce: Bytes<32>
* ): ShieldedCoinInfo {
* const coin = Token__mint(recipient, amount, nonce);
* Supply__addMinted(amount);
* return coin;
* }
*
* export circuit burn(
* coin: ShieldedCoinInfo,
* amount: Uint<128>,
* refundTo: Either<ZswapCoinPublicKey, ContractAddress>
* ): Maybe<ShieldedCoinInfo> {
* const refund = Token__burn(coin, amount, refundTo);
* Supply__addBurned(amount);
* return refund;
* }
*
* @notice Privacy trade-off (read before composing): composing this extension
* makes contract-mediated BURN amounts PUBLIC. The bare token's `_burn` /
* `_burnFromSelf` are amount-private (their coin operations emit only
* commitments and nullifiers, never the value; the `disclose()` wrappers they
* require are compiler permission markers, not disclosure sinks). The
* `_addBurned` write here is what puts the burned amount into the public
* transcript. Compose this only when on-chain auditability is worth more than
* burn-amount privacy.
*
* @dev Mint amounts are already public via the protocol's `shieldedMints`
* effect, so `_addMinted` adds accounting, not disclosure. See
* `NativeShieldedTokenSupplyCore` for the exact / lower-bound / upper-bound
* guarantees, the `burned <= minted` invariant, and the mint-side drift caveat.
*
* @warning Pair every mint with `_addMinted` and every burn with `_addBurned` on
* every path; mis-wiring is a security-critical, undetectable error (e.g. an
* undercounted mint makes `_addBurned` reject legitimate burns). See
* `NativeShieldedTokenSupplyCore`.
*/
module NativeShieldedTokenSupply {
import CompactStandardLibrary;
import "./NativeShieldedTokenSupplyCore" prefix Core_;
// Circuits use the `Core_` prefix above. The named import/re-export below is
// only for the core supply ledger keys, so the implementing contract's ledger
// keys read as `_totalMinted` / `_totalBurned` rather than
// `NativeShieldedTokenSupplyCore__totalMinted`. The circuit names are
// intentionally NOT imported unprefixed, to avoid potential clashes.
import { _totalMinted, _totalBurned } from "./NativeShieldedTokenSupplyCore";
export { _totalMinted, _totalBurned };
/**
* @description The single fixed domain key. The paired `NativeShieldedToken`
* has exactly one token type, so all accounting lives under one key.
*/
circuit key(): Bytes<32> {
return default<Bytes<32>>;
}
/**
* @description Adds `amount` to the exact minted total. Call once per
* successful `NativeShieldedToken._mint`.
*
* @circuitInfo k=9, rows=375
*
* @param {Uint<64>} amount - The minted amount.
* @return {[]} - Empty tuple.
*/
export circuit _addMinted(amount: Uint<64>): [] {
Core__addMinted(key(), amount);
}
/**
* @description Adds `amount` to the contract-mediated burned total. Call once
* per successful `NativeShieldedToken._burn` / `_burnFromSelf`.
* @dev Enforces `burned <= minted`; an unpaired or duplicate burn reverts.
*
* @circuitInfo k=9, rows=477
*
* @param {Uint<128>} amount - The burned amount.
* @return {[]} - Empty tuple.
*/
export circuit _addBurned(amount: Uint<128>): [] {
Core__addBurned(key(), amount);
}
/**
* @description Returns the exact amount ever minted.
*
* @circuitInfo k=7, rows=65
*
* @return {Uint<128>} - The total amount minted.
*/
export circuit totalMinted(): Uint<128> {
return Core_totalMinted(key());
}
/**
* @description Returns the contract-mediated amount burned.
* @notice This is a lower bound: coins sent directly to the burn address
* without going through the contract are not counted.
*
* @circuitInfo k=7, rows=62
*
* @return {Uint<128>} - The total amount burned through the contract.
*/
export circuit totalBurned(): Uint<128> {
return Core_totalBurned(key());
}
/**
* @description Returns `totalMinted() - totalBurned()`.
* @notice This is an UPPER BOUND on circulating supply, not an exact value:
* burns that bypass the contract are invisible. See the module notes.
*
* @circuitInfo k=9, rows=160
*
* @return {Uint<128>} - The upper bound on tokens in existence.
*/
export circuit totalSupply(): Uint<128> {
return Core_totalSupply(key());
}
}