dpp/data_contract/associated_token/token_configuration/v1/mod.rs
1mod accessors;
2
3use crate::data_contract::associated_token::token_configuration::v0::{
4 default_change_control_rules, TokenConfigurationV0,
5};
6use crate::data_contract::change_control_rules::ChangeControlRules;
7#[cfg(feature = "json-conversion")]
8use crate::serialization::json_safe_fields;
9use bincode::{Decode, DecodeUntrusted, Encode};
10use serde::{Deserialize, Serialize};
11use std::fmt;
12
13/// Version 1 of the token configuration: everything `TokenConfigurationV0` carries, plus the
14/// per-token shielded pool opt-in and the pool's outgoing notes threshold.
15///
16/// The V0 fields are nested as `base` and flattened on the JSON / Value wire, so a V1
17/// configuration reads exactly like a V0 one with `$formatVersion: "1"` and the extra
18/// `hasShieldedPool`, `minimumPoolNotesForOutgoing` and `minimumPoolNotesForOutgoingChangeRules`
19/// keys. On the bincode wire it is the V0 bytes followed by the flag, the threshold and its
20/// rules, under the enum's variant index 1, so stored V0 configurations decode unchanged.
21///
22/// `has_shielded_pool` is decided at token creation and is immutable afterwards: the pool
23/// subtree (an Orchard note commitment tree, nullifier set, anchors and a balance) is created
24/// together with the token's other trees, and a pool holding notes can never be removed.
25/// Enabling a pool on an existing token is not supported by this version.
26#[cfg_attr(feature = "json-conversion", json_safe_fields)]
27#[derive(Serialize, Deserialize, Decode, Encode, Debug, Clone, PartialEq, Eq, DecodeUntrusted)]
28// An unknown key is refused rather than dropped: what a token can do is fixed when it is
29// created, so a misspelled `hasShieldedPool` would otherwise leave the token permanently
30// without the pool it was meant to have, and say nothing. Version 0 carries the same refusal,
31// but it cannot reach a key arriving here: `serde(flatten)` never offers an unrecognized key to
32// the flattened struct, so the attribute has to sit on the version that owns the flatten.
33#[serde(rename_all = "camelCase", deny_unknown_fields)]
34pub struct TokenConfigurationV1 {
35 /// The V0 configuration this version extends.
36 #[serde(flatten)]
37 pub base: TokenConfigurationV0,
38 /// Whether this token has its own shielded pool. When `true`, holders may move balance
39 /// into the pool (`TokenShield`), transfer privately inside it (`TokenShieldedTransfer`)
40 /// and move balance back out to an identity (`TokenUnshield`).
41 #[serde(default)]
42 pub has_shielded_pool: bool,
43 /// The fewest notes the token's shielded pool must hold before tokens may leave it for a
44 /// visible destination (unshielding to an identity, burning from the pool, paying a
45 /// document's token cost from the pool). Transfers inside the pool are not limited.
46 /// `None` reads as 0, no threshold.
47 ///
48 /// The count is of note commitments, not of holders: one bundle carries several actions,
49 /// so a single depositor can reach a threshold alone. It tells holders how busy the pool
50 /// should be before they leave it and guarantees no anonymity set. A threshold above 0
51 /// also traps a new pool's first depositors until enough notes accumulate, so the default
52 /// is none. At most `max_token_pool_notes_for_outgoing`, so an issuer cannot set one no
53 /// pool reaches and strand every shielded balance. Changed through `TokenConfigUpdate`
54 /// under `minimum_pool_notes_for_outgoing_change_rules`.
55 #[serde(default)]
56 pub minimum_pool_notes_for_outgoing: Option<u64>,
57 /// Change control rules governing who can modify `minimum_pool_notes_for_outgoing`. No one
58 /// when absent.
59 #[serde(default = "default_change_control_rules")]
60 pub minimum_pool_notes_for_outgoing_change_rules: ChangeControlRules,
61}
62
63impl TokenConfigurationV1 {
64 /// Wraps a V0 configuration, opting the token into a shielded pool when `has_shielded_pool`.
65 pub fn from_v0(base: TokenConfigurationV0, has_shielded_pool: bool) -> Self {
66 Self {
67 base,
68 has_shielded_pool,
69 minimum_pool_notes_for_outgoing: None,
70 minimum_pool_notes_for_outgoing_change_rules: default_change_control_rules(),
71 }
72 }
73}
74
75impl fmt::Display for TokenConfigurationV1 {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 write!(
78 f,
79 "TokenConfigurationV1 {{\n base: {},\n has_shielded_pool: {},\n minimum_pool_notes_for_outgoing: {:?},\n minimum_pool_notes_for_outgoing_change_rules: {:?}\n}}",
80 self.base,
81 self.has_shielded_pool,
82 self.minimum_pool_notes_for_outgoing,
83 self.minimum_pool_notes_for_outgoing_change_rules
84 )
85 }
86}