chia-sdk-driver 0.34.0

Driver code for interacting with standard puzzles on the Chia blockchain.
Documentation
use std::collections::HashSet;

use chia_protocol::Bytes32;
use chia_sdk_types::Condition;
use clvmr::Allocator;

use crate::{
    AssertedPayment, DriverError, Facts, Reveals, TransferType, VerifiedSpend,
    parse_asserted_requested_payments, split_asserted_payments,
};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LinkedOffer {
    pub reserved_fee: u64,
    pub received_payments: Vec<AssertedPayment>,
    pub external_payments: Vec<AssertedPayment>,
}

pub fn build_linked_offer(
    reveals: &Reveals,
    allocator: &Allocator,
    spends: &[VerifiedSpend],
    expected_launcher_id: Bytes32,
    p2_puzzle_hashes: &HashSet<Bytes32>,
) -> Result<Option<LinkedOffer>, DriverError> {
    let mut linked_offer = LinkedOffer {
        reserved_fee: 0,
        received_payments: vec![],
        external_payments: vec![],
    };
    let mut has_offer = false;
    let mut found_puzzle_assertions: Option<HashSet<Bytes32>> = None;

    for spend in spends {
        for child in &spend.children {
            let TransferType::OfferPreSplit(info) = &child.transfer_type else {
                continue;
            };

            has_offer = true;

            if expected_launcher_id != info.launcher_id {
                return Err(DriverError::WrongLinkedOfferLauncherId);
            }

            let mut reserved_fee = 0;
            let mut puzzle_assertions = HashSet::new();

            for condition in &info.fixed_conditions {
                match condition {
                    Condition::ReserveFee(condition) => {
                        reserved_fee += condition.amount;
                    }
                    Condition::AssertPuzzleAnnouncement(condition) => {
                        puzzle_assertions.insert(condition.announcement_id);
                    }
                    _ => {}
                }
            }

            linked_offer.reserved_fee += reserved_fee;

            if let Some(found_puzzle_assertions) = &mut found_puzzle_assertions {
                *found_puzzle_assertions = found_puzzle_assertions
                    .intersection(&puzzle_assertions)
                    .copied()
                    .collect();
            } else {
                found_puzzle_assertions = Some(puzzle_assertions);
            }
        }
    }

    if let Some(found_puzzle_assertions) = found_puzzle_assertions {
        let mut offer_facts = Facts::default();

        for announcement_id in found_puzzle_assertions {
            offer_facts.assert_puzzle_announcement(announcement_id);
        }

        let asserted_payments =
            parse_asserted_requested_payments(reveals, &offer_facts, allocator)?;
        let split_payments =
            split_asserted_payments(&asserted_payments, p2_puzzle_hashes, reveals.asset_info());
        linked_offer.received_payments = split_payments.received_payments;
        linked_offer.external_payments = split_payments.external_payments;
    }

    Ok(has_offer.then_some(linked_offer))
}