rgb_invoice/
lib.rs

1// Invoice Library for RGB smart contracts
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Designed in 2019-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
6// Written in 2024-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association, Switzerland.
9// Copyright (C) 2024-2025 LNP/BP Laboratories,
10//                         Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
11// Copyright (C) 2025 RGB Consortium, Switzerland.
12// Copyright (C) 2019-2025 Dr Maxim Orlovsky.
13// All rights under the above copyrights are reserved.
14//
15// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
16// in compliance with the License. You may obtain a copy of the License at
17//
18//        http://www.apache.org/licenses/LICENSE-2.0
19//
20// Unless required by applicable law or agreed to in writing, software distributed under the License
21// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
22// or implied. See the License for the specific language governing permissions and limitations under
23// the License.
24
25// TODO: Activate once StrictEncoding will be no_std
26// #![cfg_attr(not(feature = "std"), no_std)]
27#![deny(
28    unsafe_code,
29    dead_code,
30    // TODO: Complete documentation
31    // missing_docs,
32    unused_variables,
33    unused_mut,
34    unused_imports,
35    non_upper_case_globals,
36    non_camel_case_types,
37    non_snake_case
38)]
39#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
40#![cfg_attr(docsrs, feature(doc_auto_cfg))]
41
42#[macro_use]
43extern crate amplify;
44#[cfg(any(feature = "bitcoin", feature = "liquid"))]
45#[macro_use]
46extern crate strict_encoding;
47#[cfg(feature = "serde")]
48#[macro_use]
49extern crate serde;
50
51#[cfg(any(feature = "bitcoin", feature = "liquid"))]
52pub mod bp;
53
54use core::fmt::{self, Display, Formatter};
55use core::str::FromStr;
56
57use baid64::Baid64ParseError;
58use hypersonic::{AuthToken, Consensus};
59use sonic_callreq::{CallRequest, CallScope};
60
61pub type RgbInvoice<T = CallScope<ContractQuery>> = CallRequest<T, RgbBeneficiary>;
62
63#[derive(Clone, Eq, PartialEq, Debug, Display)]
64#[display(inner)]
65#[cfg_attr(
66    feature = "serde",
67    derive(Serialize, Deserialize),
68    serde(rename_all = "camelCase", untagged)
69)]
70pub enum RgbBeneficiary {
71    Token(AuthToken),
72
73    #[cfg(any(feature = "bitcoin", feature = "liquid"))]
74    WitnessOut(bp::WitnessOut),
75}
76
77impl FromStr for RgbBeneficiary {
78    type Err = ParseInvoiceError;
79
80    fn from_str(s: &str) -> Result<Self, Self::Err> {
81        match AuthToken::from_str(s) {
82            Ok(auth) => Ok(Self::Token(auth)),
83
84            #[cfg(any(feature = "bitcoin", feature = "liquid"))]
85            Err(_) => {
86                let wout = bp::WitnessOut::from_str(s)?;
87                Ok(Self::WitnessOut(wout))
88            }
89            #[cfg(not(any(feature = "bitcoin", feature = "liquid")))]
90            Err(err) => Err(err),
91        }
92    }
93}
94
95#[cfg(any(feature = "bitcoin", feature = "liquid"))]
96impl RgbBeneficiary {
97    pub fn witness_out(&self) -> Option<&bp::WitnessOut> {
98        match self {
99            Self::WitnessOut(wout) => Some(wout),
100            _ => None,
101        }
102    }
103}
104
105#[derive(Clone, Eq, PartialEq, Debug)]
106pub struct ContractQuery {
107    pub consensus: Consensus,
108    pub testnet: bool,
109}
110
111impl Display for ContractQuery {
112    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
113        if self.testnet {
114            f.write_str("testnet@")?;
115        }
116        Display::fmt(&self.consensus, f)
117    }
118}
119
120impl FromStr for ContractQuery {
121    type Err = String;
122
123    fn from_str(s: &str) -> Result<Self, Self::Err> {
124        let testnet = s.starts_with("testnet@");
125        let s = s.trim_start_matches("testnet@");
126        Consensus::from_str(s).map(|consensus| Self { consensus, testnet })
127    }
128}
129
130#[derive(Debug, Display, Error, From)]
131#[display(doc_comments)]
132pub enum ParseInvoiceError {
133    /// RGB invoice misses URI scheme prefix `contract:`.
134    NoScheme,
135
136    /// RGB invoice contains unrecognizable URI authority, which is neither contract id nor a
137    /// contract query.
138    Unrecognizable(Baid64ParseError),
139
140    #[cfg(any(feature = "bitcoin", feature = "liquid"))]
141    #[from]
142    Bp(bp::ParseWitnessOutError),
143}