From 783adb61a50db3c61c3d91a77d90f4c978a12777 Mon Sep 17 00:00:00 2001 From: Timofey <5527315+epanchee@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:34:05 +0300 Subject: [PATCH] feat: remove outdated Vesting contract --- Cargo.lock | 25 +- contracts/tokenomics/incentives/Cargo.toml | 3 +- .../incentives/tests/helper/helper.rs | 19 +- .../tests/incentives_integration_tests.rs | 157 +- .../tests/incentives_simulations.rs | 2 +- contracts/tokenomics/vesting/.cargo/config | 6 - contracts/tokenomics/vesting/Cargo.toml | 34 - contracts/tokenomics/vesting/README.md | 152 -- .../vesting/examples/vesting_schema.rs | 12 - contracts/tokenomics/vesting/src/contract.rs | 586 ------ contracts/tokenomics/vesting/src/error.rs | 51 - contracts/tokenomics/vesting/src/lib.rs | 6 - contracts/tokenomics/vesting/src/state.rs | 137 -- contracts/tokenomics/vesting/src/testing.rs | 134 -- .../tokenomics/vesting/tests/integration.rs | 1691 ----------------- 15 files changed, 32 insertions(+), 2983 deletions(-) delete mode 100644 contracts/tokenomics/vesting/.cargo/config delete mode 100644 contracts/tokenomics/vesting/Cargo.toml delete mode 100644 contracts/tokenomics/vesting/README.md delete mode 100644 contracts/tokenomics/vesting/examples/vesting_schema.rs delete mode 100644 contracts/tokenomics/vesting/src/contract.rs delete mode 100644 contracts/tokenomics/vesting/src/error.rs delete mode 100644 contracts/tokenomics/vesting/src/lib.rs delete mode 100644 contracts/tokenomics/vesting/src/state.rs delete mode 100644 contracts/tokenomics/vesting/src/testing.rs delete mode 100644 contracts/tokenomics/vesting/tests/integration.rs diff --git a/Cargo.lock b/Cargo.lock index fc10aa2e3..ee9f11688 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,8 +256,7 @@ dependencies = [ "astroport-pair 2.1.0", "astroport-pair-stable", "astroport-test", - "astroport-vesting 1.3.1", - "astroport-vesting 1.4.0", + "astroport-vesting", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 1.2.0", @@ -603,37 +602,19 @@ dependencies = [ "thiserror", ] -[[package]] -name = "astroport-vesting" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffce7cf86bf4d4f177ef941145352499e802abc4b898032af7808d16cca6371" -dependencies = [ - "astroport 2.9.5", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.15.1", - "cw-utils 0.15.1", - "cw2 0.15.1", - "cw20 0.15.1", - "thiserror", -] - [[package]] name = "astroport-vesting" version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f8f0f390245642ccc1ec10c7f79b440979187a98aa8b0246269d6a4e0172c" dependencies = [ - "astro-token-converter", "astroport 4.0.3", - "astroport-vesting 1.3.1", "cosmwasm-schema", "cosmwasm-std", - "cw-multi-test 1.2.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", "cw20 1.1.2", - "cw20-base", "thiserror", ] diff --git a/contracts/tokenomics/incentives/Cargo.toml b/contracts/tokenomics/incentives/Cargo.toml index 35ad7f84e..19c4c6538 100644 --- a/contracts/tokenomics/incentives/Cargo.toml +++ b/contracts/tokenomics/incentives/Cargo.toml @@ -26,14 +26,13 @@ thiserror.workspace = true itertools.workspace = true [dev-dependencies] -astroport-vesting_131 = { package = "astroport-vesting", version = "=1.3.1", features = ["library"] } astro-token-converter = { path = "../../periphery/astro_converter", version = "1.0", features = ["library"] } anyhow = "1" astroport-factory = { path = "../../factory" } astroport-pair = { path = "../../pair" } astroport-pair-stable = { path = "../../pair_stable" } astroport-native-coin-registry = { path = "../../periphery/native_coin_registry" } -astroport-vesting = { path = "../vesting" } +astroport-vesting = "1.4.0" astroport-test = { path = "../../../packages/astroport_test" } cw20-base = "1" proptest = "1.3" diff --git a/contracts/tokenomics/incentives/tests/helper/helper.rs b/contracts/tokenomics/incentives/tests/helper/helper.rs index 2b6c1bd86..48a62a407 100644 --- a/contracts/tokenomics/incentives/tests/helper/helper.rs +++ b/contracts/tokenomics/incentives/tests/helper/helper.rs @@ -80,17 +80,6 @@ fn vesting_contract() -> Box> { )) } -fn vesting_contract_v131() -> Box> { - Box::new( - ContractWrapper::new_with_empty( - astroport_vesting_131::contract::execute, - astroport_vesting_131::contract::instantiate, - astroport_vesting_131::contract::query, - ) - .with_migrate_empty(astroport_vesting_131::contract::migrate), - ) -} - fn astro_converter() -> Box> { Box::new(ContractWrapper::new_with_empty( astro_token_converter::contract::execute, @@ -260,7 +249,7 @@ pub struct Helper { } impl Helper { - pub fn new(owner: &str, astro: &AssetInfo, with_old_vesting: bool) -> AnyResult { + pub fn new(owner: &str, astro: &AssetInfo) -> AnyResult { let mut app = AppBuilder::new() .with_stargate(MockStargate::default()) .with_wasm(WasmKeeper::new().with_address_generator(TestAddr)) @@ -273,11 +262,7 @@ impl Helper { .build(|_, _, _| {}); let owner = TestAddr::new(owner); - let vesting_code = if with_old_vesting { - app.store_code(vesting_contract_v131()) - } else { - app.store_code(vesting_contract()) - }; + let vesting_code = app.store_code(vesting_contract()); let vesting = app .instantiate_contract( vesting_code, diff --git a/contracts/tokenomics/incentives/tests/incentives_integration_tests.rs b/contracts/tokenomics/incentives/tests/incentives_integration_tests.rs index 3e1b7d4ac..dcefe1f89 100644 --- a/contracts/tokenomics/incentives/tests/incentives_integration_tests.rs +++ b/contracts/tokenomics/incentives/tests/incentives_integration_tests.rs @@ -18,7 +18,7 @@ mod helper; #[test] fn test_stake_unstake() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); 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(); @@ -120,7 +120,7 @@ fn test_stake_unstake() { #[test] fn test_claim_rewards() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let mut pools = vec![ @@ -296,7 +296,7 @@ fn test_claim_rewards() { #[test] fn test_incentives() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -431,7 +431,7 @@ fn test_incentives() { #[test] fn test_cw20_incentives() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -523,7 +523,7 @@ fn test_cw20_incentives() { #[test] fn test_large_incentives() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -584,7 +584,7 @@ fn test_large_incentives() { #[test] fn test_multiple_schedules_same_reward() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -695,7 +695,7 @@ 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 mut helper = Helper::new("owner", &astro).unwrap(); let incentivization_fee = helper.incentivization_fee.clone(); let lp_token = helper @@ -747,7 +747,7 @@ fn test_astro_can_bypass_rewards_limit() { #[test] fn test_multiple_schedules_different_reward() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -889,7 +889,7 @@ 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 mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -1057,7 +1057,7 @@ fn test_incentivize_many() { #[test] fn test_claim_between_different_periods() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -1139,7 +1139,7 @@ fn test_claim_between_different_periods() { #[test] fn test_astro_external_reward() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); helper .app .update_block(|block| block.time = Timestamp::from_seconds(EPOCHS_START + EPOCH_LENGTH)); @@ -1225,117 +1225,10 @@ fn test_astro_external_reward() { ); } -#[test] -fn test_astro_protocol_reward_if_denom_changed() { - let astro = native_asset_info("ibc/old_cw20_astro".to_string()); - let mut helper = Helper::new("owner", &astro, true).unwrap(); - helper - .app - .update_block(|block| block.time = Timestamp::from_seconds(EPOCHS_START + EPOCH_LENGTH)); - - let owner = helper.owner.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 incentives - ) - .unwrap(); - - // Incentivize with ASTRO - helper.setup_pools(vec![(lp_token.clone(), 100)]).unwrap(); - helper.set_tokens_per_second(100).unwrap(); - - // Prepare user's liquidity - let user = TestAddr::new("user"); - helper - .provide_liquidity(&user, &provide_assets, &pair_info.contract_addr, true) - .unwrap(); - - let time_before_claims = helper.app.block_info().time.seconds(); - - let cycle_end = helper.app.block_info().time.seconds() + 86400 * 7; - - // Iterate one week 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() > cycle_end { - break; - } else { - helper.next_block(86400); - } - } - - let new_astro = native_asset_info("new_astro".to_string()); - - // Set new astro token. It replaces old astro token for all active pools - let msg = ExecuteMsg::UpdateConfig { - astro_token: Some(new_astro.clone()), - vesting_contract: None, - generator_controller: None, - guardian: None, - incentivization_fee_info: None, - token_transfer_gas_limit: None, - }; - helper - .app - .execute_contract(helper.owner.clone(), helper.generator.clone(), &msg, &[]) - .unwrap(); - - // migrate vesting contract with new astro denom; convert all astro to astro2 under the hood - helper.migrate_vesting(&new_astro.to_string()).unwrap(); - - let cycle_end = helper.app.block_info().time.seconds() + 86400 * 7; - - // Iterate one more week by 1 day and claim rewards (should be in new ASTRO) - 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() > cycle_end { - break; - } else { - helper.next_block(86400); - } - } - - let time_now = helper.app.block_info().time.seconds(); - let astro_reward_balance = astro.query_pool(&helper.app.wrap(), &user).unwrap(); - let new_astro_reward_balance = new_astro.query_pool(&helper.app.wrap(), &user).unwrap(); - assert_eq!( - astro_reward_balance.u128() + new_astro_reward_balance.u128(), - u128::from(time_now - time_before_claims) * 100 - ); -} - #[test] fn test_blocked_tokens() { let astro = native_asset_info("ibc/old_cw20_astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let guardian = TestAddr::new("guardian"); @@ -1505,7 +1398,7 @@ fn test_blocked_tokens() { #[test] fn test_blocked_pair_types() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let tokens = [ @@ -1631,7 +1524,7 @@ fn test_blocked_pair_types() { #[test] fn test_incentives_with_blocked() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -1669,7 +1562,7 @@ fn test_incentives_with_blocked() { fn test_remove_rewards() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); helper .app .update_block(|block| block.time = Timestamp::from_seconds(EPOCHS_START + EPOCH_LENGTH)); @@ -1781,7 +1674,7 @@ fn test_remove_rewards() { fn test_long_unclaimed_rewards() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); helper .app .update_block(|block| block.time = Timestamp::from_seconds(EPOCHS_START + EPOCH_LENGTH)); @@ -1907,7 +1800,7 @@ fn test_long_unclaimed_rewards() { #[test] fn test_queries() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -2047,7 +1940,7 @@ fn test_queries() { #[test] fn test_update_config() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let new_vesting = TestAddr::new("new_vesting"); let new_generator_controller = TestAddr::new("new_generator_controller"); @@ -2099,7 +1992,7 @@ fn test_update_config() { #[test] fn test_change_ownership() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let new_owner = TestAddr::new("new_owner"); @@ -2202,7 +2095,7 @@ fn test_change_ownership() { fn test_incentive_without_funds() { let astro = native_asset_info("astro".to_string()); let usdc = native_asset_info("usdc".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let asset_infos = [AssetInfo::native("foo"), AssetInfo::native("bar")]; let pair_info = helper.create_pair(&asset_infos).unwrap(); @@ -2253,7 +2146,7 @@ fn test_incentive_without_funds() { #[test] fn test_claim_excess_rewards() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let mut pools = vec![ ("uusd", "eur", "".to_string(), vec!["user1", "user2"], 100), @@ -2341,7 +2234,7 @@ fn test_claim_excess_rewards() { #[test] fn test_user_claim_less() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -2441,7 +2334,7 @@ fn test_user_claim_less() { #[test] fn test_broken_cw20_incentives() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); @@ -2537,7 +2430,7 @@ fn test_broken_cw20_incentives() { #[test] fn test_factory_deregisters_any_pool() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let asset_infos = &[AssetInfo::native("usd"), AssetInfo::native("foo")]; // factory contract create pair @@ -2553,7 +2446,7 @@ fn test_factory_deregisters_any_pool() { #[test] fn test_orphaned_rewards() { let astro = native_asset_info("astro".to_string()); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let incentivization_fee = helper.incentivization_fee.clone(); let asset_infos = [AssetInfo::native("foo"), AssetInfo::native("bar")]; diff --git a/contracts/tokenomics/incentives/tests/incentives_simulations.rs b/contracts/tokenomics/incentives/tests/incentives_simulations.rs index a687ccb76..a22ed1cbd 100644 --- a/contracts/tokenomics/incentives/tests/incentives_simulations.rs +++ b/contracts/tokenomics/incentives/tests/incentives_simulations.rs @@ -106,7 +106,7 @@ fn update_total_rewards( fn simulate_case(events: Vec<(Event, u64)>) { let astro = AssetInfo::native("astro"); - let mut helper = Helper::new("owner", &astro, false).unwrap(); + let mut helper = Helper::new("owner", &astro).unwrap(); let owner = helper.owner.clone(); let incentivization_fee = helper.incentivization_fee.clone(); diff --git a/contracts/tokenomics/vesting/.cargo/config b/contracts/tokenomics/vesting/.cargo/config deleted file mode 100644 index a79b8fdbd..000000000 --- a/contracts/tokenomics/vesting/.cargo/config +++ /dev/null @@ -1,6 +0,0 @@ -[alias] -wasm = "build --release --target wasm32-unknown-unknown" -wasm-debug = "build --target wasm32-unknown-unknown" -unit-test = "test --lib" -integration-test = "test --test integration" -schema = "run --example vesting_schema" diff --git a/contracts/tokenomics/vesting/Cargo.toml b/contracts/tokenomics/vesting/Cargo.toml deleted file mode 100644 index e809aad56..000000000 --- a/contracts/tokenomics/vesting/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "astroport-vesting" -version = "1.4.0" -authors = ["Astroport"] -edition = "2021" -description = "Astroport Vesting Contract holds tokens and releases them to the beneficiary over time." -license = "GPL-3.0-only" -repository = "https://github.com/astroport-fi/astroport" -homepage = "https://astroport.fi" - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -backtraces = ["cosmwasm-std/backtraces"] -# use library feature to disable all init/handle/query exports -library = [] - -[dependencies] -cw2.workspace = true -cw20 = "1.1" -cosmwasm-std.workspace = true -cw-storage-plus.workspace = true -astroport = "4" -thiserror.workspace = true -cw-utils.workspace = true -cosmwasm-schema.workspace = true - -[dev-dependencies] -cw-multi-test = "1.0.0" -cw20-base = "1.1" -astro-token-converter = { path = "../../periphery/astro_converter", version = "1", features = ["library"] } -astroport-vesting_131 = { package = "astroport-vesting", version = "=1.3.1", features = ["library"] } - diff --git a/contracts/tokenomics/vesting/README.md b/contracts/tokenomics/vesting/README.md deleted file mode 100644 index ec91679f0..000000000 --- a/contracts/tokenomics/vesting/README.md +++ /dev/null @@ -1,152 +0,0 @@ -# Astroport Generator Vesting - -The Generator Vesting contract progressively unlocks ASTRO that can then be distributed to LP stakers via the Generator contract. - ---- - -## InstantiateMsg - -Initializes the contract with the address of the ASTRO token. - -```json -{ - "token_addr": "terra..." -} -``` - -### `receive` - -CW20 receive msg. - -```json -{ - "receive": { - "sender": "terra...", - "amount": "123", - "msg": "" - } -} -``` - -#### `RegisterVestingAccounts` - -Creates vesting schedules for the ASTRO token. Each vesting token should have the Generator contract address as the `VestingContractAddress`. Also, each schedule will unlock tokens at a different rate according to its time duration. - -Execute this message by calling the ASTRO token contract address. - -```json -{ - "send": { - "contract": , - "amount": "999", - "msg": "base64-encodedStringOfWithdrawMsg" - } -} -``` - -In `send.msg`, you may encode this JSON string into base64 encoding. - -```json -{ - "RegisterVestingAccounts": { - "vesting_accounts": [ - { - "address": "terra...", - "schedules": { - "start_point": { - "time": "1634125119000000000", - "amount": "123" - }, - "end_point": { - "time": "1664125119000000000", - "amount": "123" - } - } - } - ] - } -} -``` - -### `claim` - -Transfer vested tokens from all vesting schedules that have the same `VestingContractAddress` (address that's vesting tokens). - -```json -{ - "claim": { - "recipient": "terra...", - "amount": "123" - } -} -``` - -### `withdraw_from_active_schedule` - -Withdraw tokens from active vesting schedule. -Withdraw is possible if there is only one active vesting schedule. Active schedule's remaining amount must be greater than withdraw amount. -This endpoint terminates current active schedule (updates end_point) and creates a new one with remaining amount minus withdrawn amount. - -```json -{ - "withdraw_from_active_schedule": { - "account": "terra...", - "recipient": "terra...", - "withdraw_amount": "123" - } -} -``` - -## QueryMsg - -All query messages are described below. A custom struct is defined for each query response. - -### `config` - -Returns the vesting token contract address (the ASTRO token address). - -```json -{ - "config": {} -} -``` - -### `vesting_account` - -Returns all vesting schedules with their details for a specific vesting recipient. - -```json -{ - "vesting_account": { - "address": "terra..." - } -} -``` - -### `vesting_accounts` - -Returns a paginated list of vesting schedules in chronological order. Given fields are optional. - -```json -{ - "vesting_accounts": { - "start_after": "terra...", - "limit": 10, - "order_by": { - "desc": {} - } - } -} -``` - -### `available amount` - -Returns the claimable amount (vested but not yet claimed) of ASTRO tokens that a vesting target can claim. - -```json -{ - "available_amount": { - "address": "terra..." - } -} -``` diff --git a/contracts/tokenomics/vesting/examples/vesting_schema.rs b/contracts/tokenomics/vesting/examples/vesting_schema.rs deleted file mode 100644 index b1cdebf14..000000000 --- a/contracts/tokenomics/vesting/examples/vesting_schema.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cosmwasm_schema::write_api; - -use astroport::vesting::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; - -fn main() { - write_api! { - instantiate: InstantiateMsg, - query: QueryMsg, - execute: ExecuteMsg, - migrate: MigrateMsg - } -} diff --git a/contracts/tokenomics/vesting/src/contract.rs b/contracts/tokenomics/vesting/src/contract.rs deleted file mode 100644 index 544668f40..000000000 --- a/contracts/tokenomics/vesting/src/contract.rs +++ /dev/null @@ -1,586 +0,0 @@ -use cosmwasm_std::{ - attr, coins, ensure, entry_point, from_json, to_json_binary, wasm_execute, Addr, Binary, Deps, - DepsMut, Env, MessageInfo, Response, StdError, StdResult, SubMsg, Uint128, -}; -use cw2::{get_contract_version, set_contract_version}; -use cw20::Cw20ReceiveMsg; -use cw_utils::must_pay; - -use astroport::asset::{addr_opt_validate, token_asset_info, AssetInfo, AssetInfoExt}; -use astroport::astro_converter; -use astroport::common::{claim_ownership, drop_ownership_proposal, propose_new_owner}; -use astroport::vesting::{ - ConfigResponse, Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, OrderBy, QueryMsg, - VestingAccount, VestingAccountResponse, VestingAccountsResponse, VestingInfo, VestingSchedule, - VestingSchedulePoint, -}; - -use crate::error::ContractError; -use crate::state::{read_vesting_infos, Config, CONFIG, OWNERSHIP_PROPOSAL, VESTING_INFO}; - -/// Contract name that is used for migration. -const CONTRACT_NAME: &str = "astroport-vesting"; -/// Contract version that is used for migration. -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -/// Maximum limit of schedules per user -const SCHEDULES_LIMIT: usize = 8; - -/// Creates a new contract with the specified parameters in [`InstantiateMsg`]. -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn instantiate( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, -) -> StdResult { - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - msg.vesting_token.check(deps.api)?; - - CONFIG.save( - deps.storage, - &Config { - owner: deps.api.addr_validate(&msg.owner)?, - vesting_token: msg.vesting_token, - }, - )?; - - Ok(Response::new()) -} - -/// Exposes execute functions available in the contract. -/// -/// * **ExecuteMsg::Claim { recipient, amount }** Claims vested tokens and transfers them to the vesting recipient. -/// -/// * **ExecuteMsg::Receive(msg)** Receives a message of type [`Cw20ReceiveMsg`] and processes it -/// depending on the received template. -/// -/// * **ExecuteMsg::RegisterVestingAccounts { vesting_accounts }** Registers vesting accounts -/// using the provided vector of [`VestingAccount`] structures. -/// -/// * **ExecuteMsg::WithdrawFromActiveSchedule { account, recipient, withdraw_amount }** -/// Withdraws tokens from the only one active vesting schedule of the specified account. -/// -/// * **ExecuteMsg::ProposeNewOwner { owner, expires_in }** Creates a new request to change contract ownership. -/// -/// * **ExecuteMsg::DropOwnershipProposal {}** Removes a request to change contract ownership. -/// -/// * **ExecuteMsg::ClaimOwnership {}** Claims contract ownership. -/// -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::Claim { recipient, amount } => claim(deps, env, info, recipient, amount), - ExecuteMsg::Receive(msg) => receive_cw20(deps, env, info, msg), - ExecuteMsg::RegisterVestingAccounts { vesting_accounts } => { - let config = CONFIG.load(deps.storage)?; - - match &config.vesting_token { - AssetInfo::NativeToken { denom } if info.sender == config.owner => { - let amount = must_pay(&info, denom)?; - register_vesting_accounts(deps, env, vesting_accounts, amount) - } - _ => Err(ContractError::Unauthorized {}), - } - } - ExecuteMsg::WithdrawFromActiveSchedule { - account, - recipient, - withdraw_amount, - } => withdraw_from_active_schedule(deps, env, info, account, recipient, withdraw_amount), - ExecuteMsg::ProposeNewOwner { owner, expires_in } => { - let config: Config = CONFIG.load(deps.storage)?; - - propose_new_owner( - deps, - info, - env, - owner, - expires_in, - config.owner, - OWNERSHIP_PROPOSAL, - ) - .map_err(Into::into) - } - ExecuteMsg::DropOwnershipProposal {} => { - let config: Config = CONFIG.load(deps.storage)?; - - drop_ownership_proposal(deps, info, config.owner, OWNERSHIP_PROPOSAL) - .map_err(Into::into) - } - ExecuteMsg::ClaimOwnership {} => { - claim_ownership(deps, info, env, OWNERSHIP_PROPOSAL, |deps, new_owner| { - CONFIG.update::<_, StdError>(deps.storage, |mut v| { - v.owner = new_owner; - Ok(v) - })?; - - Ok(()) - }) - .map_err(Into::into) - } - } -} - -/// Receives a message of type [`Cw20ReceiveMsg`] and processes it depending on the received template. -/// -/// * **cw20_msg** CW20 message to process. -fn receive_cw20( - deps: DepsMut, - env: Env, - info: MessageInfo, - cw20_msg: Cw20ReceiveMsg, -) -> Result { - let config = CONFIG.load(deps.storage)?; - - // Permission check - if cw20_msg.sender != config.owner || token_asset_info(info.sender) != config.vesting_token { - return Err(ContractError::Unauthorized {}); - } - - match from_json(&cw20_msg.msg)? { - Cw20HookMsg::RegisterVestingAccounts { vesting_accounts } => { - register_vesting_accounts(deps, env, vesting_accounts, cw20_msg.amount) - } - } -} - -/// Create new vesting schedules. -/// -/// * **vesting_accounts** list of accounts and associated vesting schedules to create. -/// -/// * **cw20_amount** sets the amount that confirms the total amount of all accounts to register. -pub fn register_vesting_accounts( - deps: DepsMut, - env: Env, - vesting_accounts: Vec, - amount: Uint128, -) -> Result { - let response = Response::new(); - - let mut to_deposit = Uint128::zero(); - - for mut vesting_account in vesting_accounts { - let mut released_amount = Uint128::zero(); - let account_address = deps.api.addr_validate(&vesting_account.address)?; - - assert_vesting_schedules(&env, &account_address, &vesting_account.schedules)?; - - for sch in &vesting_account.schedules { - let amount = if let Some(end_point) = &sch.end_point { - end_point.amount - } else { - sch.start_point.amount - }; - to_deposit = to_deposit.checked_add(amount)?; - } - - if let Some(mut old_info) = VESTING_INFO.may_load(deps.storage, &account_address)? { - if old_info.schedules.len() + 1 > SCHEDULES_LIMIT { - return Err(ContractError::ExceedSchedulesMaximumLimit( - vesting_account.address, - )); - }; - released_amount = old_info.released_amount; - vesting_account.schedules.append(&mut old_info.schedules); - } - - VESTING_INFO.save( - deps.storage, - &account_address, - &VestingInfo { - schedules: vesting_account.schedules, - released_amount, - }, - )?; - } - - if to_deposit != amount { - return Err(ContractError::VestingScheduleAmountError {}); - } - - Ok(response.add_attributes({ - vec![ - attr("action", "register_vesting_accounts"), - attr("deposited", to_deposit), - ] - })) -} - -/// Asserts the validity of a list of vesting schedules. -/// -/// * **addr** receiver of the vested tokens. -/// -/// * **vesting_schedules** vesting schedules to validate. -fn assert_vesting_schedules( - env: &Env, - addr: &Addr, - vesting_schedules: &[VestingSchedule], -) -> Result<(), ContractError> { - for sch in vesting_schedules { - if let Some(end_point) = &sch.end_point { - if !(sch.start_point.time < end_point.time - && end_point.time > env.block.time.seconds() - && sch.start_point.amount < end_point.amount) - { - return Err(ContractError::VestingScheduleError(addr.to_string())); - } - } - } - - Ok(()) -} - -/// Claims vested tokens and transfers them to the vesting recipient. -/// -/// * **recipient** vesting recipient for which to claim tokens. -/// -/// * **amount** amount of vested tokens to claim. -pub fn claim( - deps: DepsMut, - env: Env, - info: MessageInfo, - recipient: Option, - amount: Option, -) -> Result { - let config = CONFIG.load(deps.storage)?; - let mut vesting_info = VESTING_INFO.load(deps.storage, &info.sender)?; - - let available_amount = compute_available_amount(env.block.time.seconds(), &vesting_info)?; - - let claim_amount = if let Some(a) = amount { - if a > available_amount { - return Err(ContractError::AmountIsNotAvailable {}); - }; - a - } else { - available_amount - }; - - let mut response = Response::new(); - - if !claim_amount.is_zero() { - let transfer_msg = config.vesting_token.with_balance(claim_amount).into_msg( - addr_opt_validate(deps.api, &recipient)?.unwrap_or_else(|| info.sender.clone()), - )?; - response = response.add_submessage(SubMsg::new(transfer_msg)); - - vesting_info.released_amount = vesting_info.released_amount.checked_add(claim_amount)?; - VESTING_INFO.save(deps.storage, &info.sender, &vesting_info)?; - }; - - Ok(response.add_attributes(vec![ - attr("action", "claim"), - attr("address", &info.sender), - attr("available_amount", available_amount), - attr("claimed_amount", claim_amount), - ])) -} - -/// Computes the amount of vested and yet unclaimed tokens for a specific vesting recipient. -/// Returns the computed amount if the operation is successful. -/// -/// * **current_time** timestamp from which to start querying for vesting schedules. -/// Schedules that started later than current_time will be omitted. -/// -/// * **vesting_info** vesting schedules for which to compute the amount of tokens -/// that are vested and can be claimed by the recipient. -fn compute_available_amount(current_time: u64, vesting_info: &VestingInfo) -> StdResult { - let mut available_amount: Uint128 = Uint128::zero(); - for sch in &vesting_info.schedules { - if sch.start_point.time > current_time { - continue; - } - - let unlocked_amount = calc_schedule_unlocked_amount(sch, current_time)?; - available_amount = available_amount.checked_add(unlocked_amount)?; - } - - available_amount - .checked_sub(vesting_info.released_amount) - .map_err(StdError::from) -} - -/// Calculate unlocked amount for particular [`VestingSchedule`]. -/// This function does not consider released amount. -fn calc_schedule_unlocked_amount( - schedule: &VestingSchedule, - current_time: u64, -) -> StdResult { - let mut available_amount = schedule.start_point.amount; - - if let Some(end_point) = &schedule.end_point { - let passed_time = current_time.min(end_point.time) - schedule.start_point.time; - let time_period = end_point.time - schedule.start_point.time; - if passed_time != 0 { - let release_amount = Uint128::from(passed_time).multiply_ratio( - end_point.amount.checked_sub(schedule.start_point.amount)?, - time_period, - ); - available_amount = available_amount.checked_add(release_amount)?; - } - } - - Ok(available_amount) -} - -/// Withdraw tokens from active vesting schedule. -/// -/// Withdraw is possible if there is only one active vesting schedule. -/// Only schedules with end_point are considered as active. -/// Active schedule's remaining amount must be greater than withdraw amount. -/// This function changes the current active schedule -/// setting current block time and already unlocked amount for start point -/// and reducing end point amount by the withdrawn amount. -/// -/// * **account** whose schedule to withdraw from. -/// -/// * **receiver** who will receive the withdrawn amount. -/// **info.sender** is used if it is not specified. -/// -/// * **amount** amount to withdraw from the only one active schedule. -/// -fn withdraw_from_active_schedule( - deps: DepsMut, - env: Env, - info: MessageInfo, - account: String, - receiver: Option, - amount: Uint128, -) -> Result { - if amount.is_zero() { - return Err(ContractError::ZeroAmountWithdrawal {}); - } - - let config = CONFIG.load(deps.storage)?; - if info.sender != config.owner { - return Err(ContractError::Unauthorized {}); - } - - let acc = deps.api.addr_validate(&account)?; - let mut vesting_info = VESTING_INFO.load(deps.storage, &acc)?; - let block_time = env.block.time.seconds(); - - let mut active_schedules = vesting_info.schedules.iter_mut().filter(|schedule| { - if let Some(end_point) = schedule.end_point { - block_time >= schedule.start_point.time && block_time < end_point.time - } else { - false - } - }); - - if let Some(schedule) = active_schedules.next() { - // Withdraw is not allowed if there are multiple active schedules - if active_schedules.next().is_some() { - return Err(ContractError::MultipleActiveSchedules(account)); - } - - // It's safe to unwrap here because we checked that there is an end_point - let mut end_point = schedule.end_point.unwrap(); - - let sch_unlocked_amount = calc_schedule_unlocked_amount(schedule, block_time)?; - - let amount_left = end_point.amount.checked_sub(sch_unlocked_amount)?; - if amount >= amount_left { - return Err(ContractError::NotEnoughTokens(amount_left)); - } - - schedule.start_point = VestingSchedulePoint { - time: block_time, - amount: sch_unlocked_amount, - }; - - end_point.amount -= amount; - schedule.end_point = Some(end_point); - } else { - return Err(ContractError::NoActiveVestingSchedule(account)); - }; - - VESTING_INFO.save(deps.storage, &acc, &vesting_info)?; - - let receiver = addr_opt_validate(deps.api, &receiver)?.unwrap_or(info.sender); - let transfer_msg = config - .vesting_token - .with_balance(amount) - .into_msg(receiver.clone())?; - - Ok(Response::new().add_message(transfer_msg).add_attributes([ - attr("action", "withdraw_from_active_schedule"), - attr("account", account), - attr("amount", amount), - attr("receiver", receiver), - ])) -} - -/// Exposes all the queries available in the contract. -/// -/// ## Queries -/// * **QueryMsg::Config {}** Returns the contract configuration in an object of type [`Config`]. -/// -/// * **QueryMsg::VestingAccount { address }** Returns information about the vesting schedules that have a specific vesting recipient. -/// -/// * **QueryMsg::VestingAccounts { -/// start_after, -/// limit, -/// order_by, -/// }** Returns a list of vesting schedules together with their vesting recipients. -/// -/// * **QueryMsg::AvailableAmount { address }** Returns the available amount of tokens that can be claimed by a specific vesting recipient. -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { - match msg { - QueryMsg::Config {} => Ok(to_json_binary(&query_config(deps)?)?), - QueryMsg::VestingAccount { address } => { - Ok(to_json_binary(&query_vesting_account(deps, address)?)?) - } - QueryMsg::VestingAccounts { - start_after, - limit, - order_by, - } => Ok(to_json_binary(&query_vesting_accounts( - deps, - start_after, - limit, - order_by, - )?)?), - QueryMsg::AvailableAmount { address } => Ok(to_json_binary( - &query_vesting_available_amount(deps, env, address)?, - )?), - QueryMsg::Timestamp {} => Ok(to_json_binary(&query_timestamp(env)?)?), - } -} - -/// Returns the vesting contract configuration using a [`ConfigResponse`] object. -pub fn query_config(deps: Deps) -> StdResult { - let config = CONFIG.load(deps.storage)?; - - Ok(ConfigResponse { - owner: config.owner, - vesting_token: config.vesting_token, - }) -} - -/// Return the current block timestamp (in seconds) -/// * **env** is an object of type [`Env`]. -pub fn query_timestamp(env: Env) -> StdResult { - Ok(env.block.time.seconds()) -} - -/// Returns the vesting data for a specific vesting recipient using a [`VestingAccountResponse`] object. -/// -/// * **address** vesting recipient for which to return vesting data. -pub fn query_vesting_account(deps: Deps, address: String) -> StdResult { - let address = deps.api.addr_validate(&address)?; - let info = VESTING_INFO.load(deps.storage, &address)?; - - Ok(VestingAccountResponse { address, info }) -} - -/// Returns a list of vesting schedules using a [`VestingAccountsResponse`] object. -/// -/// * **start_after** index from which to start reading vesting schedules. -/// -/// * **limit** amount of vesting schedules to return. -/// -/// * **order_by** whether results should be returned in an ascending or descending order. -pub fn query_vesting_accounts( - deps: Deps, - start_after: Option, - limit: Option, - order_by: Option, -) -> StdResult { - let start_after = addr_opt_validate(deps.api, &start_after)?; - - let vesting_infos = read_vesting_infos(deps, start_after, limit, order_by)?; - - let vesting_accounts: Vec<_> = vesting_infos - .into_iter() - .map(|(address, info)| VestingAccountResponse { address, info }) - .collect(); - - Ok(VestingAccountsResponse { vesting_accounts }) -} - -/// Returns the available amount of vested and yet to be claimed tokens for a specific vesting recipient. -/// -/// * **address** vesting recipient for which to return the available amount of tokens to claim. -pub fn query_vesting_available_amount(deps: Deps, env: Env, address: String) -> StdResult { - let address = deps.api.addr_validate(&address)?; - - let info = VESTING_INFO.load(deps.storage, &address)?; - let available_amount = compute_available_amount(env.block.time.seconds(), &info)?; - Ok(available_amount) -} - -/// Manages contract migration. -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> Result { - let contract_version = get_contract_version(deps.storage)?; - - let mut resp = Response::default(); - - match contract_version.contract.as_ref() { - "astroport-vesting" => match contract_version.version.as_ref() { - // injective-888 1.1.0 - // pacific-1, injective-1, pisco-1, atlantic-2 1.2.0 - // phoenix-1 1.3.0 - // neutron-1, pion-1 1.3.1 - "1.1.0" | "1.2.0" | "1.3.0" | "1.3.1" => { - let mut config = CONFIG.load(deps.storage)?; - - let converter_config: astro_converter::Config = deps.querier.query_wasm_smart( - &msg.converter_contract, - &astro_converter::QueryMsg::Config {}, - )?; - - ensure!( - converter_config.old_astro_asset_info == config.vesting_token, - StdError::generic_err(format!( - "Old astro asset info mismatch between vesting {} and converter {}", - config.vesting_token, converter_config.old_astro_asset_info - )) - ); - - let total_amount = config - .vesting_token - .query_pool(&deps.querier, env.contract.address)?; - - let convert_msg = match &config.vesting_token { - AssetInfo::Token { contract_addr } => wasm_execute( - contract_addr, - &cw20::Cw20ExecuteMsg::Send { - contract: msg.converter_contract, - amount: total_amount, - msg: to_json_binary(&astro_converter::Cw20HookMsg { receiver: None })?, - }, - vec![], - )?, - AssetInfo::NativeToken { denom } => wasm_execute( - &msg.converter_contract, - &astro_converter::ExecuteMsg::Convert { receiver: None }, - coins(total_amount.u128(), denom.to_string()), - )?, - }; - resp.messages.push(SubMsg::new(convert_msg)); - - config.vesting_token = AssetInfo::native(&converter_config.new_astro_denom); - CONFIG.save(deps.storage, &config)?; - } - _ => return Err(ContractError::MigrationError {}), - }, - _ => return Err(ContractError::MigrationError {}), - } - - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - Ok(resp - .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)) -} diff --git a/contracts/tokenomics/vesting/src/error.rs b/contracts/tokenomics/vesting/src/error.rs deleted file mode 100644 index f032029e3..000000000 --- a/contracts/tokenomics/vesting/src/error.rs +++ /dev/null @@ -1,51 +0,0 @@ -use cosmwasm_std::{OverflowError, StdError, Uint128}; -use cw_utils::PaymentError; -use thiserror::Error; - -/// This enum describes generator vesting contract errors -#[derive(Error, Debug, PartialEq)] -pub enum ContractError { - #[error("{0}")] - Std(#[from] StdError), - - #[error("{0}")] - PaymentError(#[from] PaymentError), - - #[error("Withdrawn amount must not be zero")] - ZeroAmountWithdrawal {}, - - #[error("Unauthorized")] - Unauthorized {}, - - #[error("Amount is not available!")] - AmountIsNotAvailable {}, - - #[error("Vesting schedule error on addr: {0}. Should satisfy: (start < end, end > current_time and start_amount < end_amount)")] - VestingScheduleError(String), - - #[error( - "Vesting schedule amount error. The total amount should be equal to the received amount." - )] - VestingScheduleAmountError {}, - - #[error("Contract can't be migrated!")] - MigrationError {}, - - #[error("Failed to withdraw tokens due to multiple active schedules for account {0}")] - MultipleActiveSchedules(String), - - #[error("Account {0} has no active vesting schedule")] - NoActiveVestingSchedule(String), - - #[error("For account {0} number of schedules exceeds maximum limit")] - ExceedSchedulesMaximumLimit(String), - - #[error("Failed to withdraw from active schedule: amount left {0}")] - NotEnoughTokens(Uint128), -} - -impl From for ContractError { - fn from(o: OverflowError) -> Self { - StdError::from(o).into() - } -} diff --git a/contracts/tokenomics/vesting/src/lib.rs b/contracts/tokenomics/vesting/src/lib.rs deleted file mode 100644 index f082c75f0..000000000 --- a/contracts/tokenomics/vesting/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod contract; -pub mod error; -pub mod state; - -#[cfg(test)] -mod testing; diff --git a/contracts/tokenomics/vesting/src/state.rs b/contracts/tokenomics/vesting/src/state.rs deleted file mode 100644 index 3dddd71c0..000000000 --- a/contracts/tokenomics/vesting/src/state.rs +++ /dev/null @@ -1,137 +0,0 @@ -use cosmwasm_schema::cw_serde; - -use astroport::asset::AssetInfo; -use astroport::common::OwnershipProposal; -use astroport::vesting::{OrderBy, VestingInfo}; -use cosmwasm_std::{Addr, Deps, StdResult}; -use cw_storage_plus::{Bound, Item, Map}; - -/// This structure stores the main parameters for the generator vesting contract. -#[cw_serde] -pub struct Config { - /// Address that's allowed to change contract parameters - pub owner: Addr, - /// [`AssetInfo`] of the ASTRO token - pub vesting_token: AssetInfo, -} - -/// Stores the contract config at the given key. -pub const CONFIG: Item = Item::new("config"); - -/// The first key is the address of an account that's vesting, the second key is an object of type [`VestingInfo`]. -pub const VESTING_INFO: Map<&Addr, VestingInfo> = Map::new("vesting_info"); - -/// Contains a proposal to change contract ownership. -pub const OWNERSHIP_PROPOSAL: Item = Item::new("ownership_proposal"); - -const MAX_LIMIT: u32 = 30; -const DEFAULT_LIMIT: u32 = 10; - -/// Returns an empty vector if it does not find data, otherwise returns a vector that -/// contains objects of type [`VESTING_INFO`]. -/// ## Params -/// -/// * **start_after** index from which to start reading vesting schedules. -/// -/// * **limit** amount of vesting schedules to read. -/// -/// * **order_by** whether results should be returned in an ascending or descending order. -pub fn read_vesting_infos( - deps: Deps, - start_after: Option, - limit: Option, - order_by: Option, -) -> StdResult> { - let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start_after = start_after.as_ref().map(Bound::exclusive); - let (start, end) = match &order_by { - Some(OrderBy::Asc) => (start_after, None), - _ => (None, start_after), - }; - - let info: Vec<(Addr, VestingInfo)> = VESTING_INFO - .range( - deps.storage, - start, - end, - order_by.unwrap_or(OrderBy::Desc).into(), - ) - .take(limit) - .filter_map(|v| v.ok()) - .collect(); - - Ok(info) -} - -#[cfg(test)] -mod testing { - use super::*; - - #[test] - fn read_vesting_infos_as_expected() { - use cosmwasm_std::{testing::mock_dependencies, Uint128}; - - let mut deps = mock_dependencies(); - - let vi_mock = VestingInfo { - released_amount: Uint128::zero(), - schedules: vec![], - }; - - for i in 1..5 { - let key = Addr::unchecked(format! {"address{}", i}); - - VESTING_INFO - .save(&mut deps.storage, &key, &vi_mock) - .unwrap(); - } - - let res = read_vesting_infos( - deps.as_ref(), - Some(Addr::unchecked("address2")), - None, - Some(OrderBy::Asc), - ) - .unwrap(); - assert_eq!( - res, - vec![ - (Addr::unchecked("address3"), vi_mock.clone()), - (Addr::unchecked("address4"), vi_mock.clone()), - ] - ); - - let res = read_vesting_infos( - deps.as_ref(), - Some(Addr::unchecked("address2")), - Some(1), - Some(OrderBy::Asc), - ) - .unwrap(); - assert_eq!(res, vec![(Addr::unchecked("address3"), vi_mock.clone())]); - - let res = read_vesting_infos( - deps.as_ref(), - Some(Addr::unchecked("address3")), - None, - Some(OrderBy::Desc), - ) - .unwrap(); - assert_eq!( - res, - vec![ - (Addr::unchecked("address2"), vi_mock.clone()), - (Addr::unchecked("address1"), vi_mock.clone()), - ] - ); - - let res = read_vesting_infos( - deps.as_ref(), - Some(Addr::unchecked("address3")), - Some(1), - Some(OrderBy::Desc), - ) - .unwrap(); - assert_eq!(res, vec![(Addr::unchecked("address2"), vi_mock.clone())]); - } -} diff --git a/contracts/tokenomics/vesting/src/testing.rs b/contracts/tokenomics/vesting/src/testing.rs deleted file mode 100644 index b8e570f89..000000000 --- a/contracts/tokenomics/vesting/src/testing.rs +++ /dev/null @@ -1,134 +0,0 @@ -use crate::contract::{execute, instantiate, query}; -use astroport::vesting::{ConfigResponse, ExecuteMsg, InstantiateMsg, QueryMsg}; - -use astroport::asset::{token_asset_info, AssetInfo}; -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; -use cosmwasm_std::{from_json, Addr}; - -#[test] -fn proper_initialization() { - let mut deps = mock_dependencies(); - - let msg = InstantiateMsg { - owner: "owner".to_string(), - vesting_token: token_asset_info(Addr::unchecked("astro_token")), - }; - - let env = mock_env(); - let info = mock_info("addr0000", &vec![]); - let _res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); - - assert_eq!( - from_json::(&query(deps.as_ref(), env, QueryMsg::Config {}).unwrap()) - .unwrap(), - ConfigResponse { - owner: Addr::unchecked("owner"), - vesting_token: token_asset_info(Addr::unchecked("astro_token")), - } - ); -} - -#[test] -fn update_owner() { - let mut deps = mock_dependencies(); - let owner = "owner0000"; - - let msg = InstantiateMsg { - owner: owner.to_string(), - vesting_token: AssetInfo::NativeToken { - denom: "ucosmos".to_owned(), - }, - }; - - let env = mock_env(); - let info = mock_info(owner, &[]); - - // We can just call .unwrap() to assert this was a success - instantiate(deps.as_mut(), env, info, msg).unwrap(); - - let new_owner = String::from("new_owner"); - - // New owner - let env = mock_env(); - let msg = ExecuteMsg::ProposeNewOwner { - owner: new_owner.clone(), - expires_in: 100, // seconds - }; - - let info = mock_info(new_owner.as_str(), &[]); - - // Unauthorized check - let err = execute(deps.as_mut(), env.clone(), info, msg.clone()).unwrap_err(); - assert_eq!(err.to_string(), "Generic error: Unauthorized"); - - // Claim before proposal - let info = mock_info(new_owner.as_str(), &[]); - execute( - deps.as_mut(), - env.clone(), - info, - ExecuteMsg::ClaimOwnership {}, - ) - .unwrap_err(); - - // Propose new owner - let info = mock_info(owner, &[]); - let res = execute(deps.as_mut(), env.clone(), info, msg.clone()).unwrap(); - assert_eq!(0, res.messages.len()); - - // Unauthorized ownership claim - let info = mock_info("invalid_addr", &[]); - let err = execute( - deps.as_mut(), - env.clone(), - info, - ExecuteMsg::ClaimOwnership {}, - ) - .unwrap_err(); - assert_eq!(err.to_string(), "Generic error: Unauthorized"); - - // Drop new owner - let info = mock_info(owner, &[]); - execute( - deps.as_mut(), - env.clone(), - info, - ExecuteMsg::DropOwnershipProposal {}, - ) - .unwrap(); - - // Claim ownership - let info = mock_info(new_owner.as_str(), &[]); - let err = execute( - deps.as_mut(), - env.clone(), - info, - ExecuteMsg::ClaimOwnership {}, - ) - .unwrap_err(); - assert_eq!( - err.to_string(), - "Generic error: Ownership proposal not found" - ); - - // Propose new owner - let info = mock_info(owner, &[]); - let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // Claim ownership - let info = mock_info(new_owner.as_str(), &[]); - let res = execute( - deps.as_mut(), - env.clone(), - info, - ExecuteMsg::ClaimOwnership {}, - ) - .unwrap(); - assert_eq!(0, res.messages.len()); - - // Let's query the state - let config: ConfigResponse = - from_json(&query(deps.as_ref(), env.clone(), QueryMsg::Config {}).unwrap()).unwrap(); - assert_eq!(new_owner, config.owner); -} diff --git a/contracts/tokenomics/vesting/tests/integration.rs b/contracts/tokenomics/vesting/tests/integration.rs deleted file mode 100644 index 99bf67d97..000000000 --- a/contracts/tokenomics/vesting/tests/integration.rs +++ /dev/null @@ -1,1691 +0,0 @@ -#![cfg(not(tarpaulin_include))] - -use cosmwasm_std::{coin, coins, to_json_binary, Addr, StdResult, Timestamp, Uint128}; -use cw20::{BalanceResponse, Cw20ExecuteMsg, Cw20QueryMsg, MinterResponse}; -use cw20_base::msg::InstantiateMsg as TokenInstantiateMsg; -use cw_multi_test::{App, ContractWrapper, Executor}; -use cw_utils::PaymentError; - -use astroport::asset::{native_asset_info, token_asset_info, AssetInfo}; -use astroport::astro_converter; -use astroport::astro_converter::OutpostBurnParams; -use astroport::querier::query_balance; -use astroport::vesting::{ - Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, VestingAccount, VestingSchedule, - VestingSchedulePoint, -}; -use astroport::vesting::{QueryMsg, VestingAccountResponse, VestingAccountsResponse, VestingInfo}; -use astroport_vesting::error::ContractError; -use astroport_vesting::state::Config; - -const OWNER1: &str = "owner1"; -const USER1: &str = "user1"; -const USER2: &str = "user2"; -const TOKEN_INITIAL_AMOUNT: u128 = 1_000_000_000_000000; -const IBC_ASTRO: &str = "ibc/ASTRO-TOKEN"; -const NEW_ASTRO_DENOM: &str = "astro"; - -#[test] -fn claim() { - let user1 = Addr::unchecked(USER1); - let owner = Addr::unchecked(OWNER1); - - let mut app = mock_app(&owner); - - let token_code_id = store_token_code(&mut app); - - let astro_token_instance = - instantiate_token(&mut app, token_code_id, "ASTRO", Some(1_000_000_000_000000)); - - let vesting_instance = instantiate_vesting(&mut app, &astro_token_instance); - - let current_time = app.block_info().time.seconds(); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![ - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 101, - amount: Uint128::new(200), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 110, - amount: Uint128::new(100), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 200, - amount: Uint128::new(100), - }), - }, - ], - }], - }) - .unwrap(), - amount: Uint128::from(300u128), - }; - - let res = app - .execute_contract(owner.clone(), astro_token_instance.clone(), &msg, &[]) - .unwrap_err(); - assert_eq!( - res.root_cause().to_string(), - "Vesting schedule amount error. The total amount should be equal to the received amount." - ); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![ - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 101, - amount: Uint128::new(100), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 110, - amount: Uint128::new(100), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 200, - amount: Uint128::new(100), - }), - }, - ], - }], - }) - .unwrap(), - amount: Uint128::from(300u128), - }; - - app.execute_contract(owner.clone(), astro_token_instance.clone(), &msg, &[]) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(200); - b.height += 200 / 5 - }); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - - let user1_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - assert_eq!(user1_vesting_amount.clone(), Uint128::new(300u128)); - - // Check owner balance - check_token_balance( - &mut app, - &astro_token_instance, - &owner.clone(), - TOKEN_INITIAL_AMOUNT - 300u128, - ); - - // Check vesting balance - check_token_balance( - &mut app, - &astro_token_instance, - &vesting_instance.clone(), - 300u128, - ); - - let msg = ExecuteMsg::Claim { - recipient: None, - amount: None, - }; - let _res = app - .execute_contract(user1.clone(), vesting_instance.clone(), &msg, &[]) - .unwrap(); - - let msg = QueryMsg::VestingAccount { - address: user1.to_string(), - }; - - let vesting_res: VestingAccountResponse = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - assert_eq!(vesting_res.info.released_amount, Uint128::from(300u128)); - - // Check vesting balance - check_token_balance( - &mut app, - &astro_token_instance, - &vesting_instance.clone(), - 0u128, - ); - - // Check user balance - check_token_balance(&mut app, &astro_token_instance, &user1.clone(), 300u128); - - // Owner balance mustn't change after claim - check_token_balance( - &mut app, - &astro_token_instance, - &owner.clone(), - TOKEN_INITIAL_AMOUNT - 300u128, - ); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - - // Check user balance after claim - let user1_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - - assert_eq!(user1_vesting_amount.clone(), Uint128::new(0u128)); -} - -#[test] -fn claim_native() { - let user1 = Addr::unchecked(USER1); - let owner = Addr::unchecked(OWNER1); - - let mut app = mock_app(&owner); - - let token_code_id = store_token_code(&mut app); - - let random_token_instance = - instantiate_token(&mut app, token_code_id, "RND", Some(1_000_000000)); - - mint_tokens(&mut app, &random_token_instance, &owner, 1_000_000000); - - let vesting_instance = instantiate_vesting_remote_chain(&mut app); - - let current_time = app.block_info().time.seconds(); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 101, - amount: Uint128::new(200), - }), - }], - }], - }) - .unwrap(), - amount: Uint128::from(300u128), - }; - - let err = app - .execute_contract(owner.clone(), random_token_instance.clone(), &msg, &[]) - .unwrap_err(); - assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); - - let msg = ExecuteMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![ - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 101, - amount: Uint128::new(100), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 110, - amount: Uint128::new(100), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 200, - amount: Uint128::new(100), - }), - }, - ], - }], - }; - - app.execute_contract( - owner.clone(), - vesting_instance.clone(), - &msg, - &coins(300, IBC_ASTRO), - ) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(200); - b.height += 200 / 5 - }); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - - let user1_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - assert_eq!(user1_vesting_amount.clone(), Uint128::new(300u128)); - - // Check owner balance - let bal = query_balance(&app.wrap(), &owner, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, TOKEN_INITIAL_AMOUNT - 300u128); - - // Check vesting balance - let bal = query_balance(&app.wrap(), &vesting_instance, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, 300u128); - - let msg = ExecuteMsg::Claim { - recipient: None, - amount: None, - }; - app.execute_contract(user1.clone(), vesting_instance.clone(), &msg, &[]) - .unwrap(); - - let vesting_res: VestingAccountResponse = app - .wrap() - .query_wasm_smart( - vesting_instance.clone(), - &QueryMsg::VestingAccount { - address: user1.to_string(), - }, - ) - .unwrap(); - assert_eq!(vesting_res.info.released_amount, Uint128::from(300u128)); - - // Check vesting balance - let bal = query_balance(&app.wrap(), &vesting_instance, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, 0); - - // Check user balance - let bal = query_balance(&app.wrap(), &user1, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, 300); - - // Owner balance mustn't change after claim - let bal = query_balance(&app.wrap(), &owner, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, TOKEN_INITIAL_AMOUNT - 300u128); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - - // Check user balance after claim - let user1_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - - assert_eq!(user1_vesting_amount.clone(), Uint128::new(0u128)); -} - -#[test] -fn claim_after_migration() { - let user1 = Addr::unchecked(USER1); - let owner = Addr::unchecked(OWNER1); - - let mut app = mock_app(&owner); - - let current_time = app.block_info().time.seconds(); - let vesting_instance = instantiate_vesting_131(&mut app); - - let msg = ExecuteMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 100_000, - amount: Uint128::new(100_000), - }), - }], - }], - }; - - app.execute_contract( - owner.clone(), - vesting_instance.clone(), - &msg, - &coins(100_000, IBC_ASTRO), - ) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(20_000); - }); - - let claim_msg = ExecuteMsg::Claim { - recipient: None, - amount: None, - }; - app.execute_contract(user1.clone(), vesting_instance.clone(), &claim_msg, &[]) - .unwrap(); - - let user_bal = query_balance(&app.wrap(), &user1, IBC_ASTRO).unwrap(); - assert_eq!(user_bal.u128(), 20_000); - - // Init converter and migrate vesting - migrate_vesting(&mut app, &vesting_instance); - - app.update_block(|b| { - b.time = b.time.plus_seconds(20_000); - }); - app.execute_contract(user1.clone(), vesting_instance.clone(), &claim_msg, &[]) - .unwrap(); - - // Old astro balance stays the same - let old_astro_bal = query_balance(&app.wrap(), &user1, IBC_ASTRO).unwrap(); - assert_eq!(old_astro_bal.u128(), 20_000); - - // Claimed new ASTRO - let new_astro_bal = query_balance(&app.wrap(), &user1, NEW_ASTRO_DENOM).unwrap(); - assert_eq!(new_astro_bal.u128(), 20_000); - - // Vesting converted all the old ASTRO to the new ASTRO - let vesting_bal = query_balance(&app.wrap(), &vesting_instance, IBC_ASTRO).unwrap(); - assert_eq!(vesting_bal.u128(), 0); - let vesting_bal = query_balance(&app.wrap(), &vesting_instance, NEW_ASTRO_DENOM).unwrap(); - assert_eq!(vesting_bal.u128(), 60_000); - - // Old arithmetic in vesting preserved - let msg = QueryMsg::VestingAccount { - address: user1.to_string(), - }; - let vesting_res: VestingAccountResponse = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - assert_eq!(vesting_res.info.released_amount, Uint128::from(40_000u128)); - - // Pass full vesting period - app.update_block(|b| { - b.time = b.time.plus_seconds(1_000_000); - }); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - let user1_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - assert_eq!(user1_vesting_amount.clone(), Uint128::new(60_000u128)); - - // Assert new asset info in config - let config: Config = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &QueryMsg::Config {}) - .unwrap(); - assert_eq!(config.vesting_token, AssetInfo::native(NEW_ASTRO_DENOM)); -} - -#[test] -fn register_vesting_accounts() { - let user1 = Addr::unchecked(USER1); - let user2 = Addr::unchecked(USER2); - let owner = Addr::unchecked(OWNER1); - - let mut app = mock_app(&owner); - - let token_code_id = store_token_code(&mut app); - - let astro_token_instance = - instantiate_token(&mut app, token_code_id, "ASTRO", Some(1_000_000_000_000000)); - - let noname_token_instance = instantiate_token( - &mut app, - token_code_id, - "NONAME", - Some(1_000_000_000_000000), - ); - - mint_tokens( - &mut app, - &noname_token_instance, - &owner, - TOKEN_INITIAL_AMOUNT, - ); - - let vesting_instance = instantiate_vesting(&mut app, &astro_token_instance); - - let current_time = app.block_info().time.seconds(); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 150, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::new(100), - }), - }], - }], - }) - .unwrap(), - amount: Uint128::from(100u128), - }; - - let res = app - .execute_contract(owner.clone(), astro_token_instance.clone(), &msg, &[]) - .unwrap_err(); - assert_eq!(res.root_cause().to_string(), "Vesting schedule error on addr: user1. Should satisfy: (start < end, end > current_time and start_amount < end_amount)"); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 150, - amount: Uint128::new(100), - }), - }], - }], - }) - .unwrap(), - amount: Uint128::from(100u128), - }; - - let res = app - .execute_contract( - user1.clone(), - astro_token_instance.clone(), - &msg.clone(), - &[], - ) - .unwrap_err(); - assert_eq!( - res.root_cause().to_string(), - "Overflow: Cannot Sub with 0 and 100" - ); - - let res = app - .execute_contract(owner.clone(), noname_token_instance.clone(), &msg, &[]) - .unwrap_err(); - assert_eq!(res.root_cause().to_string(), "Unauthorized"); - - // Checking that execute endpoint with native coin is unreachable if ASTRO is a cw20 token - let native_msg = ExecuteMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 150, - amount: Uint128::new(100), - }), - }], - }], - }; - - let err = app - .execute_contract( - owner.clone(), - vesting_instance.clone(), - &native_msg, - &coins(100u128, "random-coin"), - ) - .unwrap_err(); - assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); - - let _res = app - .execute_contract(owner.clone(), astro_token_instance.clone(), &msg, &[]) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(150); - b.height += 150 / 5 - }); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - - let user1_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - - assert_eq!(user1_vesting_amount.clone(), Uint128::new(100u128)); - check_token_balance( - &mut app, - &astro_token_instance, - &owner.clone(), - TOKEN_INITIAL_AMOUNT - 100u128, - ); - check_token_balance( - &mut app, - &astro_token_instance, - &vesting_instance.clone(), - 100u128, - ); - - let current_time = app.block_info().time.seconds(); - - // Let's check user1's final vesting amount after add schedule for a new one - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user2.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 150, - amount: Uint128::new(200), - }), - }], - }], - }) - .unwrap(), - amount: Uint128::from(200u128), - }; - - let _res = app - .execute_contract(owner.clone(), astro_token_instance.clone(), &msg, &[]) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(150); - b.height += 150 / 5 - }); - - let msg = QueryMsg::AvailableAmount { - address: user2.to_string(), - }; - - let user2_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - - check_token_balance( - &mut app, - &astro_token_instance, - &owner.clone(), - TOKEN_INITIAL_AMOUNT - 300u128, - ); - check_token_balance( - &mut app, - &astro_token_instance, - &vesting_instance.clone(), - 300u128, - ); - // A new schedule has been added successfully and an old one hasn't changed. - // The new schedule doesn't have the same value as the old one. - assert_eq!(user2_vesting_amount, Uint128::new(200u128)); - assert_eq!(user1_vesting_amount, Uint128::from(100u128)); - - let current_time = app.block_info().time.seconds(); - - // Add one more vesting schedule; final amount to vest must increase - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 200, - amount: Uint128::new(10), - }), - }], - }], - }) - .unwrap(), - amount: Uint128::from(10u128), - }; - - let _res = app - .execute_contract(owner.clone(), astro_token_instance.clone(), &msg, &[]) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(200); - b.height += 200 / 5 - }); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - - let vesting_res: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - - assert_eq!(vesting_res, Uint128::new(110u128)); - check_token_balance( - &mut app, - &astro_token_instance, - &owner.clone(), - TOKEN_INITIAL_AMOUNT - 310u128, - ); - check_token_balance( - &mut app, - &astro_token_instance, - &vesting_instance.clone(), - 310u128, - ); - - let msg = ExecuteMsg::Claim { - recipient: None, - amount: None, - }; - let _res = app - .execute_contract(user1.clone(), vesting_instance.clone(), &msg, &[]) - .unwrap(); - - let msg = QueryMsg::VestingAccount { - address: user1.to_string(), - }; - - let vesting_res: VestingAccountResponse = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - assert_eq!(vesting_res.info.released_amount, Uint128::from(110u128)); - check_token_balance( - &mut app, - &astro_token_instance, - &vesting_instance.clone(), - 200u128, - ); - check_token_balance(&mut app, &astro_token_instance, &user1.clone(), 110u128); - - // Owner balance mustn't change after claim - check_token_balance( - &mut app, - &astro_token_instance, - &owner.clone(), - TOKEN_INITIAL_AMOUNT - 310u128, - ); - - assert_eq!( - app.wrap() - .query_wasm_smart::( - vesting_instance, - &QueryMsg::VestingAccounts { - start_after: None, - limit: None, - order_by: None - } - ) - .unwrap(), - VestingAccountsResponse { - vesting_accounts: vec![ - VestingAccountResponse { - address: user2, - info: VestingInfo { - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: 1571797669, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: 1571797719, - amount: Uint128::new(200), - }), - }], - released_amount: Uint128::zero(), - } - }, - VestingAccountResponse { - address: user1, - info: VestingInfo { - schedules: vec![ - VestingSchedule { - start_point: VestingSchedulePoint { - time: 1571797819, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: 1571797919, - amount: Uint128::new(10), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: 1571797519, - amount: Uint128::new(0), - }, - end_point: Some(VestingSchedulePoint { - time: 1571797569, - amount: Uint128::new(100), - }) - } - ], - released_amount: Uint128::new(110), - } - } - ] - } - ); -} - -#[test] -fn register_vesting_accounts_native() { - let user1 = Addr::unchecked(USER1); - let user2 = Addr::unchecked(USER2); - let owner = Addr::unchecked(OWNER1); - - let mut app = mock_app(&owner); - - let token_code_id = store_token_code(&mut app); - - let random_token_instance = - instantiate_token(&mut app, token_code_id, "RND", Some(1_000_000_000_000000)); - - mint_tokens( - &mut app, - &random_token_instance, - &owner, - TOKEN_INITIAL_AMOUNT, - ); - - let vesting_instance = instantiate_vesting_remote_chain(&mut app); - - let current_time = app.block_info().time.seconds(); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 150, - amount: Uint128::new(100), - }), - }], - }], - }) - .unwrap(), - amount: Uint128::from(100u128), - }; - - let err = app - .execute_contract(owner.clone(), random_token_instance.clone(), &msg, &[]) - .unwrap_err(); - assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); - - // Checking that execute endpoint with random native coin is unreachable - let native_msg = ExecuteMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 150, - amount: Uint128::new(100), - }), - }], - }], - }; - - let err = app - .execute_contract( - owner.clone(), - vesting_instance.clone(), - &native_msg, - &coins(100u128, "random-coin"), - ) - .unwrap_err(); - assert_eq!( - ContractError::PaymentError(PaymentError::MissingDenom("ibc/ASTRO-TOKEN".to_string())), - err.downcast().unwrap() - ); - - app.execute_contract( - owner.clone(), - vesting_instance.clone(), - &native_msg, - &coins(100u128, IBC_ASTRO), - ) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(150); - b.height += 150 / 5 - }); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - - let user1_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(&vesting_instance, &msg) - .unwrap(); - assert_eq!(user1_vesting_amount.u128(), 100u128); - - let bal = query_balance(&app.wrap(), &owner, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, TOKEN_INITIAL_AMOUNT - 100u128); - - let bal = query_balance(&app.wrap(), &vesting_instance, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, 100); - - let current_time = app.block_info().time.seconds(); - - // Let's check user1's final vesting amount after add schedule for a new one - let msg = ExecuteMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user2.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 150, - amount: Uint128::new(200), - }), - }], - }], - }; - - app.execute_contract( - owner.clone(), - vesting_instance.clone(), - &msg, - &coins(200, IBC_ASTRO), - ) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(150); - b.height += 150 / 5 - }); - - let msg = QueryMsg::AvailableAmount { - address: user2.to_string(), - }; - - let user2_vesting_amount: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - - let bal = query_balance(&app.wrap(), &owner, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, TOKEN_INITIAL_AMOUNT - 300u128); - let bal = query_balance(&app.wrap(), &vesting_instance, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, 300u128); - - // A new schedule has been added successfully and an old one hasn't changed. - // The new schedule doesn't have the same value as the old one. - assert_eq!(user2_vesting_amount, Uint128::new(200u128)); - assert_eq!(user1_vesting_amount, Uint128::from(100u128)); - - let current_time = app.block_info().time.seconds(); - - // Add one more vesting schedule; final amount to vest must increase - let msg = ExecuteMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: current_time + 100, - amount: Uint128::zero(), - }, - end_point: Some(VestingSchedulePoint { - time: current_time + 200, - amount: Uint128::new(10), - }), - }], - }], - }; - - app.execute_contract( - owner.clone(), - vesting_instance.clone(), - &msg, - &coins(10, IBC_ASTRO), - ) - .unwrap(); - - app.update_block(|b| { - b.time = b.time.plus_seconds(200); - b.height += 200 / 5 - }); - - let msg = QueryMsg::AvailableAmount { - address: user1.to_string(), - }; - - let vesting_res: Uint128 = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - assert_eq!(vesting_res, Uint128::new(110u128)); - - let bal = query_balance(&app.wrap(), &owner, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, TOKEN_INITIAL_AMOUNT - 310u128); - let bal = query_balance(&app.wrap(), &vesting_instance, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, 310u128); - - let msg = ExecuteMsg::Claim { - recipient: None, - amount: None, - }; - let _res = app - .execute_contract(user1.clone(), vesting_instance.clone(), &msg, &[]) - .unwrap(); - - let msg = QueryMsg::VestingAccount { - address: user1.to_string(), - }; - - let vesting_res: VestingAccountResponse = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &msg) - .unwrap(); - assert_eq!(vesting_res.info.released_amount, Uint128::from(110u128)); - - let bal = query_balance(&app.wrap(), &vesting_instance, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, 200); - let bal = query_balance(&app.wrap(), &user1, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, 110u128); - - let bal = query_balance(&app.wrap(), &owner, IBC_ASTRO) - .unwrap() - .u128(); - assert_eq!(bal, TOKEN_INITIAL_AMOUNT - 310u128); -} - -#[test] -fn withdraw_from_active_schedule() { - let owner = Addr::unchecked(OWNER1); - let mut app = mock_app(&owner); - let token_code_id = store_token_code(&mut app); - let astro_token = instantiate_token(&mut app, token_code_id, "Astro", None); - let vesting_instance = instantiate_vesting(&mut app, &astro_token); - - let user1 = Addr::unchecked("user1"); - let vested_amount = Uint128::new(100_000_000_000000); - let start_time = 1654599600; - let end_time = 1686135600; - let now_ts = 1675159485; - - app.update_block(|b| b.time = Timestamp::from_seconds(start_time)); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![VestingSchedule { - start_point: VestingSchedulePoint { - time: start_time, - amount: Uint128::new(1_000_000_000000), - }, - end_point: Some(VestingSchedulePoint { - time: end_time, - amount: Uint128::new(100_000_000_000000), - }), - }], - }], - }) - .unwrap(), - amount: vested_amount, - }; - app.execute_contract(owner.clone(), astro_token.clone(), &msg, &[]) - .unwrap(); - - app.update_block(|b| b.time = Timestamp::from_seconds(now_ts)); - - // Claim and check current amount - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - 65_543_017_979452, - ); - - let withdraw_amount = Uint128::new(10_000_000_000000); - let recipient = Addr::unchecked("recipient"); - let withdraw_msg = ExecuteMsg::WithdrawFromActiveSchedule { - account: user1.to_string(), - recipient: Some(recipient.to_string()), - withdraw_amount, - }; - app.execute_contract(owner.clone(), vesting_instance.clone(), &withdraw_msg, &[]) - .unwrap(); - - // Recipient received tokens - let recipient_bal = query_token_balance(&mut app, &astro_token, &recipient); - assert_eq!(recipient_bal, withdraw_amount); - - // User1 did not receive tokens after withdraw event - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - 65_543_017_979452, - ); - - app.update_block(|b| b.time = b.time.plus_seconds(86400 * 7)); - - // User1 available amount is still being increased but now with reduced slope - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - 66_890_633_481478, - ); - - app.update_block(|b| b.time = Timestamp::from_seconds(end_time)); - - // In the end of the schedule user1 receives all tokens minus withdrawn amount - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - (vested_amount - withdraw_amount).u128(), - ); -} - -#[test] -fn withdraw_overlapping_schedules() { - let owner = Addr::unchecked(OWNER1); - let mut app = mock_app(&owner); - let token_code_id = store_token_code(&mut app); - let astro_token = instantiate_token(&mut app, token_code_id, "Astro", None); - let vesting_instance = instantiate_vesting(&mut app, &astro_token); - - let user1 = Addr::unchecked("user1"); - let vested_amount = Uint128::new(100_000_000_000000); - let start_time = 1654599600; - let end_time = 1686135600; - let now_ts = 1675159485; - - app.update_block(|b| b.time = Timestamp::from_seconds(start_time)); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![ - VestingSchedule { - start_point: VestingSchedulePoint { - time: start_time, - amount: Uint128::new(1_000_000_000000), - }, - end_point: Some(VestingSchedulePoint { - time: end_time, - amount: Uint128::new(50_000_000_000000), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: now_ts - 86400 * 7, - amount: Uint128::new(50_000_000_000000), - }, - end_point: None, - }, - ], - }], - }) - .unwrap(), - amount: vested_amount, - }; - app.execute_contract(owner.clone(), astro_token.clone(), &msg, &[]) - .unwrap(); - - app.update_block(|b| b.time = Timestamp::from_seconds(now_ts)); - - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - 82_945_534_151445, - ); - - let withdraw_amount = Uint128::new(10_000_000_000000); - let recipient = Addr::unchecked("recipient"); - let withdraw_msg = ExecuteMsg::WithdrawFromActiveSchedule { - account: user1.to_string(), - recipient: Some(recipient.to_string()), - withdraw_amount, - }; - - // Since we do not consider schedule without end point as active it is possible to withdraw from - // active schedule with end_point. - app.execute_contract(owner.clone(), vesting_instance.clone(), &withdraw_msg, &[]) - .unwrap(); - - // Recipient received tokens - let recipient_bal = query_token_balance(&mut app, &astro_token, &recipient); - assert_eq!(recipient_bal, withdraw_amount); - - // User1 did not receive tokens after withdraw event - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - 82_945_534_151445, - ); - - // Go to the end of the 1st schedule - app.update_block(|b| b.time = Timestamp::from_seconds(end_time)); - - // In the end of the schedule user1 receives all tokens minus withdrawn amount - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - (vested_amount - withdraw_amount).u128(), - ); -} - -#[test] -fn withdraw_overlapping_schedules2() { - let owner = Addr::unchecked(OWNER1); - let mut app = mock_app(&owner); - let token_code_id = store_token_code(&mut app); - let astro_token = instantiate_token(&mut app, token_code_id, "Astro", None); - let vesting_instance = instantiate_vesting(&mut app, &astro_token); - - let user1 = Addr::unchecked("user1"); - let vested_amount = Uint128::new(100_000_000_000000); - let start_time = 1654599600; - let end_time = 1686135600; - let now_ts = 1675159485; - - app.update_block(|b| b.time = Timestamp::from_seconds(start_time)); - - let msg = Cw20ExecuteMsg::Send { - contract: vesting_instance.to_string(), - msg: to_json_binary(&Cw20HookMsg::RegisterVestingAccounts { - vesting_accounts: vec![VestingAccount { - address: user1.to_string(), - schedules: vec![ - VestingSchedule { - start_point: VestingSchedulePoint { - time: start_time, - amount: Uint128::new(1_000_000_000000), - }, - end_point: Some(VestingSchedulePoint { - time: end_time, - amount: Uint128::new(50_000_000_000000), - }), - }, - VestingSchedule { - start_point: VestingSchedulePoint { - time: now_ts - 86400 * 7, - amount: Uint128::new(1_000_000_000000), - }, - end_point: Some(VestingSchedulePoint { - time: end_time + 86400 * 7, - amount: Uint128::new(50_000_000_000000), - }), - }, - ], - }], - }) - .unwrap(), - amount: vested_amount, - }; - app.execute_contract(owner.clone(), astro_token.clone(), &msg, &[]) - .unwrap(); - - app.update_block(|b| b.time = Timestamp::from_seconds(now_ts)); - - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - 36_377_496_494237, - ); - - let recipient = Addr::unchecked("recipient"); - let withdraw_msg = ExecuteMsg::WithdrawFromActiveSchedule { - account: user1.to_string(), - recipient: Some(recipient.to_string()), - withdraw_amount: Uint128::new(10_000_000_000000), - }; - let err = app - .execute_contract(owner.clone(), vesting_instance.clone(), &withdraw_msg, &[]) - .unwrap_err(); - assert_eq!( - ContractError::MultipleActiveSchedules(user1.to_string()), - err.downcast().unwrap(), - ); - - // Go to the end of the 1st schedule - app.update_block(|b| b.time = Timestamp::from_seconds(end_time)); - - // Trying to withdraw again - let err = app - .execute_contract(owner.clone(), vesting_instance.clone(), &withdraw_msg, &[]) - .unwrap_err(); - // There is no 10M ASTRO available for withdrawal - assert_eq!( - ContractError::NotEnoughTokens(Uint128::new(2_431_962_342793)), - err.downcast().unwrap(), - ); - - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - 97_568_037_657_207, - ); - - // Withdrawing 1M ASTRO - let withdraw_amount = Uint128::new(1_000_000_000000); - let withdraw_msg = ExecuteMsg::WithdrawFromActiveSchedule { - account: user1.to_string(), - recipient: Some(recipient.to_string()), - withdraw_amount, - }; - app.execute_contract(owner.clone(), vesting_instance.clone(), &withdraw_msg, &[]) - .unwrap(); - - // Recipient received tokens - let recipient_bal = query_token_balance(&mut app, &astro_token, &recipient); - assert_eq!(recipient_bal, withdraw_amount); - - // user1's amount was not changed - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - 97_568_037_657_207, - ); - - // Go to the end of the 2nd schedule - app.update_block(|b| b.time = Timestamp::from_seconds(end_time + 86400 * 7)); - - // user1 received all tokens except 1M ASTRO - claim_and_check( - &mut app, - &user1, - &vesting_instance, - &astro_token, - (vested_amount - withdraw_amount).u128(), - ); -} - -fn mock_app(owner: &Addr) -> App { - App::new(|app, _, storage| { - app.bank - .init_balance( - storage, - owner, - vec![ - coin(TOKEN_INITIAL_AMOUNT, IBC_ASTRO), - coin(1_000_0000000u128, "random-coin"), - ], - ) - .unwrap() - }) -} - -fn store_token_code(app: &mut App) -> u64 { - let astro_token_contract = Box::new(ContractWrapper::new_with_empty( - cw20_base::contract::execute, - cw20_base::contract::instantiate, - cw20_base::contract::query, - )); - - app.store_code(astro_token_contract) -} - -fn instantiate_token(app: &mut App, token_code_id: u64, name: &str, cap: Option) -> Addr { - let name = String::from(name); - - let msg = TokenInstantiateMsg { - name: name.clone(), - symbol: name.clone(), - decimals: 6, - initial_balances: vec![], - mint: Some(MinterResponse { - minter: String::from(OWNER1), - cap: cap.map(|v| Uint128::from(v)), - }), - marketing: None, - }; - - app.instantiate_contract( - token_code_id, - Addr::unchecked(OWNER1), - &msg, - &[], - name, - None, - ) - .unwrap() -} - -fn instantiate_vesting(mut app: &mut App, astro_token_instance: &Addr) -> Addr { - let vesting_contract = Box::new(ContractWrapper::new_with_empty( - astroport_vesting::contract::execute, - astroport_vesting::contract::instantiate, - astroport_vesting::contract::query, - )); - let owner = Addr::unchecked(OWNER1); - let vesting_code_id = app.store_code(vesting_contract); - - let init_msg = InstantiateMsg { - owner: OWNER1.to_string(), - vesting_token: token_asset_info(astro_token_instance.clone()), - }; - - let vesting_instance = app - .instantiate_contract( - vesting_code_id, - owner.clone(), - &init_msg, - &[], - "Vesting", - None, - ) - .unwrap(); - - let res: Config = app - .wrap() - .query_wasm_smart(vesting_instance.clone(), &QueryMsg::Config {}) - .unwrap(); - assert_eq!( - astro_token_instance.to_string(), - res.vesting_token.to_string() - ); - - mint_tokens( - &mut app, - &astro_token_instance, - &owner, - TOKEN_INITIAL_AMOUNT, - ); - - check_token_balance( - &mut app, - &astro_token_instance, - &owner, - TOKEN_INITIAL_AMOUNT, - ); - - vesting_instance -} - -fn instantiate_vesting_remote_chain(app: &mut App) -> Addr { - let vesting_contract = Box::new(ContractWrapper::new_with_empty( - astroport_vesting::contract::execute, - astroport_vesting::contract::instantiate, - astroport_vesting::contract::query, - )); - let owner = Addr::unchecked(OWNER1); - let vesting_code_id = app.store_code(vesting_contract); - - let init_msg = InstantiateMsg { - owner: OWNER1.to_string(), - vesting_token: native_asset_info(IBC_ASTRO.to_string()), - }; - - app.instantiate_contract( - vesting_code_id, - owner.clone(), - &init_msg, - &[], - "Vesting", - None, - ) - .unwrap() -} - -fn instantiate_vesting_131(app: &mut App) -> Addr { - let vesting_contract = Box::new(ContractWrapper::new_with_empty( - astroport_vesting_131::contract::execute, - astroport_vesting_131::contract::instantiate, - astroport_vesting_131::contract::query, - )); - let owner = Addr::unchecked(OWNER1); - let vesting_code_id = app.store_code(vesting_contract); - - let init_msg = InstantiateMsg { - owner: OWNER1.to_string(), - vesting_token: native_asset_info(IBC_ASTRO.to_string()), - }; - - app.instantiate_contract( - vesting_code_id, - owner.clone(), - &init_msg, - &[], - "Vesting", - Some(OWNER1.to_string()), - ) - .unwrap() -} - -fn migrate_vesting(app: &mut App, vesting: &Addr) { - // Setup converter - let converter_contract = Box::new(ContractWrapper::new_with_empty( - astro_token_converter::contract::execute, - astro_token_converter::contract::instantiate, - astro_token_converter::contract::query, - )); - let converter_code_id = app.store_code(converter_contract); - - let msg = astro_converter::InstantiateMsg { - old_astro_asset_info: AssetInfo::native(IBC_ASTRO), - new_astro_denom: NEW_ASTRO_DENOM.to_string(), - outpost_burn_params: Some(OutpostBurnParams { - terra_burn_addr: "terra1xxxx".to_string(), - old_astro_transfer_channel: "channel-228".to_string(), - }), - }; - - let converter_contract = app - .instantiate_contract( - converter_code_id, - Addr::unchecked(OWNER1), - &msg, - &[], - "Converter", - None, - ) - .unwrap(); - - app.init_modules(|app, _, storage| { - app.bank - .init_balance( - storage, - &converter_contract, - vec![coin(u128::MAX, NEW_ASTRO_DENOM)], - ) - .unwrap() - }); - - let vesting_contract = Box::new( - ContractWrapper::new_with_empty( - astroport_vesting::contract::execute, - astroport_vesting::contract::instantiate, - astroport_vesting::contract::query, - ) - .with_migrate(astroport_vesting::contract::migrate), - ); - let vesting_code_id = app.store_code(vesting_contract); - - app.migrate_contract( - Addr::unchecked(OWNER1), - vesting.clone(), - &MigrateMsg { - converter_contract: converter_contract.to_string(), - }, - vesting_code_id, - ) - .unwrap(); -} - -fn mint_tokens(app: &mut App, token: &Addr, recipient: &Addr, amount: u128) { - let msg = Cw20ExecuteMsg::Mint { - recipient: recipient.to_string(), - amount: Uint128::from(amount), - }; - - app.execute_contract(Addr::unchecked(OWNER1), token.to_owned(), &msg, &[]) - .unwrap(); -} - -fn check_token_balance(app: &mut App, token: &Addr, address: &Addr, expected: u128) { - let msg = Cw20QueryMsg::Balance { - address: address.to_string(), - }; - let res: StdResult = app.wrap().query_wasm_smart(token, &msg); - assert_eq!(res.unwrap().balance, Uint128::from(expected)); -} - -fn query_token_balance(app: &mut App, token: &Addr, address: &Addr) -> Uint128 { - let msg = Cw20QueryMsg::Balance { - address: address.to_string(), - }; - let res: BalanceResponse = app.wrap().query_wasm_smart(token, &msg).unwrap(); - - res.balance -} - -fn claim_and_check( - app: &mut App, - who: &Addr, - vesting: &Addr, - astro_token: &Addr, - expected_amount: u128, -) { - app.execute_contract( - who.clone(), - vesting.clone(), - &ExecuteMsg::Claim { - recipient: None, - amount: None, - }, - &[], - ) - .unwrap(); - let astro_amount = query_token_balance(app, &astro_token, &who); - assert_eq!(astro_amount.u128(), expected_amount); -}