Skip to main content

dig_options/
clawback.rs

1//! Claw back an expired option — the creator reclaims the locked XCH underlying.
2//!
3//! After expiry the holder can no longer exercise, so [`clawback`] lets the creator recover
4//! the locked underlying to `created.underlying.creator_puzzle_hash` via the underlying's
5//! clawback path (valid only strictly after `expiry_seconds`, enforced by the consensus).
6//! The creator's inner spend is authorized through the caller's [`Owner`] layer.
7
8use chia_puzzle_types::Memos;
9use chia_wallet_sdk::driver::{SpendContext, SpendWithConditions};
10use chia_wallet_sdk::types::Conditions;
11
12use crate::error::{Error, Result};
13use crate::types::{CreatedOption, OptionSpend, Owner};
14
15/// Build the unsigned coin spends that CLAW BACK `created`'s locked XCH underlying to its
16/// creator, authorized by `creator`.
17///
18/// Recovers exactly `created.underlying_coin.amount` mojos to
19/// `created.underlying.creator_puzzle_hash` through the underlying's clawback path — valid
20/// only AFTER `created.underlying.seconds` (the holder had until expiry to exercise). A
21/// [`Owner::Standard`] `creator` whose puzzle hash does not match the option's creator is
22/// rejected up front; a [`Owner::Custom`] creator cannot be checked here and relies on the
23/// consensus to reject a wrong-party spend.
24///
25/// **Pure: does NOT sign or broadcast.** Returns [`OptionSpend`] with `created: None`.
26pub fn clawback(
27    ctx: &mut SpendContext,
28    creator: &Owner,
29    created: &CreatedOption,
30) -> Result<OptionSpend> {
31    if let Some(puzzle_hash) = creator.standard_puzzle_hash() {
32        if puzzle_hash != created.underlying.creator_puzzle_hash {
33            return Err(Error::invalid(
34                "clawback owner does not match the option's creator puzzle hash",
35            ));
36        }
37    }
38
39    // The creator recovers the locked underlying to its own (creator) puzzle hash.
40    let inner = creator.spend_with_conditions(
41        ctx,
42        Conditions::new().create_coin(
43            created.underlying.creator_puzzle_hash,
44            created.underlying_coin.amount,
45            Memos::None,
46        ),
47    )?;
48
49    created
50        .underlying
51        .clawback_coin_spend(ctx, created.underlying_coin, inner)?;
52
53    Ok(OptionSpend {
54        coin_spends: ctx.take(),
55        created: None,
56    })
57}