Skip to main content

dig_options/
types.rs

1//! The public value types every dig-options builder speaks in.
2//!
3//! These types are deliberately **key-free**: an [`Owner`] carries a public key (or an
4//! arbitrary caller-supplied inner spender), never a secret; the parties an option is created
5//! for are named by plain [`Bytes32`] puzzle hashes carried in [`OptionTerms`]. A builder
6//! consumes these, appends `CoinSpend`s to a caller-owned [`SpendContext`], and returns an
7//! [`OptionSpend`] — the built spends plus, for `create`, the [`CreatedOption`] the caller
8//! keeps to later exercise or claw back the option.
9
10use chia_protocol::{Bytes32, Coin, CoinSpend};
11use chia_puzzle_types::standard::StandardArgs;
12use chia_wallet_sdk::driver::{
13    DriverError, OptionContract, OptionType, OptionUnderlying, Spend, SpendContext,
14    SpendWithConditions, StandardLayer,
15};
16use chia_wallet_sdk::prelude::PublicKey;
17use chia_wallet_sdk::types::Conditions;
18
19/// The p2 (owner) layer that authorizes an option's inner spend, expressed WITHOUT any secret.
20///
21/// dig-options never signs; it only builds the spend and reports the signature the caller must
22/// produce. [`Owner::Standard`] is the common case — the standard
23/// `p2_delegated_puzzle_or_hidden_puzzle` layer identified by its public key.
24/// [`Owner::Custom`] is the escape hatch: any layer implementing the SDK's
25/// [`SpendWithConditions`] (a multisig, a custom p2, a settlement layer) borrowed for the
26/// build, so a non-standard owner is fully supported through every builder.
27pub enum Owner<'a> {
28    /// A standard-layer owner identified by its BLS public key.
29    Standard(PublicKey),
30    /// An arbitrary owner layer that knows how to emit a set of conditions.
31    Custom(&'a dyn SpendWithConditions),
32}
33
34impl Owner<'_> {
35    /// The standard p2 puzzle hash of a [`Owner::Standard`] owner.
36    ///
37    /// Returns `None` for [`Owner::Custom`], whose puzzle hash dig-options cannot derive
38    /// without knowing the concrete layer. Used to reject a wrong-party clawback up front
39    /// (a Custom owner skips that check — the consensus still enforces the real path).
40    pub fn standard_puzzle_hash(&self) -> Option<Bytes32> {
41        match self {
42            Owner::Standard(public_key) => Some(StandardArgs::curry_tree_hash(*public_key).into()),
43            Owner::Custom(_) => None,
44        }
45    }
46}
47
48impl SpendWithConditions for Owner<'_> {
49    /// Route an option's output conditions through the concrete owner layer, producing the
50    /// inner [`Spend`]. Neither variant holds or uses a secret key.
51    fn spend_with_conditions(
52        &self,
53        ctx: &mut SpendContext,
54        conditions: Conditions,
55    ) -> std::result::Result<Spend, DriverError> {
56        match self {
57            Owner::Standard(public_key) => {
58                StandardLayer::new(*public_key).spend_with_conditions(ctx, conditions)
59            }
60            Owner::Custom(inner) => inner.spend_with_conditions(ctx, conditions),
61        }
62    }
63}
64
65/// The terms of a covered option: who created it, who owns (may exercise) it, how much XCH it
66/// locks as the underlying, what strike the holder must pay, and when it expires.
67///
68/// Key-free: the parties are named by their [`Bytes32`] puzzle hashes, never by keys. The
69/// underlying is XCH (v0.1.0 scope); `strike_type` may be any [`OptionType`] (it is curried
70/// into the option puzzle), though only an XCH strike can be *exercised* in v0.1.0 (see
71/// [`crate::exercise`]).
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73pub struct OptionTerms {
74    /// The puzzle hash the creator reclaims the underlying to on clawback (after expiry).
75    pub creator_puzzle_hash: Bytes32,
76    /// The puzzle hash the option singleton is minted to — its initial holder/owner.
77    pub owner_puzzle_hash: Bytes32,
78    /// The amount of XCH (in mojos) locked as the underlying.
79    pub underlying_amount: u64,
80    /// The asset + amount the holder must pay to exercise the option.
81    pub strike_type: OptionType,
82    /// The absolute unix timestamp (seconds) at which the option expires: exercise is valid
83    /// strictly before it, clawback strictly after it.
84    pub expiry_seconds: u64,
85}
86
87impl OptionTerms {
88    /// Terms with the creator as the initial owner (the common self-minted case).
89    ///
90    /// The option is minted to `creator_puzzle_hash` and, on clawback, reclaimed to it. Use
91    /// the struct literal directly to mint an option owned by a different party.
92    #[must_use]
93    pub fn new(
94        creator_puzzle_hash: Bytes32,
95        underlying_amount: u64,
96        strike_type: OptionType,
97        expiry_seconds: u64,
98    ) -> Self {
99        Self {
100            creator_puzzle_hash,
101            owner_puzzle_hash: creator_puzzle_hash,
102            underlying_amount,
103            strike_type,
104            expiry_seconds,
105        }
106    }
107}
108
109/// The operable handle for a live option: the option singleton, the underlying terms, and the
110/// locked-underlying coin needed to exercise, transfer, or claw it back.
111///
112/// Returned by [`crate::create`] (the freshly minted option), by [`crate::transfer`] (the option
113/// re-homed to its new owner), and by [`crate::rehydrate`] (reconstructed from on-chain state).
114/// The three fields only exist once the create spend is confirmed.
115///
116/// The terms are not recoverable from the option singleton coin alone (see [`crate::parse`]), so a
117/// caller either retains this handle from `create`/`transfer` or rebuilds it with
118/// [`crate::rehydrate`], which reconstructs and verifies it against the option's on-chain
119/// commitments.
120#[derive(Clone, Debug)]
121pub struct CreatedOption {
122    /// The option singleton (the transferable "ticket"); its holder may exercise it.
123    pub option: OptionContract,
124    /// The underlying terms (launcher id, creator ph, expiry seconds, locked amount, strike
125    /// type) — needed to build the exercise / clawback spend.
126    pub underlying: OptionUnderlying,
127    /// The locked-underlying XCH coin (parent = the funding coin, puzzle hash = the
128    /// underlying's 1-of-2 path, amount = the locked amount).
129    pub underlying_coin: Coin,
130}
131
132/// The result of a dig-options builder: the unsigned coin spends it produced, and — for the
133/// builders that yield an operable option — the [`CreatedOption`] handle the caller keeps to
134/// operate it later.
135///
136/// `created` is `Some` for [`crate::create`] (the minted option) and [`crate::transfer`] (the
137/// option re-homed to its new owner), and `None` for [`crate::exercise`]/[`crate::clawback`],
138/// which consume an existing option rather than producing an ongoing one.
139#[derive(Clone, Debug)]
140pub struct OptionSpend {
141    /// The unsigned coin spends this operation produced.
142    pub coin_spends: Vec<CoinSpend>,
143    /// The operable option handle — `Some` for `create` and `transfer`, `None` otherwise.
144    pub created: Option<CreatedOption>,
145}