chia_sdk_driver/layers/datalayer/
oracle_layer.rs

1use chia_protocol::{Bytes, Bytes32};
2use chia_puzzle_types::Memos;
3use chia_sdk_types::Condition;
4use clvm_traits::{clvm_quote, match_quote, FromClvm, ToClvm};
5use clvmr::{Allocator, NodePtr};
6
7use crate::{DriverError, Layer, Puzzle, Spend, SpendContext};
8
9/// The Oracle [`Layer`] enables anyone to spend a coin provided they pay an XCH fee to an address.
10/// It's typically used with [`DelegationLayer`](crate::DelegationLayer).
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct OracleLayer {
13    /// The puzzle hash corresponding to the address the fee should be paid to.
14    pub oracle_puzzle_hash: Bytes32,
15    /// The amount of XCH that should be paid to the oracle.
16    pub oracle_fee: u64,
17}
18
19impl OracleLayer {
20    /// Creates a new [`OracleLayer`] if the fee is even.
21    /// Returns `None` if the fee is odd, which would make the puzzle invalid.
22    pub fn new(oracle_puzzle_hash: Bytes32, oracle_fee: u64) -> Option<Self> {
23        if oracle_fee % 2 != 0 {
24            return None;
25        }
26
27        Some(Self {
28            oracle_puzzle_hash,
29            oracle_fee,
30        })
31    }
32}
33
34impl Layer for OracleLayer {
35    type Solution = ();
36
37    fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
38        let Some(puzzle) = puzzle.as_raw() else {
39            return Ok(None);
40        };
41
42        let (_q, conditions) =
43            <match_quote!(Vec<Condition<NodePtr>>)>::from_clvm(allocator, puzzle.ptr)?;
44        if conditions.len() != 2 {
45            return Ok(None);
46        }
47
48        if let Some(Condition::CreateCoin(create_coin)) = conditions.first() {
49            Ok(Self::new(create_coin.puzzle_hash, create_coin.amount))
50        } else {
51            Ok(None)
52        }
53    }
54
55    fn parse_solution(_: &Allocator, _: NodePtr) -> Result<Self::Solution, DriverError> {
56        Ok(())
57    }
58
59    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
60        if self.oracle_fee % 2 == 1 {
61            return Err(DriverError::Custom("oracle fee must be even".to_string()));
62        }
63
64        let conditions: Vec<Condition<NodePtr>> = vec![
65            Condition::create_coin(self.oracle_puzzle_hash, self.oracle_fee, Memos::None),
66            Condition::create_puzzle_announcement(Bytes::new("$".into())),
67        ];
68
69        Ok(clvm_quote!(conditions).to_clvm(ctx)?)
70    }
71
72    fn construct_solution(
73        &self,
74        _: &mut SpendContext,
75        (): Self::Solution,
76    ) -> Result<NodePtr, DriverError> {
77        Ok(NodePtr::NIL)
78    }
79}
80
81impl OracleLayer {
82    pub fn spend(self, ctx: &mut SpendContext) -> Result<Spend, DriverError> {
83        let puzzle = self.construct_puzzle(ctx)?;
84        let solution = self.construct_solution(ctx, ())?;
85
86        Ok(Spend { puzzle, solution })
87    }
88}