1use aluvm::library::LibSite;
25use amplify::confinement::Confined;
26use rgbstd::contract::{
27 AssignmentsFilter, ContractData, FungibleAllocation, IssuerWrapper, SchemaWrapper,
28};
29use rgbstd::persistence::ContractStateRead;
30use rgbstd::schema::{
31 AssignmentDetails, FungibleType, GenesisSchema, GlobalDetails, GlobalStateSchema, Occurrences,
32 Schema, TransitionDetails, TransitionSchema,
33};
34use rgbstd::stl::{rgb_contract_stl, ContractTerms, Details, Name, StandardTypes};
35use rgbstd::validation::Scripts;
36use rgbstd::{Amount, OwnedStateSchema, Precision, SchemaId};
37use strict_types::TypeSystem;
38
39use crate::nia::{nia_lib, FN_NIA_GENESIS_OFFSET, FN_NIA_TRANSFER_OFFSET};
40use crate::{
41 GS_ART, GS_DETAILS, GS_ISSUED_SUPPLY, GS_NAME, GS_PRECISION, GS_TERMS, OS_ASSET, TS_TRANSFER,
42};
43
44pub const CFA_SCHEMA_ID: SchemaId = SchemaId::from_array([
45 0x26, 0x0a, 0x8a, 0xe6, 0x12, 0x57, 0xf5, 0x80, 0x53, 0xe2, 0x8b, 0x02, 0x57, 0xb5, 0x5c, 0x5b,
46 0xe8, 0x8b, 0x4d, 0xc0, 0x39, 0x72, 0xc5, 0x02, 0x9c, 0xbc, 0xef, 0x68, 0xa4, 0xd3, 0xac, 0xd6,
47]);
48
49fn cfa_standard_types() -> StandardTypes { StandardTypes::with(rgb_contract_stl()) }
50
51pub fn cfa_schema() -> Schema {
52 let types = cfa_standard_types();
53
54 let nia_id = nia_lib().id();
55
56 Schema {
57 ffv: zero!(),
58 name: tn!("CollectibleFungibleAsset"),
59 meta_types: none!(),
60 global_types: tiny_bmap! {
61 GS_ART => GlobalDetails {
62 global_state_schema: GlobalStateSchema::once(types.get("RGBContract.Article")),
63 name: fname!("art"),
64 },
65 GS_NAME => GlobalDetails {
66 global_state_schema: GlobalStateSchema::once(types.get("RGBContract.Name")),
67 name: fname!("name"),
68 },
69 GS_DETAILS => GlobalDetails {
70 global_state_schema: GlobalStateSchema::once(types.get("RGBContract.Details")),
71 name: fname!("details"),
72 },
73 GS_PRECISION => GlobalDetails {
74 global_state_schema: GlobalStateSchema::once(types.get("RGBContract.Precision")),
75 name: fname!("precision"),
76 },
77 GS_TERMS => GlobalDetails {
78 global_state_schema: GlobalStateSchema::once(types.get("RGBContract.ContractTerms")),
79 name: fname!("terms"),
80 },
81 GS_ISSUED_SUPPLY => GlobalDetails {
82 global_state_schema: GlobalStateSchema::once(types.get("RGBContract.Amount")),
83 name: fname!("issuedSupply"),
84 },
85 },
86 owned_types: tiny_bmap! {
87 OS_ASSET => AssignmentDetails {
88 owned_state_schema: OwnedStateSchema::Fungible(FungibleType::Unsigned64Bit),
89 name: fname!("assetOwner"),
90 default_transition: TS_TRANSFER,
91 }
92 },
93 genesis: GenesisSchema {
94 metadata: none!(),
95 globals: tiny_bmap! {
96 GS_ART => Occurrences::NoneOrOnce,
97 GS_NAME => Occurrences::Once,
98 GS_DETAILS => Occurrences::NoneOrOnce,
99 GS_PRECISION => Occurrences::Once,
100 GS_TERMS => Occurrences::Once,
101 GS_ISSUED_SUPPLY => Occurrences::Once,
102 },
103 assignments: tiny_bmap! {
104 OS_ASSET => Occurrences::OnceOrMore,
105 },
106 validator: Some(LibSite::with(FN_NIA_GENESIS_OFFSET, nia_id)),
107 },
108 transitions: tiny_bmap! {
109 TS_TRANSFER => TransitionDetails {
110 transition_schema: TransitionSchema {
111 metadata: none!(),
112 globals: none!(),
113 inputs: tiny_bmap! {
114 OS_ASSET => Occurrences::OnceOrMore
115 },
116 assignments: tiny_bmap! {
117 OS_ASSET => Occurrences::OnceOrMore
118 },
119 validator: Some(LibSite::with(FN_NIA_TRANSFER_OFFSET, nia_id))
120 },
121 name: fname!("transfer"),
122 }
123 },
124 default_assignment: Some(OS_ASSET),
125 }
126}
127
128#[derive(Default)]
129pub struct CollectibleFungibleAsset;
130
131#[derive(Clone, Eq, PartialEq, Debug, From)]
132pub struct CfaWrapper<S: ContractStateRead>(ContractData<S>);
133
134impl IssuerWrapper for CollectibleFungibleAsset {
135 type Wrapper<S: ContractStateRead> = CfaWrapper<S>;
136
137 fn schema() -> Schema { cfa_schema() }
138
139 fn types() -> TypeSystem { cfa_standard_types().type_system(cfa_schema()) }
140
141 fn scripts() -> Scripts {
142 let lib = nia_lib();
143 Confined::from_checked(bmap! { lib.id() => lib })
144 }
145}
146
147impl<S: ContractStateRead> SchemaWrapper<S> for CfaWrapper<S> {
148 fn with(data: ContractData<S>) -> Self {
149 if data.schema.schema_id() != CFA_SCHEMA_ID {
150 panic!("the provided schema is not CFA");
151 }
152 Self(data)
153 }
154}
155
156impl<S: ContractStateRead> CfaWrapper<S> {
157 pub fn name(&self) -> Name {
158 let strict_val = &self
159 .0
160 .global("name")
161 .next()
162 .expect("CFA requires global state `name` to have at least one item");
163 Name::from_strict_val_unchecked(strict_val)
164 }
165
166 pub fn details(&self) -> Option<Details> {
167 self.0
168 .global("details")
169 .next()
170 .map(|strict_val| Details::from_strict_val_unchecked(&strict_val))
171 }
172
173 pub fn precision(&self) -> Precision {
174 let strict_val = &self
175 .0
176 .global("precision")
177 .next()
178 .expect("CFA requires global state `precision` to have at least one item");
179 Precision::from_strict_val_unchecked(strict_val)
180 }
181
182 pub fn total_issued_supply(&self) -> Amount {
183 self.0
184 .global("issuedSupply")
185 .map(|amount| Amount::from_strict_val_unchecked(&amount))
186 .sum()
187 }
188
189 pub fn contract_terms(&self) -> ContractTerms {
190 let strict_val = &self
191 .0
192 .global("terms")
193 .next()
194 .expect("CFA requires global state `terms` to have at least one item");
195 ContractTerms::from_strict_val_unchecked(strict_val)
196 }
197
198 pub fn allocations<'c>(
199 &'c self,
200 filter: impl AssignmentsFilter + 'c,
201 ) -> impl Iterator<Item = FungibleAllocation> + 'c {
202 self.0.fungible_raw(OS_ASSET, filter).unwrap()
203 }
204}
205
206#[cfg(test)]
207mod test {
208 use super::*;
209
210 #[test]
211 fn schema_id() {
212 let schema_id = cfa_schema().schema_id();
213 eprintln!("{:#04x?}", schema_id.to_byte_array());
214 assert_eq!(CFA_SCHEMA_ID, schema_id);
215 }
216}