use std::collections::HashMap;
use cosmwasm_std::{BankMsg, Coin, CosmosMsg, StdError, StdResult, Uint128};
pub const FACTORY_PREFIX: &str = "factory";
pub const FACTORY_MAX_SUBDENOM_SIZE: usize = 44usize;
pub fn is_factory_token(denom: &str) -> bool {
let split: Vec<&str> = denom.splitn(3, '/').collect();
if split.len() != 3 || split[0] != FACTORY_PREFIX {
return false;
}
let subdenom = split[2];
let valid_subdenom = subdenom
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '/' || c == '.');
if !valid_subdenom {
return false;
}
if subdenom.len() > FACTORY_MAX_SUBDENOM_SIZE {
return false;
}
let creator_address = split[1];
let total_len = FACTORY_PREFIX.len() + 2 + creator_address.len() + subdenom.len();
if total_len > 128 {
return false;
}
true
}
pub fn get_factory_token_subdenom(denom: &str) -> StdResult<&str> {
let subdenom = denom.splitn(3, '/').nth(2);
subdenom.map_or_else(
|| {
Err(StdError::generic_err(
"Splitting factory token subdenom failed",
))
},
Ok,
)
}
#[allow(clippy::needless_splitn)]
pub fn get_factory_token_creator(denom: &str) -> StdResult<&str> {
let creator = denom.splitn(3, '/').nth(1);
creator.map_or_else(
|| {
Err(StdError::generic_err(
"Splitting factory token creator failed",
))
},
Ok,
)
}
pub fn add_coins(coins: Vec<Coin>, to_add: Vec<Coin>) -> StdResult<Vec<Coin>> {
let mut updated_coins = coins.to_vec();
for coin in to_add {
if let Some(existing_coin) = updated_coins.iter_mut().find(|c| c.denom == coin.denom) {
existing_coin.amount = existing_coin.amount.checked_add(coin.amount)?;
} else {
return Err(StdError::generic_err(format!(
"Error: Cannot add {} {}. Coin not found.",
coin.amount, coin.denom
)));
}
}
updated_coins.retain(|coin| coin.amount > Uint128::zero());
Ok(updated_coins)
}
pub fn aggregate_coins(coins: Vec<Coin>) -> StdResult<Vec<Coin>> {
let mut aggregation_map: HashMap<String, Uint128> = HashMap::new();
for coin in coins {
if let Some(existing_amount) = aggregation_map.get_mut(&coin.denom) {
*existing_amount = existing_amount.checked_add(coin.amount)?;
} else {
aggregation_map.insert(coin.denom.clone(), coin.amount);
}
}
let mut aggregated_coins: Vec<Coin> = Vec::new();
for (denom, amount) in aggregation_map {
aggregated_coins.push(Coin { denom, amount });
}
aggregated_coins.sort_by(|a, b| a.denom.cmp(&b.denom));
Ok(aggregated_coins)
}
pub fn burn_coin_msg(coin: Coin) -> CosmosMsg {
CosmosMsg::Bank(BankMsg::Burn { amount: vec![coin] })
}
#[cfg(test)]
mod coin_tests {
use crate::coin::{get_factory_token_creator, is_factory_token};
#[test]
fn is_factory_token_test() {
let coin_0 = "ibc/3A6F4C8D5B2E7A1F0C4D5B6E7A8F9C3D4E5B6A7F8E9C4D5B6E7A8F9C3D4E5B6A";
let coin_1 = "ibc/A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6A7B8C9D0E1F2";
let coin_2 = "factory/mantra158xlpsqqkqpkmcrgnlcrc5fjyhy7j7x2vpa79r/subdenom";
let coin_3 = "factory/mantra1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqlydlr9/ibc/3A6F4C8D5B2E7A1F0C4D5B6E7A8F9C3D4E5B6A7F8E9C4D5B6E7A8F9C3D4E5B6A-ibc/A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6A7B8C9D0E1F2.pool.0.LP";
let coin_4 = "factory/mantra1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqlydlr9/invalid-denom";
let coin_5 = "uom";
assert!(!is_factory_token(coin_0));
assert!(!is_factory_token(coin_1));
assert!(is_factory_token(coin_2));
assert!(!is_factory_token(coin_3));
assert!(!is_factory_token(coin_4));
assert!(!is_factory_token(coin_5));
}
#[test]
fn test_factory_token_creator() {
let denom = "factory/creator/subdenom";
assert_eq!(get_factory_token_creator(denom).unwrap(), "creator");
}
}