diff --git a/Cargo.lock b/Cargo.lock index 9abc8a23..75dfa5f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,6 +133,25 @@ dependencies = [ [[package]] name = "astroport" version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4af74ada17a13fe7b75d0d529367eb5dcb15e06fd7e744892191856eedc742" +dependencies = [ + "astroport-circular-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cosmos-sdk-proto 0.19.0", + "cosmwasm-schema", + "cosmwasm-std", + "cw-asset", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw20 1.1.2", + "itertools 0.12.1", + "prost 0.11.9", + "uint 0.9.5", +] + +[[package]] +name = "astroport" +version = "5.3.0" dependencies = [ "astroport-circular-buffer 0.2.0", "cosmos-sdk-proto 0.19.0", @@ -177,7 +196,7 @@ name = "astroport-factory" version = "1.8.0" dependencies = [ "anyhow", - "astroport 5.2.0", + "astroport 5.3.0", "astroport-pair 2.0.1", "astroport-test", "cosmwasm-schema", @@ -222,11 +241,11 @@ dependencies = [ [[package]] name = "astroport-incentives" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "astro-token-converter", - "astroport 4.0.3", + "astroport 5.3.0", "astroport-factory", "astroport-native-coin-registry", "astroport-pair 2.0.1", @@ -322,7 +341,7 @@ dependencies = [ name = "astroport-pair" version = "2.0.1" dependencies = [ - "astroport 5.2.0", + "astroport 5.3.0", "astroport-factory", "astroport-incentives", "astroport-test", @@ -342,10 +361,10 @@ dependencies = [ [[package]] name = "astroport-pair-concentrated" -version = "4.0.1" +version = "4.0.2" dependencies = [ "anyhow", - "astroport 5.2.0", + "astroport 5.3.0", "astroport-circular-buffer 0.2.0", "astroport-factory", "astroport-incentives", @@ -394,7 +413,7 @@ name = "astroport-pair-stable" version = "4.0.0" dependencies = [ "anyhow", - "astroport 5.2.0", + "astroport 5.3.0", "astroport-circular-buffer 0.2.0", "astroport-factory", "astroport-incentives", @@ -420,7 +439,7 @@ name = "astroport-pair-transmuter" version = "1.1.1" dependencies = [ "anyhow", - "astroport 5.2.0", + "astroport 5.3.0", "astroport-factory", "astroport-native-coin-registry", "astroport-test", @@ -440,7 +459,7 @@ dependencies = [ name = "astroport-pair-xyk-sale-tax" version = "2.0.1" dependencies = [ - "astroport 5.2.0", + "astroport 5.3.0", "astroport-factory", "astroport-incentives", "astroport-pair 1.3.3", @@ -463,10 +482,10 @@ dependencies = [ [[package]] name = "astroport-pcl-common" -version = "2.0.1" +version = "2.0.2" dependencies = [ "anyhow", - "astroport 5.2.0", + "astroport 5.3.0", "astroport-factory", "astroport-test", "cosmwasm-schema", @@ -519,7 +538,7 @@ name = "astroport-test" version = "0.1.0" dependencies = [ "anyhow", - "astroport 5.2.0", + "astroport 5.3.0", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test 1.0.0", diff --git a/Cargo.toml b/Cargo.toml index 05b638f0..c0ba86a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ thiserror = "1.0" itertools = "0.12" cosmwasm-schema = "1.5" cw-utils = "1" +astroport = { path = "./packages/astroport", version = "5.3.0" } [profile.release] opt-level = "z" diff --git a/contracts/factory/Cargo.toml b/contracts/factory/Cargo.toml index 91f23831..110ec624 100644 --- a/contracts/factory/Cargo.toml +++ b/contracts/factory/Cargo.toml @@ -27,7 +27,7 @@ library = [] [dependencies] cosmwasm-std.workspace = true -astroport = { path = "../../packages/astroport", version = "5" } +astroport.workspace = true cw-storage-plus.workspace = true cw2.workspace = true thiserror.workspace = true diff --git a/contracts/pair/Cargo.toml b/contracts/pair/Cargo.toml index 17dfb5d7..9e8b6701 100644 --- a/contracts/pair/Cargo.toml +++ b/contracts/pair/Cargo.toml @@ -29,7 +29,7 @@ library = [] [dependencies] integer-sqrt = "0.1" -astroport = { path = "../../packages/astroport", version = "5" } +astroport.workspace = true cw2.workspace = true cw20 = "1.1" cosmwasm-std = { workspace = true, features = ["cosmwasm_1_1", "stargate"] } diff --git a/contracts/pair/src/contract.rs b/contracts/pair/src/contract.rs index d8879660..39501988 100644 --- a/contracts/pair/src/contract.rs +++ b/contracts/pair/src/contract.rs @@ -20,6 +20,7 @@ use astroport::asset::{ addr_opt_validate, check_swap_parameters, Asset, AssetInfo, CoinsExt, PairInfo, MINIMUM_LIQUIDITY_AMOUNT, }; +use astroport::common::LP_SUBDENOM; use astroport::factory::PairType; use astroport::incentives::ExecuteMsg as IncentiveExecuteMsg; use astroport::pair::{ @@ -45,8 +46,6 @@ use crate::state::{Config, BALANCES, CONFIG}; const CONTRACT_NAME: &str = "astroport-pair"; /// Contract version that is used for migration. const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -/// Tokenfactory LP token subdenom -pub const LP_SUBDENOM: &str = "astroport/share"; /// Creates a new contract with the specified parameters in the [`InstantiateMsg`]. #[cfg_attr(not(feature = "library"), entry_point)] diff --git a/contracts/pair/src/testing.rs b/contracts/pair/src/testing.rs index fff034a0..a6527d2a 100644 --- a/contracts/pair/src/testing.rs +++ b/contracts/pair/src/testing.rs @@ -6,8 +6,10 @@ use cosmwasm_std::{ }; use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg}; use proptest::prelude::*; +use prost::Message; use astroport::asset::{Asset, AssetInfo, PairInfo}; +use astroport::common::LP_SUBDENOM; use astroport::factory::PairType; use astroport::pair::{ Cw20HookMsg, ExecuteMsg, InstantiateMsg, PoolResponse, ReverseSimulationResponse, @@ -15,18 +17,16 @@ use astroport::pair::{ }; use astroport::token_factory::{MsgBurn, MsgCreateDenom, MsgCreateDenomResponse, MsgMint}; +use crate::contract::compute_offer_amount; use crate::contract::reply; use crate::contract::{ accumulate_prices, assert_max_spread, compute_swap, execute, instantiate, query_pool, query_reverse_simulation, query_share, query_simulation, }; -use crate::contract::{compute_offer_amount, LP_SUBDENOM}; use crate::error::ContractError; use crate::mock_querier::mock_dependencies; use crate::state::{Config, CONFIG}; -use prost::Message; - #[derive(Clone, PartialEq, Message)] struct MsgInstantiateContractResponse { #[prost(string, tag = "1")] diff --git a/contracts/pair/tests/integration.rs b/contracts/pair/tests/integration.rs index 28866c6c..db600150 100644 --- a/contracts/pair/tests/integration.rs +++ b/contracts/pair/tests/integration.rs @@ -18,9 +18,9 @@ use astroport::tokenfactory_tracker::{ use astroport_test::cw_multi_test::{AppBuilder, ContractWrapper, Executor, TOKEN_FACTORY_MODULE}; use astroport_test::modules::stargate::{MockStargate, StargateApp as TestApp}; -use astroport_pair::contract::LP_SUBDENOM; use astroport_pair::error::ContractError; +use astroport::common::LP_SUBDENOM; use cosmwasm_std::{ attr, coin, to_json_binary, Addr, Coin, Decimal, DepsMut, Empty, Env, MessageInfo, Response, StdResult, Uint128, Uint64, @@ -162,7 +162,7 @@ fn instantiate_pair(mut router: &mut TestApp, owner: &Addr) -> Addr { .unwrap(); assert_eq!("contract1", res.contract_addr); assert_eq!( - format!("factory/contract1/{}", LP_SUBDENOM), + format!("factory/contract1/{LP_SUBDENOM}"), res.liquidity_token ); @@ -383,10 +383,7 @@ fn test_provide_and_withdraw_liquidity() { assert_eq!( err.root_cause().to_string(), - format!( - "Must send reserve token 'factory/contract1/{}'", - LP_SUBDENOM - ) + format!("Must send reserve token 'factory/contract1/{LP_SUBDENOM}'",) ); // Withdraw liquidity doubling the minimum to recieve diff --git a/contracts/pair_concentrated/Cargo.toml b/contracts/pair_concentrated/Cargo.toml index bedbc7bc..3ab1b0a7 100644 --- a/contracts/pair_concentrated/Cargo.toml +++ b/contracts/pair_concentrated/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astroport-pair-concentrated" -version = "4.0.1" +version = "4.0.2" authors = ["Astroport"] edition = "2021" description = "The Astroport concentrated liquidity pair" @@ -29,7 +29,7 @@ injective = ["astroport/injective", "astroport-pcl-common/injective"] sei = ["astroport/sei", "astroport-pcl-common/sei"] [dependencies] -astroport = { path = "../../packages/astroport", version = "5" } +astroport.workspace = true astroport-factory = { path = "../factory", features = ["library"], version = "1" } astroport-circular-buffer = { path = "../../packages/circular_buffer", version = "0.2" } astroport-pcl-common = { path = "../../packages/astroport_pcl_common", version = "2" } diff --git a/contracts/pair_concentrated/src/contract.rs b/contracts/pair_concentrated/src/contract.rs index 7da045df..20aa763f 100644 --- a/contracts/pair_concentrated/src/contract.rs +++ b/contracts/pair_concentrated/src/contract.rs @@ -18,7 +18,7 @@ use astroport::asset::AssetInfoExt; use astroport::asset::{ addr_opt_validate, token_asset, Asset, AssetInfo, CoinsExt, PairInfo, MINIMUM_LIQUIDITY_AMOUNT, }; -use astroport::common::{claim_ownership, drop_ownership_proposal, propose_new_owner}; +use astroport::common::{claim_ownership, drop_ownership_proposal, propose_new_owner, LP_SUBDENOM}; use astroport::cosmwasm_ext::{DecimalToInteger, IntegerToDecimal}; use astroport::factory::PairType; use astroport::observation::{PrecommitObservation, OBSERVATIONS_SIZE}; @@ -56,8 +56,6 @@ use crate::utils::{ const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); /// Contract version that is used for migration. const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -/// Tokenfactory LP token subdenom -pub const LP_SUBDENOM: &str = "astroport/share"; /// An LP token's precision. pub(crate) const LP_TOKEN_PRECISION: u8 = 6; diff --git a/contracts/pair_concentrated/tests/pair_concentrated_integration.rs b/contracts/pair_concentrated/tests/pair_concentrated_integration.rs index 45c1c407..55cb4378 100644 --- a/contracts/pair_concentrated/tests/pair_concentrated_integration.rs +++ b/contracts/pair_concentrated/tests/pair_concentrated_integration.rs @@ -1563,6 +1563,17 @@ fn check_correct_fee_share() { helper.give_me_money(&[offer_asset.clone()], &user); helper.swap(&user, &offer_asset, None).unwrap(); + let last_price = helper + .query_config() + .unwrap() + .pool_state + .price_state + .last_price; + assert_eq!( + last_price, + Decimal256::from_str("1.001187607454013938").unwrap() + ); + // Check that the shared fees are sent let expected_fee_share = 26081u128; let recipient_balance = helper.coin_balance(&test_coins[1], &share_recipient); @@ -1580,6 +1591,17 @@ fn check_correct_fee_share() { helper.give_me_money(&[offer_asset.clone()], &user); helper.swap(&user, &offer_asset, None).unwrap(); + let last_price = helper + .query_config() + .unwrap() + .pool_state + .price_state + .last_price; + assert_eq!( + last_price, + Decimal256::from_str("0.998842355796925899").unwrap() + ); + helper .withdraw_liquidity(&provider, 999_999354, vec![]) .unwrap(); @@ -1588,6 +1610,17 @@ fn check_correct_fee_share() { helper.give_me_money(&[offer_asset.clone()], &user); helper.swap(&user, &offer_asset, None).unwrap(); + let last_price = helper + .query_config() + .unwrap() + .pool_state + .price_state + .last_price; + assert_eq!( + last_price, + Decimal256::from_str("1.00118760696709103").unwrap() + ); + // Disable fee share let action = ConcentratedPoolUpdateParams::DisableFeeShare {}; helper.update_config(&owner, &action).unwrap(); diff --git a/contracts/pair_stable/Cargo.toml b/contracts/pair_stable/Cargo.toml index 7c8e3959..fd266463 100644 --- a/contracts/pair_stable/Cargo.toml +++ b/contracts/pair_stable/Cargo.toml @@ -28,7 +28,7 @@ sei = ["astroport/sei"] library = [] [dependencies] -astroport = { path = "../../packages/astroport", version = "5" } +astroport.workspace = true cw2.workspace = true cw20 = "1.1" cosmwasm-std = { workspace = true, features = ["cosmwasm_1_1", "stargate"] } diff --git a/contracts/pair_stable/src/contract.rs b/contracts/pair_stable/src/contract.rs index bee8d3f2..7a3fc0b9 100644 --- a/contracts/pair_stable/src/contract.rs +++ b/contracts/pair_stable/src/contract.rs @@ -18,7 +18,7 @@ use astroport::asset::{ addr_opt_validate, check_swap_parameters, Asset, AssetInfo, CoinsExt, Decimal256Ext, DecimalAsset, PairInfo, MINIMUM_LIQUIDITY_AMOUNT, }; -use astroport::common::{claim_ownership, drop_ownership_proposal, propose_new_owner}; +use astroport::common::{claim_ownership, drop_ownership_proposal, propose_new_owner, LP_SUBDENOM}; use astroport::cosmwasm_ext::IntegerToDecimal; use astroport::factory::PairType; use astroport::observation::{query_observation, PrecommitObservation, OBSERVATIONS_SIZE}; @@ -56,8 +56,6 @@ const CONTRACT_NAME: &str = "astroport-pair-stable"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); /// Reply ID for create denom reply const CREATE_DENOM_REPLY_ID: u64 = 1; -/// Tokenfactory LP token subdenom -pub const LP_SUBDENOM: &str = "astroport/share"; /// Number of assets in the pool. const N_COINS: usize = 2; diff --git a/contracts/pair_stable/src/testing.rs b/contracts/pair_stable/src/testing.rs index b9295aee..f37f0074 100644 --- a/contracts/pair_stable/src/testing.rs +++ b/contracts/pair_stable/src/testing.rs @@ -1,4 +1,3 @@ -use astroport::token_factory::{MsgBurn, MsgCreateDenom, MsgCreateDenomResponse, MsgMint}; use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ attr, coin, from_json, to_json_binary, Addr, BankMsg, Binary, BlockInfo, Coin, CosmosMsg, @@ -12,6 +11,7 @@ use prost::Message; use sim::StableSwapModel; use astroport::asset::{native_asset, native_asset_info, Asset, AssetInfo}; +use astroport::common::LP_SUBDENOM; use astroport::observation::query_observation; use astroport::observation::Observation; use astroport::observation::OracleObservation; @@ -19,17 +19,18 @@ use astroport::pair::{ ConfigResponse, Cw20HookMsg, ExecuteMsg, InstantiateMsg, PoolResponse, QueryMsg, SimulationResponse, StablePoolParams, }; +use astroport::token_factory::{MsgBurn, MsgCreateDenom, MsgCreateDenomResponse, MsgMint}; use astroport_circular_buffer::BufferManager; +use astroport_test::convert::f64_to_dec; use crate::contract::{ assert_max_spread, execute, instantiate, query, query_pool, query_reverse_simulation, - query_share, query_simulation, reply, LP_SUBDENOM, + query_share, query_simulation, reply, }; use crate::error::ContractError; use crate::mock_querier::mock_dependencies; use crate::state::{CONFIG, OBSERVATIONS}; use crate::utils::{compute_swap, select_pools}; -use astroport_test::convert::f64_to_dec; #[derive(Clone, PartialEq, Message)] struct MsgInstantiateContractResponse { diff --git a/contracts/pair_stable/tests/integration.rs b/contracts/pair_stable/tests/integration.rs index 0790eeb5..78f5113f 100644 --- a/contracts/pair_stable/tests/integration.rs +++ b/contracts/pair_stable/tests/integration.rs @@ -12,11 +12,11 @@ use astroport::pair::{ MAX_FEE_SHARE_BPS, TWAP_PRECISION, }; -use astroport_pair_stable::contract::LP_SUBDENOM; use astroport_pair_stable::error::ContractError; use std::str::FromStr; +use astroport::common::LP_SUBDENOM; use astroport::token::InstantiateMsg as TokenInstantiateMsg; use astroport_pair_stable::math::{MAX_AMP, MAX_AMP_CHANGE, MIN_AMP_CHANGING_TIME}; use astroport_test::cw_multi_test::{AppBuilder, ContractWrapper, Executor}; @@ -231,7 +231,7 @@ fn instantiate_pair(mut router: &mut TestApp, owner: &Addr) -> Addr { .unwrap(); assert_eq!("contract2", res.contract_addr); assert_eq!( - format!("factory/contract2/{}", LP_SUBDENOM), + format!("factory/contract2/{LP_SUBDENOM}"), res.liquidity_token ); diff --git a/contracts/pair_transmuter/Cargo.toml b/contracts/pair_transmuter/Cargo.toml index 3f5fe504..2425da3c 100644 --- a/contracts/pair_transmuter/Cargo.toml +++ b/contracts/pair_transmuter/Cargo.toml @@ -17,7 +17,7 @@ injective = ["astroport/injective"] sei = ["astroport/sei"] [dependencies] -astroport = { version = "5", path = "../../packages/astroport" } +astroport.workspace = true cosmwasm-std = { workspace = true, features = ["cosmwasm_1_1", "stargate"] } cw-storage-plus = "1.2.0" cosmwasm-schema = "1.5.0" diff --git a/contracts/pair_transmuter/src/contract.rs b/contracts/pair_transmuter/src/contract.rs index 1b86fc7a..ae878701 100644 --- a/contracts/pair_transmuter/src/contract.rs +++ b/contracts/pair_transmuter/src/contract.rs @@ -9,6 +9,7 @@ use cw_utils::{one_coin, PaymentError}; use itertools::Itertools; use astroport::asset::{addr_opt_validate, Asset, AssetInfo, CoinsExt, PairInfo}; +use astroport::common::LP_SUBDENOM; use astroport::factory::PairType; use astroport::pair::{ExecuteMsg, InstantiateMsg}; use astroport::token_factory::{ @@ -27,8 +28,6 @@ const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); /// Reply ID for create denom reply const CREATE_DENOM_REPLY_ID: u64 = 1; -/// Tokenfactory LP token subdenom -pub const LP_SUBDENOM: &str = "astroport/share"; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( diff --git a/contracts/pair_xyk_sale_tax/Cargo.toml b/contracts/pair_xyk_sale_tax/Cargo.toml index 1f802f13..f2d497a8 100644 --- a/contracts/pair_xyk_sale_tax/Cargo.toml +++ b/contracts/pair_xyk_sale_tax/Cargo.toml @@ -29,7 +29,7 @@ library = [] [dependencies] integer-sqrt = "0.1" -astroport = { path = "../../packages/astroport", version = "5" } +astroport.workspace = true cw2.workspace = true cw20 = "1.1" cosmwasm-std = { workspace = true, features = ["cosmwasm_1_1", "stargate"] } diff --git a/contracts/pair_xyk_sale_tax/src/contract.rs b/contracts/pair_xyk_sale_tax/src/contract.rs index b87bbc0e..9df48f64 100644 --- a/contracts/pair_xyk_sale_tax/src/contract.rs +++ b/contracts/pair_xyk_sale_tax/src/contract.rs @@ -13,11 +13,15 @@ use cosmwasm_std::{ }; use cw2::set_contract_version; use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg}; +use cw_utils::{ + one_coin, parse_reply_instantiate_data, MsgInstantiateContractResponse, PaymentError, +}; use astroport::asset::{ addr_opt_validate, check_swap_parameters, Asset, AssetInfo, CoinsExt, PairInfo, MINIMUM_LIQUIDITY_AMOUNT, }; +use astroport::common::LP_SUBDENOM; use astroport::factory::PairType; use astroport::incentives::ExecuteMsg as IncentiveExecuteMsg; use astroport::pair::{ConfigResponse, ReplyIds, DEFAULT_SLIPPAGE, MAX_ALLOWED_SLIPPAGE}; @@ -25,6 +29,9 @@ use astroport::pair::{ CumulativePricesResponse, Cw20HookMsg, ExecuteMsg, InstantiateMsg, PoolResponse, QueryMsg, ReverseSimulationResponse, SimulationResponse, TWAP_PRECISION, }; +use astroport::pair_xyk_sale_tax::{ + MigrateMsg, SaleTaxConfigUpdates, SaleTaxInitParams, TaxConfigChecked, +}; use astroport::querier::{ query_factory_config, query_fee_info, query_native_supply, query_tracker_config, }; @@ -32,16 +39,10 @@ use astroport::token_factory::{ tf_before_send_hook_msg, tf_burn_msg, tf_create_denom_msg, tf_mint_msg, MsgCreateDenomResponse, }; use astroport::{tokenfactory_tracker, U256}; -use cw_utils::{ - one_coin, parse_reply_instantiate_data, MsgInstantiateContractResponse, PaymentError, -}; +use astroport_pair::state::{Config as XykConfig, CONFIG as XYK_CONFIG}; use crate::error::ContractError; use crate::state::{Config, BALANCES, CONFIG}; -use astroport::pair_xyk_sale_tax::{ - MigrateMsg, SaleTaxConfigUpdates, SaleTaxInitParams, TaxConfigChecked, -}; -use astroport_pair::state::{Config as XykConfig, CONFIG as XYK_CONFIG}; /// Contract name that is used for migration. const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -49,8 +50,6 @@ const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); /// Reply ID for create denom reply const CREATE_DENOM_REPLY_ID: u64 = 1; -/// Tokenfactory LP token subdenom -pub const LP_SUBDENOM: &str = "astroport/share"; /// Creates a new contract with the specified parameters in the [`InstantiateMsg`]. #[cfg_attr(not(feature = "library"), entry_point)] @@ -1480,11 +1479,12 @@ pub fn pool_info(querier: QuerierWrapper, config: &Config) -> StdResult<(Vec Addr { .unwrap(); assert_eq!("contract1", res.contract_addr); assert_eq!( - format!("factory/contract1/{}", LP_SUBDENOM), + format!("factory/contract1/{LP_SUBDENOM}"), res.liquidity_token ); diff --git a/contracts/periphery/native_coin_registry/Cargo.toml b/contracts/periphery/native_coin_registry/Cargo.toml index feeefd2a..a1dac6d9 100644 --- a/contracts/periphery/native_coin_registry/Cargo.toml +++ b/contracts/periphery/native_coin_registry/Cargo.toml @@ -32,7 +32,7 @@ cw-storage-plus.workspace = true cw2.workspace = true thiserror.workspace = true itertools.workspace = true -astroport = { path = "../../../packages/astroport", version = "5.2" } +astroport = "5.2" [dev-dependencies] cw-multi-test = "1.0.0" diff --git a/contracts/periphery/tokenfactory_tracker/Cargo.toml b/contracts/periphery/tokenfactory_tracker/Cargo.toml index 3d1936b6..e7edc7a1 100644 --- a/contracts/periphery/tokenfactory_tracker/Cargo.toml +++ b/contracts/periphery/tokenfactory_tracker/Cargo.toml @@ -21,7 +21,7 @@ cosmwasm-std = { workspace = true, features = ["cosmwasm_1_1"] } cw-storage-plus.workspace = true cosmwasm-schema.workspace = true thiserror.workspace = true -astroport = { path = "../../../packages/astroport", version = "5.1" } +astroport = "5.1" # Optional dependencies neutron-test-tube = { git = "https://github.com/j0nl1/neutron-test-tube", optional = true } diff --git a/contracts/tokenomics/incentives/Cargo.toml b/contracts/tokenomics/incentives/Cargo.toml index ad41b7ed..3b76ac67 100644 --- a/contracts/tokenomics/incentives/Cargo.toml +++ b/contracts/tokenomics/incentives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astroport-incentives" -version = "1.1.0" +version = "1.2.0" authors = ["Astroport"] edition = "2021" description = "Astroport Incentives Contract distributing rewards to LP stakers" @@ -21,7 +21,7 @@ cosmwasm-schema.workspace = true cw2.workspace = true cw20 = "1" cw-utils.workspace = true -astroport = "4" +astroport.workspace = true thiserror.workspace = true itertools.workspace = true diff --git a/contracts/tokenomics/incentives/src/error.rs b/contracts/tokenomics/incentives/src/error.rs index 0c6d737d..b582e681 100644 --- a/contracts/tokenomics/incentives/src/error.rs +++ b/contracts/tokenomics/incentives/src/error.rs @@ -69,4 +69,7 @@ pub enum ContractError { #[error("Failed to migrate contract")] MigrationError {}, + + #[error("Sent insufficient reward {reward} for pool {lp_token}")] + InsuffiicientRewardToken { reward: String, lp_token: String }, } diff --git a/contracts/tokenomics/incentives/src/execute.rs b/contracts/tokenomics/incentives/src/execute.rs index 7868efec..ab986296 100644 --- a/contracts/tokenomics/incentives/src/execute.rs +++ b/contracts/tokenomics/incentives/src/execute.rs @@ -23,7 +23,8 @@ use crate::state::{ }; use crate::utils::{ asset_info_key, claim_orphaned_rewards, claim_rewards, deactivate_blocked_pools, - deactivate_pool, incentivize, is_pool_registered, query_pair_info, remove_reward_from_pool, + deactivate_pool, incentivize_many, is_pool_registered, query_pair_info, + remove_reward_from_pool, }; #[cfg_attr(not(feature = "library"), entry_point)] @@ -94,8 +95,9 @@ pub fn execute( ExecuteMsg::Withdraw { lp_token, amount } => withdraw(deps, env, info, lp_token, amount), ExecuteMsg::SetTokensPerSecond { amount } => set_tokens_per_second(deps, env, info, amount), ExecuteMsg::Incentivize { lp_token, schedule } => { - incentivize(deps, info, env, lp_token, schedule) + incentivize_many(deps, info, env, vec![(lp_token, schedule)]) } + ExecuteMsg::IncentivizeMany(incentives) => incentivize_many(deps, info, env, incentives), ExecuteMsg::RemoveRewardFromPool { lp_token, reward, diff --git a/contracts/tokenomics/incentives/src/migrate.rs b/contracts/tokenomics/incentives/src/migrate.rs index 885ec237..48c6ed9f 100644 --- a/contracts/tokenomics/incentives/src/migrate.rs +++ b/contracts/tokenomics/incentives/src/migrate.rs @@ -13,12 +13,14 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result match contract_version.version.as_ref() { - "1.0.0" | "1.0.1" => {} + "1.0.0" | "1.0.1" | "1.1.0" => {} _ => return Err(ContractError::MigrationError {}), }, _ => return Err(ContractError::MigrationError {}), }; + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + Ok(Response::new() .add_attribute("previous_contract_name", &contract_version.contract) .add_attribute("previous_contract_version", &contract_version.version) diff --git a/contracts/tokenomics/incentives/src/query.rs b/contracts/tokenomics/incentives/src/query.rs index 520c7a6d..a5f9e72e 100644 --- a/contracts/tokenomics/incentives/src/query.rs +++ b/contracts/tokenomics/incentives/src/query.rs @@ -59,25 +59,32 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { - let lp_asset = determine_asset_info(&lp_token, deps.api)?; - let pool_info = PoolInfo::may_load(deps.storage, &lp_asset)?; + let reward_asset = determine_asset_info(&reward, deps.api)?; + let config = CONFIG.load(deps.storage)?; - let is_fee_expected = pool_info - .map(|mut x| -> StdResult<_> { - // update_rewards() removes finished schedules - x.update_rewards(deps.storage, &env, &lp_asset)?; - let reward_asset = determine_asset_info(&reward, deps.api)?; + let is_fee_expected = if reward_asset == config.astro_token { + // ASTRO rewards don't require incentivize fee. + false + } else { + let lp_asset = determine_asset_info(&lp_token, deps.api)?; + let pool_info = PoolInfo::may_load(deps.storage, &lp_asset)?; - let expected = x - .rewards - .into_iter() - .filter(|x| x.reward.is_external()) - .all(|x| x.reward.asset_info() != &reward_asset); + pool_info + .map(|mut x| -> StdResult<_> { + // update_rewards() removes finished schedules + x.update_rewards(deps.storage, &env, &lp_asset)?; - Ok(expected) - }) - .transpose()? - .unwrap_or(true); + let expected = x + .rewards + .into_iter() + .filter(|x| x.reward.is_external()) + .all(|x| x.reward.asset_info() != &reward_asset); + + Ok(expected) + }) + .transpose()? + .unwrap_or(true) + }; Ok(to_json_binary(&is_fee_expected)?) } diff --git a/contracts/tokenomics/incentives/src/state.rs b/contracts/tokenomics/incentives/src/state.rs index bee43b86..d06659a6 100644 --- a/contracts/tokenomics/incentives/src/state.rs +++ b/contracts/tokenomics/incentives/src/state.rs @@ -266,6 +266,7 @@ impl PoolInfo { storage: &mut dyn Storage, lp_asset: &AssetInfo, schedule: &IncentivesSchedule, + astro_token: &AssetInfo, ) -> Result<(), ContractError> { let ext_rewards_len = self .rewards @@ -277,8 +278,12 @@ impl PoolInfo { |r| matches!(&r.reward, RewardType::Ext { info, .. } if info == &schedule.reward_info), ); - // Check that we don't exceed the maximum number of reward tokens per pool - if ext_rewards_len == MAX_REWARD_TOKENS as usize && maybe_active_schedule.is_none() { + // Check that we don't exceed the maximum number of reward tokens per pool. + // Allowing ASTRO reward to exceed this limit + if ext_rewards_len == MAX_REWARD_TOKENS as usize + && maybe_active_schedule.is_none() + && schedule.reward_info.ne(astro_token) + { return Err(ContractError::TooManyRewardTokens { lp_token: lp_asset.to_string(), }); @@ -288,7 +293,7 @@ impl PoolInfo { let next_update_ts = match &active_schedule.reward { RewardType::Ext { next_update_ts, .. } => *next_update_ts, RewardType::Int(_) => { - unreachable!("Only external rewards can be deregistered") + unreachable!("Only external rewards can be extended") } }; diff --git a/contracts/tokenomics/incentives/src/utils.rs b/contracts/tokenomics/incentives/src/utils.rs index 10ffcc74..e1c36fe4 100644 --- a/contracts/tokenomics/incentives/src/utils.rs +++ b/contracts/tokenomics/incentives/src/utils.rs @@ -5,7 +5,7 @@ use cosmwasm_std::{ use itertools::Itertools; use astroport::asset::{ - determine_asset_info, pair_info_by_pool, AssetInfo, AssetInfoExt, CoinsExt, PairInfo, + determine_asset_info, pair_info_by_pool, AssetInfo, AssetInfoExt, PairInfo, }; use astroport::factory::PairType; use astroport::incentives::{Config, IncentivesSchedule, InputSchedule, MAX_ORPHANED_REWARD_LIMIT}; @@ -202,14 +202,15 @@ pub fn deactivate_blocked_pools(deps: DepsMut, env: Env) -> Result Result { - let schedule = IncentivesSchedule::from_input(&env, &input)?; + let schedule = IncentivesSchedule::from_input(env, &input)?; - let mut response = Response::new().add_attributes([ + let mut response = response.add_attributes([ attr("action", "incentivize"), attr("lp_token", lp_token.clone()), attr("start_ts", env.block.time.seconds().to_string()), @@ -231,49 +232,46 @@ pub fn incentivize( is_pool_registered(deps.querier, &config, &pair_info, &lp_token)?; let mut pool_info = PoolInfo::may_load(deps.storage, &lp_token_asset)?.unwrap_or_default(); - pool_info.update_rewards(deps.storage, &env, &lp_token_asset)?; + pool_info.update_rewards(deps.storage, env, &lp_token_asset)?; let rewards_number_before = pool_info.rewards.len(); - pool_info.incentivize(deps.storage, &lp_token_asset, &schedule)?; - - let mut funds = info.funds.clone(); + pool_info.incentivize( + deps.storage, + &lp_token_asset, + &schedule, + &config.astro_token, + )?; // Check whether this is a new external reward token. // 3rd parties are encouraged to keep endless schedules without breaks even with the small rewards. // Otherwise, reward token will be removed from the pool info and go to outstanding rewards. // Next schedules with the same token will be considered as "new". - if rewards_number_before < pool_info.rewards.len() { + // ASTRO rewards don't require incentivize fee. + if rewards_number_before < pool_info.rewards.len() && schedule.reward_info != config.astro_token + { // If fee set we expect to receive it if let Some(incentivization_fee_info) = &config.incentivization_fee_info { - let fee_coin_pos = funds - .iter() - .find_position(|coin| coin.denom == incentivization_fee_info.fee.denom); - if let Some((ind, fee_coin)) = fee_coin_pos { - // Mutate funds array so we can assert below that reward coins properly sent - funds[ind].amount = fee_coin - .amount - .checked_sub(incentivization_fee_info.fee.amount) - .map_err(|_| ContractError::IncentivizationFeeExpected { - fee: incentivization_fee_info.fee.to_string(), - lp_token, - new_reward_token: schedule.reward_info.to_string(), - })?; - if funds[ind].amount.is_zero() { - funds.remove(ind); - } - - // Send fee to fee receiver - response = response.add_message(BankMsg::Send { - to_address: incentivization_fee_info.fee_receiver.to_string(), - amount: vec![incentivization_fee_info.fee.clone()], - }); - } else { - return Err(ContractError::IncentivizationFeeExpected { + info.funds + .iter_mut() + .find(|coin| coin.denom == incentivization_fee_info.fee.denom) + .and_then(|found| { + found.amount = found + .amount + .checked_sub(incentivization_fee_info.fee.amount) + .ok()?; + Some(()) + }) + .ok_or_else(|| ContractError::IncentivizationFeeExpected { fee: incentivization_fee_info.fee.to_string(), - lp_token, + lp_token: lp_token.clone(), new_reward_token: schedule.reward_info.to_string(), - }); - } + })?; + + // Send fee to fee receiver + response = response.add_message(BankMsg::Send { + to_address: incentivization_fee_info.fee_receiver.to_string(), + amount: vec![incentivization_fee_info.fee.clone()], + }); } } @@ -290,8 +288,19 @@ pub fn incentivize( vec![], )?); } - AssetInfo::NativeToken { .. } => { - funds.assert_coins_properly_sent(&[input.reward], &[schedule.reward_info.clone()])? + AssetInfo::NativeToken { denom } => { + // Mutate funds array + info.funds + .iter_mut() + .find(|coin| coin.denom.eq(denom)) + .and_then(|found| { + found.amount = found.amount.checked_sub(input.reward.amount).ok()?; + Some(()) + }) + .ok_or_else(|| ContractError::InsuffiicientRewardToken { + reward: input.reward.info.to_string(), + lp_token, + })?; } } @@ -300,6 +309,30 @@ pub fn incentivize( Ok(response) } +pub fn incentivize_many( + mut deps: DepsMut, + mut info: MessageInfo, + env: Env, + incentives: Vec<(String, InputSchedule)>, +) -> Result { + let mut response = Response::default(); + for (lp_token, schedule) in incentives { + response = incentivize(deps.branch(), &mut info, &env, response, lp_token, schedule)?; + } + + for coin in info.funds { + ensure!( + coin.amount.is_zero(), + StdError::generic_err(format!( + "Supplied coins contain {} that is not in the input asset vector", + &coin.denom + )) + ); + } + + Ok(response) +} + pub fn remove_reward_from_pool( deps: DepsMut, info: MessageInfo, diff --git a/contracts/tokenomics/incentives/tests/helper/helper.rs b/contracts/tokenomics/incentives/tests/helper/helper.rs index a0af6736..2b6c1bd8 100644 --- a/contracts/tokenomics/incentives/tests/helper/helper.rs +++ b/contracts/tokenomics/incentives/tests/helper/helper.rs @@ -4,11 +4,16 @@ use std::collections::hash_map::Entry; use std::collections::HashMap; use anyhow::Result as AnyResult; -use astroport_test::cw_multi_test::{ - AddressGenerator, App, AppBuilder, AppResponse, BankKeeper, Contract, ContractWrapper, - DistributionKeeper, Executor, FailingModule, StakeKeeper, WasmKeeper, +use astroport::asset::{Asset, AssetInfo, AssetInfoExt, PairInfo}; +use astroport::astro_converter::OutpostBurnParams; +use astroport::factory::{PairConfig, PairType}; +use astroport::incentives::{ + Config, ExecuteMsg, IncentivesSchedule, IncentivizationFeeInfo, InputSchedule, + PoolInfoResponse, QueryMsg, RewardInfo, ScheduleResponse, }; -use astroport_test::modules::stargate::MockStargate; +use astroport::pair::StablePoolParams; +use astroport::vesting::{MigrateMsg, VestingAccount, VestingSchedule, VestingSchedulePoint}; +use astroport::{astro_converter, factory, native_coin_registry, pair, vesting}; use cosmwasm_std::testing::{mock_env, MockApi, MockStorage}; use cosmwasm_std::{ coin, to_json_binary, Addr, Api, BlockInfo, CanonicalAddr, Coin, Decimal256, Empty, Env, @@ -18,17 +23,13 @@ use cosmwasm_std::{ use cw20::MinterResponse; use itertools::Itertools; -use crate::helper::broken_cw20; -use astroport::asset::{Asset, AssetInfo, AssetInfoExt, PairInfo}; -use astroport::astro_converter::OutpostBurnParams; -use astroport::factory::{PairConfig, PairType}; -use astroport::incentives::{ - Config, ExecuteMsg, IncentivesSchedule, IncentivizationFeeInfo, InputSchedule, - PoolInfoResponse, QueryMsg, RewardInfo, ScheduleResponse, +use astroport_test::cw_multi_test::{ + AddressGenerator, App, AppBuilder, AppResponse, BankKeeper, Contract, ContractWrapper, + DistributionKeeper, Executor, FailingModule, StakeKeeper, WasmKeeper, }; -use astroport::pair::StablePoolParams; -use astroport::vesting::{MigrateMsg, VestingAccount, VestingSchedule, VestingSchedulePoint}; -use astroport::{astro_converter, factory, native_coin_registry, pair, vesting}; +use astroport_test::modules::stargate::MockStargate; + +use crate::helper::broken_cw20; fn factory_contract() -> Box> { Box::new( @@ -340,6 +341,7 @@ impl Helper { owner: owner.to_string(), whitelist_code_id: 0, coin_registry_address: coin_registry_address.to_string(), + tracker_config: None, }, &[], "Astroport Factory", @@ -657,6 +659,60 @@ impl Helper { .unwrap() } + pub fn incentivize_many( + &mut self, + from: &Addr, + incentives: Vec<(&str, InputSchedule)>, + attach_funds: &[Coin], + ) -> AnyResult { + let mut funds = HashMap::new(); + for (_, schedule) in &incentives { + match &schedule.reward.info { + AssetInfo::Token { contract_addr } => { + self.app + .execute_contract( + from.clone(), + contract_addr.clone(), + &cw20::Cw20ExecuteMsg::IncreaseAllowance { + spender: self.generator.to_string(), + amount: schedule.reward.amount, + expires: None, + }, + &[], + ) + .unwrap(); + } + AssetInfo::NativeToken { .. } => { + let coin = schedule.reward.as_coin().unwrap(); + funds.insert(coin.denom.clone(), coin); + } + } + } + for coin in attach_funds { + match funds.entry(coin.denom.clone()) { + Entry::Occupied(mut entry) => { + entry.get_mut().amount += coin.amount; + } + Entry::Vacant(entry) => { + entry.insert(coin.clone()); + } + } + } + let funds = funds.values().cloned().collect_vec(); + + self.app.execute_contract( + from.clone(), + self.generator.clone(), + &ExecuteMsg::IncentivizeMany( + incentives + .into_iter() + .map(|(lp_token, schedule)| (lp_token.to_string(), schedule)) + .collect(), + ), + &funds, + ) + } + pub fn incentivize( &mut self, from: &Addr, @@ -1021,6 +1077,7 @@ impl Helper { slippage_tolerance: None, auto_stake: Some(auto_stake), receiver: None, + min_lp_to_receive: None, }; self.app diff --git a/contracts/tokenomics/incentives/tests/incentives_integration_tests.rs b/contracts/tokenomics/incentives/tests/incentives_integration_tests.rs index 11af1288..ea5678f5 100644 --- a/contracts/tokenomics/incentives/tests/incentives_integration_tests.rs +++ b/contracts/tokenomics/incentives/tests/incentives_integration_tests.rs @@ -1,12 +1,13 @@ use std::str::FromStr; -use cosmwasm_std::{coin, coins, Decimal256, Timestamp, Uint128}; - use astroport::asset::{native_asset_info, AssetInfo, AssetInfoExt}; use astroport::incentives::{ - ExecuteMsg, IncentivizationFeeInfo, ScheduleResponse, EPOCHS_START, EPOCH_LENGTH, - MAX_REWARD_TOKENS, + ExecuteMsg, IncentivizationFeeInfo, InputSchedule, ScheduleResponse, EPOCHS_START, + EPOCH_LENGTH, MAX_REWARD_TOKENS, }; +use cosmwasm_std::{coin, coins, Decimal256, Timestamp, Uint128}; +use itertools::Itertools; + use astroport_incentives::error::ContractError; use astroport_test::cw_multi_test::Executor; @@ -691,6 +692,58 @@ fn test_multiple_schedules_same_reward() { ); } +#[test] +fn test_astro_can_bypass_rewards_limit() { + let astro = native_asset_info("astro".to_string()); + let mut helper = Helper::new("owner", &astro, false).unwrap(); + let incentivization_fee = helper.incentivization_fee.clone(); + + let lp_token = helper + .create_pair(&[AssetInfo::native("foo"), AssetInfo::native("bar")]) + .unwrap() + .liquidity_token + .to_string(); + + let bank = TestAddr::new("bank"); + + let schedules: Vec<_> = (1..=MAX_REWARD_TOKENS) + .into_iter() + .map(|i| { + let reward_asset_info = AssetInfo::native(format!("reward{i}")); + let reward = reward_asset_info.with_balance(1000_000000u128); + helper.create_schedule(&reward, 1).unwrap() + }) + .collect(); + // Create multiple schedules with different rewards (starts on the next week) + for (schedule, _) in &schedules { + helper.mint_assets(&bank, &[schedule.reward.clone()]); + helper.mint_coin(&bank, &incentivization_fee); + helper + .incentivize( + &bank, + &lp_token, + schedule.clone(), + &[incentivization_fee.clone()], + ) + .unwrap(); + } + + // ASTRO can always be added no matter what MAX_REWARD_TOKENS limit is + let astro_reward = astro.with_balance(1000_000000u128); + helper.mint_assets(&bank, &[astro_reward.clone()]); + helper + .incentivize( + &bank, + &lp_token, + InputSchedule { + reward: astro_reward.clone(), + duration_periods: 1, + }, + &[], + ) + .unwrap(); +} + #[test] fn test_multiple_schedules_different_reward() { let astro = native_asset_info("astro".to_string()); @@ -833,6 +886,174 @@ fn test_multiple_schedules_different_reward() { ); } +#[test] +fn test_incentivize_many() { + let astro = native_asset_info("astro".to_string()); + let mut helper = Helper::new("owner", &astro, false).unwrap(); + let owner = helper.owner.clone(); + let incentivization_fee = helper.incentivization_fee.clone(); + + let asset_infos = [AssetInfo::native("foo"), AssetInfo::native("bar")]; + let pair_info = helper.create_pair(&asset_infos).unwrap(); + let lp_token = pair_info.liquidity_token.to_string(); + + let provide_assets = [ + asset_infos[0].with_balance(100000u64), + asset_infos[1].with_balance(100000u64), + ]; + // Owner provides liquidity first just to make following calculations easier + // since first depositor gets small cut of LP tokens + helper + .provide_liquidity( + &owner, + &provide_assets, + &pair_info.contract_addr, + false, // Owner doesn't stake in generator + ) + .unwrap(); + + let user = TestAddr::new("user"); + helper + .provide_liquidity(&user, &provide_assets, &pair_info.contract_addr, true) + .unwrap(); + + let bank = TestAddr::new("bank"); + + let schedules: Vec<_> = (1..=MAX_REWARD_TOKENS) + .into_iter() + .map(|i| { + let reward_asset_info = AssetInfo::native(format!("reward{i}")); + let reward = reward_asset_info.with_balance(1000_000000u128); + helper.create_schedule(&reward, 2).unwrap() + }) + .collect(); + + // Create multiple schedules with different rewards (starts on the next week) + let incentives = schedules + .iter() + .map(|(schedule, _)| { + helper.mint_assets(&bank, &[schedule.reward.clone()]); + helper.mint_coin(&bank, &incentivization_fee); + (lp_token.as_str(), schedule.clone()) + }) + .collect_vec(); + + // Missing incentivize fee + let err = helper + .incentivize_many(&bank, incentives.clone(), &[]) + .unwrap_err(); + assert_eq!( + err.downcast::().unwrap(), + ContractError::IncentivizationFeeExpected { + fee: incentivization_fee.to_string(), + lp_token: lp_token.clone(), + new_reward_token: schedules[0].0.reward.info.to_string(), + } + ); + + // Add random coins + let random_coin = coin(1000, "random"); + helper.mint_coin(&bank, &random_coin); + let err = helper + .incentivize_many( + &bank, + incentives.clone(), + &[ + coin( + incentivization_fee.amount.u128() * schedules.len() as u128, + &incentivization_fee.denom, + ), + random_coin, + ], + ) + .unwrap_err(); + assert_eq!( + err.root_cause().to_string(), + "Generic error: Supplied coins contain random that is not in the input asset vector" + ); + + helper + .incentivize_many( + &bank, + incentives, + &coins( + incentivization_fee.amount.u128() * schedules.len() as u128, + &incentivization_fee.denom, + ), + ) + .unwrap(); + + // Can't incentivize with one more reward token + let reward_asset_info = AssetInfo::native(format!("reward{}", MAX_REWARD_TOKENS + 1)); + let reward = reward_asset_info.with_balance(1000_000000u128); + let (schedule, _) = helper.create_schedule(&reward, 2).unwrap(); + helper.mint_assets(&bank, &[schedule.reward.clone()]); + helper.mint_coin(&bank, &incentivization_fee); + let err = helper + .incentivize( + &bank, + &lp_token, + schedule.clone(), + &[incentivization_fee.clone()], + ) + .unwrap_err(); + assert_eq!( + err.downcast::().unwrap(), + ContractError::TooManyRewardTokens { + lp_token: lp_token.clone() + } + ); + + // Rewards started right away + helper + .app + .update_block(|block| block.time = block.time.plus_seconds(86400)); + for (schedule, _) in &schedules { + helper.claim_rewards(&user, vec![lp_token.clone()]).unwrap(); + let reward_balance = schedule + .reward + .info + .query_pool(&helper.app.wrap(), &user) + .unwrap(); + assert_eq!(reward_balance.u128(), 47_629547); + } + + // Iterate till the end of the longest schedule by 1 day and claim rewards + loop { + let pending = helper.query_pending_rewards(&user, &lp_token); + let bal_before = helper.snapshot_balances(&user, &pending); + + helper.claim_rewards(&user, vec![lp_token.clone()]).unwrap(); + + let bal_after = helper.snapshot_balances(&user, &pending); + assert_rewards(&bal_before, &bal_after, &pending); + + if helper.app.block_info().time.seconds() > schedules.last().cloned().unwrap().1.end_ts { + break; + } else { + helper + .app + .update_block(|block| block.time = block.time.plus_seconds(86400)); + } + } + + for (schedule, _) in &schedules { + helper.claim_rewards(&user, vec![lp_token.clone()]).unwrap(); + let reward_balance = schedule + .reward + .info + .query_pool(&helper.app.wrap(), &user) + .unwrap(); + // Total amount is a bit off because of rounding due to Decimal256 type + assert_eq!( + reward_balance.u128(), + 999_999980, + "Balance for {} is wrong", + schedule.reward.info + ); + } +} + #[test] fn test_claim_between_different_periods() { let astro = native_asset_info("astro".to_string()); @@ -924,7 +1145,6 @@ fn test_astro_external_reward() { .update_block(|block| block.time = Timestamp::from_seconds(EPOCHS_START + EPOCH_LENGTH)); let owner = helper.owner.clone(); - let incentivization_fee = helper.incentivization_fee.clone(); let asset_infos = [AssetInfo::native("foo"), AssetInfo::native("bar")]; let pair_info = helper.create_pair(&asset_infos).unwrap(); @@ -955,18 +1175,11 @@ fn test_astro_external_reward() { // Setup external rewards: 2 equal external ASTRO rewards that must be summed up let bank = TestAddr::new("bank"); let reward = astro.with_balance(2u128 * 7 * 86400 * 25); // 25 uastro per second - let (schedule, internal_sch) = helper.create_schedule(&reward, 2).unwrap(); + let (schedule, internal_sch) = helper.create_schedule(&reward, 1).unwrap(); helper.mint_assets(&bank, &[reward.clone()]); - helper.mint_coin(&bank, &incentivization_fee); helper - .incentivize( - &bank, - &lp_token, - schedule.clone(), - &[incentivization_fee.clone()], - ) + .incentivize(&bank, &lp_token, schedule.clone(), &[]) .unwrap(); - // 2nd schedule doesn't require incentivization fee helper.mint_assets(&bank, &[reward.clone()]); helper .incentivize(&bank, &lp_token, schedule.clone(), &[]) @@ -1008,7 +1221,7 @@ fn test_astro_external_reward() { assert_eq!( astro_reward_balance.u128(), u128::from(time_now - time_before_claims) * 100 // protocol rewards - + u128::from(internal_sch.end_ts - internal_sch.next_epoch_start_ts) * 50 // external rewards + + u128::from(internal_sch.end_ts - time_before_claims) * 50 // external rewards ); } @@ -1490,7 +1703,7 @@ fn test_remove_rewards() { let bank = TestAddr::new("bank"); let reward_asset_info = AssetInfo::native("reward"); let reward = reward_asset_info.with_balance(1000_000000u128); - let (schedule, internal_sch) = helper.create_schedule(&reward, 2).unwrap(); + let (schedule, _) = helper.create_schedule(&reward, 1).unwrap(); helper.mint_assets(&bank, &[reward.clone()]); helper.mint_coin(&bank, &incentivization_fee); @@ -1504,10 +1717,6 @@ fn test_remove_rewards() { ) .unwrap(); - helper.app.update_block(|block| { - block.time = Timestamp::from_seconds(internal_sch.next_epoch_start_ts) - }); - // 5 days for _ in 0..5 { helper.next_block(86400); @@ -1655,7 +1864,7 @@ fn test_long_unclaimed_rewards() { .query_pool(&helper.app.wrap(), &receiver) .unwrap() .u128(); - assert_eq!(deregister_amount, 62499_999999); + assert_eq!(deregister_amount, 58441_558441); // Iterate till the end of the longest schedule by 1 day and claim rewards loop { @@ -1681,7 +1890,7 @@ fn test_long_unclaimed_rewards() { .query_pool(&helper.app.wrap(), &user) .unwrap() .u128(); - assert_eq!(deregister_amount + claimed_reward1, 99999_999998); + assert_eq!(deregister_amount + claimed_reward1, 99999_999999); for (schedule, _) in schedules.iter().skip(2) { let bal = schedule @@ -1690,7 +1899,7 @@ fn test_long_unclaimed_rewards() { .query_pool(&helper.app.wrap(), &user) .unwrap() .u128(); - assert_eq!(bal, 99_999_999974); // All rewards are claimed + assert_eq!(bal, 99_999_999943); // All rewards are claimed } } @@ -2028,7 +2237,13 @@ fn test_incentive_without_funds() { ) .unwrap_err(); - assert_eq!(err.root_cause().to_string(), "Generic error: Native token balance mismatch between the argument (1000000000usdc) and the transferred (0usdc)") + assert_eq!( + err.downcast::().unwrap(), + ContractError::InsuffiicientRewardToken { + reward: reward_asset_info.to_string(), + lp_token + } + ) } #[test] diff --git a/packages/astroport/Cargo.toml b/packages/astroport/Cargo.toml index 54ea577c..da9a7af0 100644 --- a/packages/astroport/Cargo.toml +++ b/packages/astroport/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astroport" -version = "5.2.0" +version = "5.3.0" authors = ["Astroport"] edition = "2021" description = "Common Astroport types, queriers and other utils" diff --git a/packages/astroport/src/common.rs b/packages/astroport/src/common.rs index d364b463..ce5b4c0e 100644 --- a/packages/astroport/src/common.rs +++ b/packages/astroport/src/common.rs @@ -5,6 +5,8 @@ use cosmwasm_std::{ use cw_storage_plus::Item; const MAX_PROPOSAL_TTL: u64 = 1209600; +/// Tokenfactory LP token subdenom +pub const LP_SUBDENOM: &str = "astroport/share"; /// This structure describes the parameters used for creating a request for a change of contract ownership. #[cw_serde] diff --git a/packages/astroport/src/incentives.rs b/packages/astroport/src/incentives.rs index bf28d217..9264c47b 100644 --- a/packages/astroport/src/incentives.rs +++ b/packages/astroport/src/incentives.rs @@ -61,14 +61,11 @@ impl IncentivesSchedule { let block_ts = env.block.time.seconds(); let rem = block_ts % EPOCHS_START; - let next_epoch_start_ts = if rem % EPOCH_LENGTH == 0 { - // Hit at the beginning of the current epoch - block_ts - } else { - // Hit somewhere in the middle. - // Partially distribute rewards for the current epoch and add input.duration_periods periods more - EPOCHS_START + (rem / EPOCH_LENGTH + 1) * EPOCH_LENGTH - }; + // If rem == 0 then we are at the beginning of the current epoch. + // To keep logic consistent, we always add 1 week more. + // Hence, minimal possible duration varies from 7 days 1 second to 14 days, + // which depends on how far from Monday block time is. + let next_epoch_start_ts = EPOCHS_START + (rem / EPOCH_LENGTH + 1) * EPOCH_LENGTH; let end_ts = next_epoch_start_ts + input.duration_periods * EPOCH_LENGTH; let rps = Decimal256::from_ratio(input.reward.amount, end_ts - block_ts); @@ -131,6 +128,8 @@ pub enum ExecuteMsg { /// Incentives schedule schedule: InputSchedule, }, + /// Same as Incentivize endpoint but for multiple pools in one go. + IncentivizeMany(Vec<(String, InputSchedule)>), /// Remove specific reward token from the pool. /// Only the owner can execute this. RemoveRewardFromPool { @@ -412,13 +411,13 @@ mod tests { let schedule = IncentivesSchedule::from_input( &env, &InputSchedule { - reward: AssetInfo::native("test").with_balance(EPOCH_LENGTH), + reward: AssetInfo::native("test").with_balance(2 * EPOCH_LENGTH), duration_periods: 1, }, ) .unwrap(); - assert_eq!(schedule.next_epoch_start_ts, EPOCHS_START); + assert_eq!(schedule.next_epoch_start_ts, EPOCHS_START + EPOCH_LENGTH); assert_eq!(schedule.end_ts, schedule.next_epoch_start_ts + EPOCH_LENGTH); assert_eq!(schedule.rps, Decimal256::one()); diff --git a/packages/astroport_pcl_common/Cargo.toml b/packages/astroport_pcl_common/Cargo.toml index 6cc5d46f..02819ab5 100644 --- a/packages/astroport_pcl_common/Cargo.toml +++ b/packages/astroport_pcl_common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astroport-pcl-common" -version = "2.0.1" +version = "2.0.2" edition = "2021" description = "Common package contains math tools and utils for Astroport PCL pairs" license = "GPL-3.0-only" @@ -19,7 +19,7 @@ cosmwasm-schema.workspace = true cw-storage-plus.workspace = true cw20 = "1" thiserror.workspace = true -astroport = { path = "../astroport", version = "5" } +astroport.workspace = true astroport-factory = { path = "../../contracts/factory", version = "1.5", features = ["library"] } itertools.workspace = true diff --git a/packages/astroport_pcl_common/src/utils.rs b/packages/astroport_pcl_common/src/utils.rs index ec9c7b23..8231d953 100644 --- a/packages/astroport_pcl_common/src/utils.rs +++ b/packages/astroport_pcl_common/src/utils.rs @@ -194,14 +194,12 @@ pub struct SwapResult { } impl SwapResult { - /// Calculates **last price** and **last real price**. - /// Returns (last_price, last_real_price) where: - /// - last_price is a price for repeg algo, + /// Calculates **last price** for PCL repeg algo pub fn calc_last_price(&self, offer_amount: Decimal256, offer_ind: usize) -> Decimal256 { if offer_ind == 0 { - offer_amount / (self.dy + self.maker_fee) + offer_amount / (self.dy + self.maker_fee + self.share_fee) } else { - (self.dy + self.maker_fee) / offer_amount + (self.dy + self.maker_fee + self.share_fee) / offer_amount } } } diff --git a/schemas/astroport-incentives/astroport-incentives.json b/schemas/astroport-incentives/astroport-incentives.json index fcd0f78f..0d0eb129 100644 --- a/schemas/astroport-incentives/astroport-incentives.json +++ b/schemas/astroport-incentives/astroport-incentives.json @@ -1,6 +1,6 @@ { "contract_name": "astroport-incentives", - "contract_version": "1.1.0", + "contract_version": "1.2.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -336,6 +336,32 @@ }, "additionalProperties": false }, + { + "description": "Same as Incentivize endpoint but for multiple pools in one go.", + "type": "object", + "required": [ + "incentivize_many" + ], + "properties": { + "incentivize_many": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/InputSchedule" + } + ], + "maxItems": 2, + "minItems": 2 + } + } + }, + "additionalProperties": false + }, { "description": "Remove specific reward token from the pool. Only the owner can execute this.", "type": "object", @@ -1711,7 +1737,7 @@ "additionalProperties": false }, { - "description": "External rewards always have corresponding schedules. Reward is paid out from Generator contract balance.", + "description": "External rewards always have corresponding schedules. Reward is paid out from Incentives contract balance.", "type": "object", "required": [ "ext" @@ -1897,7 +1923,7 @@ "additionalProperties": false }, { - "description": "External rewards always have corresponding schedules. Reward is paid out from Generator contract balance.", + "description": "External rewards always have corresponding schedules. Reward is paid out from Incentives contract balance.", "type": "object", "required": [ "ext" diff --git a/schemas/astroport-incentives/raw/execute.json b/schemas/astroport-incentives/raw/execute.json index bc88e8dc..142ae33c 100644 --- a/schemas/astroport-incentives/raw/execute.json +++ b/schemas/astroport-incentives/raw/execute.json @@ -190,6 +190,32 @@ }, "additionalProperties": false }, + { + "description": "Same as Incentivize endpoint but for multiple pools in one go.", + "type": "object", + "required": [ + "incentivize_many" + ], + "properties": { + "incentivize_many": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/InputSchedule" + } + ], + "maxItems": 2, + "minItems": 2 + } + } + }, + "additionalProperties": false + }, { "description": "Remove specific reward token from the pool. Only the owner can execute this.", "type": "object", diff --git a/schemas/astroport-incentives/raw/response_to_pool_info.json b/schemas/astroport-incentives/raw/response_to_pool_info.json index 8a059e0e..74b4e4a6 100644 --- a/schemas/astroport-incentives/raw/response_to_pool_info.json +++ b/schemas/astroport-incentives/raw/response_to_pool_info.json @@ -150,7 +150,7 @@ "additionalProperties": false }, { - "description": "External rewards always have corresponding schedules. Reward is paid out from Generator contract balance.", + "description": "External rewards always have corresponding schedules. Reward is paid out from Incentives contract balance.", "type": "object", "required": [ "ext" diff --git a/schemas/astroport-incentives/raw/response_to_reward_info.json b/schemas/astroport-incentives/raw/response_to_reward_info.json index a04f33c4..7dee6a0f 100644 --- a/schemas/astroport-incentives/raw/response_to_reward_info.json +++ b/schemas/astroport-incentives/raw/response_to_reward_info.json @@ -124,7 +124,7 @@ "additionalProperties": false }, { - "description": "External rewards always have corresponding schedules. Reward is paid out from Generator contract balance.", + "description": "External rewards always have corresponding schedules. Reward is paid out from Incentives contract balance.", "type": "object", "required": [ "ext" diff --git a/schemas/astroport-pair-concentrated/astroport-pair-concentrated.json b/schemas/astroport-pair-concentrated/astroport-pair-concentrated.json index d9fdb54d..e0ba342d 100644 --- a/schemas/astroport-pair-concentrated/astroport-pair-concentrated.json +++ b/schemas/astroport-pair-concentrated/astroport-pair-concentrated.json @@ -1,6 +1,6 @@ { "contract_name": "astroport-pair-concentrated", - "contract_version": "4.0.1", + "contract_version": "4.0.2", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#",