use chik_protocol::Bytes32;
use chik_sdk_types::{
puzzles::{OptionContractArgs, OptionContractSolution, OPTION_CONTRACT_PUZZLE_HASH},
Mod,
};
use klvm_traits::FromKlvm;
use klvm_utils::{ToTreeHash, TreeHash};
use klvmr::{Allocator, NodePtr};
use crate::{DriverError, Layer, Puzzle, SpendContext};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OptionContractLayer<I> {
pub underlying_coin_id: Bytes32,
pub underlying_delegated_puzzle_hash: Bytes32,
pub inner_puzzle: I,
}
impl<I> OptionContractLayer<I> {
pub fn new(
underlying_coin_id: Bytes32,
underlying_delegated_puzzle_hash: Bytes32,
inner_puzzle: I,
) -> Self {
Self {
underlying_coin_id,
underlying_delegated_puzzle_hash,
inner_puzzle,
}
}
}
impl<I> Layer for OptionContractLayer<I>
where
I: Layer,
I::Solution: FromKlvm<Allocator>,
{
type Solution = OptionContractSolution<I::Solution>;
fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
let Some(puzzle) = puzzle.as_curried() else {
return Ok(None);
};
if puzzle.mod_hash != OPTION_CONTRACT_PUZZLE_HASH {
return Ok(None);
}
let args = OptionContractArgs::<NodePtr>::from_klvm(allocator, puzzle.args)?;
let Some(inner_puzzle) =
I::parse_puzzle(allocator, Puzzle::parse(allocator, args.inner_puzzle))?
else {
return Ok(None);
};
Ok(Some(Self {
underlying_coin_id: args.underlying_coin_id,
underlying_delegated_puzzle_hash: args.underlying_delegated_puzzle_hash,
inner_puzzle,
}))
}
fn parse_solution(
allocator: &Allocator,
solution: NodePtr,
) -> Result<Self::Solution, DriverError> {
Ok(Self::Solution::from_klvm(allocator, solution)?)
}
fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
let inner_puzzle = self.inner_puzzle.construct_puzzle(ctx)?;
ctx.curry(OptionContractArgs::new(
self.underlying_coin_id,
self.underlying_delegated_puzzle_hash,
inner_puzzle,
))
}
fn construct_solution(
&self,
ctx: &mut SpendContext,
solution: Self::Solution,
) -> Result<NodePtr, DriverError> {
let inner_solution = self
.inner_puzzle
.construct_solution(ctx, solution.inner_solution)?;
ctx.alloc(&OptionContractSolution::new(inner_solution))
}
}
impl<I> ToTreeHash for OptionContractLayer<I>
where
I: ToTreeHash,
I: ToTreeHash,
{
fn tree_hash(&self) -> TreeHash {
let inner_puzzle_hash = self.inner_puzzle.tree_hash();
OptionContractArgs::new(
self.underlying_coin_id,
self.underlying_delegated_puzzle_hash,
inner_puzzle_hash,
)
.curry_tree_hash()
}
}