diff --git a/Cargo.lock b/Cargo.lock index 96decfcfa..87e32a36f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,7 +221,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "itertools 0.12.1", "prost 0.11.9", "thiserror", @@ -275,7 +275,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "itertools 0.12.1", "proptest", "thiserror", @@ -297,7 +297,7 @@ dependencies = [ "cw-storage-plus 1.2.0", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "thiserror", ] @@ -331,7 +331,7 @@ dependencies = [ "cw-storage-plus 0.15.1", "cw2 1.1.2", "cw20 0.15.1", - "cw20-base 1.1.2", + "cw20-base", "itertools 0.10.5", "thiserror", ] @@ -368,7 +368,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "integer-sqrt", "proptest", "prost 0.11.9", @@ -394,7 +394,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "derivative", "itertools 0.12.1", "proptest", @@ -417,7 +417,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "derivative", "itertools 0.12.1", "serde", @@ -441,7 +441,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "derivative", "itertools 0.12.1", "proptest", @@ -465,7 +465,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "derivative", "itertools 0.12.1", "thiserror", @@ -507,7 +507,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "integer-sqrt", "proptest", "prost 0.11.9", @@ -545,7 +545,7 @@ dependencies = [ "cw-storage-plus 0.15.1", "cw2 1.1.2", "cw20 0.15.1", - "cw20-base 1.1.2", + "cw20-base", "integer-sqrt", "thiserror", ] @@ -646,7 +646,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base 1.1.2", + "cw20-base", "thiserror", ] @@ -662,21 +662,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "astroport-xastro-token" -version = "1.1.0" -dependencies = [ - "astroport 3.12.2", - "cosmwasm-schema", - "cosmwasm-std", - "cw-multi-test 1.2.0", - "cw-storage-plus 0.15.1", - "cw2 0.15.1", - "cw20 0.15.1", - "cw20-base 0.15.1", - "snafu", -] - [[package]] name = "async-trait" version = "0.1.80" @@ -1358,24 +1343,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cw20-base" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0909c56d0c14601fbdc69382189799482799dcad87587926aec1f3aa321abc41" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "cw-utils 0.15.1", - "cw2 0.15.1", - "cw20 0.15.1", - "schemars", - "semver", - "serde", - "thiserror", -] - [[package]] name = "cw20-base" version = "1.1.2" @@ -1459,12 +1426,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "dyn-clone" version = "1.0.17" @@ -3204,27 +3165,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "snafu" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "socket2" version = "0.5.7" diff --git a/contracts/tokenomics/xastro_token/.cargo/config b/contracts/tokenomics/xastro_token/.cargo/config deleted file mode 100644 index 13f51ac69..000000000 --- a/contracts/tokenomics/xastro_token/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[alias] -wasm = "build --release --target wasm32-unknown-unknown" -wasm-debug = "build --target wasm32-unknown-unknown" -unit-test = "test --lib" -schema = "run --example xastro_token_schema" diff --git a/contracts/tokenomics/xastro_token/Cargo.toml b/contracts/tokenomics/xastro_token/Cargo.toml deleted file mode 100644 index c545b75d3..000000000 --- a/contracts/tokenomics/xastro_token/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "astroport-xastro-token" -version = "1.1.0" -authors = ["Astroport"] -edition = "2021" -description = "Expanded implementation of a CosmWasm-20 compliant token for post intialization and saving history" -license = "MIT" -repository = "https://github.com/CosmWasm/cosmwasm-plus" -homepage = "https://cosmwasm.com" -documentation = "https://docs.cosmwasm.com" - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -backtraces = ["cosmwasm-std/backtraces"] -# use library feature to disable all init/handle/query exports -library = [] - -[dependencies] -astroport = "3" -cw2 = "0.15" -cw20 = "0.15" -cw20-base = { version = "0.15", features = ["library"] } -cw-storage-plus = "0.15" -cosmwasm-std = { version = "1.1", features = ["iterator"] } -snafu = { version = "0.6" } -cosmwasm-schema = "1.1" - -[dev-dependencies] -cw-multi-test = "1.0.0" diff --git a/contracts/tokenomics/xastro_token/README.md b/contracts/tokenomics/xastro_token/README.md deleted file mode 100644 index 77ebccbd7..000000000 --- a/contracts/tokenomics/xastro_token/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Astroport xASTRO Token - -This is the Astroport xASTRO token implementation. - ---- - -## CW20 Based Token Contract - -This is a basic implementation of a CW20 base contract which can be found [here](https://github.com/CosmWasm/cw-plus/tree/main/contracts/cw20-base). It implements the [CW20 spec](https://github.com/CosmWasm/cosmwasm-plus/tree/master/packages/cw20) and is designed to be imported into other contracts in order to easily build other CW20-compatible tokens with balance snapshotting logic. diff --git a/contracts/tokenomics/xastro_token/examples/xastro_token_schema.rs b/contracts/tokenomics/xastro_token/examples/xastro_token_schema.rs deleted file mode 100644 index bf9c7f961..000000000 --- a/contracts/tokenomics/xastro_token/examples/xastro_token_schema.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cosmwasm_schema::write_api; - -use astroport::xastro_token::{InstantiateMsg, QueryMsg}; -use cw20_base::msg::ExecuteMsg; - -fn main() { - write_api! { - instantiate: InstantiateMsg, - query: QueryMsg, - execute: ExecuteMsg - } -} diff --git a/contracts/tokenomics/xastro_token/src/contract.rs b/contracts/tokenomics/xastro_token/src/contract.rs deleted file mode 100644 index c49495e45..000000000 --- a/contracts/tokenomics/xastro_token/src/contract.rs +++ /dev/null @@ -1,850 +0,0 @@ -use cosmwasm_std::{ - attr, entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, - StdError, StdResult, Uint128, -}; -use cw20::{ - AllAccountsResponse, BalanceResponse, Cw20Coin, Cw20ReceiveMsg, EmbeddedLogo, Logo, LogoInfo, - MarketingInfoResponse, -}; -use cw20_base::allowances::{ - deduct_allowance, execute_decrease_allowance, execute_increase_allowance, query_allowance, -}; - -use crate::state::{capture_total_supply_history, check_minter, get_total_supply_at, BALANCES}; -use astroport::asset::addr_opt_validate; -use astroport::xastro_token::{InstantiateMsg, MigrateMsg, QueryMsg}; -use cw2::{get_contract_version, set_contract_version}; -use cw20_base::contract::{ - execute_update_marketing, execute_upload_logo, query_download_logo, query_marketing_info, - query_minter, query_token_info, -}; -use cw20_base::enumerable::query_owner_allowances; -use cw20_base::msg::ExecuteMsg; -use cw20_base::state::{MinterData, TokenInfo, LOGO, MARKETING_INFO, TOKEN_INFO}; -use cw20_base::ContractError; -use cw_storage_plus::Bound; - -/// Contract name that is used for migration. -const CONTRACT_NAME: &str = "astroport-xastro-token"; -/// Contract version that is used for migration. -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); - -// Settings for pagination. -const MAX_LIMIT: u32 = 30; -const DEFAULT_LIMIT: u32 = 10; - -const LOGO_SIZE_CAP: usize = 5 * 1024; - -/// Checks if data starts with XML preamble -fn verify_xml_preamble(data: &[u8]) -> Result<(), ContractError> { - // The easiest way to perform this check would be just match on regex, however regex - // compilation is heavy and probably not worth it. - - let preamble = data - .split_inclusive(|c| *c == b'>') - .next() - .ok_or(ContractError::InvalidXmlPreamble {})?; - - const PREFIX: &[u8] = b""; - - if !(preamble.starts_with(PREFIX) && preamble.ends_with(POSTFIX)) { - Err(ContractError::InvalidXmlPreamble {}) - } else { - Ok(()) - } - - // Additionally attributes format could be validated as they are well defined, as well as - // comments presence inside of preable, but it is probably not worth it. -} - -/// Validates XML logo -fn verify_xml_logo(logo: &[u8]) -> Result<(), ContractError> { - verify_xml_preamble(logo)?; - - if logo.len() > LOGO_SIZE_CAP { - Err(ContractError::LogoTooBig {}) - } else { - Ok(()) - } -} - -/// Validates png logo -fn verify_png_logo(logo: &[u8]) -> Result<(), ContractError> { - // PNG header format: - // 0x89 - magic byte, out of ASCII table to fail on 7-bit systems - // "PNG" ascii representation - // [0x0d, 0x0a] - dos style line ending - // 0x1a - dos control character, stop displaying rest of the file - // 0x0a - unix style line ending - const HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; - if logo.len() > LOGO_SIZE_CAP { - Err(ContractError::LogoTooBig {}) - } else if !logo.starts_with(&HEADER) { - Err(ContractError::InvalidPngHeader {}) - } else { - Ok(()) - } -} - -/// Checks if passed logo is correct, and if not, returns an error -fn verify_logo(logo: &Logo) -> Result<(), ContractError> { - match logo { - Logo::Embedded(EmbeddedLogo::Svg(logo)) => verify_xml_logo(logo), - Logo::Embedded(EmbeddedLogo::Png(logo)) => verify_png_logo(logo), - Logo::Url(_) => Ok(()), // Any reasonable url validation would be regex based, probably not worth it - } -} - -/// Creates a new contract with the specified parameters in the [`InstantiateMsg`]. -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn instantiate( - mut deps: DepsMut, - env: Env, - _info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - // Check valid token info - msg.validate()?; - - // Create initial accounts - let total_supply = create_accounts(&mut deps, &env, &msg.initial_balances)?; - - if !total_supply.is_zero() { - capture_total_supply_history(deps.storage, &env, total_supply)?; - } - - // Check supply cap - if let Some(limit) = msg.get_cap() { - if total_supply > limit { - return Err(StdError::generic_err("Initial supply greater than cap").into()); - } - } - - let mint = match msg.mint { - Some(m) => Some(MinterData { - minter: deps.api.addr_validate(&m.minter)?, - cap: m.cap, - }), - None => None, - }; - - // Store token info - let data = TokenInfo { - name: msg.name, - symbol: msg.symbol, - decimals: msg.decimals, - total_supply, - mint, - }; - TOKEN_INFO.save(deps.storage, &data)?; - - if let Some(marketing) = msg.marketing { - let logo = if let Some(logo) = marketing.logo { - verify_logo(&logo)?; - LOGO.save(deps.storage, &logo)?; - - match logo { - Logo::Url(url) => Some(LogoInfo::Url(url)), - Logo::Embedded(_) => Some(LogoInfo::Embedded), - } - } else { - None - }; - - let data = MarketingInfoResponse { - project: marketing.project, - description: marketing.description, - marketing: marketing - .marketing - .map(|addr| deps.api.addr_validate(&addr)) - .transpose()?, - logo, - }; - MARKETING_INFO.save(deps.storage, &data)?; - } - - Ok(Response::default()) -} - -/// Mints tokens for specific accounts. -/// -/// * **accounts** array with accounts for which to mint tokens. -pub fn create_accounts(deps: &mut DepsMut, env: &Env, accounts: &[Cw20Coin]) -> StdResult { - let mut total_supply = Uint128::zero(); - - for row in accounts { - let address = deps.api.addr_validate(&row.address)?; - BALANCES.save(deps.storage, &address, &row.amount, env.block.height)?; - total_supply += row.amount; - } - - Ok(total_supply) -} - -/// Exposes execute functions available in the contract. -/// -/// ## Variants -/// * **ExecuteMsg::Transfer { recipient, amount }** Transfers tokens to recipient. -/// -/// * **ExecuteMsg::Burn { amount }** Burns tokens. -/// -/// * **ExecuteMsg::Send { contract, amount, msg }** Sends tokens to contract and executes message. -/// -/// * **ExecuteMsg::Mint { recipient, amount }** Mints tokens. -/// -/// * **ExecuteMsg::IncreaseAllowance { spender, amount, expires }** Increases allowance. -/// -/// * **ExecuteMsg::DecreaseAllowance { spender, amount, expires }** Decreases allowance. -/// -/// * **ExecuteMsg::TransferFrom { owner, recipient, amount }** Transfers tokens from. -/// -/// * **ExecuteMsg::BurnFrom { owner, amount }** Burns tokens from. -/// -/// * **ExecuteMsg::SendFrom { owner, contract, amount, msg }** Sends tokens from. -/// -/// * **ExecuteMsg::UpdateMarketing { project, description, marketing }** Updates marketing info. -/// -/// * **ExecuteMsg::UploadLogo(logo)** Uploads logo. -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::Transfer { recipient, amount } => { - execute_transfer(deps, env, info, recipient, amount) - } - ExecuteMsg::Burn { amount } => execute_burn(deps, env, info, amount), - ExecuteMsg::Send { - contract, - amount, - msg, - } => execute_send(deps, env, info, contract, amount, msg), - ExecuteMsg::Mint { recipient, amount } => execute_mint(deps, env, info, recipient, amount), - ExecuteMsg::IncreaseAllowance { - spender, - amount, - expires, - } => execute_increase_allowance(deps, env, info, spender, amount, expires), - ExecuteMsg::DecreaseAllowance { - spender, - amount, - expires, - } => execute_decrease_allowance(deps, env, info, spender, amount, expires), - ExecuteMsg::TransferFrom { - owner, - recipient, - amount, - } => execute_transfer_from(deps, env, info, owner, recipient, amount), - ExecuteMsg::BurnFrom { owner, amount } => execute_burn_from(deps, env, info, owner, amount), - ExecuteMsg::SendFrom { - owner, - contract, - amount, - msg, - } => execute_send_from(deps, env, info, owner, contract, amount, msg), - ExecuteMsg::UpdateMarketing { - project, - description, - marketing, - } => execute_update_marketing(deps, env, info, project, description, marketing), - ExecuteMsg::UploadLogo(logo) => execute_upload_logo(deps, env, info, logo), - _ => Err(StdError::generic_err("Unsupported execute message").into()), - } -} - -/// Executes a token transfer. -pub fn execute_transfer( - deps: DepsMut, - env: Env, - info: MessageInfo, - recipient: String, - amount: Uint128, -) -> Result { - if amount == Uint128::zero() { - return Err(ContractError::InvalidZeroAmount {}); - } - - let rcpt_addr = deps.api.addr_validate(&recipient)?; - - BALANCES.update( - deps.storage, - &info.sender, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default().checked_sub(amount)?) }, - )?; - BALANCES.update( - deps.storage, - &rcpt_addr, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, - )?; - - Ok(Response::new().add_attributes(vec![ - attr("action", "transfer"), - attr("from", info.sender), - attr("to", rcpt_addr), - attr("amount", amount), - ])) -} - -/// Burns a token. -/// -/// * **amount** amount of tokens that the function caller wants to burn from their own account. -pub fn execute_burn( - deps: DepsMut, - env: Env, - info: MessageInfo, - amount: Uint128, -) -> Result { - if amount == Uint128::zero() { - return Err(ContractError::InvalidZeroAmount {}); - } - - let config = TOKEN_INFO.load(deps.storage)?; - check_minter(&info.sender, &config)?; - - // Lower the sender's balance - BALANCES.update( - deps.storage, - &info.sender, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default().checked_sub(amount)?) }, - )?; - - // Reduce total_supply - let token_info = TOKEN_INFO.update(deps.storage, |mut info| -> StdResult<_> { - info.total_supply = info.total_supply.checked_sub(amount)?; - Ok(info) - })?; - - capture_total_supply_history(deps.storage, &env, token_info.total_supply)?; - - let res = Response::new().add_attributes(vec![ - attr("action", "burn"), - attr("from", info.sender), - attr("amount", amount), - ]); - Ok(res) -} - -/// Mints a token. -pub fn execute_mint( - deps: DepsMut, - env: Env, - info: MessageInfo, - recipient: String, - amount: Uint128, -) -> Result { - if amount == Uint128::zero() { - return Err(ContractError::InvalidZeroAmount {}); - } - - let mut config = TOKEN_INFO.load(deps.storage)?; - check_minter(&info.sender, &config)?; - - // Update supply and enforce cap - config.total_supply = config - .total_supply - .checked_add(amount) - .map_err(StdError::from)?; - if let Some(limit) = config.get_cap() { - if config.total_supply > limit { - return Err(ContractError::CannotExceedCap {}); - } - } - - TOKEN_INFO.save(deps.storage, &config)?; - - capture_total_supply_history(deps.storage, &env, config.total_supply)?; - - // Add amount to recipient balance - let rcpt_addr = deps.api.addr_validate(&recipient)?; - BALANCES.update( - deps.storage, - &rcpt_addr, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, - )?; - - Ok(Response::new().add_attributes(vec![ - attr("action", "mint"), - attr("to", rcpt_addr), - attr("amount", amount), - ])) -} - -/// Executes a token send. -/// -/// * **contract** token contract. -/// -/// * **amount** amount of tokens to send. -/// -/// * **msg** internal serialized message. -pub fn execute_send( - deps: DepsMut, - env: Env, - info: MessageInfo, - contract: String, - amount: Uint128, - msg: Binary, -) -> Result { - if amount == Uint128::zero() { - return Err(ContractError::InvalidZeroAmount {}); - } - - let rcpt_addr = deps.api.addr_validate(&contract)?; - - // Move the tokens to the contract - BALANCES.update( - deps.storage, - &info.sender, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default().checked_sub(amount)?) }, - )?; - BALANCES.update( - deps.storage, - &rcpt_addr, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) }, - )?; - - let res = Response::new() - .add_attributes(vec![ - attr("action", "send"), - attr("from", &info.sender), - attr("to", &rcpt_addr), - attr("amount", amount), - ]) - .add_message( - Cw20ReceiveMsg { - sender: info.sender.into(), - amount, - msg, - } - .into_cosmos_msg(contract)?, - ); - Ok(res) -} - -/// Executes a transfer from. -/// -/// * **owner** account from which to transfer tokens. -/// -/// * **recipient** transfer recipient. -/// -/// * **amount** amount to transfer. -pub fn execute_transfer_from( - deps: DepsMut, - env: Env, - info: MessageInfo, - owner: String, - recipient: String, - amount: Uint128, -) -> Result { - let rcpt_addr = deps.api.addr_validate(&recipient)?; - let owner_addr = deps.api.addr_validate(&owner)?; - - // Deduct allowance before doing anything else - deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; - - BALANCES.update( - deps.storage, - &owner_addr, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default().checked_sub(amount)?) }, - )?; - BALANCES.update( - deps.storage, - &rcpt_addr, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default().checked_add(amount)?) }, - )?; - - let res = Response::new().add_attributes(vec![ - attr("action", "transfer_from"), - attr("from", owner), - attr("to", recipient), - attr("by", info.sender), - attr("amount", amount), - ]); - Ok(res) -} - -/// Executes a burn from. -/// -/// * **owner** account from which to burn tokens. -/// -/// * **amount** amount of tokens to burn. -pub fn execute_burn_from( - deps: DepsMut, - env: Env, - info: MessageInfo, - owner: String, - amount: Uint128, -) -> Result { - let owner_addr = deps.api.addr_validate(&owner)?; - - let config = TOKEN_INFO.load(deps.storage)?; - check_minter(&info.sender, &config)?; - - // Deduct allowance before doing anything else - deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; - - // Lower balance - BALANCES.update( - deps.storage, - &owner_addr, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default().checked_sub(amount)?) }, - )?; - - // Reduce total_supply - let token_info = TOKEN_INFO.update(deps.storage, |mut meta| -> StdResult<_> { - meta.total_supply = meta.total_supply.checked_sub(amount)?; - Ok(meta) - })?; - - capture_total_supply_history(deps.storage, &env, token_info.total_supply)?; - - let res = Response::new().add_attributes(vec![ - attr("action", "burn_from"), - attr("from", owner), - attr("by", info.sender), - attr("amount", amount), - ]); - Ok(res) -} - -/// Executes a send from. -/// -/// * **owner** account from which to send tokens. -/// -/// * **contract** token contract address. -/// -/// * **amount** amount of tokens to send. -/// -/// * **msg** internal serialized message. -pub fn execute_send_from( - deps: DepsMut, - env: Env, - info: MessageInfo, - owner: String, - contract: String, - amount: Uint128, - msg: Binary, -) -> Result { - let rcpt_addr = deps.api.addr_validate(&contract)?; - let owner_addr = deps.api.addr_validate(&owner)?; - - // Deduct allowance before doing anything else - deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?; - - // Move the tokens to the contract - BALANCES.update( - deps.storage, - &owner_addr, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default().checked_sub(amount)?) }, - )?; - BALANCES.update( - deps.storage, - &rcpt_addr, - env.block.height, - |balance| -> StdResult<_> { Ok(balance.unwrap_or_default().checked_add(amount)?) }, - )?; - - let res = Response::new() - .add_attributes(vec![ - attr("action", "send_from"), - attr("from", &owner), - attr("to", &contract), - attr("by", &info.sender), - attr("amount", amount), - ]) - .add_message( - Cw20ReceiveMsg { - sender: info.sender.into(), - amount, - msg, - } - .into_cosmos_msg(contract)?, - ); - Ok(res) -} - -/// Exposes all the queries available in the contract. -/// -/// ## Queries -/// * **Balance { address: String }** Returns the current balance of the given address, 0 if unset. -/// Uses a [`BalanceResponse`] object. -/// -/// * **BalanceAt { address, block }** Returns the balance of the given address at the given block -/// using a [`BalanceResponse`] object. -/// -/// * **TotalSupplyAt { block }** Returns the total supply at the given block. -/// -/// * **TokenInfo {}** Returns the token metadata - name, decimals, supply, etc -/// using a [`cw20::TokenInfoResponse`] object. -/// -/// * **Minter {}** Returns the address that can mint tokens and the hard cap on the total amount of tokens using -/// a [`cw20::MinterResponse`] object. -/// -/// * **QueryMsg::Allowance { owner, spender }** Returns how much the spender can use from the owner account, 0 if unset. -/// Uses a [`cw20::AllowanceResponse`] object. -/// -/// * **QueryMsg::AllAllowances { owner, start_after, limit }** Returns all allowances this owner has approved -/// using a [`cw20::AllAllowancesResponse`] object. -/// -/// * **QueryMsg::AllAccounts { start_after, limit }** Returns all accounts that have a balance -/// using a [`cw20::AllAccountsResponse`] object. -/// -/// * **QueryMsg::MarketingInfo {}** Returns the token metadata -/// using a [`cw20::MarketingInfoResponse`] object. -/// -/// * **QueryMsg::DownloadLogo {}** Downloads the embedded logo data (if stored on-chain) -/// and returns the result using a [`cw20::DownloadLogoResponse`] object. -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg { - QueryMsg::Balance { address } => to_json_binary(&query_balance(deps, address)?), - QueryMsg::BalanceAt { address, block } => { - to_json_binary(&query_balance_at(deps, address, block)?) - } - QueryMsg::TotalSupplyAt { block } => { - to_json_binary(&get_total_supply_at(deps.storage, block)?) - } - QueryMsg::TokenInfo {} => to_json_binary(&query_token_info(deps)?), - QueryMsg::Minter {} => to_json_binary(&query_minter(deps)?), - QueryMsg::Allowance { owner, spender } => { - to_json_binary(&query_allowance(deps, owner, spender)?) - } - QueryMsg::AllAllowances { - owner, - start_after, - limit, - } => to_json_binary(&query_owner_allowances(deps, owner, start_after, limit)?), - QueryMsg::AllAccounts { start_after, limit } => { - to_json_binary(&query_all_accounts(deps, start_after, limit)?) - } - QueryMsg::MarketingInfo {} => to_json_binary(&query_marketing_info(deps)?), - QueryMsg::DownloadLogo {} => to_json_binary(&query_download_logo(deps)?), - } -} - -/// Returns the specified account's balance. -pub fn query_balance(deps: Deps, address: String) -> StdResult { - let address = deps.api.addr_validate(&address)?; - let balance = BALANCES - .may_load(deps.storage, &address)? - .unwrap_or_default(); - Ok(BalanceResponse { balance }) -} - -/// Returns the balance of the given address at the given block. -pub fn query_balance_at(deps: Deps, address: String, block: u64) -> StdResult { - let address = deps.api.addr_validate(&address)?; - let balance = BALANCES - .may_load_at_height(deps.storage, &address, block)? - .unwrap_or_default(); - Ok(BalanceResponse { balance }) -} - -/// Returns the current balances of multiple accounts. -/// -/// * **start_after** account from which to start querying for balances. -/// -/// * **limit** amount of account balances to return. -pub fn query_all_accounts( - deps: Deps, - start_after: Option, - limit: Option, -) -> StdResult { - let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start = addr_opt_validate(deps.api, &start_after)?; - let start = start.as_ref().map(Bound::exclusive); - - let accounts = BALANCES - .keys(deps.storage, start, None, Order::Ascending) - .take(limit) - .map(|addr| addr.map(Into::into)) - .collect::>()?; - - Ok(AllAccountsResponse { accounts }) -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { - let contract_version = get_contract_version(deps.storage)?; - - match contract_version.contract.as_ref() { - "astroport-xastro-token" => match contract_version.version.as_ref() { - "1.0.0" | "1.0.1" | "1.0.2" => {} - _ => { - return Err(StdError::generic_err( - "Cannot migrate. Unsupported contract version", - )) - } - }, - _ => { - return Err(StdError::generic_err( - "Cannot migrate. Unsupported contract name", - )) - } - } - - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - Ok(Response::default() - .add_attribute("previous_contract_name", &contract_version.contract) - .add_attribute("previous_contract_version", &contract_version.version) - .add_attribute("new_contract_name", CONTRACT_NAME) - .add_attribute("new_contract_version", CONTRACT_VERSION)) -} - -#[cfg(test)] -mod tests { - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{Addr, StdError}; - - use super::*; - use astroport::xastro_token::InstantiateMarketingInfo; - - mod marketing { - use cw20::DownloadLogoResponse; - use cw20_base::contract::{query_download_logo, query_marketing_info}; - - use super::*; - - #[test] - fn basic() { - let mut deps = mock_dependencies(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("marketing".to_owned()), - logo: Some(Logo::Url("url".to_owned())), - }), - }; - - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("marketing")), - logo: Some(LogoInfo::Url("url".to_owned())), - } - ); - - let err = query_download_logo(deps.as_ref()).unwrap_err(); - assert!( - matches!(err, StdError::NotFound { .. }), - "Expected StdError::NotFound, received {}", - err - ); - } - - #[test] - fn svg() { - let mut deps = mock_dependencies(); - let img = "".as_bytes(); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("marketing".to_owned()), - logo: Some(Logo::Embedded(EmbeddedLogo::Svg(img.into()))), - }), - }; - - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("marketing")), - logo: Some(LogoInfo::Embedded), - } - ); - - let res: DownloadLogoResponse = query_download_logo(deps.as_ref()).unwrap(); - assert_eq! { - res, - DownloadLogoResponse{ - data: img.into(), - mime_type: "image/svg+xml".to_owned(), - } - } - } - - #[test] - fn png() { - let mut deps = mock_dependencies(); - const PNG_HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![], - mint: None, - marketing: Some(InstantiateMarketingInfo { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some("marketing".to_owned()), - logo: Some(Logo::Embedded(EmbeddedLogo::Png(PNG_HEADER.into()))), - }), - }; - - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_marketing_info(deps.as_ref()).unwrap(), - MarketingInfoResponse { - project: Some("Project".to_owned()), - description: Some("Description".to_owned()), - marketing: Some(Addr::unchecked("marketing")), - logo: Some(LogoInfo::Embedded), - } - ); - - let res: DownloadLogoResponse = query_download_logo(deps.as_ref()).unwrap(); - assert_eq! { - res, - DownloadLogoResponse{ - data: PNG_HEADER.into(), - mime_type: "image/png".to_owned(), - } - } - } - } -} diff --git a/contracts/tokenomics/xastro_token/src/lib.rs b/contracts/tokenomics/xastro_token/src/lib.rs deleted file mode 100644 index fea2cb877..000000000 --- a/contracts/tokenomics/xastro_token/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod contract; -pub mod state; - -#[cfg(test)] -mod testing; diff --git a/contracts/tokenomics/xastro_token/src/state.rs b/contracts/tokenomics/xastro_token/src/state.rs deleted file mode 100644 index 9b458ec75..000000000 --- a/contracts/tokenomics/xastro_token/src/state.rs +++ /dev/null @@ -1,54 +0,0 @@ -use cosmwasm_std::{Addr, Env, Order, StdResult, Storage, Uint128}; -use cw20_base::state::TokenInfo; -use cw20_base::ContractError; -use cw_storage_plus::{Bound, Map, SnapshotMap, Strategy}; - -/// Contains snapshotted coins balances at every block. -pub const BALANCES: SnapshotMap<&Addr, Uint128> = SnapshotMap::new( - "balance", - "balance__checkpoints", - "balance__changelog", - Strategy::EveryBlock, -); - -/// Contains the history of the xASTRO total supply. -pub const TOTAL_SUPPLY_HISTORY: Map = Map::new("total_supply_history"); - -/// Snapshots the total token supply at current block. -/// -/// * **total_supply** current token total supply. -pub fn capture_total_supply_history( - storage: &mut dyn Storage, - env: &Env, - total_supply: Uint128, -) -> StdResult<()> { - TOTAL_SUPPLY_HISTORY.save(storage, env.block.height, &total_supply) -} - -/// Returns the total token supply at the given block. -pub fn get_total_supply_at(storage: &dyn Storage, block: u64) -> StdResult { - // Look for the last value recorded before the current block (if none then value is zero) - let end = Bound::inclusive(block); - let last_value_up_to_block = TOTAL_SUPPLY_HISTORY - .range(storage, None, Some(end), Order::Descending) - .next(); - - if let Some(value) = last_value_up_to_block { - let (_, v) = value?; - return Ok(v); - } - - Ok(Uint128::zero()) -} - -pub fn check_minter(sender: &Addr, config: &TokenInfo) -> Result<(), ContractError> { - if let Some(ref mint_data) = config.mint { - if mint_data.minter != sender { - return Err(ContractError::Unauthorized {}); - } - } else { - return Err(ContractError::Unauthorized {}); - } - - Ok(()) -} diff --git a/contracts/tokenomics/xastro_token/src/testing.rs b/contracts/tokenomics/xastro_token/src/testing.rs deleted file mode 100644 index 927edda15..000000000 --- a/contracts/tokenomics/xastro_token/src/testing.rs +++ /dev/null @@ -1,984 +0,0 @@ -use crate::contract::{ - execute, execute_burn_from, execute_send_from, execute_transfer_from, instantiate, - query_all_accounts, query_balance, query_balance_at, -}; -use crate::state::get_total_supply_at; -use astroport::xastro_token::InstantiateMsg; -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MOCK_CONTRACT_ADDR}; -use cosmwasm_std::{ - Addr, Binary, BlockInfo, ContractInfo, CosmosMsg, Deps, DepsMut, Env, StdError, SubMsg, - Timestamp, Uint128, WasmMsg, -}; -use cw20::{ - AllAccountsResponse, BalanceResponse, Cw20Coin, Cw20ReceiveMsg, MinterResponse, - TokenInfoResponse, -}; -use cw20_base::allowances::execute_increase_allowance; -use cw20_base::contract::{query_minter, query_token_info}; -use cw20_base::msg::ExecuteMsg; -use cw20_base::ContractError; - -pub struct MockEnvParams { - pub block_time: Timestamp, - pub block_height: u64, -} - -impl Default for MockEnvParams { - fn default() -> Self { - MockEnvParams { - block_time: Timestamp::from_nanos(1_571_797_419_879_305_533), - block_height: 1, - } - } -} - -pub fn test_mock_env(mock_env_params: MockEnvParams) -> Env { - Env { - block: BlockInfo { - height: mock_env_params.block_height, - time: mock_env_params.block_time, - chain_id: "cosmos-testnet-14002".to_string(), - }, - transaction: None, - contract: ContractInfo { - address: Addr::unchecked(MOCK_CONTRACT_ADDR), - }, - } -} - -fn get_balance>(deps: Deps, address: T) -> Uint128 { - query_balance(deps, address.into()).unwrap().balance -} - -// This will set up the instantiation for other tests -fn do_instantiate_with_minter( - deps: DepsMut, - addr: &str, - amount: Uint128, - minter: &str, - cap: Option, -) -> TokenInfoResponse { - _do_instantiate( - deps, - addr, - amount, - Some(MinterResponse { - minter: minter.to_string(), - cap, - }), - ) -} - -// This will set up the instantiation for other tests -fn do_instantiate(deps: DepsMut, addr: &str, amount: Uint128) -> TokenInfoResponse { - _do_instantiate(deps, addr, amount, None) -} - -// This will set up the instantiation for other tests -fn _do_instantiate( - mut deps: DepsMut, - addr: &str, - amount: Uint128, - mint: Option, -) -> TokenInfoResponse { - let instantiate_msg = InstantiateMsg { - name: "Auto Gen".to_string(), - symbol: "AUTO".to_string(), - decimals: 3, - initial_balances: vec![Cw20Coin { - address: addr.to_string(), - amount, - }], - mint: mint.clone(), - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.branch(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - let meta = query_token_info(deps.as_ref()).unwrap(); - assert_eq!( - meta, - TokenInfoResponse { - name: "Auto Gen".to_string(), - symbol: "AUTO".to_string(), - decimals: 3, - total_supply: amount, - } - ); - assert_eq!(get_balance(deps.as_ref(), addr), amount); - assert_eq!(query_minter(deps.as_ref()).unwrap(), mint,); - meta -} - -mod instantiate { - use super::*; - - #[test] - fn basic() { - let mut deps = mock_dependencies(); - let amount = Uint128::from(11223344u128); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![Cw20Coin { - address: String::from("addr0000"), - amount, - }], - mint: None, - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_token_info(deps.as_ref()).unwrap(), - TokenInfoResponse { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - total_supply: amount, - } - ); - assert_eq!( - get_balance(deps.as_ref(), "addr0000"), - Uint128::new(11223344) - ); - } - - #[test] - fn mintable() { - let mut deps = mock_dependencies(); - let amount = Uint128::new(11223344); - let minter = String::from("asmodat"); - let limit = Uint128::new(511223344); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![Cw20Coin { - address: "addr0000".into(), - amount, - }], - mint: Some(MinterResponse { - minter: minter.clone(), - cap: Some(limit), - }), - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_token_info(deps.as_ref()).unwrap(), - TokenInfoResponse { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - total_supply: amount, - } - ); - assert_eq!( - get_balance(deps.as_ref(), "addr0000"), - Uint128::new(11223344) - ); - assert_eq!( - query_minter(deps.as_ref()).unwrap(), - Some(MinterResponse { - minter, - cap: Some(limit), - }), - ); - } - - #[test] - fn mintable_over_cap() { - let mut deps = mock_dependencies(); - let amount = Uint128::new(11223344); - let minter = String::from("asmodat"); - let limit = Uint128::new(11223300); - let instantiate_msg = InstantiateMsg { - name: "Cash Token".to_string(), - symbol: "CASH".to_string(), - decimals: 9, - initial_balances: vec![Cw20Coin { - address: String::from("addr0000"), - amount, - }], - mint: Some(MinterResponse { - minter, - cap: Some(limit), - }), - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let err = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap_err(); - assert_eq!( - err, - StdError::generic_err("Initial supply greater than cap").into() - ); - } -} - -#[test] -fn can_mint_by_minter() { - let mut deps = mock_dependencies(); - - let genesis = String::from("genesis"); - let amount = Uint128::new(11223344); - let minter = String::from("asmodat"); - let limit = Uint128::new(511223344); - do_instantiate_with_minter(deps.as_mut(), &genesis, amount, &minter, Some(limit)); - - // Minter can mint coins to some winner - let winner = String::from("lucky"); - let prize = Uint128::new(222_222_222); - let msg = ExecuteMsg::Mint { - recipient: winner.clone(), - amount: prize, - }; - - let info = mock_info(minter.as_ref(), &[]); - let env = mock_env(); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - assert_eq!(get_balance(deps.as_ref(), genesis), amount); - assert_eq!(get_balance(deps.as_ref(), winner.clone()), prize); - - // But cannot mint nothing - let msg = ExecuteMsg::Mint { - recipient: winner.clone(), - amount: Uint128::zero(), - }; - let info = mock_info(minter.as_ref(), &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::InvalidZeroAmount {}); - - // But if it exceeds cap (even over multiple rounds), it fails - let msg = ExecuteMsg::Mint { - recipient: winner, - amount: Uint128::new(333_222_222), - }; - let info = mock_info(minter.as_ref(), &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::CannotExceedCap {}); -} - -#[test] -fn others_cannot_mint() { - let mut deps = mock_dependencies(); - do_instantiate_with_minter( - deps.as_mut(), - &String::from("genesis"), - Uint128::new(1234), - &String::from("minter"), - None, - ); - - let msg = ExecuteMsg::Mint { - recipient: String::from("lucky"), - amount: Uint128::new(222), - }; - let info = mock_info("anyone else", &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); -} - -#[test] -fn no_one_mints_if_minter_unset() { - let mut deps = mock_dependencies(); - do_instantiate(deps.as_mut(), &String::from("genesis"), Uint128::new(1234)); - - let msg = ExecuteMsg::Mint { - recipient: String::from("lucky"), - amount: Uint128::new(222), - }; - let info = mock_info("genesis", &[]); - let env = mock_env(); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); -} - -#[test] -fn instantiate_multiple_accounts() { - let mut deps = mock_dependencies(); - let amount1 = Uint128::from(11223344u128); - let addr1 = String::from("addr0001"); - let amount2 = Uint128::from(7890987u128); - let addr2 = String::from("addr0002"); - let instantiate_msg = InstantiateMsg { - name: "Bash Shell".to_string(), - symbol: "BASH".to_string(), - decimals: 6, - initial_balances: vec![ - Cw20Coin { - address: addr1.clone(), - amount: amount1, - }, - Cw20Coin { - address: addr2.clone(), - amount: amount2, - }, - ], - mint: None, - marketing: None, - }; - let info = mock_info("creator", &[]); - let env = mock_env(); - let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); - assert_eq!(0, res.messages.len()); - - assert_eq!( - query_token_info(deps.as_ref()).unwrap(), - TokenInfoResponse { - name: "Bash Shell".to_string(), - symbol: "BASH".to_string(), - decimals: 6, - total_supply: amount1 + amount2, - } - ); - assert_eq!(get_balance(deps.as_ref(), addr1), amount1); - assert_eq!(get_balance(deps.as_ref(), addr2), amount2); -} - -#[test] -fn transfer() { - let mut deps = mock_dependencies(); - let addr1 = String::from("addr0001"); - let addr2 = String::from("addr0002"); - let amount1 = Uint128::from(12340000u128); - let transfer = Uint128::from(76543u128); - let too_much = Uint128::from(12340321u128); - - do_instantiate(deps.as_mut(), &addr1, amount1); - - // Cannot transfer nothing - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Transfer { - recipient: addr2.clone(), - amount: Uint128::zero(), - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::InvalidZeroAmount {}); - - // Cannot send more than we have - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Transfer { - recipient: addr2.clone(), - amount: too_much, - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // Cannot send from empty account - let info = mock_info(addr2.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Transfer { - recipient: addr1.clone(), - amount: transfer, - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // Valid transfer - let info = mock_info(addr1.as_ref(), &[]); - let env = test_mock_env(MockEnvParams { - block_height: 100_000, - ..Default::default() - }); - let msg = ExecuteMsg::Transfer { - recipient: addr2.clone(), - amount: transfer, - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.messages.len(), 0); - - let remainder = amount1.checked_sub(transfer).unwrap(); - assert_eq!(get_balance(deps.as_ref(), addr1.clone()), remainder); - assert_eq!(get_balance(deps.as_ref(), addr2.clone()), transfer); - assert_eq!( - query_balance_at(deps.as_ref(), addr1, 100_000) - .unwrap() - .balance, - amount1 - ); - assert_eq!( - query_balance_at(deps.as_ref(), addr2, 100_000) - .unwrap() - .balance, - Uint128::zero() - ); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - amount1 - ); -} - -#[test] -fn burn() { - let mut deps = mock_dependencies(); - let addr1 = String::from("addr0001"); - let minter = String::from("minter"); - let amount1 = Uint128::from(12340000u128); - let burn = Uint128::from(76543u128); - let too_much = Uint128::from(12340321u128); - - do_instantiate_with_minter(deps.as_mut(), &minter, amount1, &minter, None); - - // Cannot burn nothing - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Burn { - amount: Uint128::zero(), - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::InvalidZeroAmount {}); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - amount1 - ); - - // Can burn only from a minter - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Burn { amount: too_much }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Unauthorized {})); - - // Cannot burn more than we have - let info = mock_info(minter.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Burn { amount: too_much }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - amount1 - ); - - // valid burn reduces total supply - let info = mock_info(minter.as_ref(), &[]); - let env = test_mock_env(MockEnvParams { - block_height: 200_000, - ..Default::default() - }); - let msg = ExecuteMsg::Burn { amount: burn }; - execute(deps.as_mut(), env, info, msg).unwrap(); - - let remainder = amount1.checked_sub(burn).unwrap(); - assert_eq!(get_balance(deps.as_ref(), minter.clone()), remainder); - assert_eq!( - query_balance_at(deps.as_ref(), minter, 200_000) - .unwrap() - .balance, - amount1 - ); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - remainder - ); - assert_eq!( - get_total_supply_at(&deps.storage, 200_000).unwrap(), - remainder - ); -} - -#[test] -fn send() { - let mut deps = mock_dependencies(); - let addr1 = String::from("addr0001"); - let contract = String::from("addr0002"); - let amount1 = Uint128::from(12340000u128); - let transfer = Uint128::from(76543u128); - let too_much = Uint128::from(12340321u128); - let send_msg = Binary::from(r#"{"some":123}"#.as_bytes()); - - do_instantiate(deps.as_mut(), &addr1, amount1); - - // Cannot send nothing - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Send { - contract: contract.clone(), - amount: Uint128::zero(), - msg: send_msg.clone(), - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(err, ContractError::InvalidZeroAmount {}); - - // Cannot send more than we have - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Send { - contract: contract.clone(), - amount: too_much, - msg: send_msg.clone(), - }; - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert!(matches!(err, ContractError::Std(StdError::Overflow { .. }))); - - // Valid transfer - let info = mock_info(addr1.as_ref(), &[]); - let env = mock_env(); - let msg = ExecuteMsg::Send { - contract: contract.clone(), - amount: transfer, - msg: send_msg.clone(), - }; - let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - assert_eq!(res.messages.len(), 1); - - // Ensure proper send message was sent - // This is the message we want delivered to the other side - let binary_msg = Cw20ReceiveMsg { - sender: addr1.clone(), - amount: transfer, - msg: send_msg, - } - .into_binary() - .unwrap(); - // And this is how it must be wrapped for the vm to process it - assert_eq!( - res.messages[0], - SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: contract.clone(), - msg: binary_msg, - funds: vec![], - })) - ); - - // Ensure balance is properly transferred - let remainder = amount1.checked_sub(transfer).unwrap(); - assert_eq!(get_balance(deps.as_ref(), addr1.clone()), remainder); - assert_eq!(get_balance(deps.as_ref(), contract.clone()), transfer); - assert_eq!( - query_token_info(deps.as_ref()).unwrap().total_supply, - amount1 - ); - assert_eq!( - query_balance_at(deps.as_ref(), addr1, env.block.height) - .unwrap() - .balance, - Uint128::zero() - ); - assert_eq!( - query_balance_at(deps.as_ref(), contract, env.block.height) - .unwrap() - .balance, - Uint128::zero() - ); -} - -#[test] -fn snapshots_are_taken_and_retrieved_correctly() { - let mut deps = mock_dependencies(); - - let addr1 = String::from("addr1"); - let addr2 = String::from("addr2"); - let minter = String::from("minter"); - - let mut current_total_supply = Uint128::new(100_000); - let mut current_block = 12_345; - let mut current_addr1_balance = current_total_supply; - let mut current_minter_balance = Uint128::zero(); - - do_instantiate_with_minter(deps.as_mut(), &addr1, current_total_supply, &minter, None); - - let mut expected_total_supplies = vec![(current_block, current_total_supply)]; - let mut expected_addr1_balances = vec![(current_block, current_addr1_balance)]; - let mut expected_minter_balances: Vec<(u64, Uint128)> = vec![]; - - // Mint to addr2 3 times - for _i in 0..3 { - current_block += 100_000; - - let mint_amount = Uint128::new(20_000); - current_total_supply += mint_amount; - current_minter_balance += mint_amount; - - let info = mock_info(minter.as_str(), &[]); - let env = test_mock_env(MockEnvParams { - block_height: current_block, - ..Default::default() - }); - - let msg = ExecuteMsg::Mint { - recipient: minter.clone(), - amount: mint_amount, - }; - - execute(deps.as_mut(), env, info, msg).unwrap(); - - expected_total_supplies.push((current_block, current_total_supply)); - expected_minter_balances.push((current_block, current_minter_balance)); - } - - // Transfer from addr1 to minter 4 times - for _i in 0..4 { - current_block += 60_000; - - let transfer_amount = Uint128::new(10_000); - current_addr1_balance = current_addr1_balance - transfer_amount; - current_minter_balance += transfer_amount; - - let info = mock_info(addr1.as_str(), &[]); - let env = test_mock_env(MockEnvParams { - block_height: current_block, - ..Default::default() - }); - - let msg = ExecuteMsg::Transfer { - recipient: minter.clone(), - amount: transfer_amount, - }; - - execute(deps.as_mut(), env, info, msg).unwrap(); - - expected_addr1_balances.push((current_block, current_addr1_balance)); - expected_minter_balances.push((current_block, current_minter_balance)); - } - - // Burn is allowed only for a Minter. - let info = mock_info(addr2.as_str(), &[]); - let env = test_mock_env(MockEnvParams { - block_height: current_block, - ..Default::default() - }); - - assert_eq!( - execute( - deps.as_mut(), - env.clone(), - info.clone(), - ExecuteMsg::Burn { - amount: Uint128::new(20_000), - } - ) - .unwrap_err(), - ContractError::Unauthorized {} - ); - - // Burn from minter 3 times - for _i in 0..3 { - current_block += 50_000; - - let burn_amount = Uint128::new(20_000); - current_total_supply = current_total_supply - burn_amount; - current_minter_balance = current_minter_balance - burn_amount; - - let info = mock_info(minter.as_str(), &[]); - let env = test_mock_env(MockEnvParams { - block_height: current_block, - ..Default::default() - }); - - let msg = ExecuteMsg::Burn { - amount: burn_amount, - }; - - execute(deps.as_mut(), env, info, msg).unwrap(); - - expected_total_supplies.push((current_block, current_total_supply)); - expected_minter_balances.push((current_block, current_minter_balance)); - } - - // Check total supply - let mut total_supply_previous_value = Uint128::zero(); - for (block, expected_total_supply) in expected_total_supplies { - // Previous block gives previous value - assert_eq!( - get_total_supply_at(&deps.storage, block - 1).unwrap(), - total_supply_previous_value - ); - - // Current block gives expected value - assert_eq!( - get_total_supply_at(&deps.storage, block).unwrap(), - expected_total_supply, - ); - - // Next block still gives expected value - assert_eq!( - get_total_supply_at(&deps.storage, block + 10).unwrap(), - expected_total_supply, - ); - - total_supply_previous_value = expected_total_supply; - } - - // Check addr1 balances - let mut balance_previous_value = Uint128::zero(); - for (block, expected_balance) in expected_addr1_balances { - // Previous block gives previous value - assert_eq!( - query_balance_at(deps.as_ref(), addr1.clone(), block - 10) - .unwrap() - .balance, - balance_previous_value - ); - - // Current block gives previous value - assert_eq!( - query_balance_at(deps.as_ref(), addr1.clone(), block) - .unwrap() - .balance, - balance_previous_value - ); - - // Only the next block still gives expected value - assert_eq!( - query_balance_at(deps.as_ref(), addr1.clone(), block + 1) - .unwrap() - .balance, - expected_balance - ); - - balance_previous_value = expected_balance; - } - - // Check addr2 balances - let mut balance_previous_value = Uint128::zero(); - for (block, expected_balance) in expected_minter_balances { - // Previous block gives previous value - assert_eq!( - query_balance_at(deps.as_ref(), minter.clone(), block - 10) - .unwrap() - .balance, - balance_previous_value - ); - - // The current block gives the previous value - assert_eq!( - query_balance_at(deps.as_ref(), minter.clone(), block) - .unwrap() - .balance, - balance_previous_value - ); - - // Only the next block still gives expected value - assert_eq!( - query_balance_at(deps.as_ref(), minter.clone(), block + 1) - .unwrap() - .balance, - expected_balance - ); - - balance_previous_value = expected_balance; - } -} - -#[test] -fn test_balance_history() { - let mut deps = mock_dependencies(); - let user1 = mock_info("user1", &[]); - let minter = mock_info("minter", &[]); - do_instantiate_with_minter( - deps.as_mut(), - user1.sender.as_str(), - Uint128::new(1_000), - &String::from("minter"), - None, - ); - - // Test transfer_from - let mut env = mock_env(); - env.block.height += 1; - let user2 = mock_info("user2", &[]); - - execute_increase_allowance( - deps.as_mut(), - env.clone(), - user1.clone(), - user2.sender.to_string(), - Uint128::new(1000), - None, - ) - .unwrap(); - - execute_transfer_from( - deps.as_mut(), - env.clone(), - user2.clone(), - user1.sender.to_string(), - user2.sender.to_string(), - Uint128::new(1), - ) - .unwrap(); - - execute_increase_allowance( - deps.as_mut(), - env.clone(), - user1.clone(), - minter.sender.to_string(), - Uint128::new(1000), - None, - ) - .unwrap(); - - execute_transfer_from( - deps.as_mut(), - env.clone(), - user2.clone(), - user1.sender.to_string(), - minter.sender.to_string(), - Uint128::new(1), - ) - .unwrap(); - - assert_eq!( - query_balance_at(deps.as_ref(), user1.sender.to_string(), env.block.height).unwrap(), - BalanceResponse { - balance: Uint128::new(1000) - } - ); - assert_eq!( - query_balance_at(deps.as_ref(), minter.sender.to_string(), env.block.height).unwrap(), - BalanceResponse { - balance: Uint128::new(0) - } - ); - assert_eq!( - query_balance(deps.as_ref(), user1.sender.to_string()).unwrap(), - BalanceResponse { - balance: Uint128::new(998) - } - ); - assert_eq!( - query_balance(deps.as_ref(), minter.sender.to_string()).unwrap(), - BalanceResponse { - balance: Uint128::new(1) - } - ); - - // Test burn_from - let mut env = mock_env(); - env.block.height += 2; - - // Try burn from user2 - let err = execute_burn_from( - deps.as_mut(), - env.clone(), - user2.clone(), - user1.sender.to_string(), - Uint128::new(1), - ) - .unwrap_err(); - assert_eq!(err, ContractError::Unauthorized {}); - - // Try burn from minter - execute_burn_from( - deps.as_mut(), - env.clone(), - minter.clone(), - user1.sender.to_string(), - Uint128::new(1), - ) - .unwrap(); - - assert_eq!( - query_balance_at(deps.as_ref(), user1.sender.to_string(), env.block.height).unwrap(), - BalanceResponse { - balance: Uint128::new(998) - } - ); - assert_eq!( - query_balance_at(deps.as_ref(), minter.sender.to_string(), env.block.height).unwrap(), - BalanceResponse { - balance: Uint128::new(1) - } - ); - assert_eq!( - query_balance(deps.as_ref(), user1.sender.to_string()).unwrap(), - BalanceResponse { - balance: Uint128::new(997) - } - ); - assert_eq!( - query_balance(deps.as_ref(), minter.sender.to_string()).unwrap(), - BalanceResponse { - balance: Uint128::new(1) - } - ); - - // Test send_from - let mut env = mock_env(); - env.block.height += 3; - - execute_send_from( - deps.as_mut(), - env.clone(), - user2.clone(), - user1.sender.to_string(), - MOCK_CONTRACT_ADDR.to_string(), - Uint128::new(1), - Binary::default(), - ) - .unwrap(); - - assert_eq!( - query_balance_at(deps.as_ref(), user1.sender.to_string(), env.block.height).unwrap(), - BalanceResponse { - balance: Uint128::new(997) - } - ); - assert_eq!( - query_balance_at(deps.as_ref(), user2.sender.to_string(), env.block.height).unwrap(), - BalanceResponse { - balance: Uint128::new(1) - } - ); - assert_eq!( - query_balance_at( - deps.as_ref(), - MOCK_CONTRACT_ADDR.to_string(), - env.block.height - ) - .unwrap(), - BalanceResponse { - balance: Uint128::new(0) - } - ); - assert_eq!( - query_balance(deps.as_ref(), user1.sender.to_string()).unwrap(), - BalanceResponse { - balance: Uint128::new(996) - } - ); - assert_eq!( - query_balance(deps.as_ref(), user2.sender.to_string()).unwrap(), - BalanceResponse { - balance: Uint128::new(1) - } - ); - assert_eq!( - query_balance(deps.as_ref(), MOCK_CONTRACT_ADDR.to_string()).unwrap(), - BalanceResponse { - balance: Uint128::new(1) - } - ); - - // Test query_all_accounts - assert_eq!( - query_all_accounts(deps.as_ref(), None, None).unwrap(), - AllAccountsResponse { - accounts: vec![ - MOCK_CONTRACT_ADDR.to_string(), - minter.sender.to_string(), - user1.sender.to_string(), - user2.sender.to_string(), - ] - } - ); -} diff --git a/packages/astroport/src/lib.rs b/packages/astroport/src/lib.rs index 9862e50ad..b9422f3f8 100644 --- a/packages/astroport/src/lib.rs +++ b/packages/astroport/src/lib.rs @@ -19,14 +19,12 @@ pub mod pair_concentrated; pub mod pair_concentrated_inj; pub mod pair_xyk_sale_tax; pub mod querier; -pub mod restricted_vector; pub mod router; pub mod staking; pub mod token; pub mod token_factory; pub mod tokenfactory_tracker; pub mod vesting; -pub mod xastro_token; #[cfg(test)] mod mock_querier; diff --git a/packages/astroport/src/restricted_vector.rs b/packages/astroport/src/restricted_vector.rs deleted file mode 100644 index eab10dc30..000000000 --- a/packages/astroport/src/restricted_vector.rs +++ /dev/null @@ -1,90 +0,0 @@ -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal, StdError, StdResult, Uint128}; -use std::fmt::Display; - -/// Vec wrapper for internal use. -/// Some business logic relies on an order of this vector, thus it is forbidden to sort it -/// or remove elements. New values can be added using .update() ONLY. -#[cw_serde] -pub struct RestrictedVector(Vec<(K, V)>); - -pub trait Increaseable -where - Self: Sized, -{ - fn increase(self, new: Self) -> StdResult; -} - -impl RestrictedVector -where - K: Clone + PartialEq + Display, - V: Copy + Increaseable, -{ - pub fn new(key: K, value: V) -> Self { - Self(vec![(key, value)]) - } - - pub fn get_last(&self, key: &K) -> StdResult { - self.0 - .last() - .filter(|(k, _)| k == key) - .map(|(_, v)| v) - .cloned() - .ok_or_else(|| StdError::generic_err(format!("Key {key} not found"))) - } - - pub fn update(&mut self, key: &K, value: V) -> StdResult { - let found = self.0.iter_mut().find(|(k, _)| k == key); - let r = match found { - Some((_, v)) => { - *v = v.increase(value)?; - *v - } - None => { - self.0.push((key.clone(), value)); - value - } - }; - - Ok(r) - } - - pub fn load(&self, key: &K) -> Option { - self.0 - .iter() - .find(|(k, _)| k == key) - .map(|(_, value)| *value) - } - - pub fn inner_ref(&self) -> &Vec<(K, V)> { - &self.0 - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - -impl Increaseable for Decimal { - fn increase(self, new: Decimal) -> StdResult { - self.checked_add(new).map_err(Into::into) - } -} - -impl Increaseable for Uint128 { - fn increase(self, new: Uint128) -> StdResult { - self.checked_add(new).map_err(Into::into) - } -} - -impl Default for RestrictedVector { - fn default() -> Self { - Self(vec![]) - } -} - -impl From> for RestrictedVector { - fn from(v: Vec<(K, V)>) -> Self { - Self(v) - } -} diff --git a/packages/astroport/src/xastro_token.rs b/packages/astroport/src/xastro_token.rs deleted file mode 100644 index 321af65aa..000000000 --- a/packages/astroport/src/xastro_token.rs +++ /dev/null @@ -1,133 +0,0 @@ -// TODO: DEPRECATE -use cosmwasm_schema::{cw_serde, QueryResponses}; - -use cosmwasm_std::{StdError, StdResult, Uint128}; -use cw20::{ - AllAccountsResponse, AllAllowancesResponse, AllowanceResponse, BalanceResponse, Cw20Coin, - DownloadLogoResponse, Logo, MarketingInfoResponse, MinterResponse, TokenInfoResponse, -}; - -/// This structure describes the marketing info settings such as project, description, and token logo. -#[cw_serde] -pub struct InstantiateMarketingInfo { - /// The project name - pub project: Option, - /// The project description - pub description: Option, - /// The address of an admin who is able to update marketing info - pub marketing: Option, - /// The token logo - pub logo: Option, -} - -/// This structure describes the parameters used for creating a xASTRO token contract. -#[cw_serde] -pub struct InstantiateMsg { - /// Token name - pub name: String, - /// Token symbol - pub symbol: String, - /// The number of decimals the token has - pub decimals: u8, - /// Initial token balances - pub initial_balances: Vec, - /// Token minting permissions - pub mint: Option, - /// the marketing info of type [`InstantiateMarketingInfo`] - pub marketing: Option, -} - -/// This enum describes the query messages available in the contract. -#[cw_serde] -#[derive(QueryResponses)] -pub enum QueryMsg { - /// Balance returns the current balance of a given address, 0 if unset. - #[returns(BalanceResponse)] - Balance { address: String }, - /// BalanceAt returns balance of the given address at the given block, 0 if unset. - #[returns(BalanceResponse)] - BalanceAt { address: String, block: u64 }, - /// TotalSupplyAt returns the total token supply at the given block. - #[returns(Uint128)] - TotalSupplyAt { block: u64 }, - /// TokenInfo returns the contract's metadata - name, decimals, supply, etc. - #[returns(TokenInfoResponse)] - TokenInfo {}, - /// Returns who can mint xASTRO and the hard cap on maximum tokens after minting. - #[returns(Option)] - Minter {}, - /// Allowance returns an amount of tokens the spender can spend from the owner account, 0 if unset. - #[returns(AllowanceResponse)] - Allowance { owner: String, spender: String }, - /// AllAllowances returns all the allowances this token holder has approved. Supports pagination. - #[returns(AllAllowancesResponse)] - AllAllowances { - owner: String, - start_after: Option, - limit: Option, - }, - /// AllAccounts returns all the accounts that have xASTRO balances. Supports pagination. - #[returns(AllAccountsResponse)] - AllAccounts { - start_after: Option, - limit: Option, - }, - /// Returns marketing related contract metadata: - /// - description, logo, project url, etc. - #[returns(MarketingInfoResponse)] - MarketingInfo {}, - /// Downloads embeded logo data (if stored on chain). Errors if no logo data was stored for this contract. - #[returns(DownloadLogoResponse)] - DownloadLogo {}, -} - -/// This structure describes a migration message. -#[cw_serde] -pub struct MigrateMsg {} - -impl InstantiateMsg { - pub fn get_cap(&self) -> Option { - self.mint.as_ref().and_then(|v| v.cap) - } - - pub fn validate(&self) -> StdResult<()> { - // Check name, symbol, decimals - if !is_valid_name(&self.name) { - return Err(StdError::generic_err( - "Name is not in the expected format (3-50 UTF-8 bytes)", - )); - } - if !is_valid_symbol(&self.symbol) { - return Err(StdError::generic_err( - "Ticker symbol is not in expected format [a-zA-Z\\-]{3,12}", - )); - } - if self.decimals > 18 { - return Err(StdError::generic_err("Decimals must not exceed 18")); - } - Ok(()) - } -} - -/// Checks the validity of a token's name. -fn is_valid_name(name: &str) -> bool { - let bytes = name.as_bytes(); - if bytes.len() < 3 || bytes.len() > 50 { - return false; - } - true -} - -/// Checks the validity of a token's symbol. -fn is_valid_symbol(symbol: &str) -> bool { - let bytes = symbol.as_bytes(); - if bytes.len() < 3 || bytes.len() > 12 { - return false; - } - for byte in bytes.iter() { - if (*byte != 45) && (*byte < 65 || *byte > 90) && (*byte < 97 || *byte > 122) { - return false; - } - } - true -}