Skip to main content

rgbinvoice/
builder.rs

1// RGB wallet library for smart contracts on Bitcoin & Lightning network
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2024 by
6//     Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved.
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use std::str::FromStr;
23
24use rgb::{ContractId, SchemaId};
25use strict_types::FieldName;
26
27use crate::invoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport, XChainNet};
28use crate::{Allocation, Amount, CoinAmount, NonFungible, Precision, TransportParseError};
29
30#[derive(Clone, Eq, PartialEq, Debug)]
31pub struct RgbInvoiceBuilder(RgbInvoice);
32
33#[allow(clippy::result_large_err)]
34impl RgbInvoiceBuilder {
35    pub fn new(beneficiary: impl Into<XChainNet<Beneficiary>>) -> Self {
36        Self(RgbInvoice {
37            transports: vec![],
38            contract: None,
39            schema: None,
40            assignment_name: None,
41            assignment_state: None,
42            beneficiary: beneficiary.into(),
43            expiry: None,
44            unknown_query: none!(),
45        })
46    }
47
48    pub fn with(contract_id: ContractId, beneficiary: impl Into<XChainNet<Beneficiary>>) -> Self {
49        Self::new(beneficiary).set_contract(contract_id)
50    }
51
52    pub fn set_contract(mut self, contract_id: ContractId) -> Self {
53        self.0.contract = Some(contract_id);
54        self
55    }
56
57    pub fn set_schema(mut self, schema_id: SchemaId) -> Self {
58        self.0.schema = Some(schema_id);
59        self
60    }
61
62    pub fn set_assignment_name(mut self, assignment_name: impl Into<FieldName>) -> Self {
63        self.0.assignment_name = Some(assignment_name.into());
64        self
65    }
66
67    pub fn set_void(mut self) -> Self {
68        self.0.assignment_state = Some(InvoiceState::Void);
69        self
70    }
71
72    pub fn set_amount_raw(mut self, amount: impl Into<Amount>) -> Self {
73        self.0.assignment_state = Some(InvoiceState::Amount(amount.into()));
74        self
75    }
76
77    pub fn set_amount(
78        mut self,
79        integer: u64,
80        decimals: u64,
81        precision: Precision,
82    ) -> Result<Self, Self> {
83        let amount = match CoinAmount::with(integer, decimals, precision) {
84            Ok(amount) => amount,
85            Err(_) => return Err(self),
86        }
87        .to_amount_unchecked();
88        self.0.assignment_state = Some(InvoiceState::Amount(amount));
89        Ok(self)
90    }
91
92    pub fn set_allocation_raw(mut self, allocation: impl Into<Allocation>) -> Self {
93        self.0.assignment_state =
94            Some(InvoiceState::Data(NonFungible::FractionedToken(allocation.into())));
95        self
96    }
97
98    pub fn set_allocation(self, token_index: u32, fraction: u64) -> Result<Self, Self> {
99        Ok(self.set_allocation_raw(Allocation::with(token_index, fraction)))
100    }
101
102    /// # Safety
103    ///
104    /// The function may cause the loss of the information about the precise
105    /// amount of the asset, since f64 type doesn't provide full precision
106    /// required for that.
107    pub unsafe fn set_amount_approx(self, amount: f64, precision: Precision) -> Result<Self, Self> {
108        if amount <= 0.0 {
109            return Err(self);
110        }
111        let coins = amount.floor();
112        let cents = amount - coins;
113        self.set_amount(coins as u64, cents as u64, precision)
114    }
115
116    pub fn set_expiry_timestamp(mut self, expiry: i64) -> Self {
117        self.0.expiry = Some(expiry);
118        self
119    }
120
121    pub fn add_transport(self, transport: &str) -> Result<Self, (Self, TransportParseError)> {
122        let transport = match RgbTransport::from_str(transport) {
123            Err(err) => return Err((self, err)),
124            Ok(transport) => transport,
125        };
126        Ok(self.add_transport_raw(transport))
127    }
128
129    pub fn add_transport_raw(mut self, transport: RgbTransport) -> Self {
130        self.0.transports.push(transport);
131        self
132    }
133
134    pub fn add_transports<'a>(
135        self,
136        transports: impl IntoIterator<Item = &'a str>,
137    ) -> Result<Self, (Self, TransportParseError)> {
138        let res = transports
139            .into_iter()
140            .map(RgbTransport::from_str)
141            .collect::<Result<Vec<_>, TransportParseError>>();
142        let transports = match res {
143            Err(err) => return Err((self, err)),
144            Ok(transports) => transports,
145        };
146        Ok(self.add_transports_raw(transports))
147    }
148
149    pub fn add_transports_raw(
150        mut self,
151        transports: impl IntoIterator<Item = RgbTransport>,
152    ) -> Self {
153        self.0.transports.extend(transports);
154        self
155    }
156
157    pub fn finish(self) -> RgbInvoice { self.0 }
158}