use chia_protocol::Coin;
use chia_puzzles::SETTLEMENT_PAYMENT_HASH;
use clvmr::Allocator;
use crate::{
Bulletin, Cat, ClawbackInfo, ClawbackPath, CustodyInfo, DriverError, Nft, RevealedCoinSpend,
Reveals, parse_inner_spend,
};
#[derive(Debug, Clone)]
pub struct ParsedSpend {
pub asset: ParsedAsset,
pub clawback: Option<ClawbackInfo>,
pub custody: Option<CustodyInfo>,
pub required_expiration_time: Option<u64>,
pub revoked: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParsedAsset {
Cat(Cat),
Nft(Nft),
Xch(Coin),
Bulletin(Bulletin),
}
impl ParsedAsset {
pub fn coin(&self) -> Coin {
match self {
Self::Cat(cat) => cat.coin,
Self::Nft(nft) => nft.coin,
Self::Xch(coin) => *coin,
Self::Bulletin(bulletin) => bulletin.coin,
}
}
}
pub fn parse_spend(
reveals: &mut Reveals,
allocator: &mut Allocator,
spend: &RevealedCoinSpend,
) -> Result<ParsedSpend, DriverError> {
let mut asset = ParsedAsset::Xch(spend.coin);
let mut inner_puzzle = spend.puzzle;
let mut inner_solution = spend.solution;
let mut revoked = false;
if let Some(parsed) = Cat::parse(allocator, spend.coin, spend.puzzle, spend.solution)? {
asset = ParsedAsset::Cat(parsed.cat);
inner_puzzle = parsed.p2_puzzle;
inner_solution = parsed.p2_solution;
revoked = parsed.revoked;
} else if let Some((nft, parsed_inner_puzzle, parsed_inner_solution)) =
Nft::parse(allocator, spend.coin, spend.puzzle, spend.solution)?
{
asset = ParsedAsset::Nft(nft);
inner_puzzle = parsed_inner_puzzle;
inner_solution = parsed_inner_solution;
} else if let Some((bulletin, parsed_inner_puzzle, parsed_inner_solution)) =
Bulletin::parse(allocator, spend.coin, spend.puzzle, spend.solution)?
{
asset = ParsedAsset::Bulletin(bulletin);
inner_puzzle = parsed_inner_puzzle;
inner_solution = parsed_inner_solution;
}
if inner_puzzle.curried_puzzle_hash() == SETTLEMENT_PAYMENT_HASH.into() {
reveals.reveal_settlement_payment(allocator, spend.puzzle, inner_solution)?;
}
let inner_spend = parse_inner_spend(reveals, allocator, inner_puzzle, inner_solution)?;
let mut required_expiration_time = None;
if let Some(clawback_info) = &inner_spend.clawback
&& clawback_info.path == ClawbackPath::Sender
{
required_expiration_time = Some(clawback_info.clawback.seconds);
}
Ok(ParsedSpend {
asset,
clawback: inner_spend.clawback,
custody: inner_spend.custody,
required_expiration_time,
revoked,
})
}