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}