use chia_protocol::Bytes32;
use chia_puzzle_types::singleton::SingletonStruct;
use chia_sdk_types::{
Conditions, Mod, announcement_id,
puzzles::{
ANY_METADATA_UPDATER_HASH, CatalogRegisterActionArgs, CatalogRegisterActionSolution,
CatalogSlotValue, DefaultCatMakerArgs, NftPack, PrecommitSpendMode,
},
};
use clvm_traits::{FromClvm, ToClvm, clvm_tuple};
use clvm_utils::{ToTreeHash, TreeHash};
use clvmr::NodePtr;
use crate::{
CatalogPrecommitValue, CatalogRegistry, CatalogRegistryConstants, DriverError, HashedPtr,
PrecommitCoin, PrecommitLayer, SingletonAction, Slot, Spend, SpendContext,
UniquenessPrelauncher,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CatalogRegisterAction {
pub launcher_id: Bytes32,
pub royalty_puzzle_hash_hash: Bytes32,
pub trade_price_percentage: u16,
pub relative_block_height: u32,
pub payout_puzzle_hash: Bytes32,
}
impl ToTreeHash for CatalogRegisterAction {
fn tree_hash(&self) -> TreeHash {
Self::new_args(
self.launcher_id,
self.royalty_puzzle_hash_hash,
self.trade_price_percentage,
self.relative_block_height,
self.payout_puzzle_hash,
)
.curry_tree_hash()
}
}
impl SingletonAction<CatalogRegistry> for CatalogRegisterAction {
fn from_constants(constants: &CatalogRegistryConstants) -> Self {
Self {
launcher_id: constants.launcher_id,
royalty_puzzle_hash_hash: constants.royalty_address.tree_hash().into(),
trade_price_percentage: constants.royalty_basis_points,
relative_block_height: constants.relative_block_height,
payout_puzzle_hash: constants.precommit_payout_puzzle_hash,
}
}
}
impl CatalogRegisterAction {
pub fn new_args(
launcher_id: Bytes32,
royalty_puzzle_hash_hash: Bytes32,
trade_price_percentage: u16,
relative_block_height: u32,
payout_puzzle_hash: Bytes32,
) -> CatalogRegisterActionArgs {
CatalogRegisterActionArgs {
nft_pack: NftPack::new(royalty_puzzle_hash_hash, trade_price_percentage),
uniqueness_prelauncher_1st_curry_hash: UniquenessPrelauncher::<()>::first_curry_hash()
.into(),
precommit_1st_curry_hash: PrecommitLayer::<()>::first_curry_hash(
SingletonStruct::new(launcher_id).tree_hash().into(),
relative_block_height,
payout_puzzle_hash,
)
.into(),
slot_1st_curry_hash: Slot::<CatalogSlotValue>::first_curry_hash(launcher_id, 0).into(),
}
}
pub fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
ctx.curry(Self::new_args(
self.launcher_id,
self.royalty_puzzle_hash_hash,
self.trade_price_percentage,
self.relative_block_height,
self.payout_puzzle_hash,
))
}
pub fn spent_slot_values(
&self,
ctx: &SpendContext,
solution: NodePtr,
) -> Result<[CatalogSlotValue; 2], DriverError> {
let params = CatalogRegisterActionSolution::<NodePtr, ()>::from_clvm(ctx, solution)?;
Ok([
CatalogSlotValue::new(
params.left_tail_hash,
params.left_left_tail_hash,
params.right_tail_hash,
),
CatalogSlotValue::new(
params.right_tail_hash,
params.left_tail_hash,
params.right_right_tail_hash,
),
])
}
pub fn created_slot_values(
&self,
ctx: &SpendContext,
solution: NodePtr,
) -> Result<[CatalogSlotValue; 3], DriverError> {
let params = CatalogRegisterActionSolution::<NodePtr, ()>::from_clvm(ctx, solution)?;
Ok([
CatalogSlotValue::new(
params.left_tail_hash,
params.left_left_tail_hash,
params.tail_hash,
),
CatalogSlotValue::new(
params.tail_hash,
params.left_tail_hash,
params.right_tail_hash,
),
CatalogSlotValue::new(
params.right_tail_hash,
params.tail_hash,
params.right_right_tail_hash,
),
])
}
#[allow(clippy::too_many_arguments)]
pub fn spend(
self,
ctx: &mut SpendContext,
catalog: &mut CatalogRegistry,
tail_hash: Bytes32,
left_slot: Slot<CatalogSlotValue>,
right_slot: Slot<CatalogSlotValue>,
precommit_coin: &PrecommitCoin<CatalogPrecommitValue>,
eve_nft_inner_spend: Spend,
) -> Result<Conditions, DriverError> {
let mut register_announcement =
clvm_tuple!(tail_hash, precommit_coin.value.initial_inner_puzzle_hash)
.tree_hash()
.to_vec();
register_announcement.insert(0, b'r');
let initial_inner_puzzle_hash = precommit_coin.value.initial_inner_puzzle_hash;
let my_inner_puzzle_hash = catalog.info.inner_puzzle_hash().into();
precommit_coin.spend(ctx, PrecommitSpendMode::REGISTER, my_inner_puzzle_hash)?;
let uniqueness_prelauncher =
UniquenessPrelauncher::<Bytes32>::new(ctx, catalog.coin.coin_id(), tail_hash)?;
let nft_launcher = uniqueness_prelauncher.spend(ctx)?;
let (_, nft) = nft_launcher.mint_eve_nft(
ctx,
initial_inner_puzzle_hash,
HashedPtr::NIL,
ANY_METADATA_UPDATER_HASH.into(),
catalog.info.constants.royalty_address,
catalog.info.constants.royalty_basis_points,
)?;
let _new_nft = nft.spend(ctx, eve_nft_inner_spend)?;
let (left_slot, right_slot) = catalog.actual_neigbors(tail_hash, left_slot, right_slot);
let my_solution = CatalogRegisterActionSolution {
cat_maker_reveal: ctx.curry(DefaultCatMakerArgs::new(
precommit_coin.asset_id.tree_hash().into(),
))?,
cat_maker_solution: (),
tail_hash,
initial_nft_owner_ph: initial_inner_puzzle_hash,
refund_puzzle_hash_hash: precommit_coin.refund_puzzle_hash.tree_hash().into(),
left_tail_hash: left_slot.info.value.asset_id,
left_left_tail_hash: left_slot.info.value.neighbors.left_value,
right_tail_hash: right_slot.info.value.asset_id,
right_right_tail_hash: right_slot.info.value.neighbors.right_value,
my_id: catalog.coin.coin_id(),
};
let my_solution = my_solution.to_clvm(ctx)?;
let my_puzzle = self.construct_puzzle(ctx)?;
catalog.insert_action_spend(ctx, Spend::new(my_puzzle, my_solution))?;
left_slot.spend(ctx, my_inner_puzzle_hash)?;
right_slot.spend(ctx, my_inner_puzzle_hash)?;
Ok(
Conditions::new().assert_puzzle_announcement(announcement_id(
catalog.coin.puzzle_hash,
register_announcement,
)),
)
}
}