chia_sdk_driver/layers/
option_contract_layer.rs

1use chia_protocol::Bytes32;
2use chia_sdk_types::{
3    puzzles::{OptionContractArgs, OptionContractSolution, OPTION_CONTRACT_HASH},
4    Mod,
5};
6use clvm_traits::FromClvm;
7use clvm_utils::{ToTreeHash, TreeHash};
8use clvmr::{Allocator, NodePtr};
9
10use crate::{DriverError, Layer, Puzzle, SpendContext};
11
12/// The option contract [`Layer`] keeps track of the underlying coin and right to exercise the option.
13/// It's typically an inner layer of the [`SingletonLayer`](crate::SingletonLayer).
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct OptionContractLayer<I> {
16    pub underlying_coin_id: Bytes32,
17    pub underlying_delegated_puzzle_hash: Bytes32,
18    pub inner_puzzle: I,
19}
20
21impl<I> OptionContractLayer<I> {
22    pub fn new(
23        underlying_coin_id: Bytes32,
24        underlying_delegated_puzzle_hash: Bytes32,
25        inner_puzzle: I,
26    ) -> Self {
27        Self {
28            underlying_coin_id,
29            underlying_delegated_puzzle_hash,
30            inner_puzzle,
31        }
32    }
33}
34
35impl<I> Layer for OptionContractLayer<I>
36where
37    I: Layer,
38    I::Solution: FromClvm<Allocator>,
39{
40    type Solution = OptionContractSolution<I::Solution>;
41
42    fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
43        let Some(puzzle) = puzzle.as_curried() else {
44            return Ok(None);
45        };
46
47        if puzzle.mod_hash != OPTION_CONTRACT_HASH {
48            return Ok(None);
49        }
50
51        let args = OptionContractArgs::<NodePtr>::from_clvm(allocator, puzzle.args)?;
52
53        let Some(inner_puzzle) =
54            I::parse_puzzle(allocator, Puzzle::parse(allocator, args.inner_puzzle))?
55        else {
56            return Ok(None);
57        };
58
59        Ok(Some(Self {
60            underlying_coin_id: args.underlying_coin_id,
61            underlying_delegated_puzzle_hash: args.underlying_delegated_puzzle_hash,
62            inner_puzzle,
63        }))
64    }
65
66    fn parse_solution(
67        allocator: &Allocator,
68        solution: NodePtr,
69    ) -> Result<Self::Solution, DriverError> {
70        Ok(Self::Solution::from_clvm(allocator, solution)?)
71    }
72
73    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
74        let inner_puzzle = self.inner_puzzle.construct_puzzle(ctx)?;
75        ctx.curry(OptionContractArgs::new(
76            self.underlying_coin_id,
77            self.underlying_delegated_puzzle_hash,
78            inner_puzzle,
79        ))
80    }
81
82    fn construct_solution(
83        &self,
84        ctx: &mut SpendContext,
85        solution: Self::Solution,
86    ) -> Result<NodePtr, DriverError> {
87        let inner_solution = self
88            .inner_puzzle
89            .construct_solution(ctx, solution.inner_solution)?;
90        ctx.alloc(&OptionContractSolution::new(inner_solution))
91    }
92}
93
94impl<I> ToTreeHash for OptionContractLayer<I>
95where
96    I: ToTreeHash,
97    I: ToTreeHash,
98{
99    fn tree_hash(&self) -> TreeHash {
100        let inner_puzzle_hash = self.inner_puzzle.tree_hash();
101        OptionContractArgs::new(
102            self.underlying_coin_id,
103            self.underlying_delegated_puzzle_hash,
104            inner_puzzle_hash,
105        )
106        .curry_tree_hash()
107    }
108}