chia_sdk_driver/clear_signing/
linked_offer.rs1use 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}