use cosmwasm_std::{Addr, Deps, StdError, StdResult, Uint256};
pub type Token = (Addr, Uint256);
pub type TokenHuman = (String, Uint256);
pub type Tokens = Vec<Token>;
pub type TokensHuman = Vec<TokenHuman>;
pub trait TokensMath {
fn sub(&mut self, collaterals: Tokens) -> StdResult<()>;
fn add(&mut self, collaterals: Tokens);
fn assert_duplicate_token(&self);
}
pub trait TokensToHuman {
fn to_human(&self, deps: Deps) -> StdResult<TokensHuman>;
}
pub trait TokensToRaw {
fn to_raw(&self, deps: Deps) -> StdResult<Tokens>;
}
impl TokensMath for Tokens {
fn sub(&mut self, tokens: Tokens) -> StdResult<()> {
self.sort_by(|a, b| a.0.as_bytes().cmp(b.0.as_bytes()));
self.assert_duplicate_token();
let mut tokens = tokens;
tokens.sort_by(|a, b| a.0.as_bytes().cmp(b.0.as_bytes()));
tokens.assert_duplicate_token();
let mut i = 0;
let mut j = 0;
while i < self.len() && j < tokens.len() {
if self[i].0 == tokens[j].0 {
if self[i].1 < tokens[j].1 {
return Err(StdError::generic_err("Subtraction underflow"));
}
self[i].1 = self[i].1 - tokens[j].1;
i += 1;
j += 1;
} else if self[i].0.as_bytes().cmp(tokens[j].0.as_bytes()) == std::cmp::Ordering::Less {
i += 1;
} else {
return Err(StdError::generic_err("Subtraction underflow"));
}
}
if j != tokens.len() {
return Err(StdError::generic_err("Subtraction underflow"));
}
self.retain(|v| v.1 > Uint256::zero());
Ok(())
}
fn add(&mut self, tokens: Tokens) {
self.sort_by(|a, b| a.0.as_bytes().cmp(b.0.as_bytes()));
self.assert_duplicate_token();
let mut tokens = tokens;
tokens.sort_by(|a, b| a.0.as_bytes().cmp(b.0.as_bytes()));
tokens.assert_duplicate_token();
let mut tmp_tokens: Tokens = vec![];
let mut i = 0;
let mut j = 0;
while i < self.len() && j < tokens.len() {
if self[i].0 == tokens[j].0 {
tmp_tokens.push((self[i].0.clone(), self[i].1 + tokens[j].1));
i += 1;
j += 1;
} else if self[i].0.as_bytes().cmp(tokens[j].0.as_bytes())
== std::cmp::Ordering::Greater
{
tmp_tokens.push((tokens[j].0.clone(), tokens[j].1));
j += 1;
} else {
tmp_tokens.push((self[i].0.clone(), self[i].1));
i += 1;
}
}
while j < tokens.len() {
tmp_tokens.push((tokens[j].0.clone(), tokens[j].1));
j += 1;
}
while i < self.len() {
tmp_tokens.push((self[i].0.clone(), self[i].1));
i += 1;
}
tmp_tokens.retain(|v| v.1 > Uint256::zero());
self.clear();
self.extend(tmp_tokens);
}
fn assert_duplicate_token(&self) {
if self.len() > 1 {
let mut before_token = self[0].0.as_bytes();
let mut i = 1;
while i < self.len() {
let next_token = self[i].0.as_bytes();
if before_token == next_token {
panic!("duplicate token address");
}
before_token = next_token;
i += 1;
}
}
}
}
impl TokensToHuman for Tokens {
fn to_human(&self, _deps: Deps) -> StdResult<TokensHuman> {
let collaterals: TokensHuman = self
.iter()
.map(|c| Ok((c.0.to_string(), c.1)))
.collect::<StdResult<TokensHuman>>()?;
Ok(collaterals)
}
}
impl TokensToRaw for TokensHuman {
fn to_raw(&self, deps: Deps) -> StdResult<Tokens> {
let collaterals: Tokens = self
.iter()
.map(|c| Ok((deps.api.addr_validate(c.0.as_str())?, c.1)))
.collect::<StdResult<Tokens>>()?;
Ok(collaterals)
}
}