dig_offers/types.rs
1//! The public value types every dig-offers builder speaks in.
2//!
3//! These types are deliberately **key-free**: every side of an offer carries its participant's
4//! *public* keys (an `IndexMap<Bytes32, PublicKey>` from a coin's p2 puzzle hash to the public
5//! key that authorizes it), never a secret. A builder consumes these, appends unsigned
6//! `CoinSpend`s to a caller-owned [`SpendContext`](chia_wallet_sdk::driver::SpendContext), and
7//! returns the unsigned artifact for the caller to sign.
8
9use std::marker::PhantomData;
10
11use chia_protocol::{Bytes32, Coin, CoinSpend};
12use chia_wallet_sdk::driver::{AssetInfo, Cat, Nft, NftAssetInfo, Offer, RequestedPayments};
13use chia_wallet_sdk::prelude::PublicKey;
14use indexmap::IndexMap;
15
16/// A single asset leg, used to describe what an offer offers or requests in a read-only
17/// [`OfferSummary`]. Amounts are the asset's base units (mojos for XCH, base units for CATs);
18/// an NFT is always quantity one and identified by its launcher id.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum OfferAsset {
21 /// XCH, amount in mojos.
22 Xch(u64),
23 /// A CAT identified by its `asset_id` (TAIL hash), amount in base units.
24 Cat {
25 /// The CAT's asset id (TAIL hash).
26 asset_id: Bytes32,
27 /// The amount in the CAT's base units.
28 amount: u64,
29 },
30 /// An NFT identified by its launcher id (quantity is always one).
31 Nft {
32 /// The NFT singleton's launcher id — its stable on-chain identity.
33 launcher_id: Bytes32,
34 },
35}
36
37impl OfferAsset {
38 /// The fungible amount this leg carries (zero for an NFT, which is not fungible).
39 #[must_use]
40 pub fn amount(&self) -> u64 {
41 match self {
42 OfferAsset::Xch(amount) | OfferAsset::Cat { amount, .. } => *amount,
43 OfferAsset::Nft { .. } => 0,
44 }
45 }
46}
47
48/// The maker's side of a make-offer: the coins it spends into the settlement puzzle (its funding
49/// XCH, CAT, and NFT coins), the fungible amounts it offers, and where change returns.
50///
51/// The funding coins fund the offered amounts (plus any network fee); surplus returns to
52/// `change_puzzle_hash`. `owner_keys` maps every funding coin's p2 (inner) puzzle hash to the
53/// **public** key that authorizes it — the builder never sees a secret. `nfts` are the NFTs to
54/// offer (each spent whole into settlement); they must be parsed in the SAME
55/// [`SpendContext`](chia_wallet_sdk::driver::SpendContext) passed to `make_build`, since an NFT
56/// carries an allocator-relative metadata pointer.
57pub struct OfferedSide<'a> {
58 /// Where offered-coin change (and fee surplus) returns.
59 pub change_puzzle_hash: Bytes32,
60 /// Every funding coin's p2 puzzle hash → the public key authorizing it.
61 pub owner_keys: IndexMap<Bytes32, PublicKey>,
62 /// Spendable XCH coins available to fund the offered XCH and the fee.
63 pub xch_coins: Vec<Coin>,
64 /// Spendable CAT coins (with lineage proofs) available to fund the offered CAT legs.
65 pub cat_coins: Vec<Cat>,
66 /// The NFTs to offer, each spent whole into settlement (parsed in the build context).
67 pub nfts: Vec<Nft>,
68 /// The XCH (mojos) to offer.
69 pub offer_xch: u64,
70 /// The CATs to offer, as `(asset_id, amount)` in base units.
71 pub offer_cats: Vec<(Bytes32, u64)>,
72 /// Ties the borrowed NFTs' lifetime to the build context.
73 pub _pd: PhantomData<&'a ()>,
74}
75
76/// The maker's requested side of a make-offer: what the taker must pay, and where it is paid.
77///
78/// Every requested payment is notarized to the offer's nonce and asserted (never self-funded).
79/// A requested NFT needs its [`NftAssetInfo`] (metadata pointer, royalty) so the settlement
80/// puzzle hash can be rebuilt correctly.
81pub struct RequestedSide {
82 /// The address every requested payment is paid to (the maker's receive address).
83 pub payee_puzzle_hash: Bytes32,
84 /// The XCH (mojos) requested.
85 pub xch: u64,
86 /// The CATs requested, as `(asset_id, amount)` in base units.
87 pub cats: Vec<(Bytes32, u64)>,
88 /// The NFTs requested, as `(launcher_id, asset_info)` — the asset info rebuilds settlement.
89 pub nfts: Vec<(Bytes32, NftAssetInfo)>,
90}
91
92/// The taker's funding coins for taking an offer: its spendable XCH, CAT, and NFT coins, and
93/// where change / received assets return.
94///
95/// `owner_keys` maps every funding coin's p2 puzzle hash to the **public** key authorizing it;
96/// `change_puzzle_hash` receives change, surplus, and the assets the offer delivers to the taker.
97/// NFTs the taker gives up (for an NFT-for-NFT take) are supplied in `nfts`, parsed in the SAME
98/// context passed to `take_build`.
99pub struct TakerFunds<'a> {
100 /// Where change, surplus, and received assets return.
101 pub change_puzzle_hash: Bytes32,
102 /// Every funding coin's p2 puzzle hash → the public key authorizing it.
103 pub owner_keys: IndexMap<Bytes32, PublicKey>,
104 /// Spendable XCH coins available to fund the requested XCH and the fee.
105 pub xch_coins: Vec<Coin>,
106 /// Spendable CAT coins (with lineage proofs) available to fund requested CAT payments.
107 pub cat_coins: Vec<Cat>,
108 /// NFTs the taker gives up to fulfil a requested-NFT leg (parsed in the build context).
109 pub nfts: Vec<Nft>,
110 /// Ties the borrowed NFTs' lifetime to the build context.
111 pub _pd: PhantomData<&'a ()>,
112}
113
114/// What a taker must fund to take an offer: the requested-over-offered surplus (the offer's
115/// arbitrage). NFTs the taker receives are not a cost; NFTs the taker gives up are expressed by
116/// the requested-NFT legs, not here.
117#[derive(Debug, Default, Clone, PartialEq, Eq)]
118pub struct OfferCost {
119 /// XCH (mojos) the taker must pay.
120 pub xch: u64,
121 /// Per-asset-id CAT base units the taker must pay.
122 pub cats: Vec<(Bytes32, u64)>,
123}
124
125/// A read-only summary of an `offer1…` string: what it offers, what it requests, the taker's
126/// arbitrage cost, and any NFT royalties it carries. Produced by
127/// [`summarize`](crate::summarize) without committing to the offer.
128#[derive(Debug, Default, Clone, PartialEq, Eq)]
129pub struct OfferSummary {
130 /// Assets the offer gives the taker.
131 pub offered: Vec<OfferAsset>,
132 /// Assets the offer asks the taker to pay.
133 pub requested: Vec<OfferAsset>,
134 /// The net the taker must fund (the requested-over-offered surplus).
135 pub arbitrage: OfferCost,
136 /// Royalty legs the offer carries: `(NFT launcher id, royalty basis points)`.
137 pub royalties: Vec<(Bytes32, u16)>,
138}
139
140/// The unsigned artifact of [`make_build`](crate::make_build): the coin spends the caller must
141/// sign, and the requested-payment context to hand back to [`make_assemble`](crate::make_assemble)
142/// once signed.
143///
144/// **The custody boundary.** `coin_spends` are unsigned; the caller computes their required
145/// signatures with [`required_signatures`](crate::required_signatures), signs, aggregates into a
146/// `SpendBundle`, and passes that plus `requested_payments` + `requested_asset_info` back to
147/// `make_assemble`. dig-offers never produces the signature.
148#[derive(Debug)]
149pub struct UnsignedMake {
150 /// The unsigned coin spends the caller must sign.
151 pub coin_spends: Vec<CoinSpend>,
152 /// The requested payments (what the taker will pay), carried to `make_assemble`.
153 pub requested_payments: RequestedPayments,
154 /// The requested asset metadata (rebuilds settlement for requested CAT/NFT legs).
155 pub requested_asset_info: AssetInfo,
156 /// The offer nonce derived from the offered coins — reported for audit; already baked into
157 /// the requested payments.
158 pub nonce: Bytes32,
159}
160
161/// The unsigned artifact of [`take_build`](crate::take_build): the taker's own coin spends to
162/// sign, the maker's already-signed offer, and the cost the take funds.
163///
164/// **The custody boundary.** `coin_spends` are the TAKER's unsigned spends only; the maker's half
165/// is already signed inside `offer`. The caller signs `coin_spends`, wraps them in a `SpendBundle`,
166/// and calls [`take_combine`](crate::take_combine) with `offer` to produce the atomic settlement.
167#[derive(Debug)]
168pub struct UnsignedTake {
169 /// The taker's unsigned coin spends (funding + received-asset routing).
170 pub coin_spends: Vec<CoinSpend>,
171 /// The maker's already-signed offer, carried to `take_combine`.
172 pub offer: Offer,
173 /// What the taker funds to take the offer (the arbitrage).
174 pub cost: OfferCost,
175}
176
177/// The unsigned artifact of [`cancel_build`](crate::cancel_build): the reclaim coin spends the
178/// maker must sign to invalidate an outstanding offer.
179#[derive(Debug)]
180pub struct UnsignedCancel {
181 /// The unsigned coin spends that reclaim the offered coins to the maker.
182 pub coin_spends: Vec<CoinSpend>,
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn offer_asset_amount_reports_fungible_value() {
191 assert_eq!(OfferAsset::Xch(500).amount(), 500);
192 assert_eq!(
193 OfferAsset::Cat {
194 asset_id: Bytes32::default(),
195 amount: 42
196 }
197 .amount(),
198 42
199 );
200 // An NFT is non-fungible — it carries no fungible amount.
201 assert_eq!(
202 OfferAsset::Nft {
203 launcher_id: Bytes32::default()
204 }
205 .amount(),
206 0
207 );
208 }
209}