1#![allow(unused_braces)]
23
24use std::fmt::Debug;
25
26use bp::bc::stl::bp_tx_stl;
27use strict_encoding::{StrictDumb, StrictEncode};
28use strict_types::stl::std_stl;
29use strict_types::{CompileError, LibBuilder, TypeLib};
30
31use super::{
32 AssignIface, GenesisIface, GlobalIface, Iface, OwnedIface, Req, TransitionIface, VerNo,
33};
34use crate::interface::{ArgSpec, ContractIface};
35use crate::stl::{rgb_contract_stl, Amount, ContractData, Details, Name, Precision, StandardTypes};
36
37pub const LIB_NAME_RGB25: &str = "RGB25";
38pub const LIB_ID_RGB25: &str =
40 "urn:ubideco:stl:4JmGrg7oTgwuCQtyC4ezC38ToHMzgMCVS5kMSDPwo2ee#camera-betty-bank";
41
42const SUPPLY_MISMATCH: u8 = 1;
43const NON_EQUAL_AMOUNTS: u8 = 2;
44const INVALID_PROOF: u8 = 3;
45const INSUFFICIENT_RESERVES: u8 = 4;
46const INSUFFICIENT_COVERAGE: u8 = 5;
47
48#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
49#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
50#[strict_type(lib = LIB_NAME_RGB25, tags = repr, into_u8, try_from_u8)]
51#[repr(u8)]
52pub enum Error {
53 #[strict_type(dumb)]
54 SupplyMismatch = SUPPLY_MISMATCH,
55 NonEqualAmounts = NON_EQUAL_AMOUNTS,
56 InvalidProof = INVALID_PROOF,
57 InsufficientReserves = INSUFFICIENT_RESERVES,
58 InsufficientCoverage = INSUFFICIENT_COVERAGE,
59}
60
61fn _rgb25_stl() -> Result<TypeLib, CompileError> {
62 LibBuilder::new(libname!(LIB_NAME_RGB25), tiny_bset! {
63 std_stl().to_dependency(),
64 bp_tx_stl().to_dependency(),
65 rgb_contract_stl().to_dependency(),
66 })
67 .transpile::<Error>()
68 .compile()
69}
70
71pub fn rgb25_stl() -> TypeLib { _rgb25_stl().expect("invalid strict type RGB25 library") }
73
74pub fn rgb25() -> Iface {
75 let types = StandardTypes::with(rgb25_stl());
76
77 Iface {
78 version: VerNo::V1,
79 name: tn!("RGB25"),
80 global_state: tiny_bmap! {
81 fname!("name") => GlobalIface::required(types.get("RGBContract.Name")),
82 fname!("details") => GlobalIface::optional(types.get("RGBContract.Details")),
83 fname!("precision") => GlobalIface::required(types.get("RGBContract.Precision")),
84 fname!("data") => GlobalIface::required(types.get("RGBContract.ContractData")),
85 fname!("created") => GlobalIface::required(types.get("RGBContract.Timestamp")),
86 fname!("issuedSupply") => GlobalIface::required(types.get("RGBContract.Amount")),
87 fname!("burnedSupply") => GlobalIface::none_or_many(types.get("RGBContract.Amount")),
88 },
89 assignments: tiny_bmap! {
90 fname!("assetOwner") => AssignIface::private(OwnedIface::Amount, Req::OneOrMore),
91 fname!("burnRight") => AssignIface::public(OwnedIface::Rights, Req::NoneOrMore),
92 },
93 valencies: none!(),
94 genesis: GenesisIface {
95 metadata: Some(types.get("RGBContract.IssueMeta")),
96 global: tiny_bmap! {
97 fname!("name") => ArgSpec::required(),
98 fname!("details") => ArgSpec::optional(),
99 fname!("precision") => ArgSpec::required(),
100 fname!("data") => ArgSpec::required(),
101 fname!("created") => ArgSpec::required(),
102 fname!("issuedSupply") => ArgSpec::required(),
103 },
104 assignments: tiny_bmap! {
105 fname!("assetOwner") => ArgSpec::non_empty(),
106 },
107 valencies: none!(),
108 errors: tiny_bset! {
109 SUPPLY_MISMATCH,
110 INVALID_PROOF,
111 INSUFFICIENT_RESERVES
112 },
113 },
114 transitions: tiny_bmap! {
115 tn!("Transfer") => TransitionIface {
116 optional: false,
117 metadata: None,
118 globals: none!(),
119 inputs: tiny_bmap! {
120 fname!("previous") => ArgSpec::from_non_empty("assetOwner"),
121 },
122 assignments: tiny_bmap! {
123 fname!("beneficiary") => ArgSpec::from_non_empty("assetOwner"),
124 },
125 valencies: none!(),
126 errors: tiny_bset! {
127 NON_EQUAL_AMOUNTS
128 },
129 default_assignment: Some(fname!("beneficiary")),
130 },
131 tn!("Burn") => TransitionIface {
132 optional: true,
133 metadata: Some(types.get("RGBContract.BurnMeta")),
134 globals: tiny_bmap! {
135 fname!("burnedSupply") => ArgSpec::required(),
136 },
137 inputs: tiny_bmap! {
138 fname!("used") => ArgSpec::from_required("burnRight"),
139 },
140 assignments: tiny_bmap! {
141 fname!("future") => ArgSpec::from_optional("burnRight"),
142 },
143 valencies: none!(),
144 errors: tiny_bset! {
145 SUPPLY_MISMATCH,
146 INVALID_PROOF,
147 INSUFFICIENT_COVERAGE
148 },
149 default_assignment: None,
150 },
151 },
152 extensions: none!(),
153 error_type: types.get("RGB25.Error"),
154 default_operation: Some(tn!("Transfer")),
155 type_system: types.type_system(),
156 }
157}
158
159#[derive(Wrapper, WrapperMut, Clone, Eq, PartialEq, Debug)]
160#[wrapper(Deref)]
161#[wrapper_mut(DerefMut)]
162pub struct Rgb25(ContractIface);
163
164impl From<ContractIface> for Rgb25 {
165 fn from(iface: ContractIface) -> Self {
166 if iface.iface.iface_id != rgb25().iface_id() {
167 panic!("the provided interface is not RGB25 interface");
168 }
169 Self(iface)
170 }
171}
172
173impl Rgb25 {
174 pub fn name(&self) -> Name {
175 let strict_val = &self
176 .0
177 .global("name")
178 .expect("RGB25 interface requires global `name`")[0];
179 Name::from_strict_val_unchecked(strict_val)
180 }
181
182 pub fn details(&self) -> Option<Details> {
183 let strict_val = &self
184 .0
185 .global("details")
186 .expect("RGB25 interface requires global `details`");
187 if strict_val.len() == 0 {
188 None
189 } else {
190 Some(Details::from_strict_val_unchecked(&strict_val[0]))
191 }
192 }
193
194 pub fn precision(&self) -> Precision {
195 let strict_val = &self
196 .0
197 .global("precision")
198 .expect("RGB25 interface requires global `precision`")[0];
199 Precision::from_strict_val_unchecked(strict_val)
200 }
201
202 pub fn total_issued_supply(&self) -> Amount {
203 self.0
204 .global("issuedSupply")
205 .expect("RGB25 interface requires global `issuedSupply`")
206 .iter()
207 .map(Amount::from_strict_val_unchecked)
208 .sum()
209 }
210
211 pub fn total_burned_supply(&self) -> Amount {
212 self.0
213 .global("burnedSupply")
214 .unwrap_or_default()
215 .iter()
216 .map(Amount::from_strict_val_unchecked)
217 .sum()
218 }
219
220 pub fn contract_data(&self) -> ContractData {
221 let strict_val = &self
222 .0
223 .global("data")
224 .expect("RGB25 interface requires global `data`")[0];
225 ContractData::from_strict_val_unchecked(strict_val)
226 }
227}
228
229#[cfg(test)]
230mod test {
231 use super::*;
232 use crate::containers::BindleContent;
233
234 const RGB25: &str = include_str!("../../tests/data/rgb25.rgba");
235
236 #[test]
237 fn lib_id() {
238 let lib = rgb25_stl();
239 assert_eq!(lib.id().to_string(), LIB_ID_RGB25);
240 }
241
242 #[test]
243 fn iface_creation() { rgb25(); }
244
245 #[test]
246 fn iface_bindle() {
247 assert_eq!(format!("{}", rgb25().bindle()), RGB25);
248 }
249}