use ink::{
prelude::{string::String, vec, vec::Vec},
primitives::AccountId,
storage::Mapping,
};
#[cfg(feature = "std")]
use ink::storage::traits::StorageLayout;
use crate::data::balance_manager::Balances;
use crate::PSP34Error;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo, StorageLayout))]
pub enum Id {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
Bytes(Vec<u8>),
}
pub enum PSP34Event {
Transfer {
from: Option<AccountId>,
to: Option<AccountId>,
id: Id,
},
Approval {
owner: AccountId,
operator: AccountId,
id: Option<Id>,
approved: bool,
},
AttributeSet {
id: Id,
key: Vec<u8>,
data: Vec<u8>,
},
}
#[ink::storage_item]
#[derive(Debug, Default)]
pub struct PSP34Data {
token_owner: Mapping<Id, AccountId>,
operator_approvals: Mapping<(AccountId, AccountId, Option<Id>), ()>,
balance: Balances,
}
impl PSP34Data {
pub fn new() -> PSP34Data {
Default::default()
}
pub fn total_supply(&self) -> u128 {
self.balance.total_supply()
}
pub fn balance_of(&self, owner: AccountId) -> u32 {
self.balance.balance_of(&owner)
}
pub fn owner_of(&self, id: &Id) -> Option<AccountId> {
self.token_owner.get(id)
}
pub fn allowance(&self, owner: AccountId, operator: AccountId, id: Option<&Id>) -> bool {
self.operator_approvals
.get((owner, operator, &None))
.is_some()
|| id.is_some() && self.operator_approvals.get((owner, operator, id)).is_some()
}
pub fn collection_id(&self, account_id: AccountId) -> Id {
Id::Bytes(<_ as AsRef<[u8; 32]>>::as_ref(&account_id).to_vec())
}
pub fn approve(
&mut self,
mut caller: AccountId,
operator: AccountId,
id: Option<Id>,
approved: bool,
) -> Result<Vec<PSP34Event>, PSP34Error> {
if let Some(id) = &id {
let owner = self.owner_of(id).ok_or(PSP34Error::TokenNotExists)?;
if approved && owner == operator {
return Err(PSP34Error::SelfApprove);
}
if owner != caller && !self.allowance(owner, caller, None) {
return Err(PSP34Error::NotApproved);
}
if !approved && self.allowance(owner, operator, None) {
return Err(PSP34Error::Custom(String::from(
"Cannot revoke approval for a single token, when the operator has approval for all tokens."
)));
}
caller = owner;
}
if approved {
self.operator_approvals
.insert((caller, operator, id.as_ref()), &());
} else {
self.operator_approvals
.remove((caller, operator, id.as_ref()));
}
Ok(vec![PSP34Event::Approval {
owner: caller,
operator,
id,
approved,
}])
}
pub fn transfer(
&mut self,
caller: AccountId,
to: AccountId,
id: Id,
_data: Vec<u8>,
) -> Result<Vec<PSP34Event>, PSP34Error> {
let owner = self.owner_of(&id).ok_or(PSP34Error::TokenNotExists)?;
if owner == to {
return Ok(vec![]);
}
if owner != caller && !self.allowance(owner, caller, Some(&id)) {
return Err(PSP34Error::NotApproved);
}
self.balance.decrease_balance(&owner, &id, false);
self.operator_approvals.remove((owner, caller, Some(&id)));
self.token_owner.remove(&id);
self.token_owner.insert(&id, &to);
self.balance.increase_balance(&to, &id, false)?;
Ok(vec![PSP34Event::Transfer {
from: Some(caller),
to: Some(to),
id,
}])
}
pub fn mint(&mut self, account: AccountId, id: Id) -> Result<Vec<PSP34Event>, PSP34Error> {
if self.owner_of(&id).is_some() {
return Err(PSP34Error::TokenExists);
}
self.balance.increase_balance(&account, &id, true)?;
self.token_owner.insert(&id, &account);
Ok(vec![PSP34Event::Transfer {
from: None,
to: Some(account),
id,
}])
}
pub fn burn(
&mut self,
caller: AccountId,
account: AccountId,
id: Id,
) -> Result<Vec<PSP34Event>, PSP34Error> {
if self.owner_of(&id).is_none() {
return Err(PSP34Error::TokenNotExists);
}
if account != caller && !self.allowance(caller, account, None) {
return Err(PSP34Error::NotApproved);
}
self.balance.decrease_balance(&account, &id, true);
self.token_owner.remove(&id);
Ok(vec![PSP34Event::Transfer {
from: Some(account),
to: None,
id,
}])
}
pub fn owners_token_by_index(&self, owner: AccountId, index: u128) -> Result<Id, PSP34Error> {
self.balance.owners_token_by_index(owner, index)
}
pub fn token_by_index(&self, index: u128) -> Result<Id, PSP34Error> {
self.balance.token_by_index(index)
}
}
impl Default for Id {
fn default() -> Self {
Self::U128(0)
}
}
impl From<Id> for u128 {
fn from(id: Id) -> Self {
match id {
Id::U8(val) => val as u128,
Id::U16(val) => val as u128,
Id::U32(val) => val as u128,
Id::U64(val) => val as u128,
Id::U128(val) => val,
Id::Bytes(val) => u128::from_be_bytes(val.as_slice().try_into().unwrap()),
}
}
}
pub mod balance_manager {
use ink::{prelude::vec::Vec, primitives::AccountId, storage::Mapping};
use crate::{data::Id, PSP34Error};
#[ink::storage_item]
#[derive(Default, Debug)]
pub struct Balances {
enumerable: Mapping<Option<AccountId>, Vec<Id>>,
}
impl Balances {
pub fn new() -> Balances {
Default::default()
}
pub fn owners_token_by_index(
&self,
owner: AccountId,
index: u128,
) -> Result<Id, PSP34Error> {
self._get_value(&Some(owner), index)
.ok_or(PSP34Error::TokenNotExists)
}
pub fn token_by_index(&self, index: u128) -> Result<Id, PSP34Error> {
self._get_value(&None, index)
.ok_or(PSP34Error::TokenNotExists)
}
fn _get_value(&self, key: &Option<AccountId>, index: u128) -> Option<Id> {
self.enumerable
.get(key)
.and_then(|values| values.get(usize::try_from(index).unwrap()).cloned())
}
fn _insert(&mut self, key: &Option<AccountId>, value: &Id) {
let mut values = self.enumerable.get(key).unwrap_or_default();
values.push(value.clone());
self.enumerable.insert(key, &values);
}
pub fn insert(&mut self, key: &Option<AccountId>, value: &Id){
self._insert(key, value);
}
fn _remove(&mut self, key: &Option<AccountId>, value: &Id) {
if let Some(mut values) = self.enumerable.get(key) {
if let Some(pos) = values.iter().position(|v| v == value) {
values.swap_remove(pos);
self.enumerable.insert(key, &values);
}
}
}
fn _count(&self, key: &Option<AccountId>) -> u128 {
self.enumerable
.get(key)
.map_or(0, |values| values.len())
.try_into()
.unwrap()
}
pub fn balance_of(&self, owner: &AccountId) -> u32 {
self._count(&Some(*owner)) as u32
}
pub fn increase_balance(
&mut self,
owner: &AccountId,
id: &Id,
increase_supply: bool,
) -> Result<(), PSP34Error> {
self._insert(&Some(*owner), id);
if increase_supply {
self._insert(&None, id);
}
Ok(())
}
pub fn decrease_balance(&mut self, owner: &AccountId, id: &Id, decrease_supply: bool) {
self._remove(&Some(*owner), id);
if self.balance_of(owner) == 0 {
self.enumerable.remove(Some(owner));
}
if decrease_supply {
self._remove(&None, id);
}
}
pub fn total_supply(&self) -> u128 {
self._count(&None)
}
}
}
#[ink::storage_item]
#[derive(Default, Debug)]
pub struct Data {
attributes: Mapping<(Id, Vec<u8>), Vec<u8>>,
}
impl Data {
pub fn get_attribute(&self, id: Id, key: Vec<u8>) -> Option<Vec<u8>> {
self.attributes.get((&id, &key))
}
pub fn set_attribute(
&mut self,
id: Id,
key: Vec<u8>,
value: Vec<u8>,
) -> Result<Vec<PSP34Event>, PSP34Error> {
self.attributes.insert((&id, &key), &value);
Ok(vec![PSP34Event::AttributeSet {
id,
key,
data: value,
}])
}
}