Skip to main content

chia_sdk_driver/clear_signing/
linked_offer.rs

1use std::collections::HashSet;
2
3use chia_protocol::Bytes32;
4use chia_sdk_types::Condition;
5use clvmr::Allocator;
6
7use crate::{
8    AssertedPayment, DriverError, Facts, Reveals, TransferType, VerifiedSpend,
9    parse_asserted_requested_payments, split_asserted_payments,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct LinkedOffer {
14    pub reserved_fee: u64,
15    pub received_payments: Vec<AssertedPayment>,
16    pub external_payments: Vec<AssertedPayment>,
17}
18
19pub fn build_linked_offer(
20    reveals: &Reveals,
21    allocator: &Allocator,
22    spends: &[VerifiedSpend],
23    expected_launcher_id: Bytes32,
24    p2_puzzle_hashes: &HashSet<Bytes32>,
25) -> Result<Option<LinkedOffer>, DriverError> {
26    let mut linked_offer = LinkedOffer {
27        reserved_fee: 0,
28        received_payments: vec![],
29        external_payments: vec![],
30    };
31    let mut has_offer = false;
32    let mut found_puzzle_assertions: Option<HashSet<Bytes32>> = None;
33
34    for spend in spends {
35        for child in &spend.children {
36            let TransferType::OfferPreSplit(info) = &child.transfer_type else {
37                continue;
38            };
39
40            has_offer = true;
41
42            if expected_launcher_id != info.launcher_id {
43                return Err(DriverError::WrongLinkedOfferLauncherId);
44            }
45
46            let mut reserved_fee = 0;
47            let mut puzzle_assertions = HashSet::new();
48
49            for condition in &info.fixed_conditions {
50                match condition {
51                    Condition::ReserveFee(condition) => {
52                        reserved_fee += condition.amount;
53                    }
54                    Condition::AssertPuzzleAnnouncement(condition) => {
55                        puzzle_assertions.insert(condition.announcement_id);
56                    }
57                    _ => {}
58                }
59            }
60
61            linked_offer.reserved_fee += reserved_fee;
62
63            if let Some(found_puzzle_assertions) = &mut found_puzzle_assertions {
64                *found_puzzle_assertions = found_puzzle_assertions
65                    .intersection(&puzzle_assertions)
66                    .copied()
67                    .collect();
68            } else {
69                found_puzzle_assertions = Some(puzzle_assertions);
70            }
71        }
72    }
73
74    if let Some(found_puzzle_assertions) = found_puzzle_assertions {
75        let mut offer_facts = Facts::default();
76
77        for announcement_id in found_puzzle_assertions {
78            offer_facts.assert_puzzle_announcement(announcement_id);
79        }
80
81        let asserted_payments =
82            parse_asserted_requested_payments(reveals, &offer_facts, allocator)?;
83        let split_payments =
84            split_asserted_payments(&asserted_payments, p2_puzzle_hashes, reveals.asset_info());
85        linked_offer.received_payments = split_payments.received_payments;
86        linked_offer.external_payments = split_payments.external_payments;
87    }
88
89    Ok(has_offer.then_some(linked_offer))
90}