komple_framework_attribute_permission/
contract.rs

1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{
4    from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
5};
6use cw2::set_contract_version;
7use komple_framework_metadata_module::helper::KompleMetadataModule;
8use komple_framework_types::modules::permission::AttributeConditions;
9use komple_framework_types::modules::Modules;
10use komple_framework_types::shared::query::ResponseWrapper;
11use komple_framework_types::shared::{RegisterMsg, PARENT_ADDR_NAMESPACE};
12use komple_framework_utils::response::EventHelper;
13use komple_framework_utils::response::ResponseHelper;
14use komple_framework_utils::storage::StorageHelper;
15
16use crate::error::ContractError;
17use crate::msg::{AttributeMsg, AttributeTypes, ExecuteMsg, QueryMsg};
18use crate::state::{Config, CONFIG, PERMISSION_MODULE_ADDR};
19
20// version info for migration info
21const CONTRACT_NAME: &str = "crates.io:komple-framework-attribute-permission";
22const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
23
24#[cfg_attr(not(feature = "library"), entry_point)]
25pub fn instantiate(
26    deps: DepsMut,
27    _env: Env,
28    info: MessageInfo,
29    msg: RegisterMsg,
30) -> Result<Response, ContractError> {
31    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
32
33    let admin = deps.api.addr_validate(&msg.admin)?;
34    let config = Config {
35        admin: admin.clone(),
36    };
37    CONFIG.save(deps.storage, &config)?;
38
39    PERMISSION_MODULE_ADDR.save(deps.storage, &info.sender)?;
40
41    Ok(
42        ResponseHelper::new_permission("attribute", "instantiate").add_event(
43            EventHelper::new("attribute_permission_instantiate")
44                .add_attribute("admin", admin)
45                .add_attribute("permission_module_addr", info.sender)
46                .get(),
47        ),
48    )
49}
50
51#[cfg_attr(not(feature = "library"), entry_point)]
52pub fn execute(
53    deps: DepsMut,
54    env: Env,
55    info: MessageInfo,
56    msg: ExecuteMsg,
57) -> Result<Response, ContractError> {
58    match msg {
59        ExecuteMsg::Check { data } => execute_check(deps, env, info, data),
60    }
61}
62
63pub fn execute_check(
64    deps: DepsMut,
65    _env: Env,
66    _info: MessageInfo,
67    data: Binary,
68) -> Result<Response, ContractError> {
69    let permission_addr = PERMISSION_MODULE_ADDR.load(deps.storage)?;
70    let hub_addr = StorageHelper::query_storage::<Addr>(
71        &deps.querier,
72        &permission_addr,
73        PARENT_ADDR_NAMESPACE,
74    )?;
75    let mint_module_addr = StorageHelper::query_module_address(
76        &deps.querier,
77        &hub_addr.unwrap(),
78        Modules::Mint.to_string(),
79    )?;
80
81    let msgs: Vec<AttributeMsg> = from_binary(&data)?;
82
83    // TODO: Cache if the metadata is same
84    for msg in msgs {
85        // Get collection address
86        let collection_addr = StorageHelper::query_collection_address(
87            &deps.querier,
88            &mint_module_addr,
89            &msg.collection_id,
90        )?;
91
92        // Check metadata sub module in collection
93        let sub_modules = StorageHelper::query_token_sub_modules(&deps.querier, &collection_addr)?;
94        if sub_modules.metadata.is_none() {
95            return Err(ContractError::MetadataNotFound {});
96        };
97
98        // Query token metadata to get attributes
99        let response = KompleMetadataModule(sub_modules.metadata.unwrap())
100            .query_metadata(&deps.querier, msg.token_id)?;
101        let attributes = response.metadata.attributes;
102
103        // Get the attribute value
104        let attribute = attributes
105            .into_iter()
106            .find(|attr| attr.trait_type == msg.trait_type);
107
108        // Check if attribute exists
109        if msg.condition != AttributeConditions::Absent && attribute.is_none() {
110            return Err(ContractError::AttributeNotFound {});
111        }
112        // If it is a comparison
113        // Check if value is integer and both are same type
114        if msg.condition == AttributeConditions::GreaterThan
115            || msg.condition == AttributeConditions::GreaterThanOrEqual
116            || msg.condition == AttributeConditions::LessThan
117            || msg.condition == AttributeConditions::LessThanOrEqual
118        {
119            // If the types are not number, return error
120            if get_value_type(&msg.value) != AttributeTypes::Integer
121                && get_value_type(&attribute.as_ref().unwrap().value) != AttributeTypes::Integer
122            {
123                return Err(ContractError::AttributeTypeMismatch {});
124            }
125        }
126        // Rest of the conditions
127        match msg.condition {
128            AttributeConditions::Absent => {
129                if attribute.is_some() {
130                    return Err(ContractError::AttributeFound {});
131                }
132            }
133            AttributeConditions::Equal => {
134                if attribute.unwrap().value != msg.value {
135                    return Err(ContractError::AttributeNotEqual {});
136                }
137            }
138            AttributeConditions::NotEqual => {
139                if attribute.unwrap().value == msg.value {
140                    return Err(ContractError::AttributeEqual {});
141                }
142            }
143            AttributeConditions::GreaterThan => {
144                let attribute_value = attribute.as_ref().unwrap().value.parse::<u32>()?;
145                let msg_value = msg.value.parse::<u32>().unwrap();
146                if attribute_value <= msg_value {
147                    return Err(ContractError::AttributeLessThanOrEqual {});
148                }
149            }
150            AttributeConditions::GreaterThanOrEqual => {
151                let attribute_value = attribute.as_ref().unwrap().value.parse::<u32>()?;
152                let msg_value = msg.value.parse::<u32>().unwrap();
153                if attribute_value < msg_value {
154                    return Err(ContractError::AttributeLessThan {});
155                }
156            }
157            AttributeConditions::LessThan => {
158                let attribute_value = attribute.as_ref().unwrap().value.parse::<u32>()?;
159                let msg_value = msg.value.parse::<u32>().unwrap();
160                if attribute_value >= msg_value {
161                    return Err(ContractError::AttributeGreaterThanOrEqual {});
162                }
163            }
164            AttributeConditions::LessThanOrEqual => {
165                let attribute_value = attribute.as_ref().unwrap().value.parse::<u32>()?;
166                let msg_value = msg.value.parse::<u32>().unwrap();
167                if attribute_value > msg_value {
168                    return Err(ContractError::AttributeGreaterThan {});
169                }
170            }
171            _ => {}
172        };
173    }
174
175    Ok(
176        ResponseHelper::new_permission("attribute", "check").add_event(
177            EventHelper::new("attribute_permission_check")
178                // .add_attributes(event_attributes)
179                .get(),
180        ),
181    )
182}
183
184// For now attribute comparison only works with
185// Strings, integers and booleans
186fn get_value_type(value: &str) -> AttributeTypes {
187    if value.parse::<u32>().is_ok() {
188        return AttributeTypes::Integer;
189    }
190    if value.parse::<bool>().is_ok() {
191        return AttributeTypes::Boolean;
192    }
193    AttributeTypes::String
194}
195
196#[cfg_attr(not(feature = "library"), entry_point)]
197pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
198    match msg {
199        QueryMsg::Config {} => to_binary(&query_config(deps)?),
200    }
201}
202
203fn query_config(deps: Deps) -> StdResult<ResponseWrapper<Config>> {
204    let config = CONFIG.load(deps.storage)?;
205    Ok(ResponseWrapper {
206        query: "config".to_string(),
207        data: config,
208    })
209}