Skip to main content

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}