namada_trans_token 0.150.1

Namada transparent token
Documentation
//! Transparent token types, storage functions, and validation.

#![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")]
#![doc(html_logo_url = "https://dev.namada.net/master/rustdoc-logo.png")]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![warn(
    missing_docs,
    rust_2018_idioms,
    clippy::cast_sign_loss,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_lossless,
    clippy::arithmetic_side_effects,
    clippy::dbg_macro,
    clippy::print_stdout,
    clippy::print_stderr
)]

pub mod event;
mod storage;
pub mod storage_key;
pub mod tx;
pub mod vp;

use std::collections::BTreeMap;
use std::marker::PhantomData;

use event::{TokenEvent, TokenOperation};
use namada_core::address::Address;
use namada_core::token;
use namada_core::uint::Uint;
use namada_events::EmitEvents;
pub use namada_state::{
    Error, Key, Result, ResultExt, StorageRead, StorageWrite,
};
pub use namada_systems::trans_token::*;
pub use storage::*;

/// Transparent token storage `Keys/Read/Write` implementation
#[derive(Debug)]
pub struct Store<S>(PhantomData<S>);

impl<S> Keys for Store<S> {
    fn balance_key(token: &Address, owner: &Address) -> storage::Key {
        storage_key::balance_key(token, owner)
    }

    fn is_balance_key<'a>(
        token_addr: &Address,
        key: &'a storage::Key,
    ) -> Option<&'a Address> {
        storage_key::is_balance_key(token_addr, key)
    }

    fn is_any_token_balance_key(key: &storage::Key) -> Option<[&Address; 2]> {
        storage_key::is_any_token_balance_key(key)
    }

    fn minter_key(token_addr: &Address) -> storage::Key {
        storage_key::minter_key(token_addr)
    }

    fn parameter_prefix(token_addr: &Address) -> storage::Key {
        storage_key::parameter_prefix(token_addr)
    }

    fn minted_balance_key(token_addr: &Address) -> namada_core::storage::Key {
        storage_key::minted_balance_key(token_addr)
    }

    fn is_any_minted_balance_key(
        key: &namada_core::storage::Key,
    ) -> Option<&Address> {
        storage_key::is_any_minted_balance_key(key)
    }
}

impl<S> Read<S> for Store<S>
where
    S: StorageRead,
{
    fn read_denom(
        storage: &S,
        token: &Address,
    ) -> Result<Option<token::Denomination>> {
        storage::read_denom(storage, token)
    }

    fn get_effective_total_native_supply(storage: &S) -> Result<token::Amount> {
        storage::get_effective_total_native_supply(storage)
    }

    fn read_balance(
        storage: &S,
        token: &Address,
        owner: &Address,
    ) -> Result<token::Amount> {
        storage::read_balance(storage, token, owner)
    }
}

impl<S> Write<S> for Store<S>
where
    S: StorageWrite + StorageRead,
{
    fn transfer(
        storage: &mut S,
        token: &Address,
        src: &Address,
        dest: &Address,
        amount: Amount,
    ) -> Result<()> {
        storage::transfer(storage, token, src, dest, amount)
    }

    fn burn_tokens(
        storage: &mut S,
        token: &Address,
        source: &Address,
        amount: Amount,
    ) -> Result<()> {
        storage::burn_tokens(storage, token, source, amount)
    }

    fn credit_tokens(
        storage: &mut S,
        token: &Address,
        dest: &Address,
        amount: Amount,
    ) -> Result<()> {
        storage::credit_tokens(storage, token, dest, amount)
    }
}

impl<S> Events<S> for Store<S>
where
    S: StorageRead + EmitEvents,
{
    fn emit_mint_event(
        storage: &mut S,
        descriptor: std::borrow::Cow<'static, str>,
        token: &Address,
        amount: token::Amount,
        target: &Address,
    ) -> Result<()> {
        let post_balance: Uint =
            Self::read_balance(storage, token, target)?.into();

        storage.emit(TokenEvent {
            descriptor,
            level: EventLevel::Tx,
            operation: TokenOperation::Mint {
                token: token.clone(),
                amount: amount.into(),
                post_balance,
                target_account: UserAccount::Internal(target.clone()),
            },
        });
        Ok(())
    }

    fn emit_burn_event(
        storage: &mut S,
        descriptor: std::borrow::Cow<'static, str>,
        token: &Address,
        amount: token::Amount,
        target: &Address,
    ) -> Result<()> {
        let post_balance: Uint =
            Self::read_balance(storage, token, target)?.into();

        storage.emit(TokenEvent {
            descriptor,
            level: EventLevel::Tx,
            operation: TokenOperation::Burn {
                token: token.clone(),
                amount: amount.into(),
                post_balance,
                target_account: UserAccount::Internal(target.clone()),
            },
        });
        Ok(())
    }

    fn emit_transfer_event(
        storage: &mut S,
        descriptor: std::borrow::Cow<'static, str>,
        level: EventLevel,
        token: &Address,
        amount: token::Amount,
        source: UserAccount,
        target: UserAccount,
    ) -> Result<()> {
        let post_source_balance: Option<Uint> = match &source {
            UserAccount::Internal(addr) => {
                Some(Self::read_balance(storage, token, addr)?.into())
            }
            UserAccount::External(_) => None,
        };
        let post_target_balance: Option<Uint> = match &target {
            UserAccount::Internal(addr) => {
                Some(Self::read_balance(storage, token, addr)?.into())
            }
            UserAccount::External(_) => None,
        };

        let sources = BTreeMap::from_iter([(
            (source.clone(), token.clone()),
            amount.into(),
        )]);
        let targets = BTreeMap::from_iter([(
            (target.clone(), token.clone()),
            amount.into(),
        )]);

        let mut post_balances = BTreeMap::new();
        if let Some(balance) = post_source_balance {
            post_balances.insert((source, token.clone()), balance);
        }
        if let Some(balance) = post_target_balance {
            post_balances.insert((target, token.clone()), balance);
        }
        storage.emit(TokenEvent {
            descriptor,
            level,
            operation: TokenOperation::Transfer {
                sources,
                targets,
                post_balances,
            },
        });
        Ok(())
    }
}