1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
use super::*;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use cosmwasm_std::{
Storage, Uint128, Addr, BlockInfo,
StdResult, StdError,
to_binary,
};
use secret_toolkit::{
storage::AppendStore, //AppendStoreMut
};
pub static PERMISSION_ID_STORE: AppendStore<PermissionKey> = AppendStore::new(PREFIX_PERMISSION_ID);
/////////////////////////////////////////////////////////////////////////////////
// Permissions
/////////////////////////////////////////////////////////////////////////////////
/// saves new permission entry and adds identifier to the list of permissions the owner address has
pub fn new_permission(
storage: &mut dyn Storage,
owner: &Addr,
token_id: &str,
allowed_addr: &Addr,
// permission_key: &PermissionKey,
permission: &Permission,
) -> StdResult<()> {
// store permission
permission_w(storage, owner, token_id).save(
to_binary(allowed_addr)?.as_slice(),
permission
)?;
// add permission to list of permissions for a given owner
append_permission_for_addr(storage, owner, token_id, allowed_addr)?;
Ok(())
}
// /// updates an existing permission entry. Does not check that existing entry exists, so
// /// riskier to use this. But saves gas from potentially loading permission twice
// pub fn update_permission_unchecked(
// storage: &mut dyn Storage,
// owner: &Addr,
// token_id: &str,
// allowed_addr: &Addr,
// permission: &Permission,
// ) -> StdResult<()> {
// permission_w(storage, owner, token_id).save(
// to_binary(allowed_addr)?.as_slice(),
// permission
// )?;
// Ok(())
// }
/// updates an existing permission entry. Returns error if permission entry does not aleady exist
pub fn update_permission(
storage: &mut dyn Storage,
owner: &Addr,
token_id: &str,
allowed_addr: &Addr,
permission: &Permission
// update_action: A,
) -> StdResult<()>
// where
// S: Storage,
// A: FnOnce(Option<Permission>) -> StdResult<Permission>
{
let update_action = |perm: Option<Permission>| -> StdResult<Permission> {
match perm {
Some(_) => Ok(permission.clone()),
None => Err(StdError::generic_err("cannot update or revoke a non-existent permission entry"))
}
};
permission_w(storage, owner, token_id).update(
to_binary(allowed_addr)?.as_slice(),
update_action
)?;
Ok(())
}
/// returns StdResult<Option<Permission>> for a given [`owner`, `token_id`, `allowed_addr`] combination.
/// Returns "dormant" permissions we well, ie: where owner doesn't currently own tokens.
/// If permission does not exist -> returns StdResult<None>
pub fn may_load_any_permission(
storage: &dyn Storage,
owner: &Addr,
token_id: &str,
allowed_addr: &Addr,
) -> StdResult<Option<Permission>> {
permission_r(storage, owner, token_id).may_load(to_binary(allowed_addr)?.as_slice())
}
// /// returns StdResult<Option<Permission>> for a given [`owner`, `token_id`, `allowed_addr`] combination.
// /// If (permission does not exist) || (owner no longer owns tokens) () -> returns StdResult<None>
// pub fn may_load_active_permission(
// storage: &dyn Storage,
// owner: &Addr,
// token_id: &str,
// allowed_addr: &Addr,
// ) -> StdResult<Option<Permission>> {
// let permission = permission_r(storage, owner, token_id).may_load(to_binary(allowed_addr)?.as_slice())?;
// let owner_amount = balances_r(storage, token_id).may_load(to_binary(owner)?.as_slice())?;
// match owner_amount {
// None => return Ok(None),
// Some(i) if i == Uint128(0) => return Ok(None),
// Some(i) if i > Uint128(0) => return Ok(permission),
// Some(_) => unreachable!("may_load_permission: this should be unreachable")
// }
// }
/// Return (Vec<`PermissionKey { token_id, allowed_addr }`>, u64)
/// returns a list and total number of PermissionKeys for a given owner. The PermissionKeys represents (part of)
/// the keys to retrieve all permissions an `owner` has currently granted
pub fn list_owner_permission_keys(
storage: &dyn Storage,
owner: &Addr,
page: u32,
page_size: u32,
) -> StdResult<(Vec<PermissionKey>, u64)> {
let owner_store = PERMISSION_ID_STORE.add_suffix(to_binary(owner)?.as_slice());
// let store = ReadonlyPrefixedStorage::multilevel(&[PREFIX_PERMISSION_ID, to_binary(owner)?.as_slice()], storage);
// Try to access the storage of PermissionKeys for the account.
// If it doesn't exist yet, return an empty list of transfers.
// let store = AppendStore::<PermissionKey, _, _>::attach(&store);
// let store = if let Some(result) = store {
// result?
// } else {
// return Ok((vec![], 0));
// };
// Take `page_size` starting from the latest entry, potentially skipping `page * page_size`
// entries from the start.
let pkeys_iter = owner_store
.iter(storage)?
.rev()
.skip((page * page_size) as _)
.take(page_size as _);
// Transform iterator to a `Vec<PermissionKey>`
let pkeys: StdResult<Vec<PermissionKey>> = pkeys_iter
// .map(|pkey| pkey)
.collect();
// return `(Vec<PermissionKey> , total_permission)`
pkeys.map(|pkeys| (pkeys, owner_store.get_len(storage).unwrap_or_default() as u64))
}
/// stores a `PermissionKey {token_id: String, allowed_addr: String]` for a given `owner`. Note that
/// permission key is [`owner`, `token_id`, `allowed_addr`]. This function does not enforce that the
/// list of PermissionKey stored is unique; while this doesn't really matter, the ref implementation's
/// functions aim to ensure each entry is unique, for storage efficiency.
fn append_permission_for_addr(
storage: &mut dyn Storage,
owner: &Addr,
token_id: &str,
allowed_addr: &Addr,
) -> StdResult<()> {
let permission_key = PermissionKey {
token_id: token_id.to_string(),
allowed_addr: allowed_addr.clone(),
};
let owner_store = PERMISSION_ID_STORE.add_suffix(to_binary(owner)?.as_slice());
owner_store.push(storage, &permission_key)
}
/// struct to store permission for a `[token_id, owner, allowed_addr]` combination
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)]
pub struct Permission {
pub view_balance_perm: bool,
pub view_balance_exp: Expiration,
pub view_pr_metadata_perm: bool,
pub view_pr_metadata_exp: Expiration,
pub trfer_allowance_perm: Uint128,
pub trfer_allowance_exp: Expiration,
}
impl Permission {
pub fn check_view_balance_perm(&self, blockinfo: &BlockInfo) -> bool {
self.view_balance_perm && !self.view_balance_exp.is_expired(blockinfo)
}
pub fn check_view_pr_metadata_perm(&self, blockinfo: &BlockInfo) -> bool {
self.view_pr_metadata_perm && !self.view_pr_metadata_exp.is_expired(blockinfo)
}
}
/// to store all keys to access all permissions for a given `owner`
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct PermissionKey {
pub token_id: String,
pub allowed_addr: Addr,
}