#![allow(clippy::missing_const_for_fn)]
use chik_protocol::{Bytes32, Coin, CoinSpend, Program};
use chik_puzzle_types::singleton::{LauncherSolution, SingletonArgs};
use chik_puzzles::{SINGLETON_LAUNCHER, SINGLETON_LAUNCHER_HASH};
use chik_sdk_types::{announcement_id, conditions::Memos, Conditions};
use klvm_traits::ToKlvm;
use klvmr::{Allocator, NodePtr};
use crate::{DriverError, SpendContext};
#[derive(Debug, Clone)]
#[must_use]
pub struct Launcher {
coin: Coin,
conditions: Conditions,
singleton_amount: u64,
}
impl Launcher {
pub fn from_coin(coin: Coin, conditions: Conditions) -> Self {
Self {
coin,
conditions,
singleton_amount: coin.amount,
}
}
pub fn new(parent_coin_id: Bytes32, amount: u64) -> Self {
Self::from_coin(
Coin::new(parent_coin_id, SINGLETON_LAUNCHER_HASH.into(), amount),
Conditions::new().create_coin(SINGLETON_LAUNCHER_HASH.into(), amount, Memos::None),
)
}
pub fn with_memos(parent_coin_id: Bytes32, amount: u64, memos: Memos<NodePtr>) -> Self {
Self::from_coin(
Coin::new(parent_coin_id, SINGLETON_LAUNCHER_HASH.into(), amount),
Conditions::new().create_coin(SINGLETON_LAUNCHER_HASH.into(), amount, memos),
)
}
pub fn create_early(parent_coin_id: Bytes32, amount: u64) -> (Conditions, Self) {
(
Conditions::new().create_coin(SINGLETON_LAUNCHER_HASH.into(), amount, Memos::None),
Self::from_coin(
Coin::new(parent_coin_id, SINGLETON_LAUNCHER_HASH.into(), amount),
Conditions::new(),
),
)
}
pub fn create_early_with_memos(
parent_coin_id: Bytes32,
amount: u64,
memos: Memos<NodePtr>,
) -> (Conditions, Self) {
(
Conditions::new().create_coin(SINGLETON_LAUNCHER_HASH.into(), amount, memos),
Self::from_coin(
Coin::new(parent_coin_id, SINGLETON_LAUNCHER_HASH.into(), amount),
Conditions::new(),
),
)
}
pub fn with_singleton_amount(mut self, singleton_amount: u64) -> Self {
self.singleton_amount = singleton_amount;
self
}
pub fn coin(&self) -> Coin {
self.coin
}
pub fn spend<T>(
self,
ctx: &mut SpendContext,
singleton_inner_puzzle_hash: Bytes32,
key_value_list: T,
) -> Result<(Conditions, Coin), DriverError>
where
T: ToKlvm<Allocator>,
{
let singleton_puzzle_hash =
SingletonArgs::curry_tree_hash(self.coin.coin_id(), singleton_inner_puzzle_hash.into())
.into();
let solution_ptr = ctx.alloc(&LauncherSolution {
singleton_puzzle_hash,
amount: self.singleton_amount,
key_value_list,
})?;
let solution = ctx.serialize(&solution_ptr)?;
ctx.insert(CoinSpend::new(
self.coin,
Program::from(SINGLETON_LAUNCHER.to_vec()),
solution,
));
let singleton_coin = Coin::new(
self.coin.coin_id(),
singleton_puzzle_hash,
self.singleton_amount,
);
Ok((
self.conditions.assert_coin_announcement(announcement_id(
self.coin.coin_id(),
ctx.tree_hash(solution_ptr),
)),
singleton_coin,
))
}
}
#[cfg(test)]
mod tests {
use crate::StandardLayer;
use super::*;
use chik_sdk_test::Simulator;
#[test]
fn test_singleton_launcher() -> anyhow::Result<()> {
let mut sim = Simulator::new();
let alice = sim.bls(1);
let alice_p2 = StandardLayer::new(alice.pk);
let ctx = &mut SpendContext::new();
let launcher = Launcher::new(alice.coin.coin_id(), 1);
assert_eq!(launcher.coin.amount, 1);
let (conditions, singleton) = launcher.spend(ctx, Bytes32::default(), ())?;
alice_p2.spend(ctx, alice.coin, conditions)?;
assert_eq!(singleton.amount, 1);
sim.spend_coins(ctx.take(), &[alice.sk])?;
Ok(())
}
#[test]
fn test_singleton_launcher_custom_amount() -> anyhow::Result<()> {
let mut sim = Simulator::new();
let alice = sim.bls(1);
let alice_p2 = StandardLayer::new(alice.pk);
let ctx = &mut SpendContext::new();
let launcher = Launcher::new(alice.coin.coin_id(), 0);
assert_eq!(launcher.coin.amount, 0);
let (conditions, singleton) =
launcher
.with_singleton_amount(1)
.spend(ctx, Bytes32::default(), ())?;
alice_p2.spend(ctx, alice.coin, conditions)?;
assert_eq!(singleton.amount, 1);
sim.spend_coins(ctx.take(), &[alice.sk])?;
Ok(())
}
}